Head First设计模式——装饰者模式
前言:对于设计模式我们有时候在想是否有必要,因为实际开发中我们没有那么多闲工夫去套用这么多设计模式,也没有必要为了模式而模式。
通常这些模式会引入新的抽象层,增加代码的复杂度,但是当我们掌握了这些设计模式,
在系统中比较棘手或者需要以后修改扩展的地方采用了合适的设计模式会让我们的系统易于扩展维护甚至工作变得轻松很多。
对于这一点我深有体会,有时候设计的比较好的功能模块在后来客户改变需求的时候变得很容易且方便添加修改。
但是如果比较糟糕偷懒的方式会让我们对自己的代码修改变得害怕,害怕客户提需求,害怕去动自己的代码。
所以对于框架或者这些设计模式我们可以不用过度使用,但是要用的时候能有储备或者脑袋里面会闪现出对应的解决方案。
这样不光会提升我们的编码兴趣对我们开发的产品也更有信心。
本篇就聚焦Head First设计模式中的装饰者模式进行学习和总结。
咖啡店结算案例
咖啡店卖各种咖啡,根据调料不一样就会有不同价格的咖啡饮品可选,那么对应的咖啡饮品价格也不一样。
咖啡是需要按照基础价格+调料组合计算价格的,那按照继承的方式我们抽象一个饮料基类Beverage,Beverage拥有Cost抽象方法用于计算价格。
其他咖啡饮品继承Beverage实现Cost方法根据自己的调料计算价格。
public abstract class Beverage {
public abstract void Description();
public abstract float Cost();
} public class DarkRoast : Beverage
{
public override void Description()
{
//深焙咖啡
}
public override float Cost()
{
//各种调料价格计算
}
}
对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作。
如果说某一种调料的价格变化我们还得去每个子类里面改变价格。
那我们针对变化的调料部分是不是让他们按照实际需求组合,在子类中确定自己加哪些调料就行了,似乎这种方式会减少重写和维护难度。
按照这个思路将Beverage改造,将是否有调料定义成变量,然后Cost方法不再抽象而是提供实现。
public abstract class Beverage
{
//牛奶
public bool Milk { get; set; }
//糖
public bool Suger { get; set; }
//摩卡
public bool Mocha { get; set; }
public abstract void Description();
public virtual float Cost()
{
float price = 0;
if (Milk)
{
price += 1;
}
if (Suger)
{
price += 2;
}
if (Mocha)
{
price += 3;
}
return price;
}
} public class DarkRoast : Beverage
{
public override void Description()
{
Console.WriteLine("深焙咖啡");
} public override float Cost()
{
Milk = true;
Suger = true;
return 1.1f+base.Cost();
}
}
这种方式比之前的继承的确好了许多,不过还是有如下几个问题:
1、调料价格改变会使我们改变现有代码。
2、需要添加新的调料,我们就需要添加新的方法并改变Beverage基类的Cost方法。
3、某些调料可能并不适合其他饮品,例如茶还要继承这些不属于它的调料。
4、如果顾客需要双份糖,Cost方法就会失效。
接下来我们就用"装饰者模式"来更好的设计该案例。
认识装饰者模式
这里有一个设计模式很重要的设计原则:类应该对扩展开放,对修改关闭(开闭原则)
装饰模式同样遵循开闭原则。
对于咖啡店来说,主体是饮料不变,变化的是调料。我们以饮料为主体,其他调料来“装饰”饮料。比如顾客想要加了Mocha(摩卡)的 DarkRoast(深焙咖啡),我们要做的是:
1、制造一个DarkRoast对象
2、以Mocha(摩卡)对象装饰它
3、调用Cost()方法,并依赖委托(非C# delegate,只是概念)将调料的价钱加上去
利用装饰者模式
首先我们修改主体饮料Beverage基类,GetDescription 返回描述,Cost由子类自己实现定价。
public abstract class Beverage
{
string description = "Unkonwn Beverage";
public virtual string GetDescription() {
return description;
}
public abstract float Cost();
}
深焙咖啡类
public class DarkRoast : Beverage
{
public DarkRoast() {
description = "深焙咖啡";
}
public override float Cost()
{
return 1.1f;
}
}
装饰类我们定义一个抽象基类Condiment(调料)并继承Beverage。
为什么要定义一个抽象装饰基类,因为装饰类可能需要抽象出其他方法,而且因为我们用装饰类去装饰了被装饰者后 我们本身也应该变成可已让别人装饰的类。所以装饰类继承CondimentDecorator,CondimentDecorator继承的是Beverage。此例暂未加入装饰类拥有的属性和方法
public abstract class CondimentDecorator:Beverage
{ }
我们再实现两个装饰类:Mik和Suger,用变量记录被装饰者,在构造函数里面设置被装饰者。
public class Milk : CondimentDecorator
{
//用变量记录被装饰者
Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
} public override string GetDescription()
{
return beverage.GetDescription() + "+Milk";
}
public override float Cost()
{
return beverage.Cost() + 1;
}
} public class Suger : CondimentDecorator
{
//用变量记录被装饰者
Beverage beverage;
public Suger(Beverage beverage)
{
this.beverage = beverage;
} public override string GetDescription()
{
return beverage.GetDescription() + "+Suger";
}
public override float Cost()
{
return beverage.Cost() + 2;
}
}
编写测试结果:
总结
装饰者模式说明:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
画出本例中装饰者模式类图以便理解和记忆
Head First设计模式——装饰者模式的更多相关文章
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- 从源码角度理解Java设计模式——装饰者模式
一.饰器者模式介绍 装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活. 适用场景:动态的给一个对象添加或者撤销功能. 优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个 ...
- 【设计模式】Java设计模式 - 装饰者模式
Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...
- [Head First设计模式]山西面馆中的设计模式——装饰者模式
引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧.吃 ...
- JAVA 设计模式 装饰者模式
用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式. 结构
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- javascript设计模式——装饰者模式
前面的话 在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责.那么可以使用装饰者模式.装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.本文将 ...
- 设计模式-装饰者模式(Decorator Pattern)
本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...
- C#设计模式--装饰器模式
0.C#设计模式-简单工厂模式 1.C#设计模式--工厂方法模式 2.C#设计模式--抽象工厂模式 3.C#设计模式--单例模式 4.C#设计模式--建造者模式 5.C#设计模式--原型模式 6.C# ...
随机推荐
- Day 16 软件管理
1.RPM基本概述 1.什么是rpm? RPM全称 RPM Package Manager 缩写,由红帽开发用于软件包的安装升级卸载与查询 2.如何获取rpm包 在我们刚开始学习rpm包,建议先使用本 ...
- 采用WPF技术,开发OFD电子文档阅读器
前言 OFD是国家标准版式文档格式,于2016年生效.OFD文档国家标准参见<电子文件存储与交换格式版式文档>.既然是国家标准,OFD随后肯定会首先在政务系统使用,并逐步推向社会各个方面. ...
- java.lang.IllegalArgumentException: System memory 259522560 must be at least 471859200.
报错信息 java.lang.IllegalArgumentException: System memory 259522560 must be at least 471859200. Please ...
- uboot学习之uboot.bin的运行流程
上篇博客:http://www.cnblogs.com/yeqluofwupheng/p/7347925.html 讲到uboot-spl的工作流程,接下来简述一下uboot.bin的工作流程,这对应 ...
- HTML连载39-外边距合并现象、盒子模型以及宽度和高度
一. 在默认布局的垂直方向上,默认情况下外边距是是不会叠加的,会出现合并现象,谁的外边距较大,就听谁的:但是在水平方向就不会出现这种状况,我们举个例子 span{ display: inline-bl ...
- 让tomcat使用指定JDK
一,前言 我们都知道,tomcat启动前需要配置JDK环境变量,如果没有配置JDK的环境变量,那么tomcat启动的时候就会报错,也就是无法启动. 但是在我们的工作或者学习过程中,有的时候会出现tom ...
- asp.net core mvc 之 DynamicApi
这段时间闲赋在家,感觉手痒,故想折腾一些东西. 由于之前移植了一个c#版本的spring cloud feign客户端(https://github.com/daixinkai/feign.net), ...
- java、python、MYSQL环境安装
JAVA的环境变量:变量值:%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 变量名:JAVA_HOME python的环境变量:变量值: %PY_HOME ...
- Hive安装与简单使用并集成SparkSQL
## Hive环境搭建1. hive下载:http://archive-primary.cloudera.com/cdh5/cdh/5/hive-1.1.0-cdh5.7.0.tar.gzwget h ...
- Go语言操作MySQL
MySQL是常用的关系型数据库,本文介绍了Go语言如何操作MySQL数据库. Go操作MySQL 连接 Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口,并不提供具体的 ...