一开始非要学习装饰者模式,我是拒绝的。因为我觉得多少有点花拳绣腿,花花架子之类的。
好吧,然后还是硬着头皮先看看吧。因为设计模式和有一些理论还不一样,设计模式不是某一个具体的技术,他不是源于高人的突发奇想,不是创造性的,是总结性的。很多经验人士在经过大量复杂业务代码的编写后总结的一些开发规律,跟着这个规律去设计代码,进行开发会让后续的维护拓展变得简单。所以先掌握了设计模式,在写复杂业务代码的时候硬着头皮套设计模式,多几次也就掌握了。
好滴,下面先说一下我对装饰者模式的理解。首先是一个大的对象有一部分功能或者特点是需要可能经常变换的横向拓展的,然后为了实现 向修改关闭,向拓展开放 的原则,需要一个好的方式来管理这些多变的行为,动态的将责任附加到对象上。
比如(后续的例子和说明基本来源于 《Head First 设计模式》),购买奶茶时可能会有基础套餐,比如 红茶,绿茶,花茶,白茶,咖啡,牛奶;另外还会提供椰果,芋圆,水果,奶盖,坚果等配料,一杯好奶茶就需要一个好的装饰者。
1. 基础类型 底子
2. 底子装饰者(继承底子)
2. 茶的类型(继承底子)
3. 套餐选择(继承底子装饰者,拥有一个 底子)
public abstract class 底子 {
String desc = "不知名底子";
public String getDesc() {
return desc;
}
public abstract double cost();
}
abstract class 奶茶底子装饰者 extends 底子 {
public abstract double cost();
}
public class 红茶奶茶 extends 底子 {
public 红茶奶茶() {
desc = "红茶奶茶!";
}
@Override
public double cost() {
return 12;
}
}
public class 绿茶奶茶 extends 底子 {
public 绿茶奶茶(){
desc = "绿茶奶茶!";
}
@Override
public double cost() {
return 11.5;
}
}
public class 坚果奶茶 extends 奶茶底子装饰者 {
底子 base;
public 坚果奶茶(底子 base) {
this.base = base;
}
@Override
public String getDesc() {
return base.getDesc() + "坚果套餐";
}
@Override
public double cost() {
return 4 + base.cost();
}
public static void main(String[] args) {
底子 红 = new 红茶奶茶();
System.out.println(红.getDesc() + 红.cost());
底子 绿 = new 绿茶奶茶();
System.out.println(绿.getDesc() + 绿.cost());
坚果奶茶 夏天的快乐 = new 坚果奶茶(绿);
坚果奶茶 冬天的快乐 = new 坚果奶茶(红);
System.out.println("夏天的快乐:" + 夏天的快乐.getDesc() + 夏天的快乐.cost());
System.out.println("冬天的快乐:" + 冬天的快乐.getDesc() + 冬天的快乐.cost());
}
}
好处就是各管哥的,分类明确。
因为使用到了继承关系,所以要考虑当时的业务场景是不是适合使用装饰者模式,或者说抽象是不是合理。这个地方我也需要在实践中加强一下理解。
再给一个小例子, FilterInputStream 是InputStream 的装饰者,所以我们也可以利用 FilterInputStream 实现一个自己的 InputStream,主要功能就是把所有的输入都转换成小写字母。
/**
* <code>FilterInputStream</code> 是所有InputStream 的抽象装饰者
*/
public class LowerCaseInputStream extends FilterInputStream {
/**
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* @param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected LowerCaseInputStream(InputStream in) {
super(in);
}
public int read() throws IOException {
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char) c));
}
public int read(byte[] b, int offet, int len) throws IOException {
int result = super.read(b, offet, len);
for (int i = offet; i < offet + result; i++) {
b[i] = (byte) Character.toLowerCase((char) b[i]);
}
return result;
}
}