设计模式---装饰者模式
2017-04-19 15:25
323 查看
首先,从字面意思上看,“装饰”? 可以联想到什么? 对,买房子嘛(吐槽下,房价真tm贵)
有句话说的好,“房子是用来炒的,不是用来住的”。啊呸,错了,说反了。房子买的时候,大部分都是毛坯,或者简装。每个人肯定都想装修一下。对,没错。“装修”的前提,肯定是有了一个房子,它没有满足你的某些需求,然后你在此房子的基础上进行了一些功能添加和扩展。
废话不多说,来了解下“装饰者模式”:
什么是装饰者模式,我就不多说了,网上随便一搜,一大坨,比如:java/android设计模式学习笔记(7):装饰者模式”
举个栗子,暴躁起来(举个栗子):
房子接口
普通住房(陈岩石的养老院)
祁同伟的别墅
山水庄园会所
随着改革开放后人民收入的增加,房子准备装修了:养老院,要贴上壁纸;别墅换高级木质瓷砖;山水庄园要贴一层金箔。
这个时候,这个需求怎么写?
有人说,这还不简单,直接在Yanglaoyuan ,Bieshu,ShanshuiHotel的类里增加个方法就行了呗。
like this:
当然可以,但是要知道这样违反了对扩展开放、对修改关闭的原则(在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试)。
解释下为什么一定要遵循对扩展开放、对修改关闭的原则:
就比如上面的栗子,Yanglaoyuan这个类中的方法肯定会在很多地方被调用,如果我们直接修改这个类,有可能引起其他地方出现错误,而这写错误出现的地方是不可预测的。因此在测试的时候,需要对跟此类相关的地方都要进行测试;但是如果我们新建一个类去扩展它的功能,只需要测试跟新建的类相关的即可了。
总的来说遵循一句话:用抽象构建框架,用实现扩展细节
为了不违反 对扩展开放、对修改关闭 的设计原则,首先想到的肯定是对每个实现类继承,子类中扩展功能,增加需求。
like this:
但是如果,后期再增加需求(比如房子里要增添家居)是不是要再写一个子类?这样会造成子类对象的臃肿。
扩展功能还有一种方法:关联。
使用装饰者模式:
装饰,装饰,肯定需要被装饰者类和装饰者类。
NewHouseWithFurniture 是装饰者类。House 是被装饰者类。装饰者类完全继承实现被装饰者的所有方法,同时还增加扩展新的方法。而且装饰者肯定会执有被装饰着的引用。(这里House是接口,NewHouseWithFurniture 通过多态传入House的子类,从而NewHouseWithFurniture 可以调用子类方法的实现,同时新增方法又可以对所有子类功能进行扩充)
首先对接口进行扩展功能:构建装饰器
创建新的装修过的养老院
然后编写测试类:
关注下NewHouseWithFurniture newYanglaoyuan = new NewYanglaoyuan(yanglaoyuan);
结果
来看下java jdk中源码使用的装饰者设计模式:
InputStream(输入流):是一个抽象类
FileInputStream(文件输入流) 是InputStream的实现类
FilterInputStream 输入流的装饰类
实现InputStream的所有方法并且执有InputStream的引用
BufferedInputStream:装饰类的实现类
功能扩展
总结:
继承属于扩展形式之一,但不一定是达到弹性设计的最佳方案;
在程序设计中,应该尽量对修改关闭,对扩展开发,无需修改原有代码;
装饰者可以在被装饰者行为的前后根据实际情况加上自己的行为,必要时也可以将被装饰者行为给替换掉;
可以用无数个装饰者包装一个
组件,也就是说,装饰者 A 包装了被装饰者 B ,装饰者 C 再包装装饰者 A,根据实际情况这种行为可以累加到多层,通俗讲就是套上多层外壳;
同时,被装饰者也可以存在多个,也就是说 ConcreteComponent 这个角色也可以是多个的。
装饰者模式的优点就是它的特点:可以在运行时动态,透明的为一个组件扩展功能,比继承更加灵活;缺点也很明显:它会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。
有句话说的好,“房子是用来炒的,不是用来住的”。啊呸,错了,说反了。房子买的时候,大部分都是毛坯,或者简装。每个人肯定都想装修一下。对,没错。“装修”的前提,肯定是有了一个房子,它没有满足你的某些需求,然后你在此房子的基础上进行了一些功能添加和扩展。
废话不多说,来了解下“装饰者模式”:
什么是装饰者模式,我就不多说了,网上随便一搜,一大坨,比如:java/android设计模式学习笔记(7):装饰者模式”
举个栗子,暴躁起来(举个栗子):
房子接口
public interface House{ void wallColour(); void price(); }
普通住房(陈岩石的养老院)
public class Yanglaoyuan implements House { public Yanglaoyuan() { System.out.println("这是Yanglaoyuan"); } @Override public void wallColour() { System.out.println("Yanglaoyuan——wallColour:白色"); } @Override public float price() { System.out.println("Yanglaoyuan——price:10w"); return 10000; } }
祁同伟的别墅
public class BieShu implements House { public BieShu() { System.out.println("这是BieShu"); } @Override public void wallColour() { System.out.println("BieShu——wallColour:银色"); } @Override public float price() { System.out.println("BieShu——price:380W"); return 38000000; } }
山水庄园会所
public class ShanshuiHotel implements House { public ShanshuiHotel() { System.out.println("这是ShanshuiHotel"); } @Override public void wallColour() { System.out.println("ShanshuiHotel——wallColour:金色"); } @Override public float price() { System.out.println("ShanshuiHotel——price:3800W"); return 380000000; } }
随着改革开放后人民收入的增加,房子准备装修了:养老院,要贴上壁纸;别墅换高级木质瓷砖;山水庄园要贴一层金箔。
这个时候,这个需求怎么写?
有人说,这还不简单,直接在Yanglaoyuan ,Bieshu,ShanshuiHotel的类里增加个方法就行了呗。
like this:
public class YanglaoyuanNew extends Yanglaoyuan { public YanglaoyuanNew() { super(); System.out.println("装修为YanglaoyuanNew"); } @Override public void wallColour() { super.wallColour(); System.out.println("YanglaoyuanNew装修——wallColour:" + "贴上漂亮的墙纸"); } @Override public float price() { System.out.println("YanglaoyuanNew装修——price:300"); return super.price() + 300; } public void furniture(){ System.out.println("YanglaoyuanNew装修---->>>添加家具"); } }
当然可以,但是要知道这样违反了对扩展开放、对修改关闭的原则(在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试)。
解释下为什么一定要遵循对扩展开放、对修改关闭的原则:
就比如上面的栗子,Yanglaoyuan这个类中的方法肯定会在很多地方被调用,如果我们直接修改这个类,有可能引起其他地方出现错误,而这写错误出现的地方是不可预测的。因此在测试的时候,需要对跟此类相关的地方都要进行测试;但是如果我们新建一个类去扩展它的功能,只需要测试跟新建的类相关的即可了。
总的来说遵循一句话:用抽象构建框架,用实现扩展细节
为了不违反 对扩展开放、对修改关闭 的设计原则,首先想到的肯定是对每个实现类继承,子类中扩展功能,增加需求。
like this:
public class YanglaoyuanNew extends Yanglaoyuan { @Override public void wallColour() { System.out.println("Yanglaoyuan——wallColour:白色" + "贴上墙纸"); } @Override public float price() { System.out.println("Yanglaoyuan——price:10W + 300"); return 100000 + 300; } }
但是如果,后期再增加需求(比如房子里要增添家居)是不是要再写一个子类?这样会造成子类对象的臃肿。
扩展功能还有一种方法:关联。
使用装饰者模式:
装饰,装饰,肯定需要被装饰者类和装饰者类。
NewHouseWithFurniture 是装饰者类。House 是被装饰者类。装饰者类完全继承实现被装饰者的所有方法,同时还增加扩展新的方法。而且装饰者肯定会执有被装饰着的引用。(这里House是接口,NewHouseWithFurniture 通过多态传入House的子类,从而NewHouseWithFurniture 可以调用子类方法的实现,同时新增方法又可以对所有子类功能进行扩充)
首先对接口进行扩展功能:构建装饰器
public abstract class NewHouseWithFurniture implements House { House house; public NewHouseWithFurniture(House house) { this.house = house; } @Override public void wallColour() { house.wallColour(); } @Override public float price() { return house.price(); } public void furniture(){ System.out.println("---->>>添加家具"); } }
创建新的装修过的养老院
public class NewYanglaoyuan extends NewHouseWithFurniture { public NewYanglaoyuan(House house) { super(house); System.out.println("装修为NewYanglaoyuan"); } @Override public void wallColour() { super.wallColour(); System.out.println("NewYanglaoyuan装修——wallColour:贴上漂亮的墙纸"); } @Override public float price() { System.out.println("NewYanglaoyuan装修——price:300"); return super.price() + 300; } @Override public void furniture() { super.furniture(); System.out.println("NewYanglaoyuan装修---->>>添加家具"); } }
然后编写测试类:
关注下NewHouseWithFurniture newYanglaoyuan = new NewYanglaoyuan(yanglaoyuan);
public class TestMain { public void start(){ System.out.println("-----------------没装修前的:---------------"); House yanglaoyuan = new Yanglaoyuan(); yanglaoyuan.wallColour(); yanglaoyuan.price(); System.out.println("-----------------直接在源码上修改的 ---------------"); YanglaoyuanNew yanglaoyuan2 = new YanglaoyuanNew(); yanglaoyuan2.wallColour(); yanglaoyuan2.price(); yanglaoyuan2.furniture(); System.out.println("-----------------装饰者模式修改的 ---------------"); NewHouseWithFurniture newYanglaoyuan = new NewYanglaoyuan(yanglaoyuan); newYanglaoyuan.wallColour(); newYanglaoyuan.price(); newYanglaoyuan.furniture(); } }
结果
来看下java jdk中源码使用的装饰者设计模式:
InputStream(输入流):是一个抽象类
public abstract class InputStream implements Closeable { // MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to // use when skipping. private static final int MAX_SKIP_BUFFER_SIZE = 2048; ...
FileInputStream(文件输入流) 是InputStream的实现类
public class FileInputStream extends InputStream { /* File Descriptor - handle to the open file */ private final FileDescriptor fd; /* The path of the referenced file (null if the stream is created with a file descriptor) */ private final String path; ...
FilterInputStream 输入流的装饰类
实现InputStream的所有方法并且执有InputStream的引用
public class FilterInputStream extends InputStream { /** * The input stream to be filtered. */ protected volatile InputStream in; /** * 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 FilterInputStream(InputStream in) { this.in = in; } ...
BufferedInputStream:装饰类的实现类
功能扩展
public class BufferedInputStream extends FilterInputStream { private static int defaultBufferSize = 8192; ... /** * Creates a <code>BufferedInputStream</code> * and saves its argument, the input stream * <code>in</code>, for later use. An internal * buffer array is created and stored in <code>buf</code>. * * @param in the underlying input stream. */ public BufferedInputStream(InputStream in) { this(in, defaultBufferSize); } ...
FileInputStream fiStream = new FileInputStream("C:\\xxxx"); BufferedInputStream bufferedInputStream = new BufferedInputStream(fiStream); bufferedInputStream.read();
总结:
继承属于扩展形式之一,但不一定是达到弹性设计的最佳方案;
在程序设计中,应该尽量对修改关闭,对扩展开发,无需修改原有代码;
装饰者可以在被装饰者行为的前后根据实际情况加上自己的行为,必要时也可以将被装饰者行为给替换掉;
可以用无数个装饰者包装一个
组件,也就是说,装饰者 A 包装了被装饰者 B ,装饰者 C 再包装装饰者 A,根据实际情况这种行为可以累加到多层,通俗讲就是套上多层外壳;
同时,被装饰者也可以存在多个,也就是说 ConcreteComponent 这个角色也可以是多个的。
装饰者模式的优点就是它的特点:可以在运行时动态,透明的为一个组件扩展功能,比继承更加灵活;缺点也很明显:它会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。
相关文章推荐
- 设计模式--装饰者模式
- 设计模式之Decorator(装饰者)
- Net设计模式实例之装饰者模式(Decorator Pattern)(2)
- Net设计模式实例之装饰者模式(Decorator Pattern)(1)
- [设计模式]head first 设计模式之PHP实现 第三章 装饰者模式
- 设计模式之装饰者模式
- 设计模式笔记3(装饰者模式)
- 装饰者设计模式
- Net设计模式实例之装饰者模式(Decorator Pattern)
- 白话设计模式 --结构型模式--装饰者(Decorator)模式,适配器模式和外观模式
- Head First 设计模式 读书摘记(四) 装饰者模式 C#代码
- 设计模式学习3——装饰者模式(Decorator)
- 设计模式示例二 Decorator(装饰者)
- 设计模式学习笔记之 装饰者模式
- 设计模式随笔系列:来杯咖啡-装饰者模式(Decorator)[转]
- 设计模式示例二 Decorator(装饰者)
- 设计模式-装饰者模式
- ABAP与设计模式之装饰者模式
- 设计模式--装饰者模式
- 设计模式与泡mm的关系之Decorator装饰者设计模式及装饰者设计模式的再思考