Java设计模式--装饰者模式【Decorator Pattern】

      有时,你想要在程序运行期间为某个对象组合新的行为,且在某种情况下,你需要对象的行为发生一些细小的变化,并且这些变化可以自由组合。这时,装饰者模式就可以满足这种需求。

 所谓装饰者模式,也叫修饰者模式、装饰器模式,意图是在运行时组合操作的新变化,即动态地给一个对象添加一些额外的职责。

 装饰模式在中国使用的那实在是多,中国的文化是中庸文化,中国人都比较“爱面子”,说话做事呢都不能太直接,必须给对方留有余地。比如某个阳光灿烂的清晨,领导兴高采烈精神焕发地出现在你面前,你抬头一看,哎,换发型了,怎么这么挫?但是你还是会一遍又一遍地压抑自己的内心,面带微笑说,领导今天真是帅气!领导一听,哎,这个员工挺有眼光,然后春风满面扬长而去。若你不识抬举,跟领导说,你这个发型怎么这么难看,像个糟老头!我敢保证,你离辞职不远了。。。

 再来说说邻居的阿姨有个女儿,名叫小红,芳龄28,生的是亭亭玉立,貌美如花,眼看着闺蜜,同学一个个结婚生子,而小红却依然孑然一身,阿姨愁得每天吃不好睡不着。然后就拖着范大妈给介绍对象,范大妈一想,哎,我刚好有个同学的儿子华哥还单着,要不给介绍给小红把,年龄大点,但是多会照顾人呢。。。

 我们就来说说这个华哥,范大妈一想,可不能直接说年龄、身高、长相,那敢情小花看不上怎么办?她就寻思着多给小花说说华哥的“优异”条件,这样一修饰,华哥就是难得的”钻石王老五”啦,小花不要还有小刘呢。

 范大妈简单画了个图,是这样的:

  定义了男人的抽象类,然后有个普通男人实现类,再有个高学历的男人实现类,再有好工作男人实现类,再有好家境男人的实现类,看看这些类: 

package com.pattern.decorator.v1;
/**
 * 那男人说了,把我的条件跟人家说说
 * 同意就交往,不行就算了,给句痛快话
 * @author 
 *
 */
public abstract class Man {
    public abstract void showAppearance();//外在条件
    public abstract void appointment(boolean yesOrNo);//是否同意呢
}
package com.pattern.decorator.v1;
/**
 * 外在条件太一般了,咋整?
 * @author 
 *
 */
public class NormalMan extends Man {

	@Override
	public void showAppearance() {
        System.out.println("年龄40,身高1.68cm,长相一般,有点黑,好在不是歪瓜裂枣...");
	}
	@Override
	public void appointment(boolean yesOrNo){
		if(yesOrNo){
			System.out.println("同意啦,交往吧!");
		}else{
			System.out.println("太挫了,休想!");
		}
	}
}
package com.pattern.decorator.v1;
/**
 * 高智商高学历,不错哦,继续看看。。。
 * @author 
 *
 */
public class GoodEducationMan extends NormalMan {

 private void showEducation(){
	 System.out.println("清华大学博士哦...");
 }
 @Override
public void showAppearance() {
	 this.showEducation();
	 super.showAppearance();
}
}
package com.pattern.decorator.v1;
/**
 * 男人40一枝花,这条件也是没谁了,老张闺女不行还设有老李家呢
 * @author 
 *
 */
public class GoodWorkMan extends GoodEducationMan {
 @Override
public void showAppearance() {
	 this.showWork();
	 super.showAppearance();
}
private void showWork(){
	 System.out.println("某证券公司部门经理...");
 }
}
package com.pattern.decorator.v1;
/**
 * 家境还这么好,老人无负担,谁看不上谁无脑
 * @author 
 *
 */
public class GoodParentsMan extends GoodWorkMan{
  private void showParents(){
	  System.out.println("爸妈都是公务员,拿着退休金在家乐呵呵....");
  }

@Override
public void showAppearance() {
	this.showParents();
	super.showAppearance();
}  
}
 然后范大妈就跟小红说,这个男人呀,是个高材生博士,在某证券公司当老总,父母是公务员,虽然年龄稍大,长相身高都一般,那都不是事儿呀,你考虑考虑?  

package com.pattern.decorator.v1;
/**
 * 老张闺女相亲录
 * @author 
 *
 */
public class Test_DatingWoman {
   public static void main(String[] args){
	   Man man=new GoodParentsMan();高学历 好工作 家境好 外在条件
	   man.showAppearance();//相亲女看条件
	   man.appointment(true);//然后觉得很开心啊,不错啊,同意同意
   }
}
  以上是通过“继承”的方式确实解决了这个问题,但是现实的情况很复杂的,可能小红听范大妈说完学历高后,就直接乐开花了,直接同意了,后面工作好,家境好都不需要了,或者小红觉得比较在意工作,先说了工作就不用说别的了,那怎么办?继续扩展类?你能扩展多少个类?这还是一个比较简单的场景,一旦需要装饰的条件非常的多,比如20 个,你还通过继承来解决,你想想的子类有多少个?类与类之间的父子关系是怎样?你是不是马上就要崩溃了!

 好,你也看到通过继承情况确实出现了问题,类爆炸,类的数量激增,光写这些类不累死你才怪,而且还要想想以后维护怎么办,谁愿意接收这么一大堆类的维护哪?并且在面向对象的设计中,如果超过2 层继承,你就应该想想是不是出设计问题了,是不是应该重新找一条道了,这是经验值,不是什么绝对的,继承层次越多你以后的维护成本越多,问题这么多,那怎么办?好办,装饰模式出场来解决这些问题,我们先来看类图:


  增加一个装饰抽象类和三个实现类,其中ManDecorator的作用是封装Man类,看源代码:

package com.pattern.decorator.v2;
/**
 * 邻居阿姨看了直叹气,这条件,怎么跟人家闺女说呢
 * 还是先把男人包装下,不能直接说,不然就泡汤了
 * @author 
 *
 */
public abstract class ManDecorator extends Man {
	private Man man;

	public ManDecorator(Man man) {
		super();
		this.man = man;
	}

	@Override
	public void showAppearance() {
        this.man.showAppearance();
	}

	@Override
	public void appointment(boolean yesOrNo) {
        this.man.appointment(yesOrNo);
	}

}
package com.pattern.decorator.v2;
/**
 * 高智商高学历,不错哦,继续看看。。。
 * @author 
 *
 */
public class GoodEducationDecorator extends ManDecorator {
	public GoodEducationDecorator(Man man){
    	super(man);
    }
	private void showEducation(){
		 System.out.println("清华大学博士哦...");
	}
	@Override
	public void showAppearance() {
		this.showEducation();
		super.showAppearance();
	}
}
package com.pattern.decorator.v2;
/**
 * 男人40一枝花,这条件也是没谁了,老张闺女不行还设有老李家呢
 * @author 
 *
 */
public class GoodWorkDecorator extends ManDecorator {

	public GoodWorkDecorator(Man man) {
		super(man);
	}
    private void showWork(){
    	System.out.println("某证券公司部门经理...");
    }
	@Override
	public void showAppearance() {
		this.showWork();
		super.showAppearance();
	}
}
package com.pattern.decorator.v2;
/**
 * 家境还这么好,老人无负担,谁看不上谁无脑
 * @author 
 *
 */
public class GoodParentsDecorator extends ManDecorator {

	public GoodParentsDecorator(Man man) {
		super(man);
	}
	private void showParents(){
		  System.out.println("爸妈都是公务员,拿着退休金在家乐呵呵....");
	}
	@Override
	public void showAppearance() {
        this.showParents();
		super.showAppearance();
	}
}
然后范大妈找到小红,就问问小红的相亲标准呀,然后根据她的品味一个条件一个条件地跟她说,直到她同意为止:

package com.pattern.decorator.v2;
/**
 * 老张闺女相亲录
 * @author 
 *
 */
public class Test_DatingWoman {
  public static void main(String[] args){
	  Man man=null;
	  man=new NormalMan();//外在条件
	  man=new GoodParentsDecorator(man);//家境好
	  man=new GoodEducationDecorator(man);//高学历
	  man=new GoodWorkDecorator(man);//好工作
	  man.showAppearance();//相亲女看条件,其实家境好,学历高就暗自点头了
	  man.appointment(true);//然后觉得很开心啊,不错啊,同意同意
  }
}

  这就是装饰者模式,装饰者模式的通用类图如下:

     

  看类图,Component 是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,比如上面的相亲男,记住在装饰模式中,必然有一个被提取出来最核心、最原始、最基本的接口或抽象类,就是Component。ConcreteComponent 这个是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是这个东西Decorator 一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private 变量指向Component。ConcreteDecoratorA 和ConcreteDecoratorB 是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰城啥东西,上面的例子就是把一个比较平庸的男人装饰成大家认可的成功男士。

 继承和装饰者模式的区别

 装饰者模式是对继承的有力补充,继承是给一个类添加行为的比较有效的途径。通过使用继承,可以使得子类在拥有自身方法的同时,还可以拥有父类的方法。但是使用继承是静态的,在编译的时候就已经决定了子类的行为,我们不便于控制增加行为的方式和时机。而装饰者模式是动态组合组合关系的优势就在于不会破坏类的封装性,且具有较好的松耦合性,可以使系统更加容易维护。但是它的缺点就在于也要创建很多的对象。

  装饰者的使用场景

  1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  2、需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。  当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。


源码下载:http://download.csdn.net/download/pelifymeng2/9994734


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值