设计模式之 装饰器模式

tech2022-12-22  58

装饰器模式

由业务需求逐渐演变成装饰器模式业务场景: 星巴克卖咖啡, 一开始,只有四种咖啡: Decaf Espresso DarkRoast HouseBlend

因为所有咖啡都有共性, 所有开发人员,把它们的共性上提到一个父类中: Beverage

步骤

先定义一个抽象Beverage(饮料)类定义一个String类型的description(描述)把费用定义成抽象方法,因为每个咖啡的价格都是不一样的 abstract class Beverage { private String description; //描述 public Beverage(String description) { this.description = description; } //花费 public abstract double cost(); public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } class Decaf extends Beverage { public Decaf() { super("无咖啡因咖啡"); } @Override public double cost() { return 1; } } class Espresso extends Beverage { public Espresso() { super("浓缩咖啡"); } @Override public double cost() { return 2; } } class DarkRoast extends Beverage { public DarkRoast() { super("焦炒咖啡"); } @Override public double cost() { return 1.5; } } class HouseBlend extends Beverage { public HouseBlend() { super("混合咖啡"); } @Override public double cost() { return 3; } } //=======================时空线============================= //=========上面为服务端(提供服务,作者),下面为客户端(使用服务)===== public class AppTest { public static void main(String[] args) { Beverage b = new Decaf(); Beverage b2 = new Espresso(); Beverage b3 = new DarkRoast(); Beverage b4 = new HouseBlend(); System.out.println(b.getDescription()+ ":" + b.cost()); System.out.println(b2.getDescription()+ ":" + b2.cost()); System.out.println(b3.getDescription()+ ":" + b3.cost()); System.out.println(b4.getDescription()+ ":" + b4.cost()); } }

目前这段代码,看起来是没有问题的.问题是: 变化来了 星巴克的老板为了提高自身的竞争力,想出来一个新的业务,调料也就是说可以给咖啡放调料: 牛奶、豆浆、摩卡、泡沫(只是为了好玩)
为了满足这个需求的变化加入调料,尝试这样解决问题: 为加牛奶的Decaf咖啡创建一个类 class DecafWithMilk { } 为加牛奶的Espresso咖啡创建一个类 class EspressoWithMilk { } 为加牛奶且加豆浆的Decaf咖啡创建一个类 class DecafWithMilkAndSoy { } .....省略 这会引起,类爆炸!这样开发,简直让人想死! 尤其是后来,又加入了一个新调料: 枸杞

所有不能这样设计类


针对类爆炸的问题我们何必为每一种加每一种调料都创建一个类呢?这样做太2了,太笨了!我们可以直接在父类Beverage中,添加4个boolean属性,分别代表是否加了对应的4中调料啊 class Beverage { private String description; //描述 private boolean milk, soy, mocha, bubble; public Beverage(String description) { this.description = description; } //花费 public double cost() { double total = 0; if (milk) { total += 0.2; } if (soy) { total += 0.3; } if (mocha) { total += 0.4; } if (bubble) { total += 0.1; } return total; } public String getDescription() { String str = description; if (milk) { str = str + " 牛奶"; } if (soy) { str = str + " 豆浆"; } if (mocha) { str = str + " 摩卡"; } if (bubble) { str = str + " 泡沫"; } return str; } public void setDescription(String description) { this.description = description; } public boolean isMilk() { return milk; } public void setMilk(boolean milk) { this.milk = milk; } public boolean isSoy() { return soy; } public void setSoy(boolean soy) { this.soy = soy; } public boolean isMocha() { return mocha; } public void setMocha(boolean mocha) { this.mocha = mocha; } public boolean isBubble() { return bubble; } public void setBubble(boolean bubble) { this.bubble = bubble; } } class Decaf extends Beverage { public Decaf() { super("无咖啡因咖啡"); } @Override public double cost() { //咖啡本身价格+调料价格 return 1+super.cost(); } } class Espresso extends Beverage { public Espresso() { super("浓缩咖啡"); } @Override public double cost() { return 2+super.cost(); } } class DarkRoast extends Beverage { public DarkRoast() { super("焦炒咖啡"); } @Override public double cost() { return 1.5+super.cost(); } } class HouseBlend extends Beverage { public HouseBlend() { super("混合咖啡"); } @Override public double cost() { return 3+super.cost(); } } //=======================时空线============================= //=========上面为服务端(提供服务,作者),下面为客户端(使用服务)===== public class AppTest { public static void main(String[] args) { Beverage b = new Tea(); b.setMilk(true); b.setSoy(true); System.out.println(b.getDescription()+ ":" + b.cost()); } } 优点: 类没有爆炸,没有出现各种各样的类!星巴克的老板,又加入了一个新的饮料: 茶, 不会有什么影响,符合开闭原则 class Tea extends Beverage { public Tea() { super("茶"); } @Override public double cost() { return 2+super.cost(); } } 缺点: 星巴克的老板,又加入了一个新的调料: 枸杞, 这就要重写改写父类Beverage的cost方法和getDescription, 这势必会违反开闭原则!
针对上面的问题, 我们要使用"装饰器设计模式"来解救我们! abstract class Beverage { private String description; //描述 public Beverage(String description) { this.description = description; } //花费 public abstract double cost(); public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } abstract class Condiment extends Beverage { //让调料类关联饮料类 protected Beverage beverage; public Condiment(Beverage beverage) { super("调料"); this.beverage = beverage; } } 判断两个类能不能有继承关系,主要看这两个类有没有"is a"关系,并且还要符合里氏替换原则!以上只是原则, 不是语法强制,也就说,在特定情况下,可以违反这个规则,必须在装饰器模式中就是这样子尽管调料不是饮料,但是为了制作出装饰器模式,我们也只能让调料去继承饮料!

abstract class Condiment extends Beverage { //让调料类关联饮料类 protected Beverage beverage; public Condiment(Beverage beverage) { super("调料"); this.beverage = beverage; } } class Decaf extends Beverage { public Decaf() { super("无咖啡因咖啡"); } @Override public double cost() { //咖啡本身价格 return 1; } } class Espresso extends Beverage { public Espresso() { super("浓缩咖啡"); } @Override public double cost() { return 2; } } class DarkRoast extends Beverage { public DarkRoast() { super("焦炒咖啡"); } @Override public double cost() { return 1.5; } } class HouseBlend extends Beverage { public HouseBlend() { super("混合咖啡"); } @Override public double cost() { return 3; } } class Milk extends Condiment { public Milk(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 0.2; } @Override public String getDescription() { return beverage.getDescription() + " 牛奶"; } } class Soy extends Condiment { public Soy(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 0.3; } @Override public String getDescription() { return beverage.getDescription() + " 豆浆"; } } class Mocha extends Condiment { public Mocha(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 0.4; } @Override public String getDescription() { return beverage.getDescription() + " 摩卡"; } } class Bubble extends Condiment { public Bubble(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 0.1; } @Override public String getDescription() { return beverage.getDescription() + " 泡沫"; } } //=======================时空线============================= //=========上面为服务端(提供服务,作者),下面为客户端(使用服务)===== public class AppTest { public static void main(String[] args) { Beverage b = new Tea(); Beverage b2 = new Milk(b); System.out.println(b2.getDescription()+ ":" + b2.cost()); } }

其实装饰器模式就是要继承和关联同一个类,并且每一个套一个(类似套娃)

优点:

加一个新的饮料,不会违反开闭原则加一个新的调料.也不会违反开闭原则 //添加一个茶的饮料 class Tea extends Beverage { public Tea() { super("茶"); } @Override public double cost() { return 2; } } //添加一个枸杞的调料 class GouQi extends Condiment { public GouQi(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 1; } @Override public String getDescription() { return beverage.getDescription() + " 枸杞"; } } 缺点: 类还是有点多,但是我们已经尽力了!!
至此,学完了"装饰器模式"我们以前学习jdk的流,就是一种装饰模式的体现 public class AppTest { public static void main(String[] args) throws Exception { InputStream in = new FileInputStream("D:\\1.txt"); BufferedInputStream bin = new BufferedInputStream(in); InputStreamReader isr = new InputStreamReader(bin); isr.close(); } }

最新回复(0)