Design Pattern 101 — Facade Pattern
Facade Pattern เป็น design pattern อีกตัวในกลุ่ม Structural Pattern ของ GoF ที่ทำให้เราสามาถซ้อนการใช้งานระบบที่ซับซ้อนได้ด้วย interface ที่ง่ายๆ ในบทความนี้จะมาทบทวนและเรียนรู้ว่า Facade Pattern แก้ปัญหาอะไรบ้าง
Facade Pattern
Problem
ถ้าเรามีระบบ Home Theater ในบ้าน ซึ่งระบบประกอบด้วย DVD Player, Projection Video System, Automated Screen, Surround Sound และแม้แต่ Popcorn Popper
จะเห็นได้ว่าระบบมีเครื่องใช้ไฟฟ้าจำนวนมาก มีขั้นตอนในการดูหนัง ดังนี้
- เปิด Projector
- ติดตั้ง Projector เข้ากับ DVD
- เปลี่ยน Projector เป็นโหมด wide-screen
- เปิดเครื่องเสียง
- ติดตั้งเครื่องเสียงเข้ากับ DVD
- ตั้งค่าเครื่องเสียงเป็น Surround sound
- หมุน volume เครื่องเสียงไปที่ระดับ 5
- เปิดเครื่องเล่น DVD
- เริ่มเล่น DVD
เมื่อเอามา map เข้ากับ method ของ device ต่างๆ จะได้ ประมาณนี้
projector.on(); // เปิด Projector
projector.setInput(dvd); // ติดตั้ง Projector เข้ากับ DVD
projector.wideScreenMode(); // เปลี่ยน Project เป็นโหมด wide-screen
amp.on(); // เปิดเครืองเสียง
amp.setDvd(dvd); // ติดตั้งเครื่องเสียงเข้ากับ DVD
amp.setSurroundSound(); // ตั้งค่าเครื่องเสียงเป็น Surround sound
amp.setVolume(5); // หมุน volume เครื่องเสียงไปที่ระดับ 5
dvd.on(); // เปิดเครื่องเล่น DVD
dvd.play(movie); // เริ่มเล่น DVD
แค่ขั้นตอนการเปิดดูหนังใน DVD ก็ยังมีขั้นตอนเยอะมากขนาดนี้ และถ้าเราจะปิดล่ะ หรือ จะเปิด CD หรือจะฟังวิทยุ ก็จะมีขั้นตอนซับซ้อน (Complexity) มาก
หรือถ้าเราจะ update ระบบเครื่องเสียใหม่ล่ะ เราจะต้องเรียนรู้ขั้นตอนวิธีใหม่ใช่ไหม??
Solution
ถึงเวลาที่จะเอา Facade Pattern มาใช้จัดการระบบซับซ้อน (Complex Subsystem) และทำให้ใช้งานง่ายจากฝั่งของผู้ใช้งานระบบ โดยใช้ Facade class ที่จะมี interface ที่ใช้งานงาน และในส่วนของ implementation จะไปใช้ขั้นตอนที่ซับซ้อนเอง
ใน interface HomeTheater
จะมี method ที่เรียกใช้งานระบบที่ซับซ้อนด้วย method watchMovie()
และ endMovie()
public interface HomeTheater {
void watchMovie(String movie);
void endMovie();
}
class HomeTheaterFacade
ซึ่ง implement HomeTheater
โดยที่ใน constructor จะ inject object ของ device ต่างๆ (Subsystem) เข้ามา และ method watchMovie
จะซ้อนการใช้งาน device ต่างๆ ที่ซับซ้อน และ endMovie
ก็เป็นการปิดใช้งาน device ต่างๆ
public class HomeTheaterFacade implements HomeTheater {
Amplifier amp;
Tuner tuner;
DvdPlayer dvd;
CdPlayer cd;
Projector projector;
public HomeTheaterFacade(Amplifier amp, Tuner tuner,
DvdPlayer dvd, CdPlayer cd,
Projector projector) {
this.amp = amp;
this.tuner = tuner;
this.dvd = dvd;
this.cd = cd;
this.projector = projector;
}
@Override
public void watchMovie(String movie) {
System.out.println("Get ready to watch a movie...");
projector.on();
projector.setInput(dvd);
projector.wideScreenMode();
amp.on();
amp.setDvd(dvd);
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play(movie);
}
@Override
public void endMovie() {
System.out.println("Shutting movie theater down...");
projector.off();
amp.off();
dvd.stop();
dvd.eject();
dvd.off();
}
}
output
Get ready to watch a movie...
Projector on
Projector in Wide Screen mode (16x9 aspect ratio)
Amplifier on
Amplifier setting DVD player
Amplifier surround sound on (5 speakers, 1 subwoofer)
Amplifier setting volume to 5
DVD Player on
DVD Player play movie "Raiders of the Lost Ark"
Shutting movie theater down...
Projector off
Amplifier off
DVD Player stopped "Raiders of the Lost Ark"
DVD Player eject
DVD Player off
Facade Pattern มี unified interface เพื่อเชื่อมต่อกับ subsystem ใน Facade จะกำหนด interface ในระดับสูง ที่ทำให้ subsystem ง่ายต่อการใช้งาน
ซึ่งตรงกับหลักการ Least Knowledge ที่จะทำให้เราลดการปฎิสัมพันธ์ (Interaction) ระหว่าง object เพื่อมีเพียง object ใกล้เคียง
สรุป
จากที่ได้ทบทวนและเรียนรู้การใช้งาน Facade Pattern ทำให้ถ้าเรามีระบบที่ใช้งานซับซ้อน เราสามาถสร้าง interface ที่เชื่อมต่อและใช้งาน subsystem หรือระบบย่อย ทำให้ client สามาถใช้งาน subsytem ที่ซับซ้อนผ่านทาง Facade class ได้เลย ซึ่งทำให้เราซ้อนการเรียกใช้งานที่ซับซ้อนด้วยเพียง method เดียว 😆