Head First设计模式之装饰者模式(Decorator Pattern)

时间:2022-12-18 19:57:39

前言:

本节将深度讨论继承滥用问题,将会学到使用对象组合的方式,在运行时装饰类,在不修改任何底层代码的情况下,给对象赋予新的职责。

1.    基本需求:咖啡连锁店业务扩张需要重新设计订单系统

背景:由于StarBuzz咖啡连锁店业务扩张,准备更新订单系统,以合乎他们的饮料供应要求。

他们原来的类设计如下:

Head First设计模式之装饰者模式(Decorator Pattern)

用户在购买咖啡的时候,可以能会要求在咖啡中加入各种调料,StarBuzz会根据用户加入的不同调收取不同费用,新的订单系统必须考虑到这些调料部分。

1.1 第一次设计

Head First设计模式之装饰者模式(Decorator Pattern)

以上的每一个类的Cost()方法将会算出咖啡加上订单的各种调料的价钱。虽然可以满足需求,但是这样会需要很多很多的类,而且也违反了OO的设计原则。

1.2 第二次设计

不需要创建那么多的类,只需要通过利用实例变量和继承,就可以追踪调料。

设计如下:

Head First设计模式之装饰者模式(Decorator Pattern)

这样做,确实可以暂时满足需求,但是还会存在一些潜在的隐患,如下:

l  调料价格变动会改变原有代码

l  新增调料,除了加上新增方法外,还需要改变超类中的Cost()方法

l  依赖继承,子类将继承一些对自身并不合适的方法

l  部分需求无法满足:如双倍摩卡咖啡

l  违反了开放—关闭原则

2.    引入装饰者模式

2.1 开发----关闭原则

在上一节的第二次设计中我们可以看出这种设计方法明显的违背了“开发—关闭”原则,那什么是开闭原则呢?定义如下:

开发—关闭原则:类应该是对扩展开放,对修改关闭。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为,这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。遵循开开放—关闭原则,通常会引入新的抽象层次,增加代码的复杂度,需要选择在设计中最可能改变的地方,然后应用开发-关闭原则。

2.2 认识装饰者模式,并以装饰者构造饮料订单

在1中我们已经了解到通过继承无法完全解决问题,这里我们以饮料为主体,然后在运行时以调料来“装饰”饮料。例如:如果客户想要摩卡和奶泡深焙咖啡,如下:

l  拿一个深焙(DarkRoast)对象

l  以摩卡(Mocha)对象装饰它

l  以奶泡(Whip)对象装饰它

l  调用Cost()方法,并依赖委托(delegate)将调料的价钱加上去

说明:以DarkRoast对象开始,顾客想要摩卡(Mocha),所以建立一个Mocha对象,用它将DarkRoast对象包起来,顾客想要奶炮(Whip),所以需要建立一个Whip装饰者,并用它将Mocha对象包起来。最后算钱,通过调用最外层的装饰者Whip的Cost()方法就可以办到。其调用过程如下:

Whip.Cost()àMocha.Cost()àDarkRoast.Cost()(返回DarkRoast的价钱)àMocha.Cost()(Mocha在DarkRoast的结果上加上自己的价钱)àWhip.Cost()(Whip在Mocha的返回结果上加上自己的价钱)

基于以上的这种分析,我们可以得出以下的结论:

装饰者和被装饰对象有相同的超类型。

可以用一个或者多个装饰者包装一个对象。

因为装饰者和被装饰者有相同的超类型,所以在任何需要原始对象的场合,可以用装饰过的对象代替它。

装饰者可以在所委托被装饰者的行为之前或者之后,加上自己的行为,达到特定目的。

对象可以在任何时候被装饰,所以可以在运行时动态的用你喜欢的装饰者来装饰对象。

2.3 定义装饰者模式

装饰者模式:

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

设计类图如下:

Head First设计模式之装饰者模式(Decorator Pattern)

依据这种装饰者模式的类图,我们设计出StarBuzz的类图,也让它符合这种结构设计,类图如下:

Head First设计模式之装饰者模式(Decorator Pattern)

装饰者和被装饰者都继承自Beverage类,也就是拥有共同的超类,这里,我们是利用继承达到“类型匹配”,而不是利用继承获得“行为”。

装饰者和组件组合时,就是在加入新的行为,所得的新行为并不是继承自超类,而是由组合对象得来的。

如果依赖继承,那么类的行为只能在编译时静态决定,行为不是来自超类就是子类覆盖后的版本,每当需要新行为时,还得修改现有代码。如果利用组合,就可以动态的实现新的装饰者增加新的行为。

3 用装饰者模式实现咖啡店需求

根据在2.3中设计的咖啡店的类图,下面就进行具体的编码实现:

3.1 Beverage类(抽象组件)

/// Description: Beverage抽象类
/// </summary>
public abstract class Beverage
{
public string description = "Unknown Beverage";
public abstract string GetDescription();
public abstract double Cost();
}

3.2Condiment(调料)基类(继承自Beverage基类,抽象装饰者)

/// Description:调料基类、派生类
/// </summary>
public abstract class CondimentDecorator:Beverage
{
//public abstract string GetDescription();
}

3.3饮料类(继承Beverage基类,具体组件)

 public class Espresso:Beverage
{
public Espresso()
{
description = "Espresso";//设置饮料的表述,description继承自Beverage类的实例变量
} public override double Cost()
{
return 1.99;
} public override string GetDescription()
{
return description;
}
} public class HouseBlend : Beverage
{
public HouseBlend()
{
description = "HouseBlend";
} public override double Cost()
{
return 0.89;
} public override string GetDescription()
{
return description;
}
} public class DarkRoase : Beverage
{
public DarkRoase()
{
description = "DarkRoase";
} public override double Cost()
{
return 1.11;
}
public override string GetDescription()
{
return description;
}
} public class Decat : Beverage
{
public Decat()
{
description = "Decat";
} public override double Cost()
{
return 1.22;
} public override string GetDescription()
{
return description;
}
}

3.4调料类(装饰者)

public class Mocha : CondimentDecorator
{
Beverage beverage;
public Mocha(Beverage beverage)
{
this.beverage = beverage;
} public override string GetDescription()
{
return beverage.GetDescription() + ",Mocha";
}
public override double Cost()
{
return 0.2 + beverage.Cost();
}
} public class Soy : CondimentDecorator
{
Beverage beverage;
public Soy(Beverage beverage)
{
this.beverage = beverage;
} public override string GetDescription()
{
return beverage.GetDescription() + ",Soy";
} public override double Cost()
{
return 0.3 + beverage.Cost();
}
} public class Whip : CondimentDecorator
{
Beverage beverage;
public Whip(Beverage beverage)
{
this.beverage = beverage;
} public override string GetDescription()
{
return beverage.GetDescription() + ",Whip";
} public override double Cost()
{
return 0.3 + beverage.Cost();
}
}

3.5 测试

Beverage.Beverage beverage = new Beverage.Espresso();
Console.WriteLine(beverage.GetDescription() + " $ " + beverage.Cost());
Beverage.Beverage beverage1 = new Beverage.DarkRoase();
beverage1 = new Beverage.Mocha(beverage1);
beverage1 = new Beverage.Mocha(beverage1);
beverage1 = new Beverage.Whip(beverage1);
Console.WriteLine(beverage1.GetDescription() + " $ " + beverage1.Cost()); Beverage.Beverage beverage2 = new Beverage.HouseBlend();
beverage2 = new Beverage.Soy(beverage2);
beverage2 = new Beverage.Mocha(beverage2);
beverage2 = new Beverage.Whip(beverage2);
Console.WriteLine(beverage2.GetDescription() + " $ " + beverage2.Cost());

结果如下图:

Head First设计模式之装饰者模式(Decorator Pattern)

4        总结

通过本章的学习,我们可以学到以下知识:

l  OO原则:

封装变化

多用组合,少用继承

针对接口编程,不针对实现编程

为交互对象之间的松耦合设计而努力

对扩展开放,对修改关闭(本章节新学习的OO原则)

l  OO模式

装饰者模式—动态地将责任附加到对象上,想要扩展功能,装饰者提供有别于继承的另一种选择。

l  要点归纳

继承和装饰者都可以让我们扩展行为,但继承不是弹性设计的最佳方案。

装饰者模式意味着一群装饰者类,装饰者类反应了被装饰组件的类型,可以用多个装饰者包装对象。

装饰者可以在被装饰者的行为之前或者之后加上自己的行为,甚至将被装饰者的行为取代,以到达特定目的。

装饰者模式会导致设计中出现许多小对象,过度使用会使程序变得复杂。

Head First设计模式之装饰者模式(Decorator Pattern)的更多相关文章

  1. 设计模式学习--装饰者模式&lpar;Decorator Pattern&rpar;

    概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...

  2. python 设计模式之装饰器模式 Decorator Pattern

    #写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...

  3. 23种设计模式之装饰器模式&lpar;Decorator Pattern&rpar;

    装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包 ...

  4. C&num;设计模式之装饰者模式&lpar;Decorator Pattern&rpar;

    1.概述 装饰者模式,英文名叫做Decorator Pattern.装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 2 ...

  5. c&num;设计模式之装饰器模式&lpar;Decorator Pattern&rpar;

    引子 在面向对象语言中,我们常常会听到这样一句话:组合优于继承.那么该如何去理解这句话呢? 下面我将以游戏装备为模型用简单的代码去展示它 先创建一个装备的抽象类,然后创建刀枪2个具体的业务子类 pub ...

  6. 【UE4 设计模式】装饰器模式 Decorator Pattern

    概述 描述 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活.是一种对象结构型模式. 套路 抽象构件(Component) 具体构 ...

  7. 浅谈设计模式--装饰者模式&lpar;Decorator Pattern&rpar;

    挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...

  8. 设计模式 - 装饰者模式&lpar;Decorator Pattern&rpar; Java的IO类 用法

    装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...

  9. 设计模式 - 装饰者模式&lpar;Decorator Pattern&rpar; 具体解释

    装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...

  10. &lbrack;设计模式&rsqb; 9 装饰者模式 Decorator

    转:http://www.jellythink.com/archives/171#prettyPhoto 什么是装饰模式? 在GOF的<设计模式:可复用面向对象软件的基础>一书中对装饰模式 ...

随机推荐

  1. iframe框架用法

    1.iframe标签: <iframe src="demo1.html" name="qiuqiu_Page" frameborder="0&q ...

  2. 【2016-11-15】【坚持学习】【Day26】【通用的SQLHelper】

    今天看DevDemo源码时候看到一个写得很好的SQLHelper 代码 public class SqlHelper<T, Command> where T : DbConnection ...

  3. CodeForces 13E 分块

    题目链接:http://codeforces.com/problemset/problem/13/E 题意:给定n个弹簧和每个弹簧初始的弹力a[].当球落在第i个位置.则球会被弹到i+a[i]的位置. ...

  4. Hibernate理论

    1.什么是Hibernate? Hibernate是数据持久层的一个轻量级框架.数据持久层的框架有很多比如:iBATIS,myBatis,Nhibernate,Siena等等.并且Hibernate是 ...

  5. 1&period;1&period;1&period;持久化存储协调器(Core Data 应用程序实践指南)

    持久化存储协调器(persistent store coordinator)里面包含一份持久化存储区,而存储区里又含有数据表里的若干行数据. 与原子存储不同,SQLite数据库会在用户提交变更日志时进 ...

  6. 如何用Ettercap实现&OpenCurlyDoubleQuote;中间人攻击”(附下载链接)

    什么是“中间人攻击”? 中间人攻击(Man-in-the-Middle Attack,简称“MiTM攻击”)是一种“间接”的入侵攻击,这种攻击模式是通过各种技术手段将受入侵者控制的一台计算机虚拟放置在 ...

  7. EXT&period;NET高效开发(二)——封装函数

    在上一篇<EXT.NET高效开发(一)--概述>中,大致的介绍了一下EXT.NET.那么本篇就要继续完成未完成的事业了.说到高效开发,那就是八仙过海各显神通.比如使用代码生成器,这点大家可 ...

  8. SiteCore Experience Analytics-体验分析

    体验分析   Sitecore Experience Analytics为营销人员和营销分析师提供仪表板和报告,以识别从其网站和可能的其他外部数据源收集的体验数据的模式和趋势. 体验分析报告示例:   ...

  9. 一加3刷不了官方recoery

    遇到 target reported max download size of 直接用救砖工具,恢复出厂. http://www.oneplusbbs.com/thread-2849353-1-1.h ...

  10. 云从科技 OCR任务 pixel-anchor 方法

    云从科技提出了一种端到端的深度学习文本检测框架Pixel-Anchor,通过特征共享的方式高效的把像素级别的图像语义分割和锚检测回归放入一个网络之中, 把像素分割结果转换为锚检测回归过程中的一种注意力 ...