The definition of DI quoted from Wikipedia states:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions. B. Abstractions should not depend upon details. Details should depend upon abstractions.
I'm trying to apply that principle to my code:
class Printer{
private $logger;
function __construct(Zend_Log $logger){
$this->logger=$logger;
}
function print(){
//Some code
$this->logger->log('Logger in action ;)');
}
}
Now since why Printer
class depends on Zend_Log
which is neither an abstract class nor an interface then I'm violating the Dependency Inversion principle.
How can I fix that knowing that Zend_Log
doesn't extend an abstract class nor implement an interface?
The Dependency Inversion Principle (DIP) states that high level modules should not depend on low level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.
The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details.
The dependency inversion principle helps us to couple software modules loosely. The principle was arrived at after many years of coupling software modules, and it states that: High-level modules should not import anything from low-level modules; they should both depend on abstractions.
High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces). Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
The easiest approach would be to use an Interface Adapter, e.g. create the API your Printer is supposed to use in an Interface and then implement that Interface in an Adapter for the Zend_Log component. Pass the concrete Adapter to the Printer. The Printer will then depend on the PrinterLog and not the concrete Zend_Log.
interface PrinterLog
{
public function log($whatever);
}
class ZendPrinterLogAdapter implements PrinterLog
{
private $logger;
public function __construct(Zend_Log $logger)
{
$this->logger = $logger
}
public function log($whatever)
{
// delegate call to $this->logger
}
}
class Printer
{
private $logger;
function __construct(PrinterLog $logger)
{
$this->logger = $logger;
}
}
$printer = new Printer(new ZendPrinterLogAdapter(new Zend_Log));
Here is somewhat alternative take on same API .. so this would be you standard setup:
interface Printerish // couldn't think of a good ajective
{
public function print();
}
interface CanLogStuff // couldn't think of a good ajective
{
public function log( $text );
}
class Printer implements Printerish
{
public function print()
{
// do something
}
}
And this would be loggable printer:
class LoggedPrinter implements Printerish
{
protected $logger;
protected $pritner;
public function __construct( Printerish $printer, CanLogStuff $logger )
{
$this->logger = $logger;
$this->printer = $printer;
}
protected function print()
{
$this->logger( 'I can print, I can print !!' );
$this->printer->print();
}
}
Where this come from is the following use-case: if in real world you want to start controlling the use of a real printer (intern has been printing out the internet again). Then you would not make a different printer. You would be trying to add some external control.
In programming it kinda relates to Open/closed principle.
Keep in mind that this is just an idea and one should take a hard look at this before trying to use in production code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With