Android 开发模式和优秀框架那些事Java知识书屋Java

设计模式之装饰器模式(十六)

2019-07-06  本文已影响5人  3d0829501918

在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象,这就是装饰器模式。

一、设计模式的角色

类图

二、适配器模式与装饰器模式的区别

  装饰器与适配器都有一个别名叫做包装模式(Wrapper)(适配器模式包装是为了接口兼容性,装饰器模式包装是为了动态的添加方法或者是行为),它们都是起到包装一个类或对象的作用,但是使用时,它们的目的不一样。

  适配器只需要将原接口转化为客户希望的另一个接口,就是适配器模式。转化无非2点:

 继承原类或者实现原接口
 持有原接口的对象,再实现目标接口。

  在装饰器模式中,必须要有被装饰的类和装饰的类。装饰模式的一定是从外部传入的,并且可以没有顺序,按照实际的需求随意调换执行的顺序。 装饰模式注重的是功能的拓展。


三、这是一个很短很短的故事

  小明和小白和小农去吃手抓饼,小明点了一个普通手抓饼、小白点了一个加蛋的手抓饼,小农点了一个奢华版的手抓饼(加蛋加牛肉)接下来我们用代码来实现吧。

public interface Cake {
    // 名字
    public String nameDetails();
    // 价格
    public double price();
}
public class CakeImpl implements Cake {
 @Override
 public String nameDetails() {
     return "普通版本手抓饼";
 }
 
 @Override
 public double price() {
     return 5.0;
}
}

 public class EggCakeImpl implements Cake {
 @Override
 public String nameDetails() {
     return "加蛋手抓饼";
 }
 
 @Override
 public double price() {
     return 6.0;
}
}

 public class BeefCakeImpl implements Cake {
 
 @Override
 public String nameDetails() {
     return "加牛肉手抓饼";
 }
 
 @Override
 public double price() {
    return 7.0;
}
}

 public class BeefAndEggCakeImpl implements  Cake {
 @Override
 public String nameDetails() {
     return "加蛋加牛肉的手抓饼";
 }
 
 @Override
 public double price() {
     return 8.0;
}
}
  public static void main(String [] args) {
     Cake cake = new CakeImpl();
     System.out.println("小明点了一份:"+cake.nameDetails()+"价格:"+cake.price());
 
 
     Cake cake1 = new EggCakeImpl();
     System.out.println("小白点了一份:"+cake1.nameDetails()+"价格:"+cake1.price());
 
 
    Cake cake2 = new BeefAndEggCakeImpl();
    System.out.println("小农点了一份:"+cake2.nameDetails()+"价格:"+cake2.price());

}
 小明点了一份:普通版本手抓饼价格:5.0
 小白点了一份:加蛋手抓饼价格:6.0
 小农点了一份:加蛋加牛肉的手抓饼价格:8.0

  通过业务我们可以想到会有多种形式的手抓饼的类型,手抓饼我们可以看成对象,对象有多种组合,比如手抓饼可能有十几种原材料,如果有多种组合方式,我们就需要多种类。目前3种原材料就需要多种类进行实现,这样做是可以实现的,但是不符合设计模式编程,接下来我们用装饰器模式进行实现。

抽象对象类

public interface Cake {
    // 名字
    public String nameDetails();
    // 价格
    public double price();
}

基础对象类

 public class CakeImpl implements Cake {
     @Override
     public String nameDetails() {
         return "手抓饼";
     }
 
     @Override
     public double price() {
         return 5.0;
    }
}

装饰者抽象类

 public abstract class CakeDecorator implements Cake {
 
    Cake cake;
     public CakeDecorator (Cake cake) {
         this.cake = cake;
     }
 
    @Override
    public String nameDetails() {
        return cake.nameDetails();
    }

    @Override
    public double price() {
        return cake.price();
    }
}

具体装饰者类

 public class EggCakeImpl extends  CakeDecorator {
 
     public EggCakeImpl(Cake cake) {
         super(cake);
     }
 
     @Override
     public String nameDetails() {
         return "鸡蛋"+cake.nameDetails();
    }

    @Override
    public double price() {
        return cake.price()+1.5;
    }
}


 public class BeefCakeImpl extends  CakeDecorator {
 
     public BeefCakeImpl(Cake cake) {
         super(cake);
     }
 
     @Override
     public String nameDetails() {
         return "牛肉"+cake.nameDetails();
    }

    @Override
    public double price() {
        return cake.price()+2.0;
    }
}

测试类

 public static void main(String [] args) {
     Cake cake = new CakeImpl();
     System.out.println("小明点了一份:"+cake.nameDetails() +"价格:"+cake.price());
 
     Cake cake1 = new EggCakeImpl(cake);
     System.out.println("小白点了一份:"+cake1.nameDetails() +"价格:"+cake1.price());
     // 标识了制作顺序与流程,再生产中流水线制作程序时,可能会比较严格的把控,制作流程。
     // 装饰者模式不仅仅完成操作细节的流程,提供很好的调用方式。
     Cake cake3 = new BeefCakeImpl(cake1);
    System.out.println("小农点了一份:"+cake3.nameDetails() +"价格:"+cake3.price());
}

测试结果

  小明点了一份:手抓饼价格:5.0
  小白点了一份:鸡蛋手抓饼价格:6.0
  小农点了一份:牛肉鸡蛋手抓饼价格:8.0

四、适配器模式的优缺点

  装饰者模式可以提供比继承更多的灵活性。
  可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
  通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
  具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。

  会产生很多的小对象,增加了系统的复杂性 。
  这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。


五、 应用场景

  在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩展或者继承。


六、jdk中的应用

java.io.BufferedInputStream(InputStream)
java.io.DataInputStream(InputStream)
java.io.BufferedOutputStream(OutputStream)


七、源码在这里

本文的代码GitHub地址:
   https://github.com/xiaonongOne/decorator-test


注意啦! 往期设计模式在这里

上一篇 下一篇

猜你喜欢

热点阅读