• 设计模式-装饰模式


    就在前几天,国家又出台了房地产政策,以促进那些有钱人买房。买房已经成为社会上一个热门话题,在中国人的心理,买房可是像成家立业一样的大事。那买房就能住吗?买房之后要达到人住的条件还需要进行装修。房屋装修又是一笔不菲的开销,花大价钱购买的房子那可得好好思考下装修相关事宜。自己肯定研究不明白,就去找装修公司。这时装修公司给你列举了他们负责的装修项目:

    • 水电装修一套:998$
    • 墙面粉刷:559$
    • 地板铺设:666$
    • 吊顶装饰:887$
    • 窗帘装饰:199$

    看到了吧,购买的房子如果想要达到居住的目的,还需要选择性的进行装修才行,除非你喜欢叙利亚风格。生活中像这样需要额外添加一些可选择性的功能再投入使用的产品还有很多。再比如说购买奶茶时,你还可以选择多加波波,少加糖,少加冰等选项。好,那同学们想象如何在代码中更好的实现这种场景呢?
    此时你可能会想到,既然是在原有的基础上增加功能,那就使用继承。使用继承的方式只能解决一些场景,比如房屋装修的可选项会有很多,每一种都使用继承的话就会出现继承类激增的问题。此外,房屋装修可能是几种功能选择后的组合,这种使用继承就无法实现了。我们知道既然继承不能,那肯定应该就是组合方式。但是如何组合呢?由于不同种类功能组合种类太多,简单的组合方式肯定也无法穷尽。我们今天要讲解的装饰模式,实际上就能解决这种问题的组合方式的实现。

    一、装饰模式

    装饰模式(Decorator Pattern)的一般定义如下:

    在不改变现有对象结构的情况下,动态地给一个对象增加一些额外的职责

    既然是动态的,就无法使用继承的方式来实现。我们可以考虑将这些“职责”封装为一个个类,其主要设计目的就是为了组合增强目标对象。为了保证能组合多个“职责”,那么“职责”之间也应该是可以互相增强。因此,装饰模式能够实现增强不同额外功能,并且可以很灵活的组合不同额外功能。
    概念总结:

    • 装饰模式解决对同一个对象的多种增强功能的组合方式
    • 装饰模式可以很灵活的扩展这种增强功能

    二、应用实践

    理论太直白,无法说明白到底是怎样的组合方式呢?本章节将通过一个具体的案例来给出装饰模式的一般实现方式。
    案例背景:我今天吃的炒饭,老板问我加点啥呢?火腿、花生米、玉米、肥牛还是鸡柳。晚上突然饿了,那就以这个为案例吧。原有产品就是炒饭,可选择增加的功能有很多,你可以不选择也可以选择其中几个组合起来。这种就十分适合使用装饰模式来描述。
    定义炒饭抽象类:

    /**
     * 炒饭抽象类
     */
    public abstract class FriedRice {
    
        protected String name;    // 每盘炒饭都有名字
        protected double price;   // 不同炒饭的价格不同
    
        public FriedRice() {
        }
    
        public FriedRice(String name, double price) {
            this.name = name;
            this.price = price;
        }
    
        public abstract String getName();
        public abstract double getPrice();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    具体的一份普通炒饭类:

    public class PlainFriedRice extends FriedRice{
    
        public PlainFriedRice(String name, double price) {
            super(name, price);
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public double getPrice() {
            return price;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    配料装饰器:

    /**
     * 配料装饰器
     */
    public class IngredientDecorator extends FriedRice {
    
        private FriedRice friedRice;
    
        public IngredientDecorator(FriedRice friedRice) {
            this.friedRice = friedRice;
        }
    
        @Override
        public String getName() {
            return friedRice.getName();
        }
    
        @Override
        public double getPrice() {
            return friedRice.getPrice();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    火腿炒饭:

    public class HamDecorator extends IngredientDecorator{
    
        public HamDecorator(FriedRice friedRice) {
            super(friedRice);
        }
    
        @Override
        public String getName() {
            return super.getName() + " + 火腿";
        }
    
        @Override
        public double getPrice() {
            return super.getPrice() + 2.0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    鸡柳炒饭:

    public class ChickenDecorator extends IngredientDecorator {
    
        public ChickenDecorator(FriedRice friedRice) {
            super(friedRice);
        }
    
        @Override
        public String getName() {
            return super.getName() + " + 鸡柳";
        }
    
        @Override
        public double getPrice() {
            return super.getPrice() + 2.5;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    如上代码及类图所示,为了能够实现对FriedRice具体实现类的增强,装饰类(如ChickenDecorator)通过组合的方式来实现增强后的逻辑。
    为何什么这里通过继承IngredientDecorator呢?因为不同装饰类可能需要修改(或增强)的方法不同,那么其他方法不应该是装饰类来声明,意即装饰类应只重写其负责的业务逻辑。那其他方法就应该调用目标对象的方法。这部分由IngredientDecorator负责,可以看到IngredientDecorator会重写所有FriedRice方法,并且都是直接调用目标对象的方法。如果需要增加新的装饰类,就只需要新增一个继承IngredientDecorator的类并实现其所要增强的方法即可。

    装饰模式的优点:

    • 装饰模式将目标类和装饰类组之间解耦
    • 装饰模式比继承更加灵活可扩展,并且可以组合不同装饰的多样化结果

    装饰的缺点:

    • 装饰也可能会出现类激增的情况

    三、装饰模式Vs代理模式

    我们在前面讲解代理模式一文中提到,实现代理模式的方式可分为继承与组合两种方式。那本文装饰模式和代理模式的区别在哪里呢?
    此处再重申下两种模式的定义:

    • 装饰模式:在不改变现有对象结构的情况下,动态地给一个对象增加一些额外的职责
    • 代理模式:为其他对象提供一种代理以控制对这个对象的访问。
      这二者虽然在代码实现上都可以通过组合方式来实现增强,但是其业务含义存在很大的差异。装饰模式中目标类是一个(或一类,即同一个抽象的多种实现),而拥有增强装饰类是可以有多个,多种不同的组合方式。相反,在代理模式中目标类可能是多种,而代理类的功能是一种。因此,二者中的目标类:增强类一个是1:N,一个却是N:1的区别。
  • 相关阅读:
    LQ0009 平方十位数【枚举】
    CMMI5认证哪些企业可以申请
    Qt 画自定义饼图统计的例子
    从Docker容器内部访问宿主的IP地址
    【计算机组成原理】第一章 概论
    Python大数据处理利器之Pyspark详解
    高效安全的无线解决方案如何为城市提供优质水源?
    Nacos 安装教程(史上最详细保姆级教程)
    3层结构+7大特点,带你认识华为云IoTEdge
    写一个函数实现:将一个5*5的矩阵中最大的元素放在中心,4个角分别放4个最小的元素(顺序为从左到右,从上到下依次从小到大存放)之解法改写
  • 原文地址:https://blog.csdn.net/Pl_Sun/article/details/132957683