zoukankan      html  css  js  c++  java
  • 装饰者设计模式

    在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等。在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰模式来实现。

    装饰模式的定义与特点

    装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

    • 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
    • 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。

    其主要缺点是:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

    UML类图

    应用实例

    星巴克咖啡订单项目(咖啡馆):

    1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式 咖啡)、Decaf(无因咖啡)

    2. 调料:Milk、Soy(豆浆)、Chocolate

    3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便

    4. 使用OO的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖 啡+调料组合


    代码示例

    首先抽象出一个Drink类,它是一个抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象

    public abstract class Drink {
    
    	public String des; // 描述
    	private float price = 0.0f;
    	// 省略setter and getter方法
    	
    	//计算费用的抽象方法
    	//子类来实现
    	public abstract float cost();
    	
    

    接下来是具体构件角色,但在这里,我们有EspressoShortBlackLongBlack等,所以我们定义一个Coffee的缓冲层,因为获取价格的这个方法,下放到具体子类也是一样的,所有加了一个缓冲层。

    public class Coffee  extends Drink {
    	@Override
    	public float cost() {
    		// TODO Auto-generated method stub
    		return super.getPrice();
    	}	
    }
    

    接下来就是一个具体的构件角色了,

    public class Espresso extends Coffee {
    	
    	public Espresso() {
    		setDes(" 意大利咖啡 ");
    		setPrice(6.0f);
    	}
    }
    
    public class LongBlack extends Coffee {
    
    	public LongBlack() {
    		setDes(" longblack ");
    		setPrice(5.0f);
    	}
    }
    
    public class ShortBlack extends Coffee{
    	
    	public ShortBlack() {
    		setDes(" shortblack ");
    		setPrice(4.0f);
    	}
    }
    
    public class DeCaf extends Coffee {
    
    	public DeCaf() {
    		setDes(" 无因咖啡 ");
    		setPrice(1.0f);
    	}
    }
    

    以上是四个具体的构件角色。


    现在要往咖啡中加调料:Milk、Soy(豆浆)、Chocolate 等

    我们也是通过抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。

    public class Decorator extends Drink {
    	private Drink obj;
    	
    	public Decorator(Drink obj) { //组合
    		// TODO Auto-generated constructor stub
    		this.obj = obj;
    	}
    	
    	@Override
    	public float cost() {
    		// TODO Auto-generated method stub
    		// getPrice 自己价格
    		return super.getPrice() + obj.cost();
    	}
    	
    	@Override
    	public String getDes() {
    		// TODO Auto-generated method stub
    		// obj.getDes() 输出被装饰者的信息
    		return des + " " + getPrice() + " && " + obj.getDes();
    	}
    }
    

    抽象装饰中聚合了Drink obj,那后续在使用中,我们通过具体传入EspressoShortBlack等来装配。


    具体装饰

    public class Milk extends Decorator {
    
    	public Milk(Drink obj) {
    		super(obj);
    		// TODO Auto-generated constructor stub
    		setDes(" 牛奶 ");
    		setPrice(2.0f); 
    	}
    
    }
    
    public class Soy extends Decorator{
    
    	public Soy(Drink obj) {
    		super(obj);
    		// TODO Auto-generated constructor stub
    		setDes(" 豆浆  ");
    		setPrice(1.5f);
    	}
    
    }
    
    //具体的Decorator, 这里就是调味品
    public class Chocolate extends Decorator {
    
    	public Chocolate(Drink obj) {
    		super(obj);
    		setDes(" 巧克力 ");
    		setPrice(3.0f); // 调味品 的价格
    	}
    
    }
    

    使用测试

    public class CoffeeBar {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		// 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack
    
    		// 1. 点一份 LongBlack
    		Drink order = new LongBlack();
    		System.out.println("费用1=" + order.cost());
    		System.out.println("描述=" + order.getDes());
    
    		// 2. order 加入一份牛奶
    		order = new Milk(order);
    
    		System.out.println("order 加入一份牛奶 费用 =" + order.cost());
    		System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
    
    		// 3. order 加入一份巧克力
    
    		order = new Chocolate(order);
    
    		System.out.println("order 加入一份牛奶 加入一份巧克力  费用 =" + order.cost());
    		System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());
    
    		// 3. order 加入一份巧克力
    
    		order = new Chocolate(order);
    
    		System.out.println("order 加入一份牛奶 加入2份巧克力   费用 =" + order.cost());
    		System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());
    	
    		System.out.println("===========================");
    		
    		Drink order2 = new DeCaf();
    		
    		System.out.println("order2 无因咖啡  费用 =" + order2.cost());
    		System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
    		
    		order2 = new Milk(order2);
    		
    		System.out.println("order2 无因咖啡 加入一份牛奶  费用 =" + order2.cost());
    		System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());
    
    	
    	}
    
    }
    

    测试结果:

    费用1=5.0
    描述= longblack 
    order 加入一份牛奶 费用 =7.0
    order 加入一份牛奶 描述 =  牛奶  2.0 &&  longblack 
    order 加入一份牛奶 加入一份巧克力  费用 =10.0
    order 加入一份牛奶 加入一份巧克力 描述 =  巧克力  3.0 &&  牛奶  2.0 &&  longblack 
    order 加入一份牛奶 加入2份巧克力   费用 =13.0
    order 加入一份牛奶 加入2份巧克力 描述 =  巧克力  3.0 &&  巧克力  3.0 &&  牛奶  2.0 &&  longblack 
    ===========================
    order2 无因咖啡  费用 =1.0
    order2 无因咖啡 描述 =  无因咖啡 
    order2 无因咖啡 加入一份牛奶  费用 =3.0
    order2 无因咖啡 加入一份牛奶 描述 =  牛奶  2.0 &&  无因咖啡 
    

    以上就是装饰者设计模式的简单应用了,JDK源码中的IO流就充分使用了装饰者设计模式。

    IO流中的装饰者设计模式

    以InputStream为例,


    InputStream就是抽象构件(Component)角色

    FilterInputStream就是抽象装饰角色(Decorator),继承并持有一个InputStram的引用

    剩下两个角色自己对应,

    你所看得到的天才不过是在你看不到的时候还在努力罢了!
  • 相关阅读:
    学习总结(二十六)
    学习总结(二十五)
    在知乎学习怎么参加工作
    连分数系列
    Kalman Filter
    五子棋的学习
    Dijkstra
    三等分角、化圆为方、倍立方体
    女朋友走丢数学模型
    传染病模型
  • 原文地址:https://www.cnblogs.com/heliusKing/p/11615716.html
Copyright © 2011-2022 走看看