关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

装饰者模式(Decorator Pattern)

发布时间:2023-06-26 18:59:57

咖啡案例

来源:

https://zhuanlan.zhihu.com/p/64584677

我们有一个原味咖啡,有顾客要原味,有顾客要加,有顾客要先加奶在加糖,有顾客要先加糖在加奶。

  • OriginalCoffee :原味咖啡
  • MilkDecorator :加奶
  • SugarDecorator :加糖

这个问题首先不变的就是原味咖啡,其他的都是在原味咖啡上进行添加。

public interface ICoffee {  void makeCoffee(); }  public class OriginalCoffee implements ICoffee {  @Override  public void makeCoffee() {  System.out.print("原味咖啡 ");  } }  public abstract class CoffeeDecorator implements ICoffee {  private ICoffee coffee;  public CoffeeDecorator(ICoffee coffee){  this.coffee=coffee;  }   @Override  public void makeCoffee() {  coffee.makeCoffee();  } }  public class MilkDecorator extends CoffeeDecorator {  public MilkDecorator(ICoffee coffee) {  super(coffee);  }  @Override  public void makeCoffee() {  super.makeCoffee();  addMilk();  }  private void addMilk(){  System.out.print("加奶 ");  } } public class SugarDecorator extends CoffeeDecorator {  public SugarDecorator(ICoffee coffee) {  super(coffee);  }  @Override  public void makeCoffee() {  super.makeCoffee();  addSugar();  }  private void addSugar(){  System.out.print("加糖");  } }  public static void main(String[] args) {  //原味咖啡  ICoffee coffee=new OriginalCoffee();  coffee.makeCoffee();  System.out.println("");   //加奶的咖啡  ICoffee coffee1 = new MilkDecorator(coffee);  coffee1.makeCoffee();  System.out.println("");   //先加奶后加糖的咖啡  ICoffee coffee2 =new SugarDecorator(coffee1);  coffee2.makeCoffee(); } // 输出 原味咖啡 原味咖啡 加奶 原味咖啡 加奶 加糖

   


煎饼案例

public abstract class Pancake {   String description = "普通煎饼";   public String getDescription(){   return description;   }   public abstract double cost();  } // 定义具体的被装饰者,这里是煎饼果子,当然还可以有鸡蛋灌饼、手抓饼等其他被装饰者 public class Battercake extends Pancake {  @Override  public double cost() {  return 8;  }   public Battercake(){  description = "煎饼果子";  } } // 抽象的装饰器对象,定义一个调料抽象类 public abstract class CondimentDecorator extends Pancake {   // 持有组件对象  protected Pancake pancake;  public CondimentDecorator(Pancake pancake){  this.pancake = pancake;  }   public abstract String getDescription(); }  // 具体的装饰者,我们定义一个鸡蛋装饰器,一个火腿装饰器 public class Egg extends CondimentDecorator {  public Egg(Pancake pancake){  super(pancake);  }   @Override  public String getDescription() {  return pancake.getDescription() + "加鸡蛋";  }   @Override  public double cost() {  return pancake.cost() + 1;  } }  public class Sausage extends CondimentDecorator{  public Sausage(Pancake pancake){  super(pancake);  }  @Override  public String getDescription() {  return pancake.getDescription() + "加火腿";  }   @Override  public double cost() {  return pancake.cost() + 2;  } }   public static void main(String[] args) {  //买一个普通的煎饼果子  Pancake battercake = new Battercake();  System.out.println(battercake.getDescription() + "花费:"+battercake.cost() + "元");   //买一个加双蛋的煎饼果子  Pancake doubleEgg = new Battercake();  doubleEgg = new Egg(doubleEgg);  doubleEgg = new Egg(doubleEgg);  System.out.println(doubleEgg.getDescription() + "花费" + doubleEgg.cost() + "元");   //加火腿和鸡蛋  Pancake battercakePlus = new Battercake();  battercakePlus = new Egg(battercakePlus);  battercakePlus = new Sausage(battercakePlus);  System.out.println(battercakePlus.getDescription() + "花费" + battercakePlus.cost() + "元");  }  煎饼果子花费:8.0元 煎饼果子加鸡蛋加鸡蛋花费10.0元 煎饼果子加鸡蛋加火腿花费11.0元

   


总结


类图

Component:抽象组件,装饰者和被装饰者共同的父类,是一个接口或者抽象类,用来定义基本行为

ConcreteComponent:具体的组件对象,实现类 ,即被装饰者,通常就是被装饰器装饰的原始对象

Decorator:所有装饰器的抽象父类,一般是抽象类,实现接口;它的属性必然有个指向

ConcreteDecorator:具体的装饰对象,实现具体要被装饰对象添加的功能。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。


理解

装饰模式的本质:动态组合而不是全部穷举

装饰对象动态的对被装饰者进行装饰。

优缺点

装饰模式的优点:

装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。

通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”


装饰模式的缺点:

使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。

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


运用

  1. Java 中的 I/O 类库:Java 中的 I/O 类库使用了装饰模式来实现对输入输出流的扩展。例如,BufferedInputStream 和 BufferedOutputStream 可以用来提高读取和写入的效率,而 InputStreamReader 和 OutputStreamWriter 可以用来处理字符集编码和解码。
  2. Spring 中的 AOP:Spring AOP(面向切面编程)是基于装饰模式实现的,它可以通过切面(Aspect)来对目标对象的方法进行拦截和增强。例如,我们可以使用 @Transactional 注解来为一个方法添加事务处理的功能,而不需要修改原始方法的代码。




/template/Home/leiyu/PC/Static