Java23中设计模式之”装饰者模式”
目录:
一 “开放-闭合原则”(开闭原则)
二 什么是“装饰者模式”--图解
三 “装饰者模式”实例--星笆兹饮料厂
四 没有完美的设计模式--“装饰模式的不足之处”
一“开放-闭合原则”(开闭原则)
开闭原则: 类应该对拓展开放, 对修改关闭. 听起来好像不太舒服, 类既然需要”拓展’,怎么会不需要修改呢? 开闭原则针对类; 不修改类, 但是需要拓展功能. 使用继承的方式通常是做不到的, 原因是: 继承在编译时决定”行为”, 不是在运行时动态修改行为,也就是所谓的”拓展”,继承的原因是”行为复用”, 不足之处是无法动态拓行为(缺少弹性).
<head first 设计模式>:
我们的目的是允许类容易拓展, 在不修改代码的情况下, 就可以匹配新的行为, 这样的设计具有弹性可以应对变化,可以接受新的功能应对改变的需求
通常实现开闭原则的方式不止一种, 装饰者模式是很好的例子! 我自己认为: 实现开闭原则要注意以下00(面向对象)原则
(1)面向接口(接口或者抽象类)编程,
(2)多用组合, 少用继承
举个例子:
//使用Person的客户 public class ObserverDesign{ Person person; public ObserverDesign() {
} public void setPerson(Personp){ this.person=p; } public void sayOK(){ person.say(); } } //Person接口 interface Person{ void say(); }
//Student类 class Student implements Person { @Override public void say() { System.out.println("I am a student"); } } //Teacher0000000000000000类 class Teacher implements Person { @Override public void say() { System.out.println("I am a Teacher"); } } |
ObserverDesign类符合”开闭原则”, ObserverDesign从来不关心Person是哪个具体实例,只关心执行sayOK行为,我们测试一下
public class Design{
public static void main(String[] args) { //创建ObserverDesign ObserverDesign obj=new ObserverDesign(); //student Student stu=new Student(); //teacher Teacher teacher=new Teacher(); //动态设置属性 obj.setPerson(stu); obj.sayOK(); //修改 obj.setPerson(teacher); obj.sayOK(); }
}
|
二 什么是“装饰者模式”--图解
(1)装饰者模式定义: 动态地将责任附加到对象上,若要拓展功能,装饰者提供比继承更有弹性的替代方案
(2)来自<head first 设计模式>的一张实现类图(自己不想画图)
其中Decorate是装饰者抽象接口, ConcreteDecoratorA、ConcreteDecoratorB是
”装饰者”类, ConcreteComponent是”被装饰者”
三“装饰者模式”实例--星笆兹饮料厂
这个案例来自<head first 设计模式>, 购买饮料时, 我们可以要求在其中加各种不同的调料(变化), 比如买一杯鲜果奶茶, 可以要求在里面加苹果汁、哈密瓜汁、奶酪等调料。
我们还需要计算出饮料+调料的总价钱(没学习设计模式之前你会怎么做? 看看你的程序有没足够的弹性, 又有多少依赖)
下面我们使用”装饰者模式”定义的类图来编码:
首先我们需要一个饮料抽象类, 装饰者和被装饰者都要直接或者间接实现该接口, 想想我们Java中IO流学习时, 我们是不是经常使用BufferInputStream"替换"InputStream? 因为他们实现了统一的接口(类型一致). 装饰者对象替换被装饰对象inputStream,并且拓展了他的功能。所以我们需要实现统一接口。 代码如下:
饮料抽象接口
public abstract class Beverage{ protected String desc="unkonw"; //实现 public String getDescription(){ return desc; } //抽象,计算价格 public abstract double cost(); }
|
2个具体的饮料
import cn.design.decorate.inter.Beverage;
public class Espressoextends Beverage { public Espresso(){ desc="Espresso"; } @Override public double cost() { return 1.2; } //设置饮料描述 public void setDesc(Stringdesc) { this.desc=desc; } } |
package cn.design.decorate;
import cn.design.decorate.inter.Beverage;
public class HouseBlendextends Beverage { public HouseBlend() { desc="HouseBlend"; } @Override public double cost() { return 0.8; } //设置饮料描述 public void setDesc(Stringdesc) { this.desc=desc; } }
|
现在: 饮料抽象接口有了, 具体的饮料也有了,现在我们来实现装饰者
首先,来一个抽象接口
package cn.design.decorate.inter;
public abstract class CondimentDecorateextends Beverage{ //从新定义描述行为 public abstract String getDescription(); }
|
具体的装饰者---调料
package cn.design.decorate;
import cn.design.decorate.inter.Beverage; import cn.design.decorate.inter.CondimentDecorate;
public class Mochaextends CondimentDecorate{ //被装饰者: 饮料 private Beverage beverage; //设置饮料具体实现---但是Mocha装饰者不关心是那种饮料 public void setBeverage(Beveragebeverage) { this.beverage =beverage; } //构造器,初始化beverage public Mocha(Beveragebeverage) { this.beverage=beverage; } //构造器: 也可以什么都不做 public Mocha() { } @Override public String getDescription() { //被装饰饮料描述 + 装饰者调料描述(可以通过设置变量来修改) return beverage.getDescription()+",Mocha"; }
@Override public double cost() { //价钱: 被装饰饮料价钱 + 装饰者调料价钱 return beverage.cost()+ 0.6; }
}
|
package cn.design.decorate;
import cn.design.decorate.inter.Beverage; import cn.design.decorate.inter.CondimentDecorate;
public class Whipextends CondimentDecorate{ //被装饰者: 饮料 private Beverage beverage; //设置饮料具体实现---但是Whip装饰者不关心是那种饮料 public void setBeverage(Beveragebeverage) { this.beverage =beverage; } //构造器,初始化beverage public Whip(Beveragebeverage) { this.beverage=beverage; } //构造器: 也可以什么都不做 public Whip() { } @Override public String getDescription() { //被装饰饮料描述 + 装饰者调料描述(可以通过设置变量来修改) return beverage.getDescription()+",Whip"; } @Override public double cost() { //价钱: 被装饰饮料价钱 + 装饰者调料价钱 return beverage.cost()+ 1.2; }
}
|
一切顺利准备好了, 现在我们坐下来喝点饮料. 你想要什么调料的饮料?
package cn.design.decorate;
import cn.design.decorate.inter.Beverage;
/* * “策略模式” * */ public class Client{ public static void main(String[] args) { //(1)用Mocha装饰Espresso饮料 Beverage beverage=new Mocha(new Espresso()); System.out.println("Description:"+beverage.getDescription()); System.out.println("cost:"+beverage.cost()); System.out.println("````````````````"); //(2)Espresso饮料中含有Mocha, 再加点Whip会不会更加美味? beverage=new Whip(beverage); System.out.println("Description:"+beverage.getDescription()); System.out.println("cost:"+beverage.cost());
}
}
|
例子(2)是带有Mocha ,whip的Espresso饮料。我们的
Beverage beverage=new Mocha(new Espresso()); 想不想: BufferedInputStreamis=new BufferedInputStream(new InputStream(){..});
总结: 代码终于写完了,简单运用了一下"装饰者模式",代码看起来好像舒服一些.
一 现在来看看, 我们的装饰者类是符合开闭原则的,再不要修改代码的情况下, 可以动态改变"被装饰者"来拓展行为.
四 没有完美的设计模式--“装饰模式的不足之处”
Java的23种设计模式或多或少都是存在不足之处的,就像我们程序员,可能在某些技术或者管理方面存在不足, 但是这不影响我们使用模式. 装饰者模式不足之处应该也有,比如上面的例子中, 饮料要是在双十一打折呢?需要对具体的被装饰者编程时,由于在装饰者中修改了这个具体,使用了统一接口(接口中没有具体的函数定义)。这是要去"修改"代码吗?在实际开发中应该根据具体情况来处理, 而我们的"装饰者"的创建也可以交给工厂模式或者生成器管理!