本章可以称为“给爱用继承的人一个全新的设计眼界”。本章会学到如何使用对象组合的方式,做到在运行时装饰类。一旦熟悉了装饰的技巧,你将能够在不修改任何底层代码的情况下,给你的(或别人的)对象赋予新的职责。
设计原则:类应该对扩展开放,对修改关闭。
我们先看看对象增强的手段有哪些:
- 继承--被增强的对象是固定的,增强的内容也是固定的
- 装饰者模式--被增强的对象是可以切换的,增强的内容是固定的
- 动态代理--被增强的对象可以切换,增强的内容也可以切换
继承的缺点:
1.增强的内容是死的,不能动
2.被增强的对象也是死的
class 咖啡类 {}
class 有糖咖啡 extends 咖啡类{
}
class 加奶咖啡 extends 咖啡类 {
}
class 加盐咖啡 extends 咖啡类 {
}
如果,加糖又加奶,
class 加糖加奶 extends 加奶 {
}
个性化的需求,使用继承,会导致类的暴增。
--------------------
装饰者模式
1.增强的内容是不能修改的
2.被增强的对象可以是任意的
class 咖啡类 {}
class 有糖咖啡 extends 咖啡类{
}
class 加奶咖啡 extends 咖啡类 {
}
class 加盐咖啡 extends 咖啡类 {
}
咖啡 a = new 加糖();咖啡 b = new 加盐(a); //对a进行装饰,就是给a加盐。
咖啡 c = new 加盐(b);
装饰者模式在java api IO流用得多。
IO流分类:
1.字节:InputStream, OutputStream
2.字符:Reader, Wirter
InputStream
FileInputStream: 节点流。就是和一个资源绑定在一起的。和磁盘上的文件绑在一起。
BufferedInputStream: 它是装饰流。创建我时一定要给我一个底层对象,我不管你给我的是什么流,我都会给它添加缓冲区。
new BufferedInputStream(任意的InputStream)
ObjectInputStream也是装饰流。
ByteArrayInputStream是一个节点流,它和内存中的一个字节数组绑定在一起
GZIPInputStream是装饰流。
java io中装饰流多,节点流少。
装饰者模式用途:不知道被增强对象的具体类型时,可以用来增强。
BufferedInputStream的作用再解析:你要从BufferedInputStream读一个字节,BufferedInputStream会去磁盘先读一塑料袋的内容,然后,在给你一个字节。你要读第二个字节,BufferedInputStream就不用去磁盘读了,直接给你第二个。这样,就减少了和磁盘的交互。因为,磁盘很慢嘛。
FileInputStream的底层涉及到C的代码,因为要和本地文件系统交互。
BufferedInputStream底层还是要用到FileInputStream,只是,一次拿一塑料袋。
==================================
装饰模式 对新房进行装修并没有改变房屋的本质,但它可以让房子变得更漂亮、更温馨、更实用。在软件设计中,对已有对象(新房)的功能进行扩展(装修)。把通用功能封装在装饰器中,用到的地方进行调用。装饰模式是一种用于替代继承的技术,使用对象之间的关联关系取代类之间的继承关系。引入装饰类,扩充新功能。角色、抽象构件、具体构件、抽象装饰类、具体装饰类。
==================================
装饰者模式可以动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
该模式的适用环境为:
(1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
(2)处理那些可以撤消的职责。
(3)当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
实现该模式的关键步骤:
(1)Component(被装饰对象基类):定义对象的接口,可以给这些对象动态增加职责;
(2)ConcreteComponent(具体被装饰对象):定义具体的对象,Decorator可以给它增加额外的职责;
(3)Decorator(装饰者抽象类):维护指向Component实例的引用,定义与Component一致的接口(也就是要继承或实现被装饰对象基类);
(4)ConcreteDecorator(具体装饰者):具体的装饰对象,给内部持有的具体被装饰对象增加具体的职责;
这样讲大家可能有些不好理解,那我们还是老规矩:
入冬以后天气越来越冷了,下班之后,做为资深吃货,约上二三好友痛快的来场火锅盛宴再爽不过了。说到火锅,不得不提在成都吃过的大龙燚火锅,各种锅底,配菜应有尽有,但我最喜欢的还是大龙燚火锅原味锅底、麻辣牛肉、大刀毛肚、天味香肠、砣砣牛肉、麻辣排骨等,想想都流口水啊。
说道这大家结合装饰者的实现步骤,应该有点感觉了吧,上文提到的锅底,其实就是被装饰对象的基类,配料其实就是装饰者抽象类,大龙燚火锅原味锅底这些具体的锅底也就是具体的被装饰对象了,麻辣牛肉、大刀毛肚、天味香肠、砣砣牛肉、麻辣排骨这些装饰锅底用的各种配菜也就是具体的装饰对象。说道这,大家应该都豁然开朗了吧,下面我们开始具体的代码实现:
第一步:定义被装饰对象基类(可以是抽象类也可以是接口)
public interface GuoDi {
float cost();// 锅底当然要有价钱啦
String name();// 名字也得有哦
}
第二步:定义具体被装饰对象(也就是各种锅底,这里定义两种)
public class YuanYang implements GuoDi {
@Override
public float cost() {
return 48.0f;
}
@Override
public String name() {
return "鸳鸯锅底";
}
}
public class DaLongYan implements GuoDi {
@Override
public float cost() {
return 59.0f;
}
@Override
public String name() {
return "大龙燚火锅原味锅底";
}
}
第三步:定义装饰者抽象类
public abstract class PeiCai implements GuoDi {
private GuoDi guodi;
public PeiCai(GuoDi guodi) {
this.guodi = guodi;
}
@Override
public float cost() {
return guodi.cost();
}
@Override
public String name() {
return guodi.name();
}
}
第四步:定义具体的装饰者对象
public class MaLaNiuRou extends PeiCai {
public MaLaNiuRou(GuoDi guodi) {
super(guodi);
}
@Override
public float cost() {
return super.cost() + 46f;
}
@Override
public String name() {
return super.name() + "+麻辣牛肉";
}
}
public class MaoDu extends PeiCai {
public MaoDu(GuoDi guodi) {
super(guodi);
}
@Override
public float cost() {
return super.cost() + 30f;
}
@Override
public String name() {
return super.name() + "+大刀毛肚";
}
}
测试类:
public class Test {
public static void main(String[] args) {
GuoDi guodi = new DaLongYan();// 点个大龙燚火锅原味锅底
MaLaNiuRou y = new MaLaNiuRou(guodi);// 来个麻辣牛肉
MaoDu x = new MaoDu(y);// 在麻辣牛肉的基础上再来个大刀毛肚
System.out.println("一共点了:" + x.name() + ",共消费:" + x.cost());
}
}
输出结果:
一共点了:大龙燚火锅原味锅底+麻辣牛肉+大刀毛肚,共消费:135.0
转自:https://www.cnblogs.com/panhouye/p/6120232.html
看看这两篇把: