前面我们学习了代理模式:
代理模式主要使用了java的多态,干活的是被代理类,代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚得很,同样一个接口。
本次我们学习下装饰模式:
装饰模式又称为包装模式,装饰模式以对客户端透明的方式扩展对象功能,相对于代理而言,代理是不让客户端知道真实对象的信息,装饰模式是基层关系的一个替代方案。
装饰模式是采用对客户端透明的方式动态的给一个对象添加了更多职责,对于原来被装饰的对象而言在装饰前和后并没有什么不同。装饰模式可以在不使用创造更多子类的情况下扩展对象的功能。
类图如下:
Component:组件对象的接口,规范那些准备添加附加功能类的对象。
ConcreateComponent:具体的组件对象,实现组件对象接口,通常就是被装饰的原始对象,也就是可以给这个对象添加附加的功能。
Decorator:所有装饰器的抽象父类,需要定义一个与组件对象一致的接口,并且持有一个Component对象,其实就是原始对象,被装饰的对象。
ConcreateDecorator:实际的装饰对象,实现具体要像被装饰对象添加的功能。
抽象的接口:
package com.njupt.study.designmodle.decorator; public interface Component { public void operation(); }
原始对象,即需要被装饰的对象:
package com.njupt.study.designmodle.decorator; public class ConcreateComponent implements Component{ public ConcreateComponent(){}; @Override public void operation() { System.out.println("开车"); } }
所有装饰对象的父类:
package com.njupt.study.designmodle.decorator; public abstract class Decorator implements Component { private Component component; public Decorator(Component component) { this.component = component; } public void operation() { this.component.operation(); } }
实际装饰对象A:
package com.njupt.study.designmodle.decorator; public class ConCreateDecoratorA extends Decorator { public ConCreateDecoratorA(Component component) { super(component); } public void addedOperation() { System.out.println("晚上"); } public void operation() { addedOperation(); super.operation(); } }
实际装饰对象B:
package com.njupt.study.designmodle.decorator; public class ConCreateDecoratorB extends Decorator { public ConCreateDecoratorB(Component component) { super(component); } public void addedOperation() { System.out.println("兜风"); } public void operation() { super.operation(); addedOperation(); } }
客户端:
package com.njupt.study.designmodle.decorator; public class Client { public static void main(String[] args) { Component component = new ConcreateComponent(); Decorator decorator = new ConCreateDecoratorB(new ConCreateDecoratorA(component)); decorator.operation(); } }
测试结果:
装饰模式的定义 : 透明的给一个对象添加功能,并能够实现功能的动态组合.
在装饰模式的实现中,为了能够实现和原来使用被装饰对象的代码无缝结合,是通过定义一个抽象类,让这个类实现与被装饰对象相同的接口,然后在具体的实现类中,转调被装饰的对象,在转调的前后添加新的功能,这就实现了给被装饰对象增加功能.
下面举一个实际的例子:
定义一个接口:
package com.njupt.study.designmodle.decorator; /** * 定义被装饰者 和 装饰者 共同的接口 * @author Pony * */ public interface Human { public void wearClothes(); public void walkToWhere(); }
定义原始对象:
package com.njupt.study.designmodle.decorator; /** * 定义被装饰者,被装饰者初始状态有些自己的装饰 * @author Pony * */ public class Person implements Human { @Override public void walkToWhere() { System.out.println("穿什么呢。。"); } @Override public void wearClothes() { System.out.println("去哪里呢。。"); } }
定义装饰对象:
package com.njupt.study.designmodle.decorator; public class DecoratorHuman implements Human { private Human human; public DecoratorHuman(Human human) { this.human = human; } public void wearClothes() { human.wearClothes(); } public void walkToWhere() { human.walkToWhere(); } }
下面定义三种装饰,这是第一个,第二个第三个功能依次细化,即装饰者的功能越来越多
package com.njupt.study.designmodle.decorator; public class Decorator_zero extends DecoratorHuman { public Decorator_zero(Human human) { super(human); // TODO Auto-generated constructor stub } public void goHome() { System.out.println("进房子。。"); } public void findMap() { System.out.println("书房找找Map。。"); } @Override public void wearClothes() { // TODO Auto-generated method stub super.wearClothes(); goHome(); } @Override public void walkToWhere() { // TODO Auto-generated method stub super.walkToWhere(); findMap(); } }
package com.njupt.study.designmodle.decorator; public class Decorator_first extends DecoratorHuman { public Decorator_first(Human human) { super(human); } public void goClothespress() { System.out.println("去衣柜找找看。。"); } public void findPlaceOnMap() { System.out.println("在Map上找找。。"); } @Override public void wearClothes() { // TODO Auto-generated method stub super.wearClothes(); goClothespress(); } @Override public void walkToWhere() { // TODO Auto-generated method stub super.walkToWhere(); findPlaceOnMap(); } }
package com.njupt.study.designmodle.decorator; public class Decorator_two extends DecoratorHuman { public Decorator_two(Human human) { super(human); } public void findClothes() { System.out.println("找到一件D&G。。"); } public void findTheTarget() { System.out.println("在Map上找到神秘花园和城堡。。"); } @Override public void wearClothes() { // TODO Auto-generated method stub super.wearClothes(); findClothes(); } @Override public void walkToWhere() { // TODO Auto-generated method stub super.walkToWhere(); findTheTarget(); } }
客户端:
package com.njupt.study.designmodle.decorator; public class Test { public static void main(String[] args) { Human person = new Person(); DecoratorHuman decorator = new Decorator_two(new Decorator_first( new Decorator_zero(person))); decorator.wearClothes(); decorator.walkToWhere(); } }
测试结果:
其实就是进房子找衣服,然后找地图这样一个过程,通过装饰者的三层装饰,把细节变得丰富。
关键点:
1、Decorator抽象类中,持有Human接口,方法全部委托给该接口调用,目的是交给该接口的实现类即子类进行调用。
2、Decorator抽象类的子类(具体装饰者),里面都有一个构造方法调用super(human),这一句就体现了抽象类依赖于子类实现即抽象依赖于实现的原则。因为构造里面参数都是Human接口,只要是该Human的实现类都可以传递进去,即表现出Decorator dt = new Decorator_second(new Decorator_first(new Decorator_zero(human)));这种结构的样子。所以当调用dt.wearClothes();dt.walkToWhere()的时候,又因为每个具体装饰者类中,都先调用super.wearClothes和super.walkToWhere()方法,而该super已经由构造传递并指向了具体的某一个装饰者类(这个可以根据需要调换顺序),那么调用的即为装饰类的方法,然后才调用自身的装饰方法,即表现出一种装饰、链式的类似于过滤的行为。
3、具体被装饰者类,可以定义初始的状态或者初始的自己的装饰,后面的装饰行为都在此基础上一步一步进行点缀、装饰。
4、装饰者模式的设计原则为:对扩展开放、对修改关闭,这句话体现在我如果想扩展被装饰者类的行为,无须修改装饰者抽象类,只需继承装饰者抽象类,实现额外的一些装饰或者叫行为即可对被装饰者进行包装。所以:扩展体现在继承、修改体现在子类中,而不是具体的抽象类,这充分体现了依赖倒置原则,这是自己理解的装饰者模式。