一、继承的缺点
最近星巴克要做一个系统,boss觉得很简单就让实习生小明来做这个系统。
星巴克的咖啡种类繁多,用代码实现这些种类繁多的咖啡让小明很苦恼。小明根据自己的开发经验决定使用继承,于是他先设计了一个咖啡基类,如下所示:
public interface 咖啡{
}
随后就开始创建各种子类:
//加糖咖啡
public class 加糖咖啡 implements 咖啡{
}
//加盐咖啡
public class 加盐咖啡 implements 咖啡{
}
//猫屎咖啡
public class 猫屎咖啡 implements 咖啡{
}
//加袜子咖啡
public class 加袜子咖啡 implements 咖啡{
}
忙活了大半天,小明以为终于可以下班了,但是boss看了以后却说,需求里还有加盐猫屎咖啡,加糖猫屎咖啡,加盐加糖猫屎咖啡…小明听了只得乖乖回到座位继续工作
//加盐加糖咖啡
public class 加盐加糖咖啡 extends 加盐咖啡{
}
。。。。。
根据排列组合知识不难发现,这些基本咖啡即是是两两组合也要n多种组合方式,这也就意味着小明今天可能得熬个通宵。。。。。。
这时开发经验丰富的王师傅看不下去了,王师傅提醒小明说:
继承虽然可以对类进行增强,但是它的缺点也不小:
1、使用不够灵活(加盐加糖加袜子咖啡怎么增强)
2、增强的对象是固定的(谁继承就对谁增强)
3、增强的内容也是固定的
4、会使子类的数量爆炸性增长
对于这种需求王师傅推荐小明使用装饰器模式来完成
二、装饰器模式
那么我们该如何使用装饰器模式来完成上面代码的改造呢?
首先仍然需要一个基类
public interface 咖啡{
}
其次,仍然需要设计一些子类,但是形式需要稍微改变
//加糖咖啡
public class 加糖咖啡 implements 咖啡{
private 咖啡 coffee;
public 加糖咖啡(咖啡 coffee){
this.coffee = coffee;
}
}
//加盐咖啡
public class 加盐咖啡 implements 咖啡{
private 咖啡 coffee;
public 加盐咖啡(咖啡 coffee){
this.coffee = coffee;
}
}
。。。。。
看起来好像并没有比上面更简单,反而变得更复杂了。。。但是别着急,接着往下看:
//加糖咖啡
咖啡 加盐加糖咖啡 = new 加盐咖啡(new 加糖咖啡());
//加盐加糖加袜子咖啡
咖啡 加盐加糖加袜子咖啡 = new 加袜子咖啡(加盐加糖咖啡);
这样看来是不是简洁多了!
总结起来就是一句话:
是你还有你,一切拜托你!
我们可以看一个实例:
//是你
public MyConnection implements Connection {
//还有你
private Connection conn;
public MyConnection(Connection conn){
this.conn = conn;
}
//一切拜托你
public Connection getConnection(){
return conn.getConnection;
}
public void close(){
//关闭方法
}
}
java的api中IO流的实现也大量使用了这种设计模式
BufferedInputStream bufferedInputStream1 = new BufferedInputStream(new FileInputStream("12"));
BufferedInputStream bufferedInputStream2 = new BufferedInputStream(new ObjectInputStream(new FileInputStream("12")));
BufferedInputStream
的作用就是给其它输入流加上缓存,它不管传入的是什么流,只管为这些流添加缓存
与继承相比,我们可以看到:
1、子类的数量减少了
2、被装饰的对象不唯一
3、装饰的内容还是固定的