博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式(4)------结构型模式------装饰者设计模式
阅读量:6236 次
发布时间:2019-06-22

本文共 6343 字,大约阅读时间需要 21 分钟。

 

假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情请点击

 

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

 

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

 

我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

介绍

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。

 

一,需求

现在在星巴克咖啡店,有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方法的道理是一样的。并不是要用这个方法,只不过是包装类必须遵循这个方法才能不断的往里面走,并且走出来。

 

假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情请点击

 

转载地址:http://nnzia.baihongyu.com/

你可能感兴趣的文章
asp.net MVC中怎样让LINQ Designer自动生成的类从别的类继承并调用其基类构造器?...
查看>>
用VMWARE模拟打造通用批量分发安装系统
查看>>
Python 字符串、列表、元组、索引、切片
查看>>
JAVA实现显示指定类型的文件的例子
查看>>
rm -rfi 防止误删除数据
查看>>
Java项目打war包的方法
查看>>
ubuntu server 10.4 下LVS-DR+heartbeat +Ldirectord实现web服务高可性负载均衡实验
查看>>
流媒体服务器支持flv格式文件
查看>>
(4)制作windows 2003自动安装盘-集成补丁/Raid及硬件驱动
查看>>
MDeamon如何限制进出邮件大小
查看>>
为C# as 类型转换及Assembly.LoadFrom埋坑!
查看>>
word的问题
查看>>
MDaemon邮件报表统计插件——MailDetective
查看>>
为何有着良好设计的系统代码反而不容易看懂?
查看>>
[Ajax]ajax入门
查看>>
什么是BPOS
查看>>
JSP中的四种属性范围(重点)
查看>>
Ubuntu init启动流程分析浅析
查看>>
linux之df命令
查看>>
HDOJ 1303 Doubles(简单题)
查看>>