• 初学设计模式【3】装饰模式——Decorator


      装饰模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

    如定义中说的那样,装饰模式可以动态的为对象添加新的职责,通过继承也能达到扩展类功能的目的。那么这两种方式的区别在哪里呢?虽然通过继承也能达到扩展功能但这种方式是在编译时静态决定的,所有子类都会继承到相同的行为,不具有弹性,应对未来变化的能力较低。而通过Decorator模式就可以在运行时动态扩展,而且被装饰对象可以自由组合新的行为,更易于应对未来的功能扩展。

    设计原则:类应该对扩展开放,对修改关闭这就是“开放关闭”原则。类应该是容易扩展的在不需要修改现有代码的情况下,这样的设计可以应对改变,而且不会引入新的bug。Decorator模式就很好的遵循了这个原则。

    UML类图

      

      分析:ConcreteComponent代表一个被装饰对象,扩展自抽象类Component;Decorator是装饰者共同实现的接口;派生自Decorator的为具体的装饰对象;至于装饰者与被装饰者共同派生自Component是为了达到“装饰者与被装饰者”的类型匹配,因为我们用Decorator装饰了ConcreteComponent后不能改变ConcreteComponent的类型;Component不实现具体的行为,只是为了达到前面所说的装饰与被装饰者的类型匹配。

    实例

      咖啡馆下单系统:顾客可以选择咖啡的类型,还可以选择加入一种或多种调料,最后系统可以根据不同的咖啡类型及所加调料计算出总金额,打印出清单。

    显然,我们用调料来装饰具体咖啡,uml图如下:

    截自:《HeadFirst设计模式》

    Beverage

    1 public abstract class Beverage {
    2     String description = "Unknown Beverage";
    3 
    4     public String getDescription() {
    5         return description;
    6     }
    7 
    8     public abstract float cost();
    9 }

    Decaf

     1 public class Decaf extends Beverage {
     2 
     3     public Decaf(String description) {
     4         super.description = description;
     5     }
     6 
     7     @Override
     8     public float cost() {
     9         return 1.2f;
    10     }
    11 
    12 }

    Espresso

    View Code
     1 public class Espresso extends Beverage {
     2 
     3     public Espresso(String descraption) {
     4         super.description = descraption;
     5     }
     6 
     7     @Override
     8     public float cost() {
     9         return 1.00f;
    10     }
    11 
    12 }

    CondimentDecorator

    1 public abstract class CondimentDecorator extends Beverage {
    2     @Override
    3     public abstract String getDescription();
    4 
    5 }

    Milk

     1 public class Milk extends CondimentDecorator {
     2 
     3     private Beverage beverage; // 代表被装饰对象
     4 
     5     public Milk(Beverage beverage) {
     6         this.beverage = beverage;
     7     }
     8 
     9     @Override
    10     public String getDescription() {
    11         return beverage.getDescription() + "+ Milk";
    12     }
    13 
    14     @Override
    15     public float cost() {
    16         return 0.31f + beverage.cost();
    17     }
    18 
    19 }

    Mocha

    View Code
     1 public class Mocha extends CondimentDecorator {
     2 
     3     private Beverage beverage; // 代表被装饰对象
     4 
     5     public Mocha(Beverage beverage) {
     6         this.beverage = beverage;
     7     }
     8 
     9     @Override
    10     public String getDescription() {
    11         return beverage.getDescription() + "+ Mocha";
    12     }
    13 
    14     @Override
    15     public float cost() {
    16         return 0.11f + beverage.cost();
    17     }
    18 
    19 }

     Soy

    View Code
     1 public class Soy extends CondimentDecorator {
     2     private Beverage beverage; // 代表被装饰对象
     3 
     4     public Soy(Beverage beverage) {
     5         this.beverage = beverage;
     6     }
     7 
     8     @Override
     9     public String getDescription() {
    10         return beverage.getDescription() + "+ Soy";
    11     }
    12 
    13     @Override
    14     public float cost() {
    15         return 0.21f + beverage.cost();
    16     }
    17 
    18 }

     测试

     1 import org.junit.Test;
     2 
     3 public class TestDecorator {
     4 
     5     @Test
     6     public void test() {
     7         Beverage beverage = new Decaf("Decaf");
     8         beverage = new Milk(beverage);
     9         beverage = new Milk(beverage);
    10         beverage = new Mocha(beverage);
    11         System.out.println(beverage.getDescription() + ":" + beverage.cost());
    12     }
    13 }

       测试结果:(双倍牛奶+摩卡+无咖啡因咖啡)

     

    总结

     1)装饰模式的根类Component一般为abstract或接口类型,设置此类的目的只为了达到装饰者与被装饰者类型的匹配,不实现具体的行为,这一点很重要,可以结合上面的例子仔细体会。

    2)装饰模式也有缺点:会增加我们所要维护类的数量。此模式一般与“工厂模式”一起使用,可克服此问题。

    3)装饰模式可以达到运行时动态扩展系统功能的目的,通过不断扩展新的具体装饰类可以提高应对未来变化的能力。

     4)java IO类就是依照这个模式设计的

  • 相关阅读:
    一周优化内存、查询速度小结
    三种不同的方式,计算欧氏距离,速度比较
    三种不同的方式,计算欧氏距离,速度比较
    多线程与多进程
    匹配错误分析
    fasttext与Linear SVC 分类测试结果
    什么是闭包
    智能客服功能页面
    客服培训
    约当产量法
  • 原文地址:https://www.cnblogs.com/byghui/p/3045026.html
Copyright © 2020-2023  润新知