1

考虑以下类层次结构:

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
         //...
    }
}

现在这个类LaserPrinter不能被它的父类替换,Printer因为Printer它没有setFile方法。
这是否意味着他的等级制度违反了 Liskov 替代原则?不允许子类有自己的公共方法吗?

4

2 回答 2

1

反过来 - 为了满足 Liskov 替换原则,您应该能够在使用父类的任何地方使用子类。在您的示例中,您应该能够在需要类型对象的地方使用LaserPrinter对象或InkJetPrinter对象Printer。因此,子类拥有哪些自己的公共方法并不重要。

这也为您提供了满足开放/封闭原则的途径 - 您可以创建新打印机,从而扩展模块(使用您的打印机)的行为,而无需修改该模块。

更新:您当前的代码中没有关于 LSP 的内容。您的 PrinterNetwork 正在使用子类来打印文件。相反,它应该使用父类。我不知道php,但它应该看起来像:

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

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

您可以使用 InkJetPrinter 或 LaserPrinter 初始化网络。您的代码不应取决于打印机的类型。他们俩都应该毫无问题地替代父母。

于 2012-10-17T20:56:16.490 回答
1

首先,我假设您在该代码中有继承,尽管我看不到任何暗示 LaserPrinter 或 InkJetPrinter 从打印机继承的东西。

不,它没有违反 Liskov 原则,因为子类在用作打印机时需要正常运行。

可能引发您的问题的一点是这些类也应该使用依赖倒置,因此应该将完全构造的 LaserPrinter 传递给任何使用打印机的东西。在您那里的示例中,创建 LaserPrinter 后文件可以更改吗?

如果文件不需要更改,那么您可能希望在 LaserPrinter 的构造函数中传递它。如果文件可以更改,那么您需要:在 LaserPrinter 中创建文件名或创建一个创建文件名的新类,然后在 LaserPrinter 中注入该类。

您可能想了解Open/ClosedDependency Inversion原则,因为我认为它们与您的问题密切相关。


编辑

再次查看代码后,我发现有些奇怪。Printer.print()应该接收一个描述打印内容的参数。让LaserPrinterorInkJetPrinter引用他们需要从实例变量打印的内容看起来是错误的。

例如,您不说:打印机,这里有一个文档……现在打印它

它更像是打印机,既然您已完全配置并运行,请打印此文档

看起来您已经有一个对象来表示您要打印的内容(封装了文档和文件)。您可以传递该对象并拥有类似print(jobDetail).

于 2012-10-17T21:02:34.553 回答