zoukankan      html  css  js  c++  java
  • 设计模式之装饰者模式

    装饰者模式

    概述

    装饰模式(decorator):表示动态的给一个对象添加一些新的功能(利用子类继承父类也可以实现),但是比生成子类方式更灵活。

    也叫装饰者模式或者装饰器模式。在理解装饰者模式最重要的就是理解组合和委托的两种思想,我们平时遇到的装饰者模式有IO集合、Android中的view等。

    UML

    - Component:定义一个对象接口,可以给这些对象动态添加职责。真实对象和装饰者对象有相同的接口,这样客户端不用知道内部有装饰者对象(Decorator)存在的,还是以之前处理真实对象的相同方式来和装饰者对象交互。

    • ConcreteComponent:是定义了一个具体的对象(例如:人),也可以给这个对象添加一些其他职责。
    • Decorator:装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对Component来说,是无需知道Decorator的
    • ConcreteDecorator:就是具体的装饰对象了(衣服,鞋子..),它起到了给Component添加职责的功能。

    使用场景

    • 需要透明且动态的扩展类的功能时
    • 实例
      • IO中输入流和输出流
      • Swing包中图形界面构件功能
      • Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,增强了request对象的功能。
      • Struts2中,request,response,session对象的处理。

    示例

    一杯主饮料(Beverage)需要加入各种调料,比如蒸奶、豆浆、摩卡、奶泡等,最后需要算出加入调料后饮料的价格。

    • 如果只是几种固定的饮料进行组合和搭配那么容易实现,但是这些都是动态随机的,并且可能以后会有更多的新饮料。那么如何进行动态的组合呢?
    • 如果使用组合的方式,效果会如何?

    用装饰者构造饮料

    以装饰者的思想构建饮料可以理解为:将饮料作为一个主体,调料作为装饰,主体和装饰是分离的,装饰可以以任何顺序和数量动态添加到主体上。也体现出组合的效果,不用在现有的代码上做任何修改,只需要添加新功能就可以(不用改变主饮料,按需求意愿添加调料),组合效果如图

    img

    装饰者可以一层层的把主体包裹起来,那么装饰者(两种调料Mocha和Soy)和主体(一种叫HouseBlend的咖啡)的类型应该保持一致。

    UML

    img

      可以看出,装饰者和主体都是Beverage类型,同时beverage可以委托给具体的饮料如Espresso和HouseBlend或者调料Mocha和Soy计算出未被装饰(未加调料)或者装饰后(加调料)的价格cost。这是可以通过继承来实现的。在结构图中Beverage和CondimentsDecorator都是虚类来控制必须实现的方法。

    代码实现

    这里只写了两个主体和两个调料,其实自己可以测试更多的主体饮料和多种调料的自由组合,在实际中肯定不止这几个类,那么弄清楚装饰者模式的结构就显得尤为重要。

    定义两个虚类:

    public abstract class Beverage {
        String description = "Unknown Beverage";
    
        public String getDescription() {
            return description;
        }
    
        public abstract double cost();
    }
    
    public abstract class CondimentDecorator extends Beverage {
        public abstract String getDescription();
    }
    
    

    两个主体饮料(Espresso和HouseBlend)

    public class Espresso extends Beverage {
    
        public Espresso(){
            description = "Espresso";
        }
    
        @Override
        public double cost() {
            return 1.99;
        }
    }
    
    public class HouseBlend extends Beverage {
    
        public HouseBlend(){
            description = "HouseBlend";
        }
    
        @Override
        public double cost() {
            // TODO Auto-generated method stub
            return 0.89;
        }
    }
    

    两种调料(Mocha和Soy)

    public class Mocha extends CondimentDecorator {
    
        Beverage beverage;
    
        public Mocha(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public String getDescription() {
            // TODO Auto-generated method stub
            return beverage.getDescription() + ", Mocha";
        }
    
        @Override
        public double cost() {
            // TODO Auto-generated method stub
            return beverage.cost() + 0.20;
        }
    }
    
    public class Soy extends CondimentDecorator {
        Beverage beverage;
    
        public Soy(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public String getDescription() {
            // TODO Auto-generated method stub
            return beverage.getDescription() + " ,Soy";
        }
    
        @Override
        public double cost() {
            // TODO Auto-generated method stub
            return beverage.cost() + 0.30;
        }
    }
    

    测试类

    public class Test {
        public static void main(String[] args) {
            Beverage beverage = new Espresso();
            //任何调料都不加
            System.out.println(beverage.getDescription() + " $" + beverage.cost());
    
            Beverage beverage2 = new HouseBlend();
            beverage2 = new Mocha(beverage2);
            beverage2 = new Soy(beverage2);
            //加Mocah和Soy
            System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
        }
    }
    

    优缺点

    优点:

    • 扩展功能强,相比继承来说更灵活。继承的话会导致子类个数增加。而装饰者模式不会出现这种情况。
    • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象。
    • 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的构件子类和具体装饰类。

    缺点:

    • 产生很多小对象,大量小对象会占据内存。一定程度上影响了性能
    • ​ 装饰模式易于出错,调试排查比较麻烦。

    see source code

  • 相关阅读:
    171 01 Android 零基础入门 03 Java常用工具类02 Java包装类 01 包装类简介 01 Java包装类内容简介
    170 01 Android 零基础入门 03 Java常用工具类01 Java异常 08 Java异常总结 01 异常总结
    169 01 Android 零基础入门 03 Java常用工具类01 Java异常 07 异常链 01 异常链简介
    168 01 Android 零基础入门 03 Java常用工具类01 Java异常 06 自定义异常 01 自定义异常类
    167 01 Android 零基础入门 03 Java常用工具类01 Java异常 05 使用throw和throws实现异常处理 02 使用throw抛出异常对象
    166 01 Android 零基础入门 03 Java常用工具类01 Java异常 05 使用throw和throws实现异常处理 01 使用throws声明异常类型
    165 01 Android 零基础入门 03 Java常用工具类01 Java异常 04 使用try…catch…finally实现异常处理 05 return关键字在异常处理中的使用
    DevExpress WPF v20.2版本亮点放送:全新升级的PDF Viewer
    界面控件DevExpress使用教程:Dashboard – 自定义导出
    DevExpress WinForms帮助文档:表单控件
  • 原文地址:https://www.cnblogs.com/Dyleaf/p/8507047.html
Copyright © 2011-2022 走看看