Decorator模式又叫装饰者模式,这种模式是为了满足Java开发的“面向扩展开放,面向修改闭源”的开发原则设计出来的。
在装饰者模式中,不修改源类的代码,却能修改源类中方法的功能。下面就以Angelababy化妆为例,详细介绍一下为什么需要装饰者模式,以及装饰者模式怎么实现:
- 先介绍angelababy类
package site.wangxin520.gof.decorator; /** * 装饰者模式 * baby化妆类 * @author wangXgnaw * */ public class Angelababy { /** * 画眉毛 */ public void meimao(){ System.out.println("baby自己画眉毛"); } /** * 画眼影 */ public void yanying(){ System.out.println("baby自己画眼影"); } /** * 点腮红 */ public void saihong(){ System.out.println("baby自己画腮红"); } /** * 画口红 */ public void kouhong(){ System.out.println("baby自己涂口红"); } }
在baby类里面,有四个方法,分别对应着baby自己给自己化妆的步骤。
- 装饰者模式测试类,首先是baby给自己化妆
package site.wangxin520.gof.decorator; /** * 装饰者模式的测试类 * @author wangXgnaw * */ public class Test { public static void main(String[] args) { Angelababy ab =new Angelababy(); //baby给自己化妆 ab.meimao(); ab.yanying(); ab.saihong(); ab.kouhong(); } }
- 结果是:
由此可见,baby自己给自己化妆成功。
虽然这样自己是能给自己化妆的,不过怎么也没有专业的化妆师给自己化妆的效果好。所以baby找来了一个专业的化妆师A。
- DresserA装饰者模式的的第一种实现,继承
package site.wangxin520.gof.decorator; /** * 装饰者模式的装饰者 * 化妆师A * @author wangXgnaw * */ public class DresserA extends Angelababy{ /** * 重写了画眼影的方法,在画眼影之后,又加上了刷睫毛 */ @Override public void yanying() { super.yanying(); //后面输出有误,注意
System.out.println("给anglebaby刷睫毛"); } /** * 重写了涂口红的方法 * 在涂口红之前,给她涂了桃红色红 */ @Override public void kouhong() { System.out.println("给anglebaby涂了桃红色的口红"); super.kouhong(); } }
- 测试类
package site.wangxin520.gof.decorator; /** * 装饰者模式的测试类 * @author wangXgnaw * */ public class Test { public static void main(String[] args) { //化妆师a给baby化妆 Angelababy ab=new DresserA(); //anglebaby化妆 ab.meimao(); ab.yanying(); ab.saihong(); ab.kouhong(); } }
- 结果:
由此可见,baby的眼影和口红都不是自己画的了,而是别人帮她画的。这就是第一种继承的方法。不过这种方法是有局限性的,如果anglebaby类是final修饰的不可继承的话,那就尴尬了。另外,化妆师实际上是不能去继承被化妆的那个人的,如果化妆师继承了anglebaby而不是小海绵继承的话,黄晓明可能会哭晕在厕所!
下面就介绍第二种方法:面向接口开发
首先提取出一个公共接口,化妆的interface接口
- MakeUp 化妆的接口
package site.wangxin520.gof.decorator; /** * 化妆的接口 * @author wangXgnaw * */ public interface MakeUp { /** * 画眉毛 */ public void meimao(); /** * 画眼影 */ public void yanying(); /** * 点腮红 */ public void saihong(); /** * 画口红 */ public void kouhong(); }
- 让anglebaby实现该接口
package site.wangxin520.gof.decorator; /** * 装饰者模式 * baby化妆类 * @author wangXgnaw * */ public class Angelababy implements MakeUp{ /** * 画眉毛 */ @Override public void meimao(){ System.out.println("baby自己画眉毛"); } /** * 画眼影 */ @Override public void yanying(){ System.out.println("baby自己画眼影"); } /** * 点腮红 */ @Override public void saihong(){ System.out.println("baby自己画腮红"); } /** * 画口红 */ @Override public void kouhong(){ System.out.println("baby自己涂口红"); } }
- DresserB 化妆师b,同样也实现这个接口。不过在构造化妆师B的时候,得把anglebaby传进来
package site.wangxin520.gof.decorator; /** * 化妆师B * @author wangXgnaw * */ public class DresserB implements MakeUp{ //用于保存化妆师B的顾客 private MakeUp makeUp=null; /** * 在构造化妆师B的类的时候,传入顾客 * @param makeUp */ public DresserB(MakeUp makeUp) { this.makeUp=makeUp; } /** * 实现化妆的类,当调用顾客对应的类的时候,实现的还是顾客的功能 * 当需要增加新功能时候,只需要在对应的方法前面或者后面实现即可 */ @Override public void meimao() { makeUp.meimao(); } /** * 化妆师B觉得涂眼影不好看,所以不涂了 * 只给baby贴了双眼皮贴 */ @Override public void yanying() { System.out.println("贴双眼皮贴"); } /** * 化妆师B想在baby涂腮红之前,就加上摸粉底 */ @Override public void saihong() { System.out.println("摸粉底"); makeUp.saihong(); } @Override public void kouhong() { makeUp.kouhong(); } }
- 测试类
package site.wangxin520.gof.decorator; /** * 装饰者模式的测试类 * @author wangXgnaw * */ public class Test { public static void main(String[] args) { //化妆师B给baby化妆 Angelababy ab=new Angelababy(); DresserB db=new DresserB(ab); db.kouhong(); db.yanying(); db.saihong(); db.meimao(); } }
- 结果为:
由此可见,装饰者模式生效了。
不过面向接口的这种装饰者模式也有一个小问题,那就是必须重写接口的实现类,并且在实现类里面调用传入参数的所有方法。
当接口中抽象方法很多的时候,这就很麻烦。
为了解决这种麻烦,就提出了一种新的设计模式,即当需要使用哪种方法的时候,就修饰哪种方法,否则就不改变。这种新的模式就是动态代理模式。后面我们将详细介绍!