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

    概述

    本章可以称为“给爱用继承的人一个全新的设计眼界”。我们即将再度探讨典型滥用问题。你将在本章学到如何使用对象组合的方式,做到在运行时装饰类。为什么呢?一旦你熟悉了装饰者的技巧,你将能够在不修改任何底层代码的情况下,给你的(或别人的)对象赋予新的职责。

                                                                               ——《Head First 设计模式》


    版权说明

    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    本文作者:Q-WHai
    发表日期: 2015年5月25日
    本文链接:http://blog.csdn.net/lemon_tree12138/article/details/45870027
    来源:CSDN
    更多内容:分类 >> 设计模式


    使用环境

    当我们有多种类型的事物,且每一种事物下面又有很多小的、细的分类。这些分类之间可以随意组合时(例如一种饮料有一个主原料和一些配料、一道菜会有一个主原料加上一些配料或是去装饰一个房间等等),我们就可以想一下是不是可以用装饰模式来实现。下面就《Head First 设计模式》中的例子星巴兹的咖啡说简单说明一下。


    定义

    装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。


    功能优点

    利用组合维护代码,通过动态地组合对象,可以写新的代码添加新功能,而无须修改现有代码。

     

    思路分析

      在星巴兹的咖啡店里,有多种咖啡,和多种调料。如果我们选择一种咖啡另外配上一种或是几种配料(主要主是一个被装饰者和多个装饰者)。那么我们如何对这些咖啡和配料进行收费呢(这里采用收费是一个好的举例,当然也可以是其他的一些相同的事件)?如果你说我们对每一种可选搭配可以封装,天呐,那该会有多少个类啊。还有一点就是,如果咖啡店里如果有一种咖啡或是配料价格有变动,或是需要新添加一种咖啡或是配料,又该怎么办呢?

      其实本文的模式就有一种很好的解决办法:我们不再使用继承,因为继承可能会导致类爆炸的。我们试想一下,能不能把咖啡店中的的所有原料(包括主原料和所有配料)都看成是一种东西(原料)。这样我们就可以在相互组合的过程中动态实现某些功能。可能你会问为什么要把其看成是一种东西,这样做的目的是为了在所有原料中实现同一功能,且无关代码中的组合顺序。

     

    类图展示

    这里为了方便起见,只绘制出了一部分内容,略去了一些重复或无关痛痒的部分。


    当然,你也可以把上面咖啡那4个类再封一层。这里我就书中的内容做了一个原样输出。

    从类图中可以看到 Milk 类持有了一个 Beverage 的引用。而对于 Beverage 而言是一个抽象类,正常的咖啡类都要继承自这个类。也就是说装饰者类 Milk 持有了全部的咖啡类,通过这样一层的聚合,这里的 Milk 类就可以对原先的 DarkRoast 类进行包装装饰。


    代码展示

    Beverage.java

    public abstract class Beverage {
    
    	public String mDescription = "UnKnown Beverage";
    	private int size = 0;
    	
    	public String getDescription() {
    		return mDescription;
    	}
    	
    	public int getSize() {
    		return size;
    	}
    
    	public void setSize(int size) {
    		this.size = size;
    	}
    
    	public abstract double cost();
    }
    CondimentDecorator.java

    public abstract class CondimentDecorator extends Beverage {
    
    	@Override
    	public double cost() {
    		return 0;
    	}
    
    	public abstract String getDescription();
    }
    DarkRoast.java

    public class DarkRoast extends Beverage {
    
    	private double mCost = 0.99;
    	
    	public DarkRoast() {
    		mDescription = "Dark Roast Coffee";
    	}
    
    	@Override
    	public double cost() {
    		return mCost;
    	}
    
    }
    Milk.java

    public class Milk extends CondimentDecorator {
    
    	private Beverage mBeverage = null;
    	private double mCost = 0.10;
    	
    	public Milk(Beverage beverage) {
    		mBeverage = beverage;
    	}
    
    	@Override
    	public String getDescription() {
    		return mBeverage.getDescription() + ", Milk";
    	}
    	
    	public double cost() {
    		return mCost + mBeverage.cost();
    	}
    
    }
    StarbuzzCoffee.java

    public class StarbuzzCoffee {
    
    	public static void main(String[] args) {
    		Beverage beverage = new Espresso();
    		System.out.println(beverage.getDescription() + " $" + beverage.cost());
    		
    		Beverage beverage2 = new DarkRoast();
    		beverage2 = new Mocha(beverage2);
    		beverage2 = new Mocha(beverage2);
    		beverage2 = new Whip(beverage2);
    		System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
    		
    		Beverage beverage3 = new HouseBlend();
    		beverage3 = new Soy(beverage3);
    		beverage3 = new Mocha(beverage3);
    		beverage3 = new Whip(beverage3);
    		System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
    	}
    }

    行图例


    源码下载

    https://github.com/William-Hai/DesignPatternCollections

  • 相关阅读:
    根据包路径输出类列表
    Logback配置
    接口的幂等性
    mock获取入参数并动态设置返回值
    linux,日志查找技巧
    scrapy 一些坑
    Pyinstaller打包附带DLL、图标和压缩EXE方法
    scrapy 爬虫教程
    买房
    Python: 在CSV文件中写入中文字符
  • 原文地址:https://www.cnblogs.com/fengju/p/6336086.html
Copyright © 2011-2022 走看看