装饰(Decorator)模式(包装模式)

  • Post author:
  • Post category:其他



概念

:动态为对象添加功能。

是一种替换继承关系的方案。无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。


装饰设计模式在设计模式定位

:结构型模式,把类或对象结合在一起形成更大的结构。


干什么



动态给

对象添加新的功能。


作用

:避免因为继承关系,某个类个数快速增长,同时也避免了继承关系中类与类之间耦合度高。详细解析见下文。


装饰模式核心知识点

抽象组件角色:一个抽象接口。装饰对象和被装饰对象【具体组件对象】共有的父类接口。这样客户端对象就能以相同的方式操作具体组件对象和装饰对象【或者说可以将装饰类作为组件类看待】

具体组件角色:抽象组件的基本实现类。装饰模式是为这个基类动态添加新功能。

抽象装饰角色:装饰对象包含一个真实组件对像的引用。它的作用是接受需要扩展功能的具体组件类;实现抽象组件接口,使得在动态添加功能时具体装饰类和具体组件类用法相同,使模式更加灵活。

具体装饰角色:为抽象装饰角色的实现类。负责为接受的具体组件类添加新的功能或优化功能。

实现:虚线、三角箭头 ;     依赖:虚线、箭头 ;    聚合:实线、菱形

ICar :抽象组件角色

RunICar:具体组件角色(被装饰类)。实现抽象组件角色。它具有组件类的基本功能,装饰类的存在是为了扩展它的功能。

ICarDecorator:抽象装饰角色。实现抽象组件角色【使具体装饰角色也可以作为具体组件添加新的功能或优化功能】,抽象类内                           部有ICar引用,它作用  是获取具体组件对象,在该组件的基础上添加新的功能。

FlyDecorator:具体装饰类,增加了车辆可以飞的功能。具体装饰类中的新功能使独立的,进行修改时不会影响其他装饰类的功                           能。将RunICar 对象传递进来,车辆除了有基本的跑功能,新添加了可以飞的功能。

SwinDecorator:具体修饰类,增加了车辆可以游泳的功能。将RunICar 对象传递进来,车辆除了有基本的跑功能,新添加了可以                          游泳的功能。

代码实例:

类之间的关系,如上UML类图
/**
 * 定义抽象组件接口
 *
 */
public interface ICar {

	public void show();
	
	public void run();
}
/**
 * 定义具体组件(被装饰类),是抽象组件的实现类
 *
 */
public class RunICar implements ICar {
	@Override
	public void show() {
		this.run();
	}
	@Override
	public void run() {
		System.out.println("车辆可以跑");
	}
}

/**
 * 定义抽象装饰角色。
1.实现抽象组件,可以将装饰类作为具体组件,对组件进行多次装饰,实现更加丰富的功能。*/
public abstract class ICarDecoeator implements ICar {
//2.抽象装饰类包含抽象组件的一个引用。作用:装饰类给传递进来的组件添加新的功能
	private ICar icar;
	
//有参构造方法用于获取组件
	public ICarDecoeator(ICar icar) {
		super();
		this.icar = icar;
	}
	public ICar getIcar() {
		return icar;
	}

	public void setIcar(ICar icar) {
		this.icar = icar;
	}

	public abstract void show();

}

/**
 * 具体的装饰角色。他的作用是为组件添加新的功能
 *
 */
public class FlyDecorator extends ICarDecoeator {

	public FlyDecorator(ICar icar) {
		super(icar);
	}

	public void fly() {
		System.out.println("车辆可以飞");
	}

	public void show() {
		this.getIcar().run();
		this.fly();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
	}
}
/**
 * 具体装饰组件,继承一个抽象类,抽象类实现类接口。具体组件需要实现接口中的方法吗?
 *
 */
public class SwinDecorator extends ICarDecoeator {

	public SwinDecorator(ICar icar) {
		super(icar);
	}
	
	public void swin() {
		System.out.println("可以潜水");
	}

	@Override
	public void run() {

	}

	@Override
	public void show() {
		this.getIcar().show();
		this.swin();
	}
}
//客户端
public class DecoratorMain {

	public static void main(String[] args) {
		/**使用装饰模式,给具体组件添加功能。在用户需要什么功能,就将具体组件作为参数传递为具体装饰类。
		客户端在调用时,是以透明的方式扩展对象功能,是继承关系的一种替换。
		
		*/
		ICar icar = new RunICar();
		ICar flyicar = new FlyDecorator(icar);
		flyicar.show();
		System.out.println("------------");
		
		ICar swinicar = new SwinDecorator(icar);
		swinicar.show();
		
		System.out.println("-----------");
		
		ICar swin = new SwinDecorator(flyicar);
		swin.show();
	}
}


装饰模式相对于继承机制存在的价值:

我们都知道如果要加强一个类的功能可以通过继承,然后重写父类中的方法或者使用装饰模式的方法对类的功能进行加强和优化【加强:增加新的功能;优化:在装饰类中对组件类中的功能进行再次处理】。为什么有了继承机制之后还要衍生出装饰模式?

下面分析装饰模式和继承机制的相同点:

大家都知道,通过继承可以是子类具有父类的属性和方法。子类继承父类后,因为一些业务的需要通过重写的方式,来加强父类的方法的一些功能,也可以通过重新定义某些属性,即覆盖父类原有的属性和方法,使其获得与父类不同的功能。而装饰模式的最基本功能就是对传入的一个对象进行功能的加强和优化。那么问题来了既然继承方式也可以对已有的类或对象进行加强,为什么还要衍生出装饰者模式这一思想?

装饰者模式的意图定义为:



动态



给一个对象添加一些额外的职责。有这句话可以知道,除了最基本的增强已有对象的功能外,装饰者模式存在更


重要的意义就在于动态的为对象添加一些功能


。以生活中的例子来说。那么假设现在一杯纯豆浆(Soya)卖1元钱,你可以选择往里边加糖0.5元(Suger),加蜂蜜0.5元(Honey),加牛奶1元(Milk),加黑豆1元(BlackBean),加鸡蛋(1元)等等。如果要计算出任意组合的豆浆的价钱该怎么做呢?

首先使用继承的方式分析以下:设豆浆类为基类,每个类中有一个money属性,那么豆浆加牛奶可模拟为Soya类继承Milk,并重写pay()方法,如此继承确实可以计算出每中组合的价钱,如下图:

会造成子类数据过多难以维护。因此采用继承虽然可行,但是会造成代码臃肿,扩展性不好,类类与类之间耦合度过高。该苏荷解决?可以使用装饰者模式。

对于装饰者模式,Java中的io机制就用到了装饰者模式:



BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filepath)));



BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

通过BufferReader对以后对象FileReader的功能进行强化和优化。其实它不仅可以加强FileReader,所有的字符输入流都可以通过这种方式进行包装。重点:它将所有的字符输入流抽象出了一个基类或接口 即Reader,然后通过构造方法的形式将Reader传递给BufferedReader,此时BufferedReader就可以对所有的字符输入流进行拦截和优化。

如果采用继承机制,每个XXXReader就要衍生出一个BufferedXXXReader,在加上字符输入输出流和字节输入输出流,那么Java的IO体系结构该多么臃肿,而装饰者模式的出现解决了这个问题,并且,装饰者的出现再一次证明了


面向对象的设计原则


:多用组合,少用继承!对扩展开放,对修改关闭!

以上是装饰者模式的价值。


对装饰者模式的特点和应用场景总结:

装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体组件类和具体装饰类可以独立变化,以便增加新的具体组件类和具体装饰类。


特点【核心知识点类似,这里只是以另一个角度对装饰模式进行阐述】:

1.装饰对象和被装饰对象【具体组件对象】共有的父类接口。这样客户端对象就能以相同的方式操作具体组件对象和装饰   对象【或者说可以将装饰类作为组件类看待】

2.装饰对象包含一个真实对像的引用

3.装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。

4.装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就能确保在运行是,不用修改给定对象的结构就可以在外部增加附加功能。


适应性:

1.需要扩展一个类的功能,或者给一个类添加附加职责。

2.需要动态的给一个对象添加功能,这些功能可以在动态的撤销。

3.需要增加有一些基本功能的排列组合而产生的大量的功能,从而使继承关系不易实现。

4.当不能采用生成子类的方法进行扩充。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类的数目成爆炸性增长。


优点:

1.可以对一个对象进行多次修饰,创造出不同的行为组合,得到功能更为强大的对象

2.具体组件类和具体装饰类可以独立变化,用户可以根据组要自己增加新的具体装饰类和具体组件类

3.扩展对象功能,比继承灵活,不会导致类个数急剧增加


缺点:

1.产生很多小对象。大量小对象占据内存,一定程度上影响性能

2.装饰模式易于出错,调式排查比较麻烦

参考博文:

https://www.cnblogs.com/rookieFly-tdiitd/p/4914593.html


https://www.cnblogs.com/zhangtianq/p/6091047.html



版权声明:本文为zhuoyuezhang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。