设计模式(4)------装饰者设计模式

一,需求

现在在星巴克咖啡店,有4中咖啡,有无数种的配料,怎样算出一种咖啡随机加配料的价格,加配料肯能是一种,也可能是多种,而且也有可能是重复的。

如图:

假如现在根据每一个不同的配料新增一个类的话会是怎么样的呢,看图。

是不是要爆炸了呢。

那怎么解决这个问题呢。

好了,现在我们来修改一下这个设计好吧!就来试试看。先从Beverage基类下手,加上实例变量代表是否加上调料(牛奶、豆浆、摩卡、奶泡……)

上面的修复可能出现什么问题呢?调料价钱的改变会使我们更改现有代码。

一旦出现新的调料,我们就需要加上新的方法,并改变超类中的cost()方法。以后可能会开发出新饮料。对这些饮料而言(例如:冰茶),某些调料可能并不适合,但是在这

个设计方式中,Tea(茶)子类仍将继承那些不适合的方法,例如:hasWhip()(加奶泡)。

万一顾客想要双倍摩卡咖啡,怎么办?

上面抛出来的问题都是这种设计模式无法解决的,那么我们现在就用开始引入装饰者设计模,装饰着设计模式

2.1,设计原则(第四个设计模式)类应该对扩展开放,对修改关闭。

现在是"关闭"状态。没错。我们花了许多时间得到了正确的代码,还解决了所有的bug,所以不能让你修改现有代码。我们必须关闭代码以防止被修改。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

就是说对原来代码逻辑的修改是关闭的,因为以前的代码是经过测试,运营等一系列的运行之后发现是没有问题的代码,现在在去修改很可能引出新的问题出来,所以是堆修改关闭的。但是我们不能因为这样就不写代码了,我们还有新的功能需要实现的,所以这个时候就是对扩展开放的。

2.2,认识装饰着设计模式

所以,在这里要采用不一样的做法:我们要以饮料为主体,然后在运行时以调料来"饰"(decorate)饮料。比方说,如果顾客想要摩卡和奶泡深焙咖啡,那么,要做的是:

1,拿一个深焙咖啡(DarkRoast)对象,

2,以摩卡(Mocha)对象装饰它

3 ,以奶泡(Whip)对象装饰它

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

2.3,定义

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

其实装饰者就是给对象穿马甲
多穿一层就多加一点功能。

下面我们开始把上面的模型套到咖啡店的设计模式上

Beverage.java(咖啡的接口或者抽象类)

package com.DesignPatterns.ac.decorator_starbuzz;

public
abstract
class
Beverage
{     String description =
"Unknown Beverage";

public String getDescription()
{

return description;

}

public
abstract
double
cost();

}

DarkRoast.java(咖啡1实现类)

public
class
DarkRoast
extends Beverage {     public
DarkRoast()
{

description =
"Dark Roast Coffee";

}

public
double
cost()
{

return
.99;

}

}

Decaf.java(咖啡2实现类)

public
class
Decaf
extends Beverage {

public
Decaf()
{

description =
"Decaf Coffee";

}

public
double
cost()
{

return
1.05;

}

}

Espresso.java(咖啡3实现类)

public
class
Espresso
extends Beverage {

public Espresso()
{

description =
"Espresso";

}

public
double
cost()
{

return
1.99;

}

}

HouseBlend.java(咖啡4实现类)

public
class
HouseBlend
extends Beverage {

public HouseBlend()
{

description =
"House Blend Coffee";

}

public
double
cost()
{

return
.89;

}

}

CondimentDecorator.java(作料接口)

public
abstract
class
CondimentDecorator
extends Beverage {

public
abstract String getDescription();

}

Milk.java(作料1实现类)

public
class
Milk
extends CondimentDecorator {     Beverage beverage;

public
Milk(Beverage beverage)
{

this.beverage
= beverage;     }

public String getDescription()
{


}


return beverage.getDescription()
+
", Milk";

}

public
double
cost()
{

return
.10
+ beverage.cost();

}

Mocha.java(佐料2实现类()

public
class
Mocha
extends CondimentDecorator {

Beverage beverage;

public
Mocha(Beverage beverage)
{

this.beverage
= beverage;

}

public String getDescription()
{

return beverage.getDescription()
+
", Mocha";

}

public
double
cost()
{

return
.20
+ beverage.cost();

}

}

Soy.java(佐料3实现类)

public
class
Soy
extends CondimentDecorator {     Beverage beverage;

public
Soy(Beverage beverage)
{

this.beverage
= beverage;     }

public String getDescription()
{

return beverage.getDescription()
+
", Soy";

}

public
double
cost()
{

return
.15
+ beverage.cost();

}

}

Whip.java(佐料4实现类)

public
class
Whip
extends CondimentDecorator {

Beverage beverage;

public
Whip(Beverage beverage)
{


}


this.beverage
= beverage;

}

public String getDescription()
{

return beverage.getDescription()
+
", Whip";

}

public
double
cost()
{

return
.10
+ beverage.cost();

}

Test.java(测试类)

public
class
Test
{

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());

}

}

结果:

Espresso $1.99

Dark Roast Coffee, Mocha, Mocha, Whip $1.49 House Blend Coffee, Soy, Mocha, Whip $1.34

其实测试类也可以这样写,更容易看清装饰着设计模式的本来面目的

public
class
Test
{

public
static
void
main(String args[])
{

Beverage beverage2 =
new Whip(new Mocha(new Mocha(new DarkRoast())));

System.out.println(beverage2.getDescription()
+
" $"
+ beverage2.cost());

}

}

这样我们就可以看到装饰着设计模式就是套马甲,套一个多一个功能的。

2.4,设计原则(第五个设计原则)

多用组合,少用继承总结上面的,我们可以看到的是一般情况下要少用继承多用组合。

因为如果依赖继承,那么类的行为只能在编译时静态决定。换句话说,行如果不是来自超类,就是子类覆盖后的版本。反之,利用组合,可以把装饰者混合着用……而且是在"运行时"。

而且,如我所理解的,我们可以在任何时候,实现新的装饰者增加新的行为。如果依赖继承,每当需要新行为时,还得修改现有的代码。

可能这里会有一个疑问。

我原以为在这个模式中不会使用继承,而是要利用组合取代继承,为什么现在还有extends 关键字的继承呢。

我们来看一下下面的对话:

Sue:这话怎么说?

Mary:看看类图。CondimentDecorator扩展自Beverage类,这用到了继承,不是吗?

Sue:的确是如此,但我认为,这么做的重点在于,装饰者和被装饰者必须是一样的类型,也就是有共同的超类,这是相当关键的地方。在这里,我们利用继承达到"类型匹配",而不是利用继承获得"行为"。

Mary:我知道为何装饰者需要和被装饰者(亦即被包装的组件)有相同的"接口",因为装饰者必须能取代被装饰者。但是行为又是从哪里来的?

Sue:当我们将装饰者与组件组合时,就是在加入新的行为。所得到的新行为,并不是继承自超类,而是由组合对象得来的。

Mary:好的。继承Beverage抽象类,是为了有正确的类型,而不是继承它的行为。行为来自装饰者和基础组件,或与其他装饰者之间的组合关系。

Sue:正是如此。

Mary:哦!我明白了。而且因为使用对象组合,可以把所有饮料和调料更有弹性地加以混和与匹配,非常方便。

Sue:是的。如果依赖继承,那么类的行为只能在编译时静态决定。换句话说,行如果不是来自超类,就是子类覆盖后的版本。反之,利用组合,可以把装饰者混合着用……而且是在"运行时"。

Mary:而且,如我所理解的,我们可以在任何时候,实现新的装饰者增加新的行为。如果依赖继承,每当需要新行为时,还得修改现有的代码。

Sue:的确如此。

通过上面的话,我们再来看下面的这行代码。

Beverage beverage2 = new Whip(new Mocha(new Mocha(new DarkRoast())));

System.out.println(beverage2.getDescription() + " $" + beverage2.cost());

组合并不意味着就不用继承了,我们在用继承的时候主要是为了达到类型匹配,而不是去获取它的行为,下面我们通过代码来理解这句话的含义。

beverage2.getDescription()只有每个包装类和被包装类都有这个方法,我们才能不断的往里面调用,用继承主要还是为了统一类型,类似定义了一个统一的接口里面的方法一样,每个类都必须遵循这个方法才能不断的往里面调用一样,到最后还是用的自己的方法的行为,并不是用的父类的。

就想io流找那个的read和write方法的道理是一样的。并不是要用这个方法,只不过是包装类必须遵循这个方法才能不断的往里面走,并且走出来。

原文地址:https://www.cnblogs.com/qingruihappy/p/9693861.html

时间: 2024-10-15 09:06:45

设计模式(4)------装饰者设计模式的相关文章

设计模式(5)------装饰者设计模式(IO流的应用)

一:io流中的装饰者设计模式 java.io包内的类太多了,简直是--"排山倒海".你第一次(还有第二次和第三次)看到这些API发出"哇"的惊叹时,放心,你不是唯一受到惊吓的人.现在,你已经知道装饰者模式,这些I/O的相关类对你来说应该更有意义了,因为其中许多类都是装饰者.下面是一个典型的对象集合,用装饰者来将功能结合起来,以读取文件数据: 我们来看一下下面的这张类图 你可以发现,和星巴兹的设计相比,java.io其实没有多大的差异.我们把java.ioAAPI范围

设计模式之---装饰器设计模式

职责:动态的为一个对象增加新的功能 装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能.使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀. 实现细节: ——Component抽象构件角色:真实对象和装饰对象有相同的接口.这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互. ——ConcreteComponent具体构件角色(真实对象):io流中的FileInputStream. FileOutputStream ——Decorator装饰角色

(java)从零开始之--装饰者设计模式

装饰者设计模式:简单定义:增强一个类的功能,而且还可以让这些装饰类互相装饰. 应用场景:当要在某个功能的基础上扩充功能,并且扩充的功能具有大量排列组合,通过继承关系会衍生出大量子类,这时候用装饰者模式来解决. 装饰者设计模式的步骤: 1. 在装饰类的内部维护一个被装饰类的引用. 2. 让装饰类有一个共同的父类或者是父接口. 例如:人有一种行为叫“吃水果”,其中水果有4种:苹果.香蕉.鸭梨.橘子现在有需求如下: A类人行为:吃苹果 B类人行为:先吃苹果,再吃香蕉 C类人行为:先吃香蕉,再吃苹果 D

设计模式之装饰者模式

设计模式系列都是学习HeadFirst设计模式得出的学习心得,中间的例子也会采用书中的例子.这里有必要解释一下,在下面星巴克咖啡的例子中,有几种基本的咖啡,还有牛奶.豆浆等等可以向咖啡中添加,这里说明防止下面不懂. 今天我们来了解一下装饰者模式. 回想一下java的io包,各种stream排上倒海,初学者根本分不清楚到底怎么用,眼花缭乱.其实,它的实验遵循了装饰者设计模式.顾名思义,装饰者就可以简单的理解成用一个东西来装饰另一个东西.比如,你要做鱼吃,在你做好出国之后需要加入香菜.我们就可以简单

设计模式--装饰者设计模式(Decorator)

装饰者模式又叫包装模式. 通过另一个对象来扩展自己的行为,在不破坏类的封装的情况下,实现松耦合,易扩展的效果. 抽象组件角色: 一个抽象接口,是被装饰类和装饰类的父接口可以给这些对象动态地添加职责. 具体组件角色:为抽象组件的实现类,是定义了一个具体的对象,也可以给这个对象添加一些职责. 抽象装饰角色:包含一个组件的引用,并定义了与抽象组件一致的接口,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的. 具体装饰角色

装饰者设计模式

import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; /* 装饰者设计模式:增强一个类的功能,而且还可以让这些装饰类互相装饰. 装饰者设计模式的步骤: 1. 在装饰类的内部维护一个被装饰类的引用. 2. 让装饰类有一个共同的父类或者是父接口. 需求1: 编写一个类拓展BufferedReader的功能, 增强readL

设计模式 2 —— 装饰者模式

设计模式目录: 设计模式 1 ——观察者模式 设计模式 2 —— 装饰者模式

设计模式 之 装饰者

装饰模式(Deocrator)           动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活. 所谓装饰,就是一些对象给主题对象做陪衬.我们可以想象,在一个公司里面,每个人都有一个办工作,办工作都需要有电脑.电话.文件夹.盆栽.签字笔.公章等作为装饰.但是不同的人的办公桌上的装饰肯定不一样.比如说,老总的办公桌上应该什么都有,但是一般工作人员的办公桌上,就不应该有电话和公章.我们怎么动态的来装饰办公桌呢? using System; using System

设计模式之: 装饰器模式

什么是装饰器模式 作为一种结构型模式, 装饰器(Decorator)模式就是对一个已有结构增加"装饰". 适配器模式, 是为现在有结构增加的是一个适配器类,.将一个类的接口,转换成客户期望的另外一个接口.适配器让原本接口不兼容的类可以很好的合作. 装饰器模式是将一个对象包装起来以增强新的行为和责任.装饰器也称为包装器(类似于适配器) 有些设计设计模式包含一个抽象类,而且该抽象类还继承了另一个抽象类,这种设计模式为数不多,而装饰器就是其中之一. 什么时候使用装饰器模式 基本说来, 如果想