@w1992wishes
2017-11-01T10:14:01.000000Z
字数 6762
阅读 935
设计模式
结构型模式
设计原则:
本文的结构如下:
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。和代理模式很相似,但在对被装饰的对象的控制程度是不同的;装饰者模式是对对象功能的加强,而代理模式是对对象施加控制,并不提供对对象本身功能的加强。
通俗点讲,假设现在一杯纯豆浆(Soya)卖1元钱,你可以选择往里边加糖0.5元(Suger),加蜂蜜0.5元(Honey),加牛奶1元(Milk),加黑豆1元(BlackBean)。这里面纯豆浆就是被装饰者,而糖、蜂蜜、牛奶、黑豆就是装饰者了。
通过继承的方式可以使子类具有父类的属性和方法。子类继承父类后,因为一些业务需求可以通过重写的方式来加强父类的方法的一些功能,也可以重新定义某些属性,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。
而装饰者模式的最基本的功能就是对传入的一个对象进行功能的加强与优化。
那么问题来了,既然继承方式也可以对已有类或对象进行加强,那为什么还要衍生出装饰者模式这一思想呢?
装饰者模式的意图定义为:动态地给一个对象添加一些额外的职责。装饰者模式存在的更重要的意义就在于动态的为对象添加一些功能(或分配额外职责)。
以豆浆的例子为例,先尝试用继承的方式分析一下:设豆浆类为基类,每个类中有一个money属性,那么豆浆加牛奶可模拟为Soya类继承Milk并重写pay()方法,如此继承确实可以计算出每种组合的价钱,于是有下图:
一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
在不想增加很多子类的情况下扩展类,如何实现呢?这时就要用到今天的主角:装饰者模式了。
Java中的IO机制就用到了装饰者模式。比如最常用的语句:
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filepath)));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
这就是最常见的装饰者模式了,通过BufferedReader对已有对象FileReader的功能进行加强和优化。其实它不仅可以加强FileReader,所有的字符输入流都可以通过这种方式进行包装。
它是如何实现的呢?
将所有的字符输入流抽象出了一个基类或接口即Reader,然后通过构造方法的形式将Reader传递给BufferedReader,此时BufferedReader就可以对所有的字符输入流进行拦截和优化了。
如果采用继承机制,每个XXXReader就要衍生出一个BufferedXXXReader,再加上字符输出流和字节输入输出流,那么Java的IO体系结构该是多么的臃肿不堪啊!而装饰者模式的出现解决了这个问题,并且,装饰者的出现也再一次的证明了面向对象的设计原则:多用组合,少用继承!对扩展开放,对修改关闭!
第一步:抽象组件
/**
* 抽象组件
*
* Created by w1992wishes on 2017/10/30.
*/
public abstract class Component {
public abstract void method();
}
第二步:具体组件
/**
* 具体组件
*
* Created by w1992wishes on 2017/10/30.
*/
public class ConcreteComponent extends Component {
public void method() {
System.out.println("work");
}
}
第三步:抽象装饰者
/**
* 抽象的装饰者
*
* Created by w1992wishes on 2017/10/30.
*/
public abstract class Decorator extends Component{
private Component component;
public Decorator(Component component){
this.component = component;
}
@Override
public void method(){
beforeM();
component.method();
afterM();
}
public abstract void beforeM();
public abstract void afterM();
}
第四步:具体的抽象者
/**
* 具体的装饰者
*
* Created by w1992wishes on 2017/10/30.
*/
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component){
super(component);
}
@Override
public void beforeM() {
System.out.println("Relax, first having a game before work!");
}
public void afterM() {
System.out.println("Relax, first having a game after work!");
}
}
第五步:客户端运行
/**
* Created by w1992wishes on 2017/10/30.
*/
public class Client {
public static void main(String[] args) {
Component c = new ConcreteComponent();
Decorator d = new ConcreteDecorator(c);
d.method();
}
}
结果:
Relax, first having a game before work!
work
Relax, first having a game after work!
前面其实可以看作是一个代码示例,下面接着用豆浆的例子来段代码(这里使用抽象接口):
首先抽象出一个接口,作为装饰者构造函数的参数,即被装饰者的父类:
/**
* Created by w1992wishes on 2017/10/30.
*/
public interface Drink {
public float money();//获取价格。
public String description();//返回商品信息。
}
接下来就是装饰者类,继承此接口并通过构造方法获取被装饰对象公共接口:
/**
* Created by w1992wishes on 2017/10/30.
*/
public abstract class Decorator implements Drink{
private Drink drink;
public Decorator (Drink drink){
this.drink = drink;
}
@Override
public String description() {
return drink.description();
}
@Override
public float money() {
return drink.money();
}
}
下面是被装饰者Soya:
/**
* 具体的装饰者对象,纯豆浆
*
* Created by w1992wishes on 2017/10/30.
*/
public class Soya implements Drink {
public String description() {
return "纯豆浆";
}
public float money() {
return 2f;
}
}
下面分别是装饰者Suger,Milk,BlackBean,Honey类:
Suger
/**
* 具体的装饰者类:糖
*
* Created by w1992wishes on 2017/10/30.
*/
public class Suger extends Decorator {
public Suger(Drink drink) {
super(drink);
}
public String description() {
return super.description()+"+糖";
}
public float money() {
return super.money()+1.5f;
}
}
Milk
/**
* 具体的装饰者对象:牛奶
*
* Created by w1992wishes on 2017/10/30.
*/
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
}
public String description() {
return super.description()+"+牛奶";
}
public float money() {
return super.money()+1.5f;
}
}
Honey
/**
* 具体的装饰者类:蜂蜜
*
* Created by w1992wishes on 2017/10/30.
*/
public class Honey extends Decorator {
public Honey(Drink drink) {
super(drink);
}
public String description() {
return super.description()+"+蜂蜜";
}
public float money() {
return super.money()+1.5f;
}
}
BlackBean
/**
* 具体的装饰者对象:黑豆
*
* Created by w1992wishes on 2017/10/30.
*/
public class BlackBean extends Decorator {
public BlackBean(Drink drink) {
super(drink);
}
public String description() {
return super.description()+"+黑豆";
}
public float money() {
return super.money()+0.5f;
}
}
最后是测试代码:
/**
* Created by w1992wishes on 2017/10/30.
*/
public class Client {
public static void main(String[] args) {
//定义一杯纯豆浆
Soya soya = new Soya();
System.out.print("-----------------");
System.out.println(soya.description()+" 价钱:"+soya.money());
//豆浆加奶
Milk milkSoya = new Milk(soya);
System.out.print("-----------------");
System.out.println(milkSoya.description()+" 价钱:"+milkSoya.money());
//黑豆豆浆+奶
BlackBean beanMilkSoya = new BlackBean(milkSoya);
System.out.print("-----------------");
System.out.println(beanMilkSoya.description()+" 价钱:"+beanMilkSoya.money());
//黑豆豆浆+奶+蜂蜜
Honey honeyBeanMilkSoya = new Honey(beanMilkSoya);
System.out.print("-----------------");
System.out.println(honeyBeanMilkSoya.description()+" 价钱:"+honeyBeanMilkSoya.money());
}
}
结果:
-----------------纯豆浆 价钱:2.0
-----------------纯豆浆+牛奶 价钱:3.5
-----------------纯豆浆+牛奶+黑豆 价钱:4.0
-----------------纯豆浆+牛奶+黑豆+蜂蜜 价钱:5.5
因为装饰者模式的以下特点:
所以在以下情况下可以使用装饰模式:
装饰模式可分为透明装饰模式和半透明装饰模式:在透明装饰模式中,要求客 户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该声明具体构件类型和具体装饰类型,而应该全部声明为抽象构件类型;半透明装饰模式允许用户在客户端声明具体装饰者类型的对象,调用在具体装饰者中新增的方法。