JavaScript Design Pattern ที่ต้องรู้ไว้ใช้งาน

NSLog0
NSLog0
Feb 1 · 4 min read

โพสเก่าจาก Blog เดิม มีนาคม 29, 2016

Image for post
Image for post

ช่วงนี้ JavaScript กำลังมาแรงจริงๆ ผมเลยต้องขยันเขียนเกี่ยวกับการเขียนเว็บที่ผมมีมาสอนกันให้ทันตามเทรนของ JavaScript เลย แน่นอนว่าต้องมี Full Stack Developer ที่มือใหม่ยังไม่เข้าใจถึงหลักการเขียน JavaScript แน่นอน เพราะว่ามันก็ไม่ใช้ภาษาอะไรจะง่ายแบบที่ทุกคนพูดกัน ถ้าจะเขียนให้ดีเพราะความเป็นภาษาแบบ Script ทำให้มันเขียนได้หลายรูปแบบและไม่ค่อยจะมีหลักการ โค้ดบางอย่างไม่คิดว่าจะทำงานได้ก็กับทำงานได้สะงั้น เอาละเรามาดูวิธีการเขียน Design Pattern ที่จำเป็นต่อการทำงานจริงๆ กันเลยครับ

ในบทความนี้จะมาอธิบาย Design Pattern ต่อไปนี้

  • Module
  • Prototype
  • Observer
  • Singleton

Module Design Pattern

ต้องบอกได้เวลาว่า Pattern ตัวนี้เป็นตัวที่ถูกใช้เยอะมากเพราะ JavaScript Module ส่วนมากจะเขียนด้วยตัวนี้ครับ มันช่วยแยกโค้ดออกเป็นส่วนๆ แถมยังทำให้โครงสร้างโค้ดดูสวยอีกด้วย

ก่อนจะไปต่อผมอยากจะยกตัวอย่างภาษาอื่นอย่าง Java เราซึ่งใน Java เรามีกระบวนการที่เรียกว่า encapsulation คือห่อหุ่มตัว Properties ไว้ใน class เพื่อไม่ให้ส่วนอื่นๆ เข้ามาเรียกใช้งานได้ คราวๆ ก็ประมาณนี้ ซึ่งตัว Module Pattern เรามันก็จะมีกระบวนการทำ private, public ซึ่งเลียนแบบมาจากพวกภาษา OOP

ในการเขียน Module Pattern เราจะมีการใช้เทคนิคที่ชื่อว่า Immediately-Invoked-Function-Expressions (IIFE) ก่อนจะไปต่อมาทำความเข้าใจ IIFE กันก่อน

James Padolsey บอกเอาไว้ว่า

An IIFE protects a module’s scope from the environment in which it is placed.

ก็ประมาณว่า IIFE มันเอาไว้กำหนดขอบเขตการทำงานของโมดูล ในสภาพแวดล้อมที่มันถูกสร้างขึ้นมา หรือป้องการถูกเรียกจากฟังก์ชันอื่นๆ ประมาณนั่นครับ วิธีการเขียนก็แบบนี้

(function(){   //code}());

ในโค้ดประกอบด้วยสองส่วนคือ

Anonymous Function คือการตั้งฟังก์ชันไม่มีชื่อ และมีวงเล็บต่อท้ายเพื่อทำฟังก์ชันทำงานทันที่ระบบเราทำงานมาถึงบรรทัดนี้

function () {}();

และ Syntax Parser คือเอาวงเล็บมาครอบจะทำให้ Syntax ไม่ Error เขาเรียก Syntax Parser

( .... )

ขอจบเรื่อง IIEF ไว้ก่อนไว้ครั่งหน้าผมจะมาเจาะลึกให้ครับ เรามาดูวิธีการเขียน Module Pattern เลยครับ Syntax จะเป็นแบบนี้

(function() {    // private variables or functions    return {
// public variables or functions
}
})();

อธิบายโค้ด ตัวส่วนที่ผมคอมเม้นว่า private variables or functions อะไรที่เราก็ตามตั้งขึ้นมามันจะเป็น private ไปโดยปริยายครับ และจะถูกเรียกใช้ได้ในเฉพาะส่วนของ IIEF เท่านั่น และส่วนที่เขียนว่า public variables or functions คือส่วนที่เราต้องการจะให้โค้ดเราเป็น public คือให้เรียกมาจากข้างนอกได้นั่นเองครับ เราก็แค่จะ return มันออกมาเท่านั่นเองครับ นี่แหละหลักการของ Design Pattern ตัวนี้ครับ เดียวลองมาดตัวอย่างโปรแกรมข้างล่าง

var foo= (function() {
var msg= 'Hi Algorithmtut'
var inner = function() {
console.log(msg);
}
return {
callFn: inner
};
})();foo.callFn(); // Hi Algorithmtut
console.log(foo.msg); // undefined (call a private property dose not allow here)

วิธีการเขียนก็ง่ายๆ แบบที่เห็น ผมคงไม่อธิบายอะไรเพิ่มเติม แต่ลองดูตรงสองบรรทัดสุดท้ายผมเรียก callFn() ตรงนี้ทำงานได้ปกติเพราะเรา return ค่าออกมาทำให้ฟังก์ชันเราเป็น public และอีกบรรทัดผมลองเรียก msg ซึ่งแน่นอนว่ามันบอกว่า undefined เพราะว่ามันหา msg ไม่เจอเพราะไม่อยู่ใน scope หากใครยังไม่รู้เรื่อง scope ผมมีอธิบายไว้ครับไปอ่านได้

สำหรับกระบวนการที่เอา private properties ออกมาให้เรียกใช้ได้นั้นเขาเรียกว่า Revealing Module Pattern ซึ่งเราจะเห็นจากโค้ดด้านบนตรงส่วนของ return { } เดียวเอาลองมาดูอีกสักตัวอย่าง

var bar= (function() {
var private_num = 0;
var privateMethod = function() {
console.log('This is a private scope');
private_num ++;
}
var public_fn1= function() {
console.log('This is a public function');
}
var public_fn2 = function() {
privateMethod();
}
return {
fn1: public_fn1,
fn2: public_fn2
};
})();
bar.fn1(); // This is a public function
bar.fn2(); // This is a private scope
bar.public_fn1; // undefined

Prototype Design Pattern

เป็นอีก Best Practice นึงที่ชาว Java Script ต้องใช้นะครับ ผมแนะนำว่าคุณควรจะต้องอ่านและควรนำไปใช้ เพราะ Design Pattern ตัวจะช่วยปัญหาของโค้ดที่เรียกว่า function spaghetti code โดยสรุปคราวๆ คือการที่เราเขียนฟังชันก์ขึ้นมาทุกๆ การทำงานที่เรานั่นต้องการประมวลผลครับ เช่น เราก็จะเขียนฟังก์ชัน A(),B(), C() ซึ่งทั่งสามฟังก์ชันอาจจะทำงานคล้ายๆ กัน แต่ประมวลผลคนละแบบแต่จำเป็นต้องใช้สามตัวเพื่อให้ได้ผลลัพธ์ของระบบ พอนึกภาพออกไหมครับ ลองมาดูตัวอย่างกันก่อน

//car.jsfunction startCar(){
console.log('Start engine');
}
function stopCar(){
console.log('Stop engine');
}
function car (name) {
return {
name:name,
stopCar:stopCar,
startCar:startCar
}
}
var honda = car('Honda');console.log(honda.name); // Hondahonda.startCar() //Start engine
honda.stopCar() //Stop engine

จากโค้ดข้างบนผมเชื่อว่าทุกคนคงเขียนแบบนี้ ถ้าลูกค้าบอกว่าอยากได้ระบบสตาร์รถ และดับเครื่องรถ ผมเองก็คงทำแบบนี้ นอนว่า JavaScript มันไม่มี Class (แต่มาใน ES6 แล้ว) ที่ทำให้โค้ดของเราอยู่เป็นระบบหรือมองในมุมของ OOP แบบ Java, C#, PHP หรืออื่นๆ พอเราเขียนแบบนี้เยอะ มันทำให้ประสิทธิภาพการทำงานดรอปลงไปด้วยนะครับ เผื่อใครยังไม่รู้มันเกี่ยว Performance ของระบบเลย เพราะเปลื่อง memory มากๆ แถมยังถ้าไล่โค้ดต่อคนอื่น หรือคนอื่นต่อจากเรานี่นรกเลยนะครับ ถ้าหลายๆ บรรทัดเข้า และอีกปัญหาผมเชื่อว่าทุกคนคงเคยเจอคือ scope มันตีกัน ดังนั่นผมเสนอให้ทำ Prototype Pattern ครับ ผมจะลองเปลี่ยนโค้ดข้างบนให้เป็น Prototype ให้ดูนะครับ

//car.jsvar Car = function (name) {
this.name = name;
}
Car.prototype.startCar = function(){
console.log('Start engine', this.name);
}
Car.prototype.stopCar = function(){
console.log('Stop engine', this.name);
}
var honda = new Car('Honda');console.log(honda.name); // Hondahonda.startCar() //Start engine
honda.stopCar() //Stop engine

พอแปลงโค้ดแล้วพอจะดูรู้แล้วใช่ไหมครับว่าฟังก์ชันของ Car ใช่ไหม ไม่ดูแยกชิ้นเหมือนข้างบน เราสามารถเขียนให้มันดูไม่รกและลดโค้ดได้อีกแบบนี้ครับ

//car.jsvar Car = function (name) {
this.name = name;
}
Car.prototype = {
startCar: function(){
console.log('Start engine', this.name);
},
stopCar: function(){
console.log('Stop engine', this.name);
}
}
var honda = new Car('Honda');console.log(honda.name); // Hondahonda.startCar() //Start engine
honda.stopCar() //Stop engine

ดูดีขึ้นมาทันตาเห็นเลยครับ จัดกรุ๊ปให้มัน ถ้าระบบเรามีหลายๆ การทำงานเราก็สามารถมองเป็นแบบ OOP ได้นะครับ เพราะผมจะบอกว่าการทำ prototype มันก็คือการทำ Inheritance นั่นเอง แต่เรามองว่า function คือ class ครับ เวลาเรียกใช้งานเราก็ทำ New Binding เพื่อให้ตรงไปตามหลักการของ OOP เรายังขาดเรื่องการทำ public/private ไปดังนั่นผมขอเสนอคำว่า Revealing Prototype Pattern ลองดูตัวอย่างครับ

//car.jsvar Car = function (name) {
this.name = name;
}
Car.prototype = function() {
var startCar = function(){
console.log('Start engine', this.name);
};
var stopCar = function(){
console.log('Stop engine', this.name);
};
return {
pressStart: startCar,
pressStop: stopCar
}
}();
var honda = new Car('Honda');
honda.pressStart() //Start engine
honda.stopCar() // Uncaught TypeError: honda.stopCar is not a function(…)

ที่นี่เราก็สามารถทำ encapsulation ได้แล้วพอเราเรียกฟังก์ชัน stopCar โปรแกรมเราก็จะ error ทันทีแบบที่เห็น ดังนั่นผมแนะนำให้เขียนแบบนี้นะครับ แนะนำว่าควรเอาไปใช้ที่ทำงานะครับ เพื่อให้ดูเป็นตัวอย่างอีกสักอันถ้าเกิดเราอยากจะเอาไปทำงานกับ HTML ละ ทำยังไง ลองดูบล๊อกนี้เขาเขียนไว้ครับโปรแกรมเครื่องคิดเลขทำด้วย Prototype Pattern ที่นี่

Observer Design Pattern

Design pattern ตัวนี้จะเป็นลักษณะของการทำงานแบบที่นึงเปลี่ยนแปลง หรือส่งค่า ปลายทางรับค่าหรือแสดงผล ยกตัวอย่าง ถ้าเกิดผมมี a.js และ b.js ผมอยากจะส่งค่าจาก a.js ไป b.js แล้วแสดงผล หรือถ้าผมมี A Controller และ B Controller ผมอยากให้ส่งจาก A ไปอัพเดทค่าที่ B แบบนี้เป็นต้น โดยปกติเราจะรู้จัก Pattern ตัวนี้กันอยู่แล้วก็คือ MVC (ผมเขียนอธิบายไว้ตรงนี้) ปกติผมเขียน AngularJs ผมมักจะใช้บ่อยๆ คือการยิง event ข้าม Controller เพื่อส่งค่าหรือสั่งให้ฟังก์ชันทำงานแบบนี้

// Controller 1
$scope.$on('caller event', function(event, args) {
console.log(args.data);
});
...// Controller 2
$scope.caller= function(name) {
$scope.$emit('caller event', {data:'Say hi'});
};

นี่เป็นตัวอย่างของการใช้คำสั่งทำ Observer เพื่อยิงค่าจากที่หนึงไปอีกที่นึง เราจะลองมาเขียนโปรแกรมยอดฮิตของการสอน Observer ก็คือการทำ subscribe list นั่นเอง ตามนี้ครับ

var Public = function() {
this.list = [];
this.addsubscribe = function(observer) {
for(var i = 0; i < observer.length; i++){
this.list.push(observer[i]);
};
},
this.unsubscribe= function(observer) {
var index = this.list.indexOf(observer);
if(index > -1) {
this.list.splice(index, 1);
}
},
this.notifytoclient = function() {
for(var i = 0; i < this.list.length; i++){
this.list[i].notify(i+1);
};
}
};
var Subscribe = function() {
return {
notify: function(index) {
console.log("Client " + index + " is notified!");
}
}
}
var serv= new Public();var client1= new Subscribe ();
var client2= new Subscribe ();
serv.addsubscribe([client1, client2])serv.notifytoclient();//Client 1 is notified!
//Client 2 is notified!

จะเห็นได้จากตัวอย่างเป็นโค้ดง่ายๆ ของการทำระบบ register ผู้ใช้งานในระบบ และทำการส่ง push notification ให้แต่ละครับ เราสามารถเอานำไปปรับใช้ได้หลายแบบมากครับ ลองมาดูอีกสักตัวอย่างจากของฝรั่งกันบ้าง ที่นี่

Singleton Design Pattern

ตัวนี้มีไว้สำหรับจำกัดการสร้าง Object หลายๆ ครั่ง เพื่อไม่ให้ระบบของกิน memory เพิ่มขึ้นโดยไม่จำเป็น เพราะเราจะเช็คว่าถ้าระบบสร้าง Object นี้แล้ว ก็ให้ส่งค่าไปเลยไม่ต้องสร้างใหม่ครับ ลองมาดูตัวอย่างกัน

var PrinterClass = (function(){    var instance;    var action = function() {
function a(){}

function b(){}
}
return {
getInstance: function(){
if (!instance) {
instance = new action();
}
return instance;
}
};
})();
var test = PrinterClass.getInstance();

จากโค้ดด้านบนผมก็แค่เช็คว่า instance ว่ามีค่าอยู่หรือยัง ถ้ายังก็ให้ใส่ค่าลงไปแล้วก็ return กลับออกมา

จริงๆ แล้ว Design pattern ถูกใช้ใน Lib หรือโปรแกรมต่างๆ มากมายครับ เวลาจะใช้ให้คิดดีๆ ก่อนนะครับว่าจะเอามาแก้ปัญหาเรื่องอะไรของระบบ เพราะ Pattern ที่ถูกคิดมาเขาจะเอานำมาแก้ไขปัญหาการเขียนโปรแกรมแบบต่างๆ ครับ ยิ่งถ้าโปรแกรมที่มีขนาดใหญ่ระดับ Enterprise แล้วจะต้องใช้พวกนี้เข้ามาแก้ปัญหาครับ เพราะมันง่ายต่อการไล่โค้ดและคนอื่นทำงานต่อ ซึ่งในบทความนี้ผมก็แนะนำคราวๆ ที่ควรนำไปใช้มำงานกับทีมซึ่งเป็นประโยชน์มากจริงๆ แต่ยังไงก็อย่าลืมวิเคราะห์ก่อนเสมอว่าโครงสร้างระบบของเราเหมาะที่จะใช้ Design pattern หรือไม่ และแก้ปัญหาเรื่องอะไร

AlgorithmTut

May the force be with you. **Tut stand for Tutorial**

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store