Part 4: The Interface Segregation Principle
The interface segregation principle (ISP) is defined as “Clients should not be forced to depend upon interfaces that they do not use”.
Your job is to work on a cyberpunk AI system that can be used for various tasks, such as hacking into secure systems, navigating through crowded city streets, and interacting with humans.
Initially, you might be tempted by the devil on your shoulder to create a single “AI interface” that exposes all of the capabilities of the AI system. However, this could lead to hellish issues if certain users only need access to certain capabilities. For example, a hacker might only be interested in the AI’s hacking abilities, while a self-driving car manufacturer might only be interested in the AI’s navigation capabilities.
To apply the ISP, you could instead create multiple, smaller interfaces that expose only the capabilities that are relevant to each user. This way, the hacker would only have access to the hacking-related interfaces, and the self-driving car manufacturer would only have access to the navigation-related interfaces.
By following the ISP, you can create more flexible and modular AI systems that are easier to maintain and extend over time.
First, let’s create two separate interfaces: HackingInterface and NavigationInterface. Each interface will define a set of methods that correspond to a particular capability of the AI system:
interface HackingInterface { public function hackIntoSecureSystem(string $system); } interface NavigationInterface { public function navigateThroughCrowdedCity(array $route); }
Next, we can create a class that implements both of these interfaces and contains the underlying implementation of the AI system’s capabilities:
class CyberpunkAI implements HackingInterface, NavigationInterface { public function hackIntoSecureSystem(string $system) { // implementation of hacking functionality goes here } public function navigateThroughCrowdedCity(array $route) { // implementation of navigation functionality goes here } }
Finally, we can use Laravel’s dependency injection container to inject the appropriate interface into each class that needs it. For example, if we have a HackerController class that only needs access to the hacking capabilities of the AI system, we can inject the HackingInterface into the controller’s constructor:
class HackerController { private $ai; public function __construct(HackingInterface $ai) { $this->ai = $ai; } public function hack(string $system) { $this->ai->hackIntoSecureSystem($system); } }
This way, the HackerController class is only given access to the methods defined in the HackingInterface, and is unaware of the other capabilities of the CyberpunkAI class.
Usage
First, make sure that you have the necessary dependencies injected into your Laravel application. For example, you might have a service provider that binds the HackingInterface and NavigationInterface to the CyberpunkAI class in the service container:
$this->app->bind(HackingInterface::class, CyberpunkAI::class); $this->app->bind(NavigationInterface::class, CyberpunkAI::class);
Next, you can use the interfaces in your controllers or other classes by type-hinting the dependencies in the constructor:
class HackerController { private $ai; public function __construct(HackingInterface $ai) { $this->ai = $ai; } public function hack(string $system) { $this->ai->hackIntoSecureSystem($system); } } class SelfDrivingCarController { private $ai; public function __construct(NavigationInterface $ai) { $this->ai = $ai; } public function navigate(array $route) { $this->ai->navigateThroughCrowdedCity($route); } }
Now, when you create an instance of the HackerController or SelfDrivingCarController, the appropriate interface will be injected and the controller will have access to the relevant methods of the CyberpunkAI class whilst the ISP is intact.