设计模式实例讲解 - 开放封闭
说明
实体(类、方法等)应当对扩展开放,对修改封闭
进一步解释:
- 对扩展开放,是指代码应当很容易添加新功能。
- 对修改封闭,是指我们在添加新功能的时候尽量不会涉及到原有代码的改动。
反面示例
类 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 协议》,转载必须注明作者和本文链接
推荐文章: