The facade pattern is a design pattern that provides a simplified interface to a complex system. The idea is to hide the underlying complexity of the system and provide a simplified, easy-to-use interface to the client:
class CyberpunkCity { private $hackers; private $corporations; private $police; public function __construct() { $this->hackers = new Hackers(); $this->corporations = new Corporations(); $this->police = new Police(); } public function hackTheSystem($target) { return $this->hackers->hack($target); } public function bribeThePolice($amount) { return $this->police->acceptBribe($amount); } public function infiltrateCorporation($corpName) { return $this->corporations->infiltrate($corpName); } } // Usage $city = new CyberpunkCity(); $city->hackTheSystem("EvilCorp"); $city->bribeThePolice(5000); $city->infiltrateCorporation("Apex Inc.");
Here, the CyberpunkCity class acts as a facade for interacting with the complex cyberpunk city system, which consists of hackers, corporations and police. The client code can use the simplified methods hackTheSystem(), bribeThePolice(), and infiltrateCorporation() to interact with the system, instead of having to deal with the complexity of the underlying classes Hackers, Corporations and Police.
Let’s go SOLID:
interface Hackable { public function hack($target); } interface Bribable { public function acceptBribe($amount); } interface Infiltrable { public function infiltrate($target); } class Hackers implements Hackable { public function hack($target) { // hacking logic } } class Police implements Bribable { public function acceptBribe($amount) { // bribery logic } } class Corporations implements Infiltrable { public function infiltrate($target) { // infiltration logic } } class CyberpunkCity { private $hackers; private $corporations; private $police; public function __construct(Hackable $hackers, Infiltrable $corporations, Bribable $police) { $this->hackers = $hackers; $this->corporations = $corporations; $this->police = $police; } public function hackTheSystem($target) { return $this->hackers->hack($target); } public function bribeThePolice($amount) { return $this->police->acceptBribe($amount); } public function infiltrateCorporation($corpName) { return $this->corporations->infiltrate($corpName); } } // Usage $hackers = new Hackers(); $police = new Police(); $corporations = new Corporations(); $city = new CyberpunkCity($hackers, $corporations, $police); $city->hackTheSystem("EvilCorp"); $city->bribeThePolice(5000); $city->infiltrateCorporation("Acme Inc.");
Here’s how we’re following the SOLID principles:
- Single Responsibility Principle (SRP): Each class has a single, well-defined responsibility. For example, the Hackers class is responsible for hacking, the Police class is responsible for accepting bribes and the Corporations class is responsible for infiltration. The CyberpunkCity class has the responsibility of providing a simplified interface for interacting with the system, but it does not have any other responsibilities.
- Open/Closed Principle (OCP): The code is open for extension but closed for modification. The interfaces Hackable, Bribable, and Infiltrable are open for extension, allowing new classes to implement them. But the CyberpunkCity class does not need to be modified in order to support these new classes.
- Liskov Substitution Principle (LSP): The code follows LSP because any class that implements the Hackable, Bribable, and Infiltrable interfaces can be used interchangeably with the Hackers, Police, and Corporations classes.
- Interface Segregation Principle (ISP): The interfaces are small and focused, each with a single responsibility. This helps to prevent clients from depending on methods they do not use, and also allows for more flexibility in implementation.
- Dependency Inversion Principle (DIP): The code adheres to DIP because the CyberpunkCity class depends on abstractions (interfaces) rather than concretions (classes). This allows for the implementation of the dependencies to change without affecting the CyberpunkCity class.