<?php
namespace App\Solid\DIP\Bad;
class Calculator
{
public function Add(float $x, float $y) : float
{
return $x + $y;
}
public function Minus(float $x, float $y) : float
{
return $x - $y;
}
// what happen if we want to add more method ex: Multiply?
}
-
Lack of Abstraction:
- Violation of Dependency Inversion Principle (DIP): The
Calculator
class is not designed with abstractions. According to DIP, high-level modules should not depend on low-level modules. Instead, both should depend on abstractions. In this code, theCalculator
class is a concrete implementation that lacks an abstraction (an interface or abstract class). As a result, any change to the calculation logic or addition of new operations (likeMultiply
) requires modifying theCalculator
class directly, which is a violation of the open/closed principle as well.
- Violation of Dependency Inversion Principle (DIP): The
-
Difficulty in Extensibility:
- Rigid Design: Adding new methods (such as
Multiply
,Divide
, etc.) requires modifying theCalculator
class. This approach tightly couples the functionality with the class itself. A better design would use interfaces or abstract classes to define a contract for calculations, allowing different implementations to be swapped in without modifying existing code.
- Rigid Design: Adding new methods (such as
-
Scalability Issues:
- Potential for Monolithic Class: As more methods are added, the
Calculator
class could become large and unwieldy. This makes it harder to maintain and understand. For example, if you need to support more complex operations or different types of calculators, the class will need to be modified extensively.
- Potential for Monolithic Class: As more methods are added, the
-
Violation of Single Responsibility Principle (SRP):
- Mixing Concerns: The
Calculator
class is responsible for performing various calculations. By adding more methods directly to this class, it starts handling multiple responsibilities, which can be split into different classes or interfaces. Each class should have a single responsibility to keep the design clean and maintainable.
- Mixing Concerns: The
To adhere to the Dependency Inversion Principle and improve the design, you could refactor the code as follows:
-
Define an Interface:
<?php namespace App\Solid\DIP; interface CalculatorInterface { public function calculate(float $x, float $y): float; }
-
Implement the Interface:
<?php namespace App\Solid\DIP; class AddCalculator implements CalculatorInterface { public function calculate(float $x, float $y): float { return $x + $y; } } class MinusCalculator implements CalculatorInterface { public function calculate(float $x, float $y): float { return $x - $y; } }
-
Use Dependency Injection:
<?php namespace App\Solid\DIP; class CalculatorService { private $calculator; public function __construct(CalculatorInterface $calculator) { $this->calculator = $calculator; } public function performCalculation(float $x, float $y): float { return $this->calculator->calculate($x, $y); } }
In this improved design:
- Abstraction is introduced by using
CalculatorInterface
. - Dependency Injection is used to inject the specific implementation of the calculator into the
CalculatorService
. - New functionalities can be added without modifying existing code, adhering to the open/closed principle.
- Each class has a single responsibility, making the design cleaner and easier to maintain.