PHP,DDD,CQRS,Event Sourcing,Kubernetes,Docker,Golang

0%

设计模式笔记:第二个设计模式原则——优先选择组合而非继承

QQ截图20151223224240.jpg

有些OOP程序员认为对象重用、扩展就等同于使用继承。一个类可以有大量属性和方法,通过继承这个类,我们增加新属性和方法,就能轻松的进行扩展,因为无需再重新编写代码。不过最后对于紧密绑定的对象,一味的使用继承方式来扩展会导致这么一个问题,那就是过度继承

所以这里要讲的就是第二个设计模式原则:应当优先选择对象组合而不是类继承

那么对象组合与继承有什么区别呢?这个说法并不是要完全消除继承。实际上,这个表示开发程序时如果有机会使用组合,就应当优先使用组合而不是继承。这样一来,子类就不会因为继承到大量不用的属性和方法而变得过度膨胀。

下面通过一个简单例子来说明一下,首先看使用继承的代码,父类是一个简单的类,包含两个方法来完成加法和除法:

class DoMath
{
    private $sum;
    private $quotient;
    public function simpleAdd($first, $second)
    {
        $this->sum = ($first+$second);
        return $this->sum;
    }
    public function simpleDivide($dividend, $divisor)
    {
        $this->quotient = ($dividend/$divisor);
        return $this->quotient;
    }
}

子类用于增加文本功能,一个方法是将数字转换为字符串,另一个方法是建立一个格式化输出。这个类通过继承DoMath的方式扩展了所有功能:

class InheritMath extends DoMath
{
    private $textOut;
    private $fullFace;
    public function numToText($num)
    {
        $this->textOut = (string)$num;
        return $this->textOut;
    }
    public function addFace($face, $msg)
    {
        $this->fullFace = "<strong>" . $face . "</strong>" . $msg;
        return $fullFace;
    }
}

最后实例化InheritMath类,使用实例化的对象不仅能够使用DoMath类中的所有功能,还能使用InheritMath类中新增的功能:

$family = new InheritMath();
$added = $family->simpleAdd(40,60);
$divided = $family->simpleDivide($added, 25);
$textNum = $family->numToText($divided);
$output = $family->addFace("Your results",$textNum);
echo $output;
//输出结果
Your results:4

接着再来看一下组合使用的代码,其中一个类依然是上面例子中的DoMath,另一个类是:

class DoText
{
    private $textOut;
    private $fullFace;
    public function numToText($num)
    {
        $this->textOut = (string)$num;
        return $this->textOut;
    }
    public function addFace($face, $msg)
    {
        $this->fullFace = "<strong>" . $face . "</strong>" . $msg;
        return $fullFace;
    }
}

这个DoText类与InheritMath类很相似,它们的区别仅仅就是一个继承了DoMath类,一个没有继承。

然后通过组合方式来使用这两个类:

$math = new DoMath();
$text = new DoText();
$added = $math->simpleAdd(40,60);
$divided = $math->simpleDivide($added, 25);
$textNum = $text->numToText($divided);
$output = $text->addFace("Your results",$textNum);
echo $output;
//输出结果
Your results:4

结果完全相同,不过这么做需要多包含一个类,并且多实例化一个类。看起来继承似乎更胜一筹,不过在较大的程序中,组合可以避免维护多个继承层次上的各个子类,而且还可以避免可能导致的错误。比如,父类的一个改变会逐级乡下传递到子类的实现,这可能会影响子类使用,如果继承过深,那么继承那么多的子类都可能要去进行相应的修改,那将是一个蛋疼的事情啊。

所以说,不是不能使用继承,而是尽量优先选择组合,并且使用继承的话,要尽量避免过度继承,要使用浅继承。主要还是避免类之间的紧密绑定。