资讯专栏INFORMATION COLUMN

通俗易懂的谈谈装饰器模式

forsigner / 539人阅读

摘要:装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这便是装饰模式,通过一层一层的装饰,我们可以灵活的得到我们想要的结果。可以轻松的添加新的装饰器类或者新的组件来创建灵活的结构。

前言

在编码的时候,我们为了扩展一个类经常是使用继承方式来实现,随着扩展功能的增多,子类会越来越膨胀,使系统变得不灵活。

装饰器模式( Decorator Pattern )允许向一个现有的对象添加新的功能,同时又不改变其结构。它能让我们在扩展类的时候让系统较好的保持灵活性。

那么装饰器模式具体是什么样的呢?

从一个情景开始

我们有一块地,在这块地上,我们要盖一栋有好几间房间的别墅,每间房间的装修费用都不同,现在,我们要对盖别墅的费用进行计算。

先定义一个Land类,表示这块地,Land类定义了在这块地上盖别墅需要花钱这个规则。

abstract class Land
{
    abstract public function cost();
}

Land已经定义好了在这块地上盖房需要花钱的这个规则了,但是盖一间房间具体花多少钱呢?

此时我们再定义一个Room类,这个类具体的定义了一个房间建造的基本费用(一个最简单房间,里面啥也没有的)。

class Room extends Land
{
    private $money = 1000;
    public function cost()
    {
        return $this->money;
    }
}

然后开始建造房间,我们建了两个房间,分别是客厅和餐厅,用LivingRoom类和DiningRoom类来表示

class LivingRoom extends Room
{
    public function cost()
    {
        return parent::cost()+200; //客厅的建造费用在房屋建造费用的基础上多200,比如要买沙发,电视
    }
    
}

class DiningRoom extends Room
{
    public function cost()
    {
        return parent::cost()+100; //餐厅的建造费用在房屋建造费用的基础上多100,比如买餐桌
    }
}

现在,我们很容易就能得到建造一间客厅所需的花费

$livingRoomCost = new LivingRoom();
echo $livingRoomCost->cost();
问题的产生

不过,这样的结构并不具备灵活性,虽然我们可以很容易的分别得出建造一间客厅和建造一间餐厅的费用,但是,如果我买的地比较小,只能把餐厅和客厅建在同一个房间里,那要怎么去计算费用?难道还要很麻烦的去创建一个包含客厅和餐厅的LivingDiningRoom类?这样做的话除了麻烦,还会使代码产生重复。

解决问题

为了更好的解决这个问题,我们得做一些调整,同样先声明Land类和Room类,不同的是,引入了一个房间的装饰类RoomDecorator,它继承了Land类,因为没有实现Land类的cost()方法,所以需将它声明为抽象类,并且定义了一个以Land类的对象为参数的构造方法,传入的对象会保存在$land属性中,该属性声明为protected,以便子类访问。具体如下。

abstract class RoomDecorator extends Land
{
    protected $land;
    public function __construct(Land $land)
    {
        $this->land = $land;
    }
}

然后我们再重新定义客厅类和餐厅类

class LivingRoom extends RoomDecorator
{
    public function cost()
    {
        return $this->land->cost()+200;
    }
}


class DiningRoom extends RoomDecorator
{
    public function cost()
    {
        return $this->land->cost()+100;
    }
}

这两个类都扩展自RoomDecorator类,这意味着它们都拥有指向Land对象的引用。当它们的cost()方法被调用时,都会先调用所引用的Land类对象的cost()方法,然后再执行自己特有的操作。

所以这时候,建造一间客厅所需的费用是这样计算

$livingRoomCost = new LivingRoom(new Room());
echo $livingRoomCost->cost(); //输出1200

建造一间餐厅所需的费用是这样计算

$diningRoomCost = new DiningRoom(new Room());
echo $diningRoomCost->cost(); //输出1100

回到刚才的问题,如果我们需计算建造一间包含客厅餐厅的房间所需费用,代码如下

$livingRoom = new DiningRoom(new LivingRoom(new Room()));
echo $livingRoom->cost(); //输出1300

看,我们现在计算建造费用的思路是:计算出基础房间的费用 --> 在基础房间上装饰成客厅的费用 --> 在客厅的基础上加装饰餐厅的费用 --> 得到包含客厅餐厅的房间费用。已经不需要麻烦的通过创建一个LivingDiningRoom类来计算包含客厅餐厅的房间建造费用了。

这便是装饰模式,通过一层一层的装饰,我们可以灵活的得到我们想要的结果。可以轻松的添加新的装饰器类或者新的组件来创建灵活的结构。

完整代码
money;
    }
}

//装饰器
abstract class RoomDecorator extends Land
{
    protected $land;
    public function __construct(Land $land)
    {
        $this->land = $land;
    }
}

class LivingRoom extends RoomDecorator
{
    public function cost()
    {
        return $this->land->cost()+200;
    }
}


class DiningRoom extends RoomDecorator
{
    public function cost()
    {
        return $this->land->cost()+100;
    }
}

$livingRoomCost = new LivingRoom(new Room());
echo $livingRoomCost->cost(); //输出1200

$diningRoomCost = new DiningRoom(new Room());
echo $diningRoomCost->cost(); //输出1100

$livingDining = new DiningRoom(new LivingRoom(new Room()));
echo $livingDining->cost(); //输出1300

the end.

happy coding! ^_^

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/25853.html

相关文章

  • 通俗易懂设计模式

    摘要:面向对象设计模式通常以类别或对象来描述其中的关系和相互作用,但不涉及用来完成应用程序的特定类别或对象。里氏代换原则里氏代换原则是面向对象设计的基本原则之一。 通俗易懂的设计模式 零、使用 1、安装 2、测试 一、什么是设计模式 二、设计模式的类型 三、设计模式的六大原则 四、UML类图 1、看懂UML类图 2、解释 五、资料 前言:花了一些时间再次熟悉了一遍...

    wuyangnju 评论0 收藏0
  • 从Express到Nestjs,谈谈Nestjs设计思想和使用方法

    摘要:三的洋葱模型这里简单讲讲在中是如何分层的,也就是说请求到达服务端后如何层层处理,直到响应请求并将结果返回客户端。从而使得端支持跨域等。   最近已经使用过一段时间的nestjs,让人写着有一种java spring的感觉,nestjs可以使用express的所有中间件,此外完美的支持typescript,与数据库关系映射typeorm配合使用可以快速的编写一个接口网关。本文会介绍一下作...

    chanjarster 评论0 收藏0
  • 从Express到Nestjs,谈谈Nestjs设计思想和使用方法

    摘要:三的洋葱模型这里简单讲讲在中是如何分层的,也就是说请求到达服务端后如何层层处理,直到响应请求并将结果返回客户端。从而使得端支持跨域等。   最近已经使用过一段时间的nestjs,让人写着有一种java spring的感觉,nestjs可以使用express的所有中间件,此外完美的支持typescript,与数据库关系映射typeorm配合使用可以快速的编写一个接口网关。本文会介绍一下作...

    wawor4827 评论0 收藏0
  • 编程中那些经典套路——设计模式汇总

    摘要:如果看不懂的话,可以在评论区中提问,我会第一时间回答你无论何时我一直都在嗯哼该文章属于编程中的那些经典套路设计模式汇总系列 在正式阅读前,我先谈谈我们该用什么姿势和心态学习设计模式: 如果你还没有过多的编程经验(泛指半年以下),我建议你把它当做小说来看,能看懂多少是多少,因为半年以下经验的程序员用到设计模式的情况只会出现在面试上,至于实际工作中?相对来说这部分不会由你负责。 如果你已...

    youkede 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<