设计模式实例讲解 - 开放封闭

说明

实体(类、方法等)应当对扩展开放,对修改封闭

进一步解释:

  • 对扩展开放,是指代码应当很容易添加新功能。
  • 对修改封闭,是指我们在添加新功能的时候尽量不会涉及到原有代码的改动。

反面示例

Square 代表「方形」

class Square {
    public $width;
    public $height;
    public function __construct($width, $height)
    {
        $this->width = $width
        $this->height = $height;
    }
}

AreaCalculator 用于计算图形的总面积

class AreaCalculator {
    public function calculate($shapes)
    {   
        foreach($shapes as $shape)
        {
            $area[] = $shape->width * $shape->height;
        }
        return array_sum($area);
    }
}

测试:计算几个方形的总面积

$area = new AreaCalculator;
$area->calculate([
    new Square(10,20),
    new Square(20,30)
]);

现在我们要添加新的功能。例如,我们要增加一个圆形

class Circle {
    public $radius;
    public function __construct($radius)
    {
        $this->radius = $radius;
    }

这时候,就必须修改 AreaCalculator 来实现面积的计算

class AreaCalculator {
    public function calculate($shape)
    {
        foreach($shapes as $shape)
        {
            if (is_a($shape, 'Square'))
            {
                $area[] = $shape->width * $shape->height;
            }
            elseif (is_a($shape, 'Circle'))
            {
                $area[] = $shape->radius * $shape->radius * pi();
            }
        }
        return array_sum($area);
    }
}

AreaCalculator 类计算总面积的时候就需要增加对圆形这个图案的判断,原有代码就必须做出变动。

对照「开放封闭」原则可以发现, AreaCalculator 类对修改是开放的,使得原有代码的改动越来越多。例如,后续可能进一步添加三角形等类的计算,导致 AreaCalculator 类的不稳定因素越来越多,变得难以维护,我们称之为代码腐烂。

改进

首先,我们要分析类中可扩展的行为是什么,在本例中,可扩展的行为是「面积计算」。接下来,我们需要将可扩展的行为分离出来,令其隐藏在接口的背后。这样的话,类就不需要依赖于可扩展的行为,即 AreaCalculator 类不依赖于具体的「面积计算」。

将「面积计算」这一变化的行为隐藏于接口的背后

interface Shape {
    public function area();
}

添加不同的子类(三角形、圆形、方形)的时候,让子类去实现对应的接口。

class Square implements Shape {
    public $width;

    public function __construct($width)
    {
        $this->width = $width;
    }
    public function area()
    {
        return $this->width * $this->width;
    }
}

class Circle implements Square {
    public $radios;
    public function __construct($radios)
    {
        $this->radios = $radios;
    }
    public function area()
    {
        return $this->radios * $this->radios * pi();
    }
}

AreaCalculator 不再依赖于具体的行为,不需要去考虑不同形状的面积如何计算,只只需依赖于抽象的接口

class AreaCalculator
{
    public function calculate($shapes)
    {
        foreach($shapes as $shape)
        {
            $area[] = $shape->area();
        }
        return array_sum($area);
    }
}

应用

开放封闭原则的一个典型应用就是「结账」功能。对于结账来说,账单是固定的,但是支付方式却是变化的(现金、信用卡、微信、支付宝)。

首先,将变化的「支付方式」抽象出来,隐藏于接口背后

interface PaymentMethodInterface
{
    public function acceptPayment($receipt);
}

不同的支付方式去实现该接口,例如现金支付

class CashPaymentMethod implements PaymentMethodInterface
{
    public function acceptPayment($receipt)
    {
        // 接受现金支付
    }
}

结账类不需要依赖具体的支付方式,只需要依赖抽象的支付接口即可

class Checkout
{
    public function begin(Receipt $receipt, PaymentMethodInterface $paymentMethod)
    {
        $paymentMethod->acceptPayment($receipt);
    }
}

参考:laracasts.com

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!