HF_装饰者模式(Decorator_Pattern)_20200308

我们讲都都是设计一个类,设计一个类,设计。

装饰者模式中的”装饰“的意思是礼物盒的装饰那种装饰。

其实就是不断地“装饰组件”嵌套“组件”的过程,只不过不是用继承技术,而是用组合和委托技术

无论是哪种模式都至少有两层向上抽象,一般都是三层抽象以上,普遍有中介者来结构,而这个中介者很多时候也是抽象出来的抽象。

利用组合思想来代替继承,利用委托(多态)来实现组合,其实就是多态,本质也是抽象。但是该如何抽象,抽象出来的东西该放在哪里?抽象出来的东西是否还需要继续抽象?抽象出来的用途是什么?等等一系列的回答不同就组成了不同的设计模式。也就有根据不同的场景有了不同的术语。于是,抽象的思想基本上贯穿了面向对象设计思想的全部,所以针对接口编程也就贯穿了全部。

装饰者相对继承的优势在于:

              装饰者可以在new一个对象的时候(具体使用的时候),传递一个组件的引用作为参数进来,在使用时动态地组合对象。而继承必须在设计阶段就考虑每一个new对象的类,属于静态设计代码,然后你用数学的组合思想来算一下下面的需求,所需要设计的类。用装饰者模式的话,要设计4+3个类,用继承的话,要继承4+3*2*1个类。

              如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。

               抽象装饰者继承于抽象组件,在抽象组件基础上增加了行为(方法)。

              具体组件继承并实现抽象组件,是一个可以使用的具体类(是类,不是实例)。

              具体装饰者类,继承并实现于装饰者类,所以装饰者也是一个具体组件,只不过它由于继承抽象装饰,也就有了抽象装饰里的额外行为,所以装饰者就是有了额外行为的具体组件类。

             那为什么在设计具体组件类的时候就不把额外的行为设计进去呢?就是因为当时没考虑到要额外的行为啊,不然怎么叫做额外的行为啊?现在亡羊补牢啊。我另外设计一个抽象装饰,抽象装饰者类,比修改系统原来就存在的大量代码要轻松得多啊,现在只是增加代码,不用做过多的修改啊。核心就是在装饰者组件里声明一个原来的具体组件的属性,从而对其扩展,以后要想有额外的行为就使用这个装饰者就好了,而以前系统存在的代码也不会受到影响啊。

装饰者模式的定义:

         动态的将责任附加到对象上(嵌套)。若要扩展功能,装饰者提供了比继承更有弹性的设计方案。

装饰者模式里的术语(角色):

        组件(抽象):   

                你用一下自下而上的抽象思想,把每一种具体的饮料,例如橙汁、苹果汁、奶茶、咖啡等等,抽象成饮料,这个“饮料”就是抽象组件了。每个组件都可以单独使用可,或者被装饰者包起来,即嵌套起来。

       具体组件:   

               就是上面说的咯,也就是在这个系统中的销售对象咯。就是可以在具体使用时,动态地加上新行为的对象。(也就是实例化时)

       抽象的装饰者类(可选):

            继承于抽象组件,并包含具体组件的实例的引用,其子类可以扩展具体组件的功能。具体组件不是实例,是一个类。

           也就是如果你想要递归地执行“每一层抽象组件里都要有”的某一个方法时,你就要为该方法设计一个(层)抽象类,用于递归(因为具体类必须实现该抽象方法嘛,也就是必须要具体子类去定义方法的具体代码咯)。而在具体的装饰者类里面,也声明了爸爸级别的引用,即定义抽象组件的引用。也就是在一个抽象类(或者接口)里面,声明了抽象组件,不是说就没有声明其他的东西了,可能也有声明抽象组件以外的方法。

       具体装饰者:

            也即是抽象装饰的子类,即具体类,实现抽象装饰的相关方法,并给具体构件对象添加附加的责任(行为)。

            其实就是不断地“装饰组件”嵌套“组件”的过程,只不过不是用继承技术,而是用组合和委托技术。每个装饰者都“有一个”(包装一个)组件,也就是说,装饰者有一个实例变量以保存某个component的引用。嵌套之后的组件也还是组件,你叫它装饰过的组件也可以。

示例的需求:

           开一家咖啡店,有不同的饮料,不同的饮料可以加不同的配料,于是你要设计这个咖啡店的(饮料)订单系统。假设我们现在的饮料有HouseBlend、DarkRoast、Decaf、Espresso;配料有Milk、Soy、Mocha、Whip。                   

需求分析:

          如果你为每一杯饮料都设计一个类,那肯定是行不通的,你试试用数学的排列组合算一下?软件工程里好像有一个方法是从需求里面提取出名词和动词,找出关键的,然后再设计类,排除类,剩下有用的,哎,忘了。

设计原则: 设计类的时候,类应该对“扩展”开放,对“修改“关闭

这里的修改的意思不是说不能修改代码,而是尽量不要去修改设计好的类的定义,因为继承什么的,会牵涉到很多其他的类也要进行修改,牵一发而动全身,所以你可以设计新的类来继承或者装饰什么,就不要去修改已经定义好的类了(这也是对“扩展”开放的意思,继承或者装饰什么的)。

所以,我们设计类的目标是允许该类很容易地被扩展,在不修改现有的类定义里面的代码情况下,就可以为现有的类搭配新的行为。其实就是要设计一个新的类了咯,对旧的类继承,装饰什么的,就是新的了呀。这样的设计,就具有弹性,可以应对改变,可以接受新的功能来应对那些经常需要改变的需求。

也就是说,如果你旧的类已经写死了,不给别人一点点扩展的机会,那么当新的需求过来时,哦呵,你要重新由头到尾设计过整一个系统了,gg。

示例代码:

抽象组件(单纯)--饮料抽象类:

package s_AbstractClass;

/**
 * 装饰者模式中的抽象组件,共同的超类
 * 装饰者模式适用于 属于同一个超类的 不同组件之间需要不断地嵌套组合的场景;虽然嵌套了很多,但最终也还是这个超类的实例
 * 装饰者模式理解的关键在于,共同的抽象接口(超类),好像其他模式的最顶层也至少有两层的抽象,针对接口编程。
 * 装饰是指礼物盒外层的装饰那种。
 * 被装饰者叫做 具体组件,超类叫做 抽象组件,有一个叫 抽象装饰者,也有一个叫 具体装饰者。
 * @author mathew
 *
 */

public abstract class Beverage {
	
	protected String description = "Unkown Beverage";
			
	public String getDescription() {
		return description;
	}
	
	public abstract double cost();

}

抽象组件的装饰者类(嵌套与被嵌套)--配料抽象类:

            为具体的装饰者再提供了一层抽象,供具体装饰者类遵循,具体装饰者类才是真正的设计了嵌套的组件的引用,其实我觉得不用这层抽象装饰者类也可以了,抽象组件类也就可以了。但是这里要多层递归抽象组件里面的方法,所以就需要再定义一层抽象,用于递归抽象组件里面的方法。

package s_AbstractDecoratorClass;

import s_AbstractClass.Beverage;

/**
 * 抽象装饰者类,继承与具体组件共同的接口,目的是嵌套各个不同的组件时,大家有共同的方法属性,方便嵌套。这也是与广义组合思想的区别。
 * 组合思想中,被组合者并不需要是共同接口的遵循者,只要是一个抽象的接口,可以利用多态就行,范围更广。
 * @author mathew
 *
 */
public abstract class CondimentDecorator extends Beverage {
	
	public abstract String getDescription();

}

具体的组件类(被装饰者)--没加配料的饮料的具体类:

package s_ConcreteCompnentClass;

import s_AbstractClass.Beverage;
/**
 * 具体组件类,这个具体组件就是不断的被装饰者拿去嵌套的,
 * 注意装饰者嵌套组件时的类定义并不是具体组件的类定义,而是利用多态,即抽象组件类,就是共同的超类。
 * @author mathew
 *浓咖啡
 */
public class Espresso extends Beverage {
	
	public Espresso() {
		super.description = "Espresso";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 1.99;
	}

}

具体的含装饰组件(装饰者类)--加了配料的饮料具体类:

package s_DecoratorClass;

import s_AbstractClass.Beverage;
import s_AbstractDecoratorClass.CondimentDecorator;

/**
 * 装饰者类,摩卡
 * 装饰者嵌套组合具体组件时,是在具体装饰者的类定义中定义抽象组件的,而不是在抽象装饰者的类定义中定义的。
 * @author mathew
 *
 */

public class Mocha extends CondimentDecorator {
	
	//被嵌套的抽象组件,被装饰的对象。礼物
	protected Beverage beverage;

	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}
	
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + " , Mocha";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 0.21 + beverage.cost();
	}

}

示例的执行:

package s_MainClass;

import s_AbstractClass.Beverage;
import s_ConcreteCompnentClass.DarkRoast;
import s_ConcreteCompnentClass.Espresso;
import s_ConcreteCompnentClass.HouseBlend;
import s_DecoratorClass.Mocha;
import s_DecoratorClass.Soy;
import s_DecoratorClass.Whip;

/**
 * 模仿星巴克的饮料来实现装饰者模式。
 * @author mathew
 *
 */
public class StarbuzzCoffee {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		//具体组件
		Beverage beverage = new Espresso();
		System.out.println(beverage.getDescription() + " $ " + beverage.cost());
		
		Beverage beverage2 = new DarkRoast();
		Beverage beverage3 = new HouseBlend();
		
		//装饰者,深度烘焙的加料饮料
		//加两个摩卡,可以设置一个临时变量,注意参数和变量都是同一个引用
		beverage2 = new Mocha(beverage2);
		beverage2 = new Mocha(beverage2);
		beverage2 = new Whip(beverage2);
		System.out.println(beverage2.getDescription() + " $ " + beverage2.cost());
		
		//装饰者,混合味的加料饮料
		beverage3 = new Soy(beverage3);
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.getDescription() + " $ " + beverage3.cost());
		
	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值