【设计模式十六之装饰模式】装饰者模式

细说装饰模式

提示:
博主:章飞 _906285288的博客
博客地址:http://blog.csdn.net/qq_29924041


细说装饰模式

装饰模式在java或者在android中的应用非常广泛,如果细细的去品味下源码的话,里面真的是太多场景使用的都是装饰模式,下面就针对这个简单讲一下什么是装饰模式,装饰模式也是一种相对比较稍微难一点点的设计模式

定义

装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
举个例子,我记得小时候上小学的时候,我们那个年代的小学生,都有一个爱书的好习惯,每当开学领书回去的时候,都要从小店买几张包书皮,然后回家把语文数学什么的课本都被包装一下,这种其实就是装饰模式。或者包装模式,给书包了层书皮

UML模型

在这里插入图片描述
从图上可以看到,在装饰模式中主要分为以下几个角色:
1:Component 是一个接口或者抽象类,是我们需要包装的原始对象,类似上面的书籍

2:Decorator 抽象装饰类是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类拓展该方法,以达到装饰的目的。可以是抽象类或者接口,实现了Component接口

3:Concreate Component 最核心,最基本的,接口或者抽象类的实现 数学课本

4:ConcreteDecorator 具体装饰类是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法以便扩充对象的行为,

场景

场景一

场景来自设计模式之禅。上过学的同学应该都知道,每年都有期中和期末考试吧,可以回想一下,期中考试考完了,成绩下来了。然后老师给了大家一份各个科目的成绩单让家长看,看完成绩之后,然后签字带回来,试想一下。如果你是班级中上游的话,但是某科目成绩整体都比较低,而你也比较低的时候,如果只是拿分数回去给家长看,家长一定会斥责为什么考那么低对吧。那肯定签字会遇到麻烦啊。如果某科目分数很低,而你已经排名很高了。如果换种方式给家长可能会更好一点,这个时候,如果你把某科目的总分数,或者某科目在班级的总排名告诉爹妈,顺便再加一下分数的话。是不是起到意想不到的效果呢??这就是装饰者模式。我们需要在原始成绩单的基础上,稍微加一点装饰,让父母看到我想给父母看到的那部分,而弱化本来的分数。

场景二

这是目前比较流行的案例。咖啡类型案例。星巴克提供不同种类的咖啡和咖啡的调料,星巴克需要一些类来描述他们并且能计算出任意一种咖啡和任意几种调料搭配在一起的价格,如果我们用继承为每一种搭配写一个类的话,我们就需要把所有的咖啡全部穷举出来,想想星巴克的咖啡种类,没有上百种,那也有几十种类啊。想想都心累。
如果去套用装饰者模式,抽象出基类Beverage作为饮料类,而添加物作为装饰类,通过添加物添加,然后装饰出不同种类的咖啡。这样也就避免了类的爆炸的情况
Beverage(饮料类):相当与Component
HouseBlend、DarkRoast…(混合咖啡类、礁炒咖啡类…):相当于ConcreteComponent
CondimentDecorator(调料装饰者类):相当于Decorator
Milk、Mocha…(牛奶类、摩卡类…):相当于ConcreteDecorator
装饰者模式的特点,一个ConcreteComponent可以被任意个ConcreteDecorator装饰。
结合实例就来解释,一种咖啡可以和任意种调料搭配。
这样我们来应对各种点单的搭配的时候只需要在一种咖啡(ConcreteComponent)加上各种(ConcreteDecorator)就可以完成了,是不是觉得特别方便。

代码

代码一
package src.com.zzf.designpattern.decoratorpattern.demo1;


/**
 * 
 * @author Administrator
 *	装饰者模式中具体的componnet部分,即需要装饰的类的顶层抽象类,
 *学校的成绩单
 */
public abstract class SchoolReport {
	//报告成绩
	public abstract void report();
	//签字
	public abstract void sign(String name);
}

package src.com.zzf.designpattern.decoratorpattern.demo1;

/**
 * 装饰者模式中具体的实现类,即需要装饰的具体对象
 * @author Administrator
 *
 */
public class FouthGradSchoolReport extends SchoolReport{

	@Override
	public void report() {
		// TODO Auto-generated method stub
		System.out.println("尊敬的XXX家长:");
		System.out.println(" ......");
		System.out.println(" 语文 62 数学65 体育 98 自然 63");
		System.out.println(" .......");
		System.out.println(" 家长签名: ");
	}

	@Override
	public void sign(String name) {
		// TODO Auto-generated method stub
		System.out.println("家长签名为:"+name);
	}

}

对成绩单进行装饰的顶层的抽象类

package src.com.zzf.designpattern.decoratorpattern.demo1;


public abstract class Decorator extends SchoolReport{
	
	private SchoolReport mSchoolReport;
	
	public Decorator(SchoolReport mSchoolReport){
		this.mSchoolReport = mSchoolReport;
	}
	
	@Override
	public void report() {
		// TODO Auto-generated method stub
		this.mSchoolReport.report();
	}

	@Override
	public void sign(String name) {
		// TODO Auto-generated method stub
		
		this.mSchoolReport.sign(name);
	}

}

最高分的装饰类

package src.com.zzf.designpattern.decoratorpattern.demo1;


public class HighScoreDecorator extends Decorator {

	public HighScoreDecorator(SchoolReport mSchoolReport) {
		super(mSchoolReport);
		// TODO Auto-generated constructor stub
	}
	
	private void reportHighScore() {
		System.out.println("这次考试语文最高是75,数学是78,自然是80");
	}
	
	@Override
	public void report() {
		// TODO Auto-generated method stub
		this.reportHighScore();
		super.report();
	}
	
	public void onCreate(){
		
	}
	
}

排名的装饰类

package src.com.zzf.designpattern.decoratorpattern.demo1;


public class SortDecorator extends Decorator {

	public SortDecorator(SchoolReport mSchoolReport) {
		super(mSchoolReport);
		// TODO Auto-generated constructor stub
	}

	//告诉老爸学校的排名情况
	private void reportSort(){
		System.out.println("我是排名第38名...");
	}
	
	
	@Override
	public void report() {
		// TODO Auto-generated method stub
		super.report();
		this.reportSort();
	}
}

对成绩单的美化后的成绩单

package src.com.zzf.designpattern.decoratorpattern.demo1;


public class SugarFouthGradSchoolReport extends FouthGradSchoolReport{
	
	private void reportHighScore(){
		System.out.println("这次考试语文38分,英语80分");
	}
	
	private void reportSort(){
		System.out.println("汇报排名为38名");
	}
	
	@Override
	public void report() {
		// TODO Auto-generated method stub
		this.reportHighScore();
		super.report();
		this.reportSort();
	}
}

测试类

package src.com.zzf.designpattern.decoratorpattern.demo1;


/**
 * 装饰者模式是一种相对比较复杂的
 * 角色:
 * Componnet:要装饰的抽象出来的类
 * ConcreteComponnet:具体的要装饰的物品
 * Decorater:装饰者,需要继承要装饰的抽象类
 * ConcreteDecoraterA:装饰形式A
 * ConcreteDecoraterB:装饰形式B
 * 
 * @author Administrator
 *
 * 通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,
 * 那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的
 *
 *
 * 装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为
 */
public class Father {
	public static void main(String[] args) {
//		SchoolReport mSchoolReport = new SugarFouthGradSchoolReport(); //通过继承的形式扩展,可能会最终导致类过多,维护成本高
//		一般情况下类的继承尽量低于3个
//		mSchoolReport.report();
//		mSchoolReport.sign("签个锤子");
		
		SchoolReport mSchoolReport = new FouthGradSchoolReport();
		System.out.println("1:"+mSchoolReport.toString());
		mSchoolReport = new HighScoreDecorator(mSchoolReport);
		System.out.println("2:"+mSchoolReport.toString());
		mSchoolReport.report();
		
		mSchoolReport = new SortDecorator(mSchoolReport);
		System.out.println("3:"+mSchoolReport.toString());
		mSchoolReport.report();
	}
}

代码二
package src.com.zzf.designpattern.decoratorpattern.demo3;

/**
 * 抽象的被装饰对象,即咖啡类,相当于Component
 * @author zhouzhangfei
 *
 */
public abstract class Coffee {
	 protected String description="";
	 
     public String getDescription(){
	        return description;
	 }
     
     public abstract double cost();
}

package src.com.zzf.designpattern.decoratorpattern.demo3;

/**
 * 抽象的装饰者,即去装饰咖啡对象,相当于Decorator
 * @author zhouzhangfei
 *
 */
public abstract class CondimentDecorator extends Coffee{

	 public abstract String getDescription();

}

package src.com.zzf.designpattern.decoratorpattern.demo3;

/**
 * 具体需要装饰的咖啡类别1,ConcreteComponent
 * @author zhouzhangfei
 *
 */
public class DarkRoastCaffee extends Coffee {
	public DarkRoastCaffee(){
        description="Dark Roast coffee";
   }
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 3;
	}

}

package src.com.zzf.designpattern.decoratorpattern.demo3;

/**
 * 具体的咖啡类别2,相当于ConcreteComponent
 * @author zhouzhangfei
 *
 */
public class HouseBlendCoffee extends Coffee{
	public HouseBlendCoffee(){
		         description="House Blend coffee";
		    }
		    @Override
		     public double cost() {
		         return 4.9;
		     }
}

package src.com.zzf.designpattern.decoratorpattern.demo3;

/**
 * 咖啡的添加物1,类比于ConcreteDecorator
 * @author zhouzhangfei
 *
 */
 public class Milk extends CondimentDecorator {
	     protected Coffee beverage;
	    public Milk(Coffee beverage){
	        this.beverage=beverage;
	    }
	    @Override
	    public String getDescription() {
        return beverage.getDescription()+",with milk";
	     }
	    @Override
	     public double cost() {
	         return 2.3+beverage.cost();
	    }
	}
package src.com.zzf.designpattern.decoratorpattern.demo3;

/**
 * 咖啡的添加物1,类比于ConcreteDecorator
 * @author zhouzhangfei
 *
 */
public class Mocha extends CondimentDecorator {
     protected Coffee beverage;
     public Mocha(Coffee beverage){
        this.beverage=beverage;
    }
     @Override
     public String getDescription() {
        return beverage.getDescription()+",with Mocha";
    }
    @Override
     public double cost() {
         return 1.2+beverage.cost();
    }
 }
package src.com.zzf.designpattern.decoratorpattern.demo3;

public class Starbucks {
	 public static void main(String[] args) {
		 Coffee  coffee1 = new HouseBlendCoffee();
		 Milk milk = new Milk(coffee1);
		 System.out.println(milk.getDescription()+"\t"+milk.cost());
		 
		 Coffee  coffee2 = new HouseBlendCoffee();
		 Milk milk2 = new Milk(coffee1);
		 System.out.println(milk2.getDescription()+"\t"+milk2.cost());
	 }
}

基于UML的代码
package src.com.zzf.designpattern.decoratorpattern.demo2;


public abstract class Component {
	public abstract void operate();
}

package src.com.zzf.designpattern.decoratorpattern.demo2;


public class ConcreteComponent extends Component {

	@Override
	public void operate() {
		// TODO Auto-generated method stub
		System.out.println("ConcreteComponet operate");
	}

}

package src.com.zzf.designpattern.decoratorpattern.demo2;


public class Decorator extends Component{
	Component mComponent;
	
	public Decorator(Component mComponent){
		this.mComponent = mComponent;
	}
	
	@Override
	public void operate() {
		// TODO Auto-generated method stub
		mComponent.operate();
	}

}

package src.com.zzf.designpattern.decoratorpattern.demo2;


public class ConcreteDecorator extends Decorator{

	public ConcreteDecorator(Component mComponent) {
		super(mComponent);
		// TODO Auto-generated constructor stub
	}
	private void operateB() {
		System.out.println("ConcreteDecorator operateB");
	}
	@Override
	public void operate() {
		// TODO Auto-generated method stub
		super.operate();
		operateB();
	}

}

package src.com.zzf.designpattern.decoratorpattern.demo2;


public class ConcreteDecorateA extends Decorator{

	public ConcreteDecorateA(Component mComponent) {
		super(mComponent);
		// TODO Auto-generated constructor stub
	}
	
	private void operateA(){
		System.out.println("ConcreteDecorateA operateA");
	}
	@Override
	public void operate() {
		// TODO Auto-generated method stub
		operateA();
		super.operate();
	}

}

测试类:

package src.com.zzf.designpattern.decoratorpattern.demo2;


public class Test {
	public static void main(String[] args) {
		Component mComponent = new ConcreteComponent();
		mComponent = new ConcreteDecorateA(mComponent);
		mComponent = new ConcreteDecorator(mComponent);
		mComponent.operate();
	}
}

装饰模式应用和注意事项

1:装饰者模式中用到了继承,但是这里装饰者模式用继承是为了保证装饰者和被装饰者有共同的超类,而不是为了给被装饰的类扩展新的特性,而装饰者模式中新的特性是通过类与类的组合(has-a的关系)而得到的,所以把装饰者模式和继承看做同一类设计思想是不恰当的。

2当你的有些设计是针对某些具体的Component类型即ConcreteComponent时,装饰者模式是不适用的,因为被装饰过的类是通过组合来实现特性的扩展,所以装饰过的类并不具有某个特定的ConcreteComponent类型,而只具有其本身的类型和它继承的Decorator以及Component的类型,所以只有当你的操作都是针对Component类型的时候,装饰者模式才是合适的,否则你就要重新考虑你的设计。

3:会引入大量的类,常常让人觉得不知所措,特别是对于初学者来说,最好的例子就是java的I/O类以及android 中的Context关系类,大量的类并不会影响其实一个好用的设计模式




欢迎继续访问,我的博客
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值