Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making this class comply with the Dependency Inversion principle

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?

like image 286
Songo Avatar asked Nov 07 '12 18:11

Songo


People also ask

How do you achieve the Dependency Inversion principle?

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.

What is the meaning of Dependency Inversion principle?

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.

What is the benefit of Dependency Inversion principle?

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.

Which of the following is correct regarding the Dependency Inversion principle?

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.


2 Answers

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));
like image 85
Gordon Avatar answered Sep 29 '22 12:09

Gordon


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.

like image 21
tereško Avatar answered Sep 29 '22 12:09

tereško