大话设计模式-装饰器模式

大话设计模式书中,作者举了一个穿衣服的例子来为我们引入装饰器模式。


概念


定义

装饰模式在书中的定义是:
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。

这句话直接去理解可能会有点抽象,我结合书中的例子来讲讲自己的理解。假设有一天,女朋友要你陪她去逛商场,她今天要做很多事情:

  • 去干洗店拿干洗的衣服
  • 去服装店取定制地衣服
  • 去做头发
  • 去美甲
  • 去美食街吃夜宵
  • ......

她去做这些事情地顺序是不确定的,而且这些事情之间也没有什么太大的关联。那我们在程序中去把这些事情串联起来呢?简单的为每一种操作写一个类,然后在主函数中排列组合吗?这样做也不是不可以,但是这就破坏了我们程序当中的封装性。用书中的话说就是:

你光着身子, 当着大家的面,先穿T恤,再穿裤子,再穿鞋,仿佛在跳穿衣舞。难道你穿衣服都是在众目睽睽下穿的吗?

实际上,大多数时候我们不希望将太多的细节暴露给用户,因此面对这种情况,我们可能就需要用到装饰模式。让我们先来看看它的结构图。

结构

这是作者在书中给出的原图。同样,直接看图可能很难让我们理解什么是策略模式。我们继续用上面那个例子来类比举例。

Component是定义一个对象接口,可以给这些对象动态地添加职责。

这句话怎么理解,其实在上面这个例子中,Conponent其实就相当于“人”。为什么这么说,因为只有“人”才能完成拿衣服,买衣服,做头发等等这些事情(不要抬杠哈,你懂我意思)。那这个Operation()方法就相当于是做完了的事情(可以是空的或者多件事情的排列组合)。

ConcreteComponent是定义了一个具体的对象,也可以给这个 对象添加一些职责。

这句话又怎么理解呢,你可以把ConcreteComponent看成是女朋友,她是一个具体的人,是她要去完成这些行为。同样Operation()方法就相当于是她已经做完了的事情(可以是空的或者多件事情的排列组合)。

Decorator,装饰抽象类,继承了Component, 从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰 对象,起到给Component添加职责的功能。

这句话比较长,也比较抽象。什么是装饰抽象类,在这个例子中还真不是很好解释,你可以理解为一个“盒子”,里面装的是“前一刻的女朋友”(可能比较牵强哈)。或者更好的理解是把他当成一种规则:你这个人在进我们店之前是什么样的,带了哪些东西;你带来的东西我们原封不动,我们店给你提供了一些服务之后,你还是完完整整的一个人从我这里走出去。所有的店都必须遵循这个规则(不然就是黑店了)。ConcreteDecorator就相当于是遵循这些规则的具体的干洗店,服装店,理发店,美甲店,小吃店等等了。

那么整个流程是怎么样的呢。

  1. 女朋友出门饿了,先来小吃店吃了小吃,变成了“吃了小吃的女朋友”
  2. “吃了小吃的女朋友”吃太饱了,准备去干洗店先拿一下衣服,变成了“拿了衣服的吃了小吃的女朋友”
  3. “拿了衣服的吃了小吃的女朋友”觉得走累了,先去理发店做个于是头发休息一下,变成了“做了头发的拿了衣服的吃了小吃的女朋友”
  4. ......
  5. 最后女朋友做完了所有的事情

经过这个流程,大家应该能够理解了具体装饰类的作用了吧。比如装饰类A的作用就是,把B的C变成A的B的C

优缺点

优点:

在书中对于装饰模式的优点是这样说的:

装饰模式是利用SetComponent来对对象进行包 装的。这样每个装饰对象的实现就和如何使用这个对象分离开了, 每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。

其实我觉得它的主要优点还是增强了扩展对象功能的灵活性。并且减少了细节的暴露。

缺点:

主要的缺点就是:可能会增加程序的复杂性,因为它的结构相较于直接在主程序中“跳穿衣舞”更加难以理解,因为它需要将对象一层一层的包裹起来,如果过度使用的话可能会让程序变得复杂。

例子


我们尝试用Java实现一下上面的例子。并对其中的一些细节进行相应的优化。

  1. 由于整个事情中,出现的人只有女朋友一个,因此可以将人这个父类省略。女朋友类的代码如下:
    /**
     * @Author yirui
     * @Date 2024/4/17 20:58
     * @Version 1.0
     */
    public class GirlFriend {
        public void Operation(){
            System.out.println("女朋友");
        }
    }
    

  2. 由于没有人这个父类(父子类的概念参考简单工厂模式中所介绍的),那修饰抽象类只能继承女朋友类了,规则变成:女朋友在进店之前是什么样的,带了哪些东西;带来的东西我们原封不动,我们店给女朋友提供了一些服务之后,女朋友还是完完整整的一个人从我这里走出去。(其实这个例子中,这个类都可以不要,直接让具体修饰类继承女朋友类就可以了,但是为了方便理解,还是把它写出来了):

    /**
     * @Author yirui
     * @Date 2024/4/17 21:03
     * @Version 1.0
     */
    public class Decorator extends GirlFriend{
        GirlFriend girlFriend;
    
        public void setGirlFriend(GirlFriend girlFriend) {
            this.girlFriend = girlFriend;
        }
    
        @Override
        public void Operation() {
            girlFriend.Operation();
        }
    }
    
  3. 接下来就是具体修饰类了,继承修饰抽象类。(这里为了省事,只写干洗店和小吃店。)
     

    /**
     * @Author yirui
     * @Date 2024/4/17 21:08
     * @Version 1.0
     */
    public class ClothingStore extends Decorator{
        private void buyCloth(){
            System.out.println("买了一件衣服。");
        }
    
        @Override
        public void Operation() {
            super.Operation();
            buyCloth();
        }
    }
    
    
    /**
     * @Author yirui
     * @Date 2024/4/17 21:11
     * @Version 1.0
     */
    public class FoodStore extends Decorator{
        private void eatFood(){
            System.out.println("吃了一碗麻辣烫。");
        }
    
        @Override
        public void Operation() {
            super.Operation();
            eatFood();
        }
    }
    
  4. 主程序:
     

    /**
     * @Author yirui
     * @Date 2024/4/17 21:13
     * @Version 1.0
     */
    public class Program {
        public static void main(String[] args) {
            GirlFriend girlFriend = new GirlFriend();
            ClothingStore clothingStore = new ClothingStore();
            FoodStore foodStore = new FoodStore();
    
            clothingStore.setGirlFriend(girlFriend);
            clothingStore.Operation();
            System.out.println("**********************");
            foodStore.setGirlFriend(clothingStore);
            foodStore.Operation();
        }
    }
    
  5. 结果如图,可以看到,女朋友先去了服装店,变成了女朋友买了一件衣服。这个时候,买了一件衣服的女朋友又去了小吃店,最终变成了,女朋友买了一件衣服,吃了一碗麻辣烫。

  • 46
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值