ข้ามไปยังเนื้อหา

Facade

Facade ให้ interface เดียวที่รวมเป็นหนึ่งเดียวกับชุดของ interface ใน subsystem มอบจุดเข้าใช้งานที่ง่ายตัวเดียวให้ client แทนที่จะบังคับให้พวกเขาต้องจัดการ class ที่ทำงานร่วมกันจำนวนมากด้วยตัวเอง

subsystem ที่ทำงานได้เยอะมักมีชิ้นส่วนเคลื่อนไหวเต็มไปหมด แค่จะแปลงไฟล์วิดีโอสักไฟล์ คุณอาจต้องใช้ decoder, audio mixer, ตัวเลือก codec, ตัวคำนวณ bitrate และ writer แต่ละตัวมีการตั้งค่า กฎการจัดลำดับ และความซับซ้อนของตัวเอง

client ที่แค่อยาก “เปลี่ยนไฟล์นี้เป็น MP4” ไม่ควรต้องมาเรียนรู้ทั้งห้า class สร้าง instance ให้ถูกลำดับ แล้วต่อ output เข้าหากันเอง ความรู้ระดับนั้นทำให้ไส้ในของ subsystem รั่วไปถึงผู้เรียกทุกราย และทำให้ subsystem แก้ยากขึ้นไปอีก

Facade คือ class ตัวเดียวที่จำท่าเต้นทั้งชุดนี้ได้ โดยเปิด method เล็ก ๆ ที่เน้นงานออกมา เช่น convert(file, format) แล้วข้างในค่อยสร้างและสั่งงาน object ของ subsystem ตามลำดับที่ถูกต้อง ฝั่ง client จึงขึ้นต่อ facade อย่างเดียว

class ของ subsystem ยังเรียกตรงได้เต็มที่สำหรับคนที่ต้องการควบคุมละเอียด แต่เคสทั่วไปเหลือแค่การเรียกครั้งเดียว และเวลา refactor subsystem คุณก็แก้ที่ facade จุดเดียว ไม่ต้องไล่แก้จุดเรียกใช้เป็นร้อย

classDiagram
  class Client
  class VideoConverter {
    +convert(file, format) string
  }
  class Decoder {
    +decode(file) string
  }
  class AudioMixer {
    +mix(stream) string
  }
  class Encoder {
    +encode(stream, format) string
  }
  Client --> VideoConverter : uses
  VideoConverter --> Decoder
  VideoConverter --> AudioMixer
  VideoConverter --> Encoder
facade วางอยู่หน้า class หลายตัวของ subsystem ไว้หลัง method เดียว
  • Facade (VideoConverter) — class เดียวที่ client คุยด้วย รู้วิธีประสานงาน subsystem ทั้งหมด
  • Subsystem classes (Decoder, AudioMixer, Encoder) — ตัวทำงานจริง ไม่รู้จัก facade เลย และยังเรียกใช้ตรง ๆ ได้
  • Client — เรียก facade และยังคงถูกแยกออกจากโครงสร้างของ subsystem

facade VideoConverter เปลี่ยน request บรรทัดเดียวให้เป็นลำดับที่ถูกต้องของการเรียก decode, mix และ encode

class Decoder {
decode = (file: string) => `raw(${file})`;
}
class AudioMixer {
mix = (stream: string) => `mixed(${stream})`;
}
class Encoder {
encode = (stream: string, format: string) => `${stream}.${format}`;
}
// Facade: one method hides the subsystem choreography.
class VideoConverter {
private decoder = new Decoder();
private mixer = new AudioMixer();
private encoder = new Encoder();
convert(file: string, format: string): string {
const raw = this.decoder.decode(file);
const mixed = this.mixer.mix(raw);
return this.encoder.encode(mixed, format);
}
}
const converter = new VideoConverter();
console.log(converter.convert('clip.avi', 'mp4')); // mixed(raw(clip.avi)).mp4
  • ข้อดี: ย่อ subsystem หลาย class ให้เหลือจุดเข้าใช้งานที่ง่ายตัวเดียวสำหรับกรณีทั่วไป
  • ข้อดี: แยก client ออกจากภายในของ subsystem ดังนั้น subsystem จึงสามารถ refactor ได้อย่างอิสระ
  • ข้อดี: ผู้เรียกใช้ขั้นสูงยังเข้าถึง subsystem โดยตรงได้ facade เป็นทางเลือก ไม่ใช่กำแพง
  • ข้อเสีย: facade อาจไหลไปเป็น god object ที่รู้มากเกินไป ถ้าคุณเอาแต่กอง feature ใส่เข้าไปเรื่อย ๆ
  • ข้อเสีย: อาจบังความยืดหยุ่นที่มีประโยชน์ไว้ ทำให้ client เลือกทางง่าย ทั้งที่งานจริงต้องการทางที่ควบคุมได้ละเอียดกว่า
  • Adapter เปลี่ยน interface ของ object หนึ่งให้ตรงกับตัวที่คาดหวัง ส่วน Facade คิดค้น interface ที่เรียบง่ายตัวใหม่เอี่ยมครอบ object จำนวนมาก
  • Abstract Factory สามารถอยู่เบื้องหลัง facade เพื่อสร้าง object ของ subsystem ที่ facade ประสานงาน
FacadeAdapterMediator
จุดประสงค์ให้ interface ง่ายต่อ subsystem ที่ซับซ้อนแก้ interface ที่ไม่ตรงกันระหว่างสองฝั่งประสาน object หลายตัวให้สื่อสารกัน
จำนวน class ที่ห่อหลาย classหนึ่ง class (adaptee)หลาย class (peers)
ซ่อน subsystemใช่ (เลือกได้)ไม่ (เพียงแปลง interface)ไม่ (ยังสื่อสารผ่าน mediator)
ตัวอย่างaxios, AWS SDKlegacy API wrapperevent bus, chat room
Facade ให้อะไร?
class ของ subsystem รู้จัก facade หรือไม่?
Facade แตกต่างจาก Adapter อย่างไร?
ความเสี่ยงของการใช้ Facade มากเกินไปคืออะไร?