设计模式随笔-装饰者模式

装饰者模式

当自己还是菜鸟的时候,曾经以为用继承处理一切。但是后来才运行时的强大之处,真的是让自己的眼界开阔了很多。
装饰者模式正是可以给喜欢用继承的人另外一个思想。

需求

初始设计

某咖啡店原设计是这样的……
原设计
但是这个设计,如果我要在HouseBlend(综合咖啡)的基础上+牛奶,那么我就必须在创建一个类:HouseBlendWithMilk,这样的话如果我每一种coffee都需要加一种配料就产生多少个子类,而且配料之间还能够重叠。这样要创建多少个类啊,啊大家可想而知。这种设计方法是不可取的。可能大家会想到可以继承原有的子类啊,例如:HouseBlend(综合咖啡)被HouseBlendWithMilk(牛奶综合咖啡)继承,可能这样可以让大家比较好的让他们分类,但是类还是没有减少哦。
很明显,如果牛奶涨价了,怎么办?某咖啡馆给自己制造了一个维护噩梦。

改进设计

于是乎,某咖啡店的程序员需要重构咖啡店的设计。
重构的设计图如下:
第一次重构
现在超类的cost()是一个具体的方法,在子类中需要覆盖和继承子类的cost()方法。这样达到的效果是不用创建那么多的子类了。比如,当我需要一杯mocha咖啡只需要在子类里面的一杯普通咖啡+setMocha(true),这样子类的cost()就可以计算出子类的咖啡价格+抽象类中设置mocha的值了。
这种方法也有一些缺陷:
1.调料价格的改变会让我们更改调料的价格
2.一旦出现新的调料,我们必须在超类中加上。
3.如果以后开发出一种饮料,是不需要牛奶的,这样继承超类的牛奶配料就不合适了,因为根本就用不到。
4.如果用户想要双倍的mocha咖啡怎么办呢?无法实现了。

这里设计到一个设计原则:类应该对扩展开放,对修改关闭。

程序员在一次重构代码

某咖啡店的成员员打算用一种设计模式:只针对扩展开发-修改关闭,他就是“装饰者模式”

认识装饰模式

拿咖啡为例子,饮料即为主体,然后让许多的配料来装饰它。
如下图所示:
咖啡装饰模式
1.首先是创建一个DarkRoast(焦糖咖啡),并有自己的价格
2.然后添加Mocha(摩卡)配料,处理被装饰的价格+摩卡的价格。
3.最后添加Whip(奶油),处理被装饰的价格+奶油的价格。
咖啡装饰模式价格走向
装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。

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

装饰者构架,如下图所示:
装饰者结构图

好了,了解完这些概念之后,我们来从新设计咖啡馆吧。

咖啡馆装饰者模式

现在我们就用装饰者模式来从新设计咖啡馆的代码吧!

首先是component装饰者的父类(名称有所不同):

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

    public String getDescription(){
        return description;
    }

    public abstract double cost();
}

接下来我们实现的是装饰器的抽象类,也就是框架的Decoretor类(名称有所不同):

public abstract class CondimentDecorator extends Beverage{
    //必须要覆盖Beverage类的getDescription方法
    public abstract String getDescription();
}

然后是罗列一些饮料的代码(即ConcreteComponent,需要被装饰的基本类):

//浓咖啡
public class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}
//综合咖啡
public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "House Blend Coffee";
    }

    @Override
    public double cost() {
        return 0.89;
    }
}

现在就以及实现了抽象组件(Beverage)、具体组件(Espresso、HouseBlend)、抽象装饰者(CondimentDecorator)
接下来是实现配料具体的装饰者了。

//摩卡配料
public class Mocha extends CondimentDecorator {
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }

    @Override
    public double cost() {
        return .20 + beverage.cost();
    }
}
//豆浆
public class Soy extends CondimentDecorator {

    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " ,soy";
    }
    @Override
    public double cost() {
        return beverage.cost() + .22;
    }
}
//奶油
public class Whip extends CondimentDecorator{

    Beverage beverage;

    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Whip";
    }

    @Override
    public double cost() {
        return .11 + beverage.cost();
    }
}

所有类都实现之后就可以写测试类了:

public class CoffeeTest extends TestCase {
    public void testCoffee(){
        //单点一杯Espresso 不需要加调料,打印价格与描述
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription() + " $" + beverage.cost());

        Beverage beverage1 = new HouseBlend();  //先定一杯综合咖啡
        beverage1 = new Soy(beverage1);  //加豆浆
        beverage1 = new Whip(beverage1);  //加奶油
        System.out.println(beverage1.getDescription() + " $" + beverage1.cost());

    }
}

console:
Espresso $1.99
House Blend Coffee ,soy, Whip $1.22

Java中的装饰者类:Java I/O

和上面的某咖啡设计相比,Java I/O流设计其实都大同小异。
具体的类图查看,大家可以打开jdk文档来具体的了解一下,假设大家都看了上面的装饰者模式和了解了下i/o类。我们现在实现一个自己的i/o类。

public class LowerCaseInputStream extends FilterInputStream {

    public LowerCaseInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int c = super.read();
        return (c == -1 ? c : Character.toLowerCase((char)c));
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int c = super.read(b, off, len);
        for (int i=off; i < off + c; i++){
            b[i] = (byte) Character.toLowerCase((char)b[i]);
        }
        return c;
    }
}

//测试方法:
public void testIO(){
        int c;
        try {
            InputStream in = new LowerCaseInputStream(
                    new BufferedInputStream(
                            new FileInputStream("D:"+ File.separator+"test.txt")));
            while ((c = in.read()) >= 0){
                System.out.print((char) c);
            }
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
//输出结果
i love you
Process finished with exit code 0

test.txt 内容为:I LOVE YOU
输出结果为小写的:i love you

总结

装饰模式让我学习到了几个点:
1、为交互对象之间的松耦合而努力
2、对扩展开发,对修改关闭
3、装饰者模式-动态的将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种实现方式。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值