ทำไมต้อง Design Patterns ?
ในการเขียนโปรแกรมอะไรขึ้นมาสักอย่างหนึ่งนอกจากการทำให้โปรแกรมใช้งานได้ ก็อาจจะต้องคิดเผื่อไปถึงการพัฒนาต่อ หรือการแก้ไขปรับปรุงในอนาคต ซึ่งทุกคนก็น่าจะรู้กันว่าเวลาที่เขียนโค้ดก็ควรจะเขียนโค้ดให้ดีให้อ่านง่าน แต่ปัญหาก็คือมาตรฐานของโค้ดที่ดีแต่ละคนนั้นไม่เหมือนกัน ถ้าเขียนโค้ดกับเพื่อนก็อาจจะทดลองกันว่าแบบของใครดีกว่าก็อาจจะตกลงกันว่าจะยึดรูปแบบการเขียนแบบไหนได้ แต่มันจะดีกว่ารึเปล่าถ้าเรามีมาตรฐานให้ทำตาม โดยไม่ต้องลองผิดลองถูกขึ้นมาใหม่
แนวคิดของ Design Patterns เป็นยังไง ?
Design Pattern เป็นพิมพ์เขียว (blueprint) สำหรับการออกแบบซอฟต์แวร์ เพื่อให้การเขียนโค้ดได้มาตรฐานเดียวกัน จะได้แก้ปัญหาโค้ดไม่ดีที่นำไปสู่ซอฟต์แวร์ที่ไม่มีคุณภาพได้ โดย design pattern นี้กว่าจะเกิดขึ้นมาแต่ละแบบก็มาจากการทดลองซ้ำๆจนหารูปแบบหรือวิธีที่ดีที่สุดในการแก้ปัญหาแต่ละอย่าง และด้วยความที่มันเป็นรูปแบบที่ใช้แก้ปัญหาต่างๆ เราจึงไม่สามารถที่จะก็อปปี้เอามาใช้งานทันที แต่ต้องทำความเข้าใจของ design pattern นั้นๆและนำไปปรับใช้กับโค้ดของเราเอง
ตัวอย่าง Design Patterns เจ๋ง ๆ 7 Pattern ที่คิดว่ามันแก้ปัญหาเดิม ๆ ได้ดีมาก ๆ
ก่อนอื่นต้องบอกก่อนว่า design pattern นั้นเค้าแบ่งออกได้เป็น 3 กลุ่มที่มีเป้าหมายต่างกันออกไป ประกอบด้วย
- Creational patterns – เป็นกลุ่มที่ไว้ใช้สร้าง object ในรูปแบบต่างๆ ให้มีความยืดหยุ่น(flexible) และนำโค้ดมาใช้ซ้ำ(reuse)ได้
- Structural patterns – กลุ่มนี้จะเป็นวิธีการนำ object และ class มาใช้งานร่วมกัน สร้างเป็นโครงสร้างที่มีความซับซ้อนยิ่งขึ้น โดยที่ยังมีความยืดหยุ่นและทำงานได้อย่างมีประสิทธิภาพ
- Behavioral patterns – กลุ่มสุดท้ายนี้เป็นวิธีการออกแบบการติดต่อกันระกว่าง object ให้มีความยืดหยุ่นและสามารถติดต่อกันกันได้อย่างไม่มีปัญหา
สนใจหัวข้อไหนกดอ่านได้เลย
1. Factory Method
2. Builder
3. Singleton
4. Adapter
5. Facade
6. Observer
7. Template Method
1. Factory Method
![](https://b2dmain-ruk.cdn.jelastic.net/wp-content/uploads/2020/04/patrick-tomasso-GXXYkSwndP4-unsplash-1024x768.jpg)
อยู่ในกลุ่ม Creational patterns ดังนั้นพอแปลตามชื่อด้วยแล้ว Factory Method นี้ก็จะเป็นโรงงานสำหรับผลิต object นั่นเอง โดยมีเป้าหมายเพื่อให้การสร้าง object สามารถสร้าง oject หลายรูปแบบได้ โดยที่ตอนเรียกใช้ ใช้วิธีที่เหมือนกันได้ ลองดูตัวอย่างอย่างการสั่งเครื่องดื่มกัน
สมมติให้โรงงานในที่นี้คือพนักงานในร้าน และ object ก็คือเครื่องดื่มต่างๆภายในร้าน ตอนที่ลูกค้าสั่งเครื่องดื่มนั้น ไม่ว่าจะต้องการกาแฟ, ชาเขียว หรืออะไรก็แล้วแต่ สิ่งที่ต้องนั้นเหมือนกันทั้งหมดคือการบอกกับพนักงานและก็จะได้เครื่องดื่มนั้นๆหลับมา โดยไม่ต้องสนใจกระบวนการใรการชงเครื่องดื่มเลย ดังนั้นประโยชน์ในการนำ pattern นี้ไปใช้ก็คือลดความซับซ้อนในการสร้าง Object ลงได้ และถ้ามีการเพิ่มเติมชนิดของ object ก็ไม่กระทบกับวิธีการสร้างให้วุ่นวาย
Main.java
class Main {
public static void main(String[] args) {
Barista barista = new Barista();
// สั่งกาแฟกับบาริสต้า
Beverage order1 = barista.order("coffee");
// สั่งให้ทำมาให้ก็จะได้กาแฟออกมา
order1.brew();
// สั่งชาเขียวกับบาริสต้า
Beverage order2 = barista.order("greentea");
// สั่งเหมือนกับเครื่องดื่มอื่นๆ
order2.brew();
// สั่งโคล่ากับบาริสต้า
Beverage order3 = barista.order("cola");
// สั่งเหมือนๆกันกับเครื่องดื่มอื่น
order3.brew();
}
}
Barista.java
public class Barista {
public Beverage order(String beverageType) {
if (beverageType.equals("coffee")) {
return new Coffee();
} else if (beverageType.equals("greentea")) {
return new Greentea();
} else if (beverageType.equals("cola")) {
return new Cola();
}
return null;
}
}
Beverage.java
public interface Beverage {
void brew();
}
Coffee.java
public class Coffee implements Beverage {
@Override
public void brew() {
make();
System.out.println("กาแฟได้แล้วครับ");
}
private void make() {
String doSomeThing = "ฉีกซองเทใส่แก้วเติมน้ำร้อน";
}
}
Cola.java
public class Cola implements Beverage {
@Override
public void brew() {
openBottle();
System.out.println("โคล่าได้แล้วครับ");
}
private void openBottle() {
String doSomeThing = "เปิดขวดแล้วเทใส่แก้ว";
}
}
Greentea.java
public class Greentea implements Beverage {
@Override
public void brew() {
System.out.println("ชาเขียวได้แล้วครับ");
}
private void collectingTeaLeaves() {
String doSomeThing = "เก็บยอดอ่อนใบชาจากความสูง 1200 เมตร เหนือน้ำทะเล";
}
}
Output:
กาแฟได้แล้วครับ
ชาเขียวได้แล้วครับ
โคล่าได้แล้วครับ
2. Builder
![](https://b2dmain-ruk.cdn.jelastic.net/wp-content/uploads/2020/04/photo-1562565651-7d4948f339eb-1024x576.jpg)
อยู่ในกลุ่ม Creational patterns เช่นกัน ลองมาดูปัญหากันก่อนว่าทำไมถึงมี pattern นี้ขึ้นมา ยกตัวอย่างการสั่งอาหารตามสั่งอย่างผัดกะเพรา ที่สมมุติให้มีวัตถุหลักคือพริก, กระเทียม, เนื้อสัตว์ และน้ำปลา ซึ่งเวลามีคนมาสั่งแม่ค้าก็จะทำให้เราโดยใช้ Builder pattern นี้อยู่ตลอด !
สมมุติมีลูกค้าสองคนคนนึงสั่งกะเพราหมูสับ อีกคนสั่งกะเพราไก่ ถ้าแม่ค้าร้านนี้ไม่ใช้ Builder เวลาลูกค้าคนแรกสั่งก็จะต้องสั่งว่า ‘เอากะเพราหมูสับ ใส่กระเทียม พริก น้ำปลา’ ลูกค้าคนที่สองมาสั่งก็จะต้องสั่งว่า ‘กะเพราไก่ใส่กระเทียม ไม่ใส่พริก ใส่น้ำปลา’ ซึ่งในความเป็นขริงเราไม่ต้องสั่งแบบนั้นด้วย Builder ที่แม่ค้าใช้อยู่ แม่ค้าจะทำกะเพราให้กับเราด้วยวัตถุดิบหลักที่กำหนดเอาไว้ ส่วนใครที่อยากระบุอะไรที่เฉพาะเจาะจงก็ค่อยบอกเท่าที่จำเป็นพอ ลูกค้าทั้งสองคนก็เพียงสั่งแค่ ‘กะเพราหมูสับ’ กับ ‘กะเพราไก่ไม่ใส่พริก’ ก็พอ ถ้าเป็นการสร้าง class ในโค้ดแล้วก้จะลดความยาว ความยุ่งยากในการสร้างลงไปได้มากๆตัวอย่าง class HolyBasil(กะเพรา) ที่ใช้ builder pattern โดยจะไม่ต้องสร้าง setter เหมือนปกติ แต่จะใช้ ฺBuilder class ที่เราสร้างขึ้นมาแทน
HolyBasil.java
public class HolyBasil {
private final String meat; // จำเป็นต้องใส่
private final String fishSauce; // ไม่จำเป็น
private final String garlic; // ไม่จำเป็น
private final String chili; // ไม่จำเป็น
private HolyBasil(HolyBasilBuilder builder) {
this.meat = builder.meat;
this.fishSauce = builder.fishSauce;
this.garlic = builder.garlic;
this.chili = builder.chili;
}
//ต้องมี getter แต่ไม่ต้องมี setter
public String getMeat() {
return meat;
}
public String getfishSauce() {
return fishSauce;
}
public String getgarlic() {
return garlic;
}
public String getchili() {
return chili;
}
@Override
public String toString() {
String order = "HolyBasil "+this.meat + ", fishSauce:"+this.fishSauce+", garlic:"+this.garlic+", chili:"+this.chili;
return order;
}
}
HolyBasilBuilder.java – Builder ที่ทำหน้าที่เป็น setter
public static class HolyBasilBuilder {
private final String meat;
private String fishSauce;
private String garlic;
private String chili;
public HolyBasilBuilder(String meat) {
this.meat = meat;
}
public HolyBasilBuilder fishSauce(String fishSauce) {
this.fishSauce = fishSauce;
return this;
}
public HolyBasilBuilder garlic(String garlic) {
this.garlic = garlic;
return this;
}
public HolyBasilBuilder chili(String chili) {
this.chili = chili;
return this;
}
//object สุดท้ายที่จะส่งออกไป
public HolyBasil build() {
HolyBasil holyBasil = new HolyBasil(this);
return holyBasil;
}
}
Main.java – การเรียกใช้
class Main {
public static void main(String[] args) {
HolyBasil dish1 = new HolyBasil.HolyBasilBuilder("Pork")
.fishSauce("นิดเดียว")
.garlic("เยอะๆ")
.chili("ไม่พริก")
.build();
System.out.println(dish1);
HolyBasil dish2 = new HolyBasil.HolyBasilBuilder("Chicken")
.chili("ขอเผ็ดๆ")
.build();
System.out.println(dish2);
HolyBasil dish3 = new HolyBasil.HolyBasilBuilder("Shimp")
.build();
System.out.println(dish3);
}
}
ผลลัพธ์ที่ออกมา:
HolyBasil Pork, fishSauce:นิดเดียว, garlic:เยอะๆ, chili:ไม่พริก
HolyBasil Chicken, fishSauce:null, garlic:null, chili:ขอเผ็ดๆ
HolyBasil Shimp, fishSauce:null, garlic:null, chili:null
เท่านี้เวลาเราสั่งแม่ค้าก็จะไม่ต้องสั่งถึงวัตถุดิบทั้งหมดสามารถสั่งแค่กะเพรากุ้ง แม่ค้าก็จะทำกะเพรากุ้งโดยที่ใส่อย่างอื่นตามสูตรของแม่ค้า
3. Singleton
![](https://b2dmain-ruk.cdn.jelastic.net/wp-content/uploads/2020/04/photo-1567936996693-b374b6b88c1c-1024x683.jpg)
อีกสักตัวนึงในกลุ่ม Creational patterns สำหรับตัวนี้จากชื่อแล้วก็น่าจะพอเดาได้ว่าเกี่ยวกับอะไรสักอย่างที่มันเดี่ยวๆ มีอันเดียว ในที่นี้ก็คือการจำกัด object ที่สร้างขึ้นมาให้มีเพียงตัวตัว ไม่ถูกสร้างซ้ำจนเกิดความซ้ำซ้อน หรือทำงานผิดพลาด มาลองดูตัวอย่างจากในชีวิตประจำวันของเรากันดีกว่า
สมมติให้มีห้องน้ำอยู่ห้องหนึ่งแต่ยังไม่ได้ใส่ประตู พอมีคนจะใช้ห้องน้ำก็สร้างประตูขึ้นมา 1 บานแล้วเข้าไปใช้งาน พอคนถัดไปจะมาใช้ห้องน้ำก็สร้างประตูใส่เข้าไปเพิ่มอีกแล้วเข้าใช้ พอคนต่อๆไปมาใช้ก็สร้างประตูเพิ่มเข้าไปอีก นานๆไปก็มีประตูติดอยู่ที่ห้องน้ำเต็มไปหมด ตอนล็อกประตูก็ยังมีประตูอื่นๆเต็มไปหมด ใช้งานห้องน้ำไม่ได้สักที เทียบเป็นโค้ดก็เช่นการที่เราไม่ควบคุมการสร้าง object ใหม่จากคลาสบางคลาส พอเรียกใช้งานก็สร้างใหม่ตลอดจนมี object อยู่ในระบบเยอะเกินไป จะควบคุมการทำงานก็ยากหรืออาจทำให้ทรพยากรต่างๆถูกใช้ไปโดยไม่จำเป็น ดังนั้นจึงมี singleton ขึ้นมาเพื่อไว้ใช้เพื่อคุมกำเนิดให้ class สามารถสร้าง object ได้เพียงตัวเดียว เหมือนกันห้องน้ำที่ถ้าสร้างประตูไว้แล้วก็ใช้ประตูเดิม ไม่ต้องสร้างใหม่ขึ้นมาอีกนั่นเอง
ToiletDoor.java
public class ToiletDoor {
// สร้างประตูที่มีได้แค่อันเดียวเอาไว้
private static ToiletDoor instance = new ToiletDoor();
// กำหนดให้ constructor เป็น private จะได้ไม่มีใครสร้างประตูขึ้นมาได้อีก
private ToiletDoor(){}
// ให้คนที่จะใช้ประตูเรียกใช้ผ่าน getInstance() แล้วส่งประตูที่มีอยู่ไปให้ใช้
public static ToiletDoor getInstance(){
return instance;
}
public void openTheDoor(){
System.out.println("Door is opened.");
}
public void closeTheDoor(){
System.out.println("Door is closed.");
}
}
Main.java
public class Main {
public static void main(String[] args) {
// คนที่จะใช้ประตูก็ใช้ผ่าน getInstance
ToiletDoor toiletDoor = ToiletDoor.getInstance();
// ลองใช้งานประตูได้ปกติ
toiletDoor.openTheDoor();
toiletDoor.closeTheDoor();
// จะไม่สามารถสร้างประตูใหม่แบบนี้ได้เพราะว่า constructor นั้นเป็น private อยู่
// ToiletDoor newToiletDoor = new ToiletDoor();
}
}
Output:
Door is opened.
Door is closed.
4. Adapter
![](https://b2dmain-ruk.cdn.jelastic.net/wp-content/uploads/2020/04/photo-1564517945244-d371c925640b-1024x683.jpg)
คราวนี้เปลี่ยนมาดูในกลุ่ม Structural patterns กันบ้าง อ่านชื่อแล้วก็ยกตัวอย่างได้ง่ายๆจากชีวิตประจำวันของเราเลยก็คือเป็นการออกแบบเพื่อให้เราสามารถใช้งานผ่านตัวแปลงได้ เช่นเรามีปลั๊กสามตาแต่ที่บ้านมีเต้ารับแบบที่เป้น 2 รู ก็ไม่สามารถใช้งานได้ เราจึงซื้อตัวแปลงมาแล้วเสียบผ่านตัวแปลงนั้นแทน
ซึ่งถ้าเราไปต่างประเทศเจอรูปลั๊กที่ต่างออกไปเราก็แค่ปรับเปลี่ยนตัวแปลงของเรา และเสียบใช้งานผ่านตัวแปลงเหมือนเดิม เช่นเดียวกันกับการเขียนโค้ด เราอาจจะใช้งาน library ภายนอกด้วยการสร้าง class มาใช้เป็น adapter ถ้าในอนาคต library นั้นมีการอัพเดตแล้วมีวิธีเรียกใช้งานเปลี่ยนไป เราก็แค่แก้ไขในส่วน adapter ก็พอ ก็สามารถใช้งานโค้ดหลักได้โดยไม่ต้องแก้ไขอะไร
Main.java
interface TypeA {
public void plugTypeA();
}
class OutletA implements TypeA {
public void plugTypeA() {
System.out.println("Plug into type A outlet.");
}
}
interface TypeB {
public void plugTypeB();
}
class OutletB implements TypeB {
public void plugTypeB() {
System.out.println("Plug into type B outlet.");
}
}
// สร้าง adapter ที่ implement type B เพื่อตอนที่ใช้งาน
// พอสร้าง object จากคลาสนี้แล้วจะได้สามารถใช้งานได้เหมือน type B
class B_to_A_Adapter implements TypeB {
TypeA typeA;
// เนื่องจากเราต้องการให้ adapter นี้ใช้ความสามารถจากคลาส type A ได้
// ดังนั้นเราจึงต้องรับ object คลาส type A ตอนที่สร้าง adapter
// เหมือนกับการที่เราต้องเอา adapter แปลงไปเสียบกับปลั๊กที่กำแพง
public B_to_A_Adapter(TypeA typeA) {
this.typeA = typeA;
}
// สร้างการเรียกใช้งานของปลั๊ฏ type B เหมือนกัน adapter ที่จะมีรูหน้าตาเป็นปลั๊ก 3 ขา
// แต่ขาที่จะไปเสียบกำแพงมีแค่ 2 ขา ก็คือเรียกใช้งาน object type a
// เหมือนกับ adapter ที่เสียบกำแพงอยู่
public void plugTypeB() {
typeA.plugTypeA();
}
}
class Main {
public static void main(String args[]) {
// สร้างรูปลั๊กแบบ A และแบบ B
OutletA myPlugTypeA = new OutletA();
OutletB myPlugTypeB = new OutletB();
// สร้าง adapter ที่รูเป็นแบบ B มี 3 รู
// แต่การทำงานจริงๆคือมีขา 2 ขาเพื่อไว้ไปเสียบ type A ได้
TypeB myAdapter = new B_to_A_Adapter(myPlugTypeA);
// เสียบปลั๊ก 2 ขาเข้าไปในรูปลั๊ก 2 รู
System.out.print("My Type A Plug: ");
myPlugTypeA.plugTypeA();
// เสียบปลั๊ก 3 ขาเข้าไปในรูปลั๊ก 3 รู
System.out.print("My Type B Plug: ");
myPlugTypeB.plugTypeB();
// เสียบปลั๊ก 3 ขาเข้าไปในรูปลั๊ก 2 รู ใช้งานได้ปกติ
System.out.print("My Type B Plug: ");
myAdapter.plugTypeB();
}
}
Output:
My Type A Plug: Plug into type A outlet.
My Type B Plug: Plug into type B outlet.
My Type B Plug: Plug into type A outlet.
5. Facade
สำหรับตัวนี้ก็ยังเป็น Structural patterns อยู่ โดย Facade นั้นไว้ใช้แก้ปัญหาการที่ต้องทำงานหลายๆอย่าง ให้สามารถเรียกใช้ได้ด้วยคำสั่งเดียว อย่างเช่นการที่จะเปลี่ยนแพ็คเกจมือถือ
เราอาจไม่ต้องไปหาข้อมูลเอง หารายละเอียด หาเบอร์กดสมัคร แต่ทำได้ด้วยการโทรหา call center ของผู้ให้บริการและ call center จะช่วยจัดการขั้นตอนทั้งหมดให้เราเองตั้งแต่นำเสนอโปรโมชัน บอกรายละเอียดและทำการสมัครให้เรา ในการนำ Facade ไปใช้ในโค้ดนั้นถ้าใช้มากเกินไปก็อาจจะเป็นการทำให้โค้ดมีความซับซ้อนและแก้ไขยากขึ้น ต้องเลือกใช้ให้เหมาะสมด้วยความระมัดระวัง
Facade Pattern จะรวมการทำงานที่มีหลายขั้นตอน และเรียกใช้ด้วยคำสั่งเดียวดังตัวอย่าง
CallCenterFacade.java
public class CallCenterFacade {
public void changePackage(String number, String noPackage) {
System.out.println("ตรวจสอบแพคเกจปัจจุบันของหมายเลข"+number);
System.out.println("ตรวจสอบข้อมูลการชำระเงิน");
System.out.println("ยืนยันสิทธิ์การเปลี่ยนแพคเกจ");
System.out.println("เปลี่ยนแพคเกจเป็นแพคเกจหมายเลข"+noPackage);
System.out.println("การดำเนินการเสร็จสิ้น");
System.out.println("===================");
}
public void review(int score, String comment) {
System.out.println("ขอบคุณที่ไว้ใจในบริการของเรา");
System.out.println("เราจะเก็บข้อมูลไว้เพื่อพัฒนาต่อไป");
}
}
การเรียกใช้ก็สามารถเรียกใช้ด้วยคำสั่งเดียว
Main.java
class Main {
public static void main(String[] args) {
CallCenterFacade userRequest = new CallCenterFacade();
userRequest.changePackage("08XX6XX11", "5");
userRequest.review(10, "งานเร็วงานไวมากครับ");
}
}
ผลลัพธ์ที่ออกมา:
ตรวจสอบแพคเกจปัจจุบันของหมายเลข08XX6XX11
ตรวจสอบข้อมูลการชำระเงิน
ยืนยันสิทธิ์การเปลี่ยนแพคเกจ
เปลี่ยนแพคเกจเป็นแพคเกจหมายเลข5
การดำเนินการเสร็จสิ้น
===================
ขอบคุณที่ไว้ใจในบริการของเรา
เราจะเก็บข้อมูลไว้เพื่อพัฒนาต่อไป
6. Observer
มาถึงตัวรองสุดท้ายกับกลุ่ม Behavioral patterns สำหรับ Observer นี้จะเป็นการออกแบบเพื่อสร้างการติดต่อรับข้อมูลกันแบบ one to many อย่างการกด subscribe ช่อง Youtube สักช่องหนึ่ง
ถ้าหากมีการลงคลิปใหม่ๆก็จะมีการแจ้งเตือนไปยังคนที่กด subscribe เอาไว้ทุกคนนั่นเอง ถ้าเป็นในโค้ดก็เกือบจะเหมือนกันก็คือไว้ใช้ออกแบบเพื่อให้สามารถกระจายข้อมูลไปยังทุก object ที่มา subscribe เอาไว้ได้นั่นเอง
BorntoDevChannel.java – เป็น subject ให้ observer มา subscribe
import java.util.ArrayList;
import java.util.List;
public class BorntoDevChannel {
// สร้าง List ไว้เก็บคนที่ subscribe ช่องเอาไว้
private List<Viewer> subscribers = new ArrayList<Viewer>();
// method สำหรับให้คนมา subscribe
public void subscribe(Viewer viewer){
subscribers.add(viewer);
}
// ส่งการแจ้งเตือนไปยังทุกคนใน list
public void notifyAllSubscriber(String videoName){
for (Viewer viewer : subscribers) {
viewer.update(videoName);
}
}
}
Viewer.java
public abstract class Viewer {
protected BorntoDevChannel channel;
// สร้าง abstract method เพื่อให้ผู้ชมทุกคนที่สืบทอดจากคลาสนี้
// สามารถรับการแจ้งเตือนได้ในรูปแบบเดียวกัน
public abstract void update(String videoName);
}
Jane.java
public class Jane extends Viewer{
public Jane(BorntoDevChannel b2d){
this.channel = b2d;
this.channel.subscribe(this);
}
@Override
public void update(String videoName) {
System.out.println( "เจนค่ะ เจนค่ะ หนูชื่อเจน มากับนุ่นและก็มากับโบว์ และก็จะไปดู '" + videoName + "' ค่ะ");
}
}
Noon.java
public class Noon extends Viewer{
public Noon(BorntoDevChannel b2d){
this.channel = b2d;
this.channel.subscribe(this);
}
@Override
public void update(String videoName) {
System.out.println( "นุ่นค่ะ นุ่นค่ะ หนูชื่อนุ่น มากับเจนและก็มากับโบว์ และก็จะไปดู '" + videoName + "' นะค่ะ");
}
}
Bow.java
public class Bow extends Viewer{
public Bow(BorntoDevChannel b2d){
this.channel = b2d;
this.channel.subscribe(this);
}
@Override
public void update(String videoName) {
System.out.println( "โบว์ค่ะ โบว์ค่ะ หนูชื่อโบว์ มากับนุ่นและก็มากับเจน และก็จะไปดู '" + videoName + "' ด้วยค่ะ");
}
}
Main.java
class Main {
public static void main(String[] args) {
BorntoDevChannel b2d = new BorntoDevChannel();
// สร้างผู้ชมทั้งสามคนพร้อมส่งช่อง BorntoDev ไปให้ Subscribe
new Jane(b2d);
new Noon(b2d);
new Bow(b2d);
// แจ้งเตือนไปยังผู้ชมทุกคนว่ามีวีดีโอใหม่
b2d.notifyAllSubscriber("เริ่มเขียนโปรแกรมด้วย Java ฉบับไว ๆ เข้าใจที่สุดในโลก");
}
}
Output:
เจนค่ะ เจนค่ะ หนูชื่อเจน มากับนุ่นและก็มากับโบว์ และก็จะไปดู 'เริ่มเขียนโปรแกรมด้วย Java ฉบับไว ๆ เข้าใจที่สุดในโลก' ค่ะ
นุ่นค่ะ นุ่นค่ะ หนูชื่อนุ่น มากับเจนและก็มากับโบว์ และก็จะไปดู 'เริ่มเขียนโปรแกรมด้วย Java ฉบับไว ๆ เข้าใจที่สุดในโลก' นะค่ะ
โบว์ค่ะ โบว์ค่ะ หนูชื่อโบว์ มากับนุ่นและก็มากับเจน และก็จะไปดู 'เริ่มเขียนโปรแกรมด้วย Java ฉบับไว ๆ เข้าใจที่สุดในโลก' ด้วยค่ะ
7. Template Method
![](https://b2dmain-ruk.cdn.jelastic.net/wp-content/uploads/2020/04/clem-onojeghuo-B6jl24Z2te0-unsplash-1024x806.jpg)
มาถึงตัวสุดท้ายที่ยกมาในวันนี้กันกับ Template Method ที่อยู่ในกลุ่ม Behavioral patterns อ่านแล้วก็น่าจะคุ้นๆ เพราะมันเป็นการใช้งานจากแบบเวลาที่เราใช้ template class ในภาษาพวก Java หรือ C นั่นเอง เป้าหมายก็เพื่อลดโค้ดที่มีการทำงานซ้ำซ้อนกันในแต่ละคลาส อย่างเช่นถ้าเรามี class สำหรับทำบะหมี่กึ่งสำเร็จรูปกับคลาสสำหรับชงชา
ทั้งสองคลาสจะต้องมีฟังก์ชันในการต้มน้ำกับเติมน้ำลงในภาชนะเหมือนๆกัน แทนที่เราจะเขียนไว้ซ้ำกันในสองคลาสก็สร้างไว้เป็น template class ที่มีฟังก์ชันการต้มน้ำและเติมน้ำเอาไว้ และให้ทั้งสองคลาสนำเอาไปใช้งานร่วมกันได้
ตัวอย่างเราจะสร้าง abstract class ชื่อ CookProcess เป็น templete ของการชงชาและทำบะหมี่โดยมี method คือ boilWater, pourWater, addIngredient และ waitAMinute ที่เรียกใช้โดย method cook โดยการต้มน้ำและการรินน้ำถูกกำหนดการกระทำไว้แล้ว
CookProcess.java
public abstract class CookProcess {
public final void cook() {
boilWater();
addIngredient();
pourWater();
waitAMinute();
}
public abstract void addIngredient();
public abstract void waitAMinute();
private final void boilWater() {
System.out.println("I'm water and I'm boiled.");
}
private final void pourWater() {
System.out.println("Pour water in container");
}
}
ต่อไปสร้าง class Tea ที่ extend CookProcess ขึ้นมาและกำหนดการกระทำของ method addIngredient และ waitAMinute
Tea.java
public class Tea extends CookProcess {
@Override
public void addIngredient() {
System.out.println("add tea bag");
}
@Override
public void waitAMinute() {
System.out.println("Wait 1 minute");
}
}
ทำแบบเดียวกันกับ class Noodle
Noodle.java
public class Noodle extends CookProcess {
@Override
public void addIngredient() {
System.out.println("add noodle and seasoning");
}
@Override
public void waitAMinute() {
System.out.println("Wait 3 minutes");
}
}
การจะชงชาเพียงสร้างobject Tea โดยประกาศเป็นตัวแปร CookProcess ที่ Tea extend มาจากนั้นใช้ method cook() ของ class CookProcess ก็เป็นอันเรียบร้อย
Main.java
class Main {
public static void main(String[] args) {
System.out.println("I'll cook tea");
CookProcess tea = new Tea();
tea.cook();
System.out.println("Tea is delicious!!!");
System.out.println("");
System.out.println("I'll cook noodle");
CookProcess noodle = new Noodle();
noodle.cook();
System.out.println("This noodle is too spicy :(");
}
}
Output:
I'll cook tea
I'm water and I'm boiled.
add tea bag
Pour water in container
Wait 1 minute
Tea is delicious!!!
I'll cook noodle
I'm water and I'm boiled.
add noodle and seasoning
Pour water in container
Wait 3 minutes
This noodle is too spicy :(
สำหรับตัวอย่างที่กล่าวถึงเป็นเพียงส่วนหนึ่งของ design pattern ที่มีอยู่มากมาย และถ้าใครอยากรู้จักกับ design pattern เพิ่มเติม ก็สามารถเข้าไปศึกษาต่อได้ที่ https://refactoring.guru/design-patterns/what-is-pattern สุดท้ายแล้วก็อย่าลืมว่าแต่ละ pattern นั้นมีข้อดีข้อเสียแตกต่างกันออกไป ดังนั้นก่อนที่จะหยิบตัวไหนไปใช้ ควรศึกษาตัวนั้นอย่างละเอียดว่าเหมาะสมกับงานที่เราจะนำไปใช้หรือไม่ เพราะ design pattern เป็นเพียงแนวทางการเขียนโค้ดเท่านั้น การนำไปใช้แบบผิดๆ อาจจะทำให้เราเขียนโค้ดได้ยากขึ้นอย่างไม่เกิดประโยชน์ก็เป็นได้