JAVA设计模式之装饰者模式

来源:互联网 发布:excel两表格数据对比 编辑:程序博客网 时间:2024/05/19 16:05

装饰者模式

1.什么是装饰者模式?

    装饰者模式指在不改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。也就是创建一些装饰对象来装饰对象,使对象具有装饰对象的行为。

Head First设计模式》书中指出,动态地将行为附加到对象上,动态的扩展对象功能,装饰者模式提供了一种有别于继承体系的另一种实现。

继承属于功能扩展形式之一,但不是弹性设计的最佳方案。

装饰者类反映出被装饰的组件类型,他们具有相同的类型,都是经过同一个接口或者继承实现。

装饰者模式能建立弹性的设计,维持开放-关闭原则。

2.角色

 

基类(Component:通常是一个接口或者是一个超类,定义了属性和方法,子类实现方法,一般不会直接使用此类,主要作用是约束和控制子类的特性。所有的“被装饰者”和“装饰者”都要继承或实现此类。实例代码中是Beverage(饮料)类。

被装饰者(ConcreteComponentComponent的直接子类。实例代码中分别为Espresso(浓缩咖啡)、HouseBlend(混合咖啡)、OrangeJuice(果汁)。

装饰者抽象类(DecoratorComponent的直接子类,是具体装饰者共同的抽象类(或接口),所有的装饰者都要实现或继承此类。实例代码中为CondimentDecorator(调料抽象类)。

装饰者(ConcreteDecoratorDecorator的子类,具体的装饰者。装饰者同时也是Component的子类,因此和被装饰者保持了“一致性”(同时继承自Component)可以方便的为被装饰者添加功能。装饰者中都应该提供一个实例变量来保存Component的引用,由于装饰者是Component的子类,因此相当于装饰者包裹了Component类,不但有Component的特性也拥有自己的特性,这就是“装饰”由来,让被装饰者动态的拥有新特性。

 

3.优点

在不改变原类文件的情况下动态的为对象添加新功能,符合弹性设计和开闭原则。

4.缺点

    装饰者模式会产生比继承体系更多的类,会对加大对功能的理解难度。

5.使用场景

    一般使用于要求能够在不改变原代码的情况下,动态的增加对象的功能的情景。

6.实例代码

此实例是模拟一个咖啡店的点餐系统

 

(1)、饮料超类

public abstract class Beverage {

/*

 * 饮料描述

 */

String description = "Unknown Beverage";

 

public String getDescription() {

return description;

}

/*

 * 计算价格

 */

public abstract float cost();

 

/*

 * 提供一个精准计算价格的静态方法

 */

public static float addMoney(float arg1,float arg2) {

return (new BigDecimal(String.valueOf(arg1)).add(new BigDecimal(String.valueOf(arg2)))).floatValue();

}

}

 

(2)、浓缩咖啡饮料

public class Espressoextends Beverage {

//饮料名称

public Espresso() {

description="Espresso";

}

@Override

public float cost() {

return 1.99f;

}

 

}

 

(3)、混合咖啡饮料

public class HouseBlendextends Beverage {

 

public HouseBlend() {

description="House Blend coffee";

}

@Override

public float cost() {

return 0.89f;

}

}

 

(4)、橙汁饮料

public class OrangeJuiceextends Beverage {

 

public OrangeJuice() {

description="OrangeJuice";

}

@Override

public float cost() {

return 1.05f;

}

}

 

(5)、调料装饰者超类

public abstract class CondimentDecoratorextends Beverage{

//获取描述

public abstract String getDescription();

}

 

(6)、牛奶调料

public class Milkextends CondimentDecorator {

Beverage beverage;

public Milk(Beveragebeverage) {

this.beverage=beverage;

}

@Override

public String getDescription() {

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

}

@Override

public float cost() {

return addMoney(0.1f,beverage.cost());

}

}

 

(7)、摩卡调料

public class Mochaextends CondimentDecorator {

/*

 * 用一个实例变量记录饮料,也就是被装饰者

 */

Beverage beverage;

/*

 * 使用构造器将饮料对象记录到实例变量中

 */

public Mocha(Beveragebeverage) {

this.beverage=beverage;

}

@Override

public String getDescription() {

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

}

 

@Override

public float cost() {

return addMoney(0.2f,beverage.cost());

}

}

 

(8)、豆奶调料

public class SoybeanMilkextends CondimentDecorator {

 

Beverage beverage;

public SoybeanMilk(Beveragebeverage) {

this.beverage=beverage;

}

@Override

public String getDescription() {

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

}

@Override

public float cost() {

return addMoney(0.15f,beverage.cost());

}

}

 

(9)、测试代码-模拟点餐

public static void main(String[]args) {

/*

 * 点一杯橙汁

 */

Beverage b1=new OrangeJuice();

System.out.println(b1.getDescription()+" 价格$"+b1.cost());

/*

 * 点一杯浓缩咖啡,加摩卡,加牛奶

 */

Beverage b2=new Espresso();

b2=new Mocha(b2);//用摩卡装饰浓缩咖啡

b2=new Milk(b2);//用牛奶装饰浓缩咖啡

System.out.println(b2.getDescription()+" 价格$"+b2.cost());

/*

 * 点一份混合咖啡,加双份豆奶

 */

Beverage b3=new HouseBlend();

b3=new SoybeanMilk(b3);

b3=new SoybeanMilk(b3);

System.out.println(b3.getDescription()+" 价格$"+b3.cost());

}

 

(10)、运行结果

OrangeJuice 价格$1.05

Espresso,Mocha,Milk 价格$2.29

House Blend coffee,SoybeanMilk,SoybeanMilk 价格$1.19

 

7.JDK内部实现:java.io

装饰者模式在JDK中最直接的使用就是java.IOjava.io中有许多的类,在了解装饰者模式之前对这些类多少有点蒙圈,不知道有些类的作用和使用方法。面对如此众多类文件的情况下,我们只需要哪些是基类(被装饰者),哪些是装饰者类就可以了。

就字节流来说,两个基类分别是 字节输入流InputStream字节输出流OutputStream,而字符流分别是字符输入流 Reader字符输出流Writer其他类似于FileInputStreamStringBufferInputStream这些类都是装饰者,也就是角色图中的ConcreteDecorator

除了以上的具体装饰者和被装饰者以外,还有四个类需要格外的注意,因为这些类为我们自己扩展java.io提供了支持,这四个类就是装饰者抽象类(Decorator,分别是InputStream的抽象装饰者:FilterInputStreamOutputStream的抽象装饰者FilterOutputStreamReader的抽象装饰者FilterReaderWriter的抽象装饰者FilterWriter

自己扩展的装饰者只要继承(extends)相应的抽象装饰者,就可以分别为字节流和字符流扩展额外的功能。

InputStream类图如下图(图片来源于网络):

 

 

例如,我们为字节输入流扩展一个可以将输入单词转换为小写的功能。

 

1)、实例代码:

import java.io.BufferedInputStream;

import java.io.FileInputStream;

import java.io.FilterInputStream;

import java.io.IOException;

import java.io.InputStream;

 

public class LowerCaseInputStreamextends FilterInputStream {

 

/*

 * FilterInputStream中已经提供InputStream的实例变量 此处直接调用父类的方法

 */

protected LowerCaseInputStream(InputStreamin) {

super(in);

}

    /**

 * 覆盖父类中的read()方法

 */

public int read()throws IOException {

int c =super.read();

return (c == -1 ?c : Character.toLowerCase((char)c));

}

}

 

(2)、输入文件test.txt中的内容

THIS IS MY JAVA.IO Decorator test;

 

(3)、测试代码

public static void main(String[]args) throws IOException {

int c;

InputStream is = new LowerCaseInputStream(

new BufferedInputStream(new FileInputStream("test.txt")));

while ((c =is.read()) >= 0) {

System.out.print((char)c);

}

is.close();

}

 

(4)运行结果

this is my java.io decorator test;


【乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】