Facade Pattern là một trong những Pattern thuộc nhóm cấu trúc (Structural Pattern). Mẫu thiết kế này cung cấp một giao diện chung đơn giản thay cho một nhóm các giao diện có trong một hệ thống phức tạp. Hệ thống phức tạp này có thể là 1 thư viện (library), 1 framework, hay tập hợp các class phức tạp. Facade Pattern định nghĩa một giao diện ở một cấp độ cao hơn để giúp cho người dùng có thể dễ dàng sử dụng hệ thống này.
Mục đích của Facade Pattern là che giấu sự phức tạp của hệ thống con đằng sau. Thông qua Facade các hoạt động trở nên dễ dàng hơn.
Facade Pattern là mẫu thường thấy trong các thư viện phức tạp của PHP. Việc này giúp các lập trình viên dễ dàng tạo ra ứng dụng web mà không cần hiểu quá sâu về hệ thống.
Đằng sau một hệ thống phức tạp là một giao diện đơn giản cho người dùng.
Hãy tưởng tượng rằng bạn phải làm cho mã của mình hoạt động với một tập hợp lớn các đối tượng thuộc về một thư viện hoặc khuôn khổ phức tạp. Thông thường, bạn sẽ cần khởi tạo tất cả các đối tượng đó, theo dõi các phụ thuộc, thực thi các phương thức theo đúng thứ tự, v.v.
Do đó, logic nghiệp vụ của các lớp của bạn sẽ được liên kết chặt chẽ với các chi tiết triển khai của các lớp của bên thứ 3, khiến việc hiểu và duy trì trở nên khó khăn.
Facade là một lớp cung cấp giao diện đơn giản cho một hệ thống con phức tạp chứa nhiều thành phần chuyển động. Facade có thể cung cấp chức năng hạn chế so với việc làm việc trực tiếp với hệ thống con. Tuy nhiên, nó chỉ bao gồm những tính năng mà khách hàng thực sự quan tâm.
Facade sẽ rất tiện lợi khi bạn cần tích hợp ứng dụng của mình với một thư viện phức tạp có hàng chục tính năng, nhưng bạn chỉ cần một chút chức năng của nó.
Facade giống như một bộ điều chỉnh đơn giản cho một số hệ thống phức tạp. Trong ví dụ này, Facade ẩn sự phức tạp của API YouTube và FFmpeg.
Ví dụ 1:
<?php namespace RefactoringGuru\Facade\Conceptual; /** * The Facade class provides a simple interface to the complex logic of one or * several subsystems. The Facade delegates the client requests to the * appropriate objects within the subsystem. The Facade is also responsible for * managing their lifecycle. All of this shields the client from the undesired * complexity of the subsystem. */ class Facade { protected $subsystem1; protected $subsystem2; /** * Depending on your application's needs, you can provide the Facade with * existing subsystem objects or force the Facade to create them on its own. */ public function __construct( Subsystem1 $subsystem1 = null, Subsystem2 $subsystem2 = null ) { $this->subsystem1 = $subsystem1 ?: new Subsystem1(); $this->subsystem2 = $subsystem2 ?: new Subsystem2(); } /** * The Facade's methods are convenient shortcuts to the sophisticated * functionality of the subsystems. However, clients get only to a fraction * of a subsystem's capabilities. */ public function operation(): string { $result = "Facade initializes subsystems:\n"; $result .= $this->subsystem1->operation1(); $result .= $this->subsystem2->operation1(); $result .= "Facade orders subsystems to perform the action:\n"; $result .= $this->subsystem1->operationN(); $result .= $this->subsystem2->operationZ(); return $result; } } /** * The Subsystem can accept requests either from the facade or client directly. * In any case, to the Subsystem, the Facade is yet another client, and it's not * a part of the Subsystem. */ class Subsystem1 { public function operation1(): string { return "Subsystem1: Ready!\n"; } // ... public function operationN(): string { return "Subsystem1: Go!\n"; } } /** * Some facades can work with multiple subsystems at the same time. */ class Subsystem2 { public function operation1(): string { return "Subsystem2: Get ready!\n"; } // ... public function operationZ(): string { return "Subsystem2: Fire!\n"; } } /** * The client code works with complex subsystems through a simple interface * provided by the Facade. When a facade manages the lifecycle of the subsystem, * the client might not even know about the existence of the subsystem. This * approach lets you keep the complexity under control. */ function clientCode(Facade $facade) { // ... echo $facade->operation(); // ... } /** * The client code may have some of the subsystem's objects already created. In * this case, it might be worthwhile to initialize the Facade with these objects * instead of letting the Facade create new instances. */ $subsystem1 = new Subsystem1(); $subsystem2 = new Subsystem2(); $facade = new Facade($subsystem1, $subsystem2); clientCode($facade);
Ví dụ 2:
<?php class Ink{ public function check(){ echo 'Đã check mực in'.PHP_EOL; } } class Paper { public function check(){ echo 'Đã check giấy'.PHP_EOL; } public function getPaperForPrinting(){ echo 'Chuẩn bị giấy để in'.PHP_EOL; } }; class PrinterEngine { public function loadDocument($file){ echo 'Tải tài liệu cần in'.PHP_EOL; } public function formatDocumentData(){ echo 'Format dữ liệu'.PHP_EOL; } public function warmUp(){ echo 'warm up Engine'.PHP_EOL; } public function prepareLaser(){ echo 'Chuẩn bị Laser'.PHP_EOL; } public function inkToPaper(){ echo 'In mực lên giấy'.PHP_EOL; } }; class PrinterFacade{ private static $instances; protected function __construct() { } protected function __clone() { } public function __wakeup() { throw new \Exception("Cannot unserialize singleton"); } public static function getInstance() { if (!isset(self::$instances)) { self::$instances = new self; } return self::$instances; } public function print($file){ $ink = new Ink; $paper = new Paper; $printerEngine = new PrinterEngine; $ink->check(); $paper->check(); $ink->check(); $printerEngine->loadDocument($file); $printerEngine->formatDocumentData(); $printerEngine->warmUp(); $printerEngine->prepareLaser(); $printerEngine->inkToPaper(); } } PrinterFacade::getInstance()->print('Tài liệu mật');