The provided code violates the Interface Segregation Principle (ISP) from SOLID design principles. Here's an analysis of the issues:
<?php
namespace App\Solid\ISP\Bad;
class Car implements IDrivable
{
function GoForward(): void
{
echo "Car going forward. \r\n";
}
function GoBackward(): void
{
echo "Car going backward. \r\n";
}
function TurnLeft(): void
{
echo "Car going turns left. \r\n";
}
function TurnRight(): void
{
echo "Car going turns right.. \r\n";
}
}
<?php
namespace App\Solid\ISP\Bad;
interface IDrivable
{
function GoForward(): void;
function GoBackward(): void;
function TurnLeft(): void;
function TurnRight(): void;
}
<?php
namespace App\Solid\ISP\Bad;
use Nette\NotImplementedException;
class Train implements IDrivable
{
function GoForward(): void
{
echo "Train going forward. \r\n";
}
function GoBackward(): void
{
echo "Train going backward. \r\n";
}
function TurnLeft(): void
{
throw new NotImplementedException();
}
function TurnRight(): void
{
throw new NotImplementedException();
}
}
-
Violation of Interface Segregation Principle (ISP):
- Unnecessary Implementation: The
IDrivable
interface requires bothCar
andTrain
classes to implement all four methods:GoForward
,GoBackward
,TurnLeft
, andTurnRight
. However, aTrain
typically does not make sharp turns like a car does. TheTurnLeft
andTurnRight
methods are not applicable to trains, resulting in a forced implementation of methods that do not make sense for all implementing classes. - Code Smell: The
Train
class has to provide default (or placeholder) implementations forTurnLeft
andTurnRight
that throw exceptions. This is a sign that the interface is not properly segmented according to the needs of the implementing classes.
- Unnecessary Implementation: The
-
Lack of Flexibility:
- Overly General Interface: A single, all-encompassing interface (
IDrivable
) imposes unnecessary requirements on classes. It forces classes to implement methods that may not be relevant to their specific functionality, reducing flexibility and increasing the likelihood of runtime errors.
- Overly General Interface: A single, all-encompassing interface (
To adhere to the Interface Segregation Principle, you should create more specific interfaces that are tailored to the different capabilities of the classes. Here’s a refactored design:
-
Define Separate Interfaces:
<?php namespace App\Solid\ISP\Good; interface IMovable { function GoForward(): void; function GoBackward(): void; } interface ITurnable { function TurnLeft(): void; function TurnRight(): void; }
-
Implement Specific Interfaces in Classes:
<?php namespace App\Solid\ISP\Good; class Car implements IMovable, ITurnable { function GoForward(): void { echo "Car going forward. \r\n"; } function GoBackward(): void { echo "Car going backward. \r\n"; } function TurnLeft(): void { echo "Car turning left. \r\n"; } function TurnRight(): void { echo "Car turning right. \r\n"; } }
<?php namespace App\Solid\ISP\Good; class Train implements IMovable { function GoForward(): void { echo "Train going forward. \r\n"; } function GoBackward(): void { echo "Train going backward. \r\n"; } }
- Adherence to ISP: Each class now only implements the methods that are relevant to it, adhering to the Interface Segregation Principle.
Car
implements bothIMovable
andITurnable
, whileTrain
only implementsIMovable
. - Increased Flexibility: This design allows for more flexibility in adding new types of vehicles that may need different sets of functionalities without impacting existing code.
- Better Maintainability: The code becomes easier to maintain and understand because each interface and class has a clear responsibility and does not contain unnecessary methods.
By splitting the interface into smaller, more specific ones, you ensure that classes are not burdened with methods that are not relevant to them, leading to a more maintainable and extensible design.