質問

Consider the following class hierarchy:

Abstract class Printer{
    public print(){
        //code to handle printing
    }
}

class LaserPrinter extends Printer{
    private $file;
    public setFile($file){
        $this->file = $file; 
    }
}

class InkJetPrinter extends Printer{
    private $document;
    public setDocument($document){
        $this->document= $document; 
    }
}

class ClientClass{
    private $filesToPrint=array();
    public __construct(InkJetPrinter $inkJetPrinter, LaserPrinter $laserPrinter){   //I was hoping to apply Dependency Inversion here by defining both inputs as type Printer instead
         //constructor stuff    
    }

    public function startPrinting(){
         //some logic to extract the $files and $documents from $this->filesToPrint
         //...
         $this->inkJetPrinter->setDocument($document);//<---Things got messy here
         $this->laserPrinter->setFile($file);//<---and here too
         //...
    }
}

Now the class LaserPrinter can't be replaced by its parent Printer because Printer doesn't have setFile method.
Does that mean that his hierarchy breaks the Liskov substitution principle? Are subclasses not allowed to have their own public methods?

役に立ちましたか?

解決

First of all, I assume that you have inheritance in that code, although I can't see anything that hints that LaserPrinter or InkJetPrinter inherit from Printer.

No, it doesn't break the Liskov principle, because the subclass needs to behave properly when it's used as a Printer.

The bit that might be triggering your question is that the classes also should use Depedency Inversion, so the fully constructed LaserPrinter should be passed to anything that uses a Printer. In the example you have there, can the file change once LaserPrinter is created?

If the file doesn't need to change, then you probably want to pass it in the constructor of LaserPrinter. If the file can change, then you want to: create the file name inside LaserPrinter or create a new class which creates the file names and then inject the class in LaserPrinter.

You might want to read about the Open/Closed and Dependency Inversion principles, as I think they are very closely related to your question.


edit

After looking at the code again, there's something that I find odd. Printer.print() should receive a parameter describing what to print. Having the LaserPrinter or InkJetPrinter reference what they need to print from an instance variable looks wrong.

For example you don't say: Printer, here you have a document... now print it.

It's more like Printer, now that you're fully configured and running, print this document

It looks like you already have an object that represents what you want to print (that encapsulates a document and file). You can pass that object and have something like print(jobDetail).

他のヒント

The other way round - to satisfy Liskov Substitution Principle you should be able to use child class anywhere your parent class is used. In your example you should be able to use LaserPrinter object or InkJetPrinter object where object of type Printer is expected. So, it does not matter which own public methods subclass have.

This also opens you way to satisfying Open/Closed Principle - you can create new printers, thus extending behavior of module (which uses your printer) without modification of that module.

UPDATE: There is nothing about LSP in your current code. Your PrinterNetwork is using child classes for printing files. Instead it should use parent class. I don't know php, but it should look like:

class PrinterNetwork{
    private $filesToPrint=array();
    public __construct(Printer $printer){
         //constructor stuff    
    }

    public function startPrinting(){
         //foreach file in $this->filesToPrint
         $this->printer->print($file);
         //...
    }
}

You can initialize network either with InkJetPrinter or LaserPrinter. Your code should not depend on type of printer. Both of them should substitute parent without problems.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top