您的位置:首页 > 其它

设计模式---装饰者模式

2017-04-19 15:25 323 查看
首先,从字面意思上看,“装饰”? 可以联想到什么? 对,买房子嘛(吐槽下,房价真tm贵)

有句话说的好,“房子是用来炒的,不是用来住的”。啊呸,错了,说反了。房子买的时候,大部分都是毛坯,或者简装。每个人肯定都想装修一下。对,没错。“装修”的前提,肯定是有了一个房子,它没有满足你的某些需求,然后你在此房子的基础上进行了一些功能添加和扩展。

废话不多说,来了解下“装饰者模式”:

什么是装饰者模式,我就不多说了,网上随便一搜,一大坨,比如: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 这个角色也可以是多个的。

装饰者模式的优点就是它的特点:可以在运行时动态,透明的为一个组件扩展功能,比继承更加灵活;缺点也很明显:它会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式