zoukankan      html  css  js  c++  java
  • 设计模式系列之装饰模式(Decorator Pattern)

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。我们通过下面的实例来演示装饰器模式的用法。我们的实例是实现餐馆点餐点,基础食物:米饭,你可以搭配啤酒,可乐.鱼或者蔬菜.

    • 意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

    • 主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

    • 何时使用:在不想增加很多子类的情况下扩展类。

    • 如何解决:将具体功能职责划分,同时继承装饰者模式。

    • 关键代码:

    • 1、Beverage(材料) 类充当抽象角色,不应该具体实现。

    • 2、修饰类引用和继承 Beverage(材料) 类,具体扩展类重写父类方法。

    • 应用实例:

      • 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。
      • 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
    • 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

    • 缺点:多层装饰比较复杂。

    • 使用场景:

      • 1、扩展一个类的功能。
      • 2、动态增加功能,动态撤销。

    注意事项:可代替继承。

    代码示例

    如上面所说,我们要实现参观点餐的需求,使用啤酒.鱼等辅助食材来装饰米饭(Rich)对象,要求这些材料具有相同的基类,那么下面我们先定义Beverage(材料类).

    package com.zhoutao123.design.pattern.decorator;
    
    
    /**
     * 材料
     */
    public abstract class Beverage {
    
        private String desc = "未知食材";
    
        // 抽象方法,返回价钱
        public abstract float getCost();
    
        // 返回描述
        public  String getDesc(){
            return desc;
        }
    }
    
    

    然后我们定义米饭,米饭需要继承Beverage类

    package com.zhoutao123.design.pattern.decorator;
    
    /**
     * 定义米饭
     */
    public class Rice extends Beverage{
    
        @Override
        public String getDesc() {
            return "米饭,";
        }
    
        @Override
        public float getCost() {
            return 12;
        }
    }
    

    定义一些其他食材.

    package com.zhoutao123.design.pattern.decorator;
    
    /**
     * 定义啤酒
     */
    public class Beer extends Beverage {
    
        private Beverage beverage;
    
        public Beer(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public String getDesc() {
            return beverage.getDesc()+"Beer,";
        }
    
        @Override
        public float getCost() {
            return 0.2f + beverage.getCost();
        }
    }
    
    

    剩下的重复性动作就不在定义了,可以仿照Beer一次实现Cola(可乐) Fish(鱼) Vegetable(蔬菜)由于这是示例,代码比较简单,就不做过多的说明了,大致可以看下UML

    • getDesc() 获取商品描述
    • getCost() 获取商品的价格

    装饰模式示例-关系继承图.png

    下面看一些测试代码,可以看到可以使用随意的商品修饰Rich,而不用担心耦合.基础Rich需要什么,我么就可以去装饰它.

    package com.zhoutao123.design.pattern.decorator;
    
    /**
     * 测试装饰者模式
     */
    public class TestDecorator {
    
        public static void main(String[] args) {
            // 首先定义米饭
            System.out.println("------------一个普通的晚餐订单---------------");
            Beverage beverage = new Rice();
    
            //分别用啤酒,可乐和鱼等食物装饰米饭
            // 得到一份晚餐
            beverage = new Beer(beverage);
            beverage = new Fish(beverage);
            beverage = new Cola(beverage);
    
            // 显示晚餐列表
            System.out.println(beverage.getDesc());
            // 显示晚餐总金额
            System.out.println(beverage.getCost());
    
    
    
            // 一位素食主义者的订单
            System.out.println("------------一位素食主义者的订单---------------");
            Beverage food = new Rice();
            food = new Vegetables(food);
            // 计算价钱和想清单
            // 显示晚餐列表
            System.out.println(food.getDesc());
            // 显示晚餐总金额
            System.out.println(food.getCost());
        }
    
    }
    
    

    结果如下,(float类型产生的误差请忽略):

    ------------一个普通的晚餐订单---------------
    米饭,啤酒,红烧鱼,可乐,
    13.400001
    ------------一位素食主义者的订单---------------
    米饭,Vegetables,
    12.5
    
  • 相关阅读:
    alpha版、beta版、rc版的意思
    回车符(CR)与换行符(LF), ' '和' '的区别
    html 图像映射(一个图像多个连接)
    前端工程师需要明白的「像素」
    通过设置Ionic-Cli代理解决ionic serve跨域调试问题
    Bootstrap Table使用方法详解
    写给移动开发者的 React Native 指南
    Mac中搭建 iOS 的 React Native 环境
    Windows版本搭建安装React Native环境配置及相关问题
    javascript获取wx.config内部字段解决微信分享
  • 原文地址:https://www.cnblogs.com/zhoutao825638/p/10382264.html
Copyright © 2011-2022 走看看