设计模式之装饰者模式

装饰(Decorator)模式又叫做包装模式,动态地给一个对象添加额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。。

我们先来看一个快餐店的例子。

快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。

如果我们使用使用继承的方式去实现

存在的问题:

  • 扩展性不好

    如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。

  • 产生过多的子类

定义:

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

结构

  • Component为统一接口,也是装饰类和被装饰类的基本类型。
  • ConcreteComponent为具体实现类,也是被装饰类,他本身是个具有一些功能的完整的类。
  • Decorator是装饰类,实现了Component接口的同时还在内部维护了一个ConcreteComponent的实例,并可以通过构造函数初始化。而Decorator本身,通常采用默认实现,他的存在仅仅是一个声明:我要生产出一些用于装饰的子类了。而其子类才是赋有具体装饰效果的装饰产品类。
  • ConcreteDecorator是具体的装饰产品类,每一种装饰产品都具有特定的装饰效果。可以通过构造器声明装饰哪种类型的ConcreteComponent,从而对其进行装饰。

 我们使用装饰者模式对快餐店案例进行改进

 快餐类为抽象类,炒面炒饭继承快餐类

Garnish 是一个装饰者,继承了快餐类,并且聚合了快餐类的对象

package com.dongguo.decorator;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:54
 * @description: 快餐接口
 */
public abstract class FastFood {
    private float price;
    private String desc;

    public FastFood() {
    }

    public FastFood(float price, String desc) {
        this.price = price;
        this.desc = desc;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public float getPrice() {
        return price;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public abstract float cost();  //获取价格
}

package com.dongguo.decorator;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:55
 * @description: 炒饭
 */
public class FriedRice extends FastFood {

    public FriedRice() {
        super(10, "炒饭");
    }

    public float cost() {
        return getPrice();
    }
}

package com.dongguo.decorator;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:55
 * @description: 炒面
 */
public class FriedNoodles extends FastFood {

    public FriedNoodles() {
        super(12, "炒面");
    }

    public float cost() {
        return getPrice();
    }
}

package com.dongguo.decorator;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:55
 * @description: 配料类
 */
public abstract class Garnish extends FastFood {

    private FastFood fastFood;

    public FastFood getFastFood() {
        return fastFood;
    }

    public void setFastFood(FastFood fastFood) {
        this.fastFood = fastFood;
    }

    public Garnish(FastFood fastFood, float price, String desc) {
        super(price,desc);
        this.fastFood = fastFood;
    }
}
package com.dongguo.decorator;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:56
 * @description: 鸡蛋配料
 */
public class Egg extends Garnish {

    public Egg(FastFood fastFood) {
        super(fastFood,1,"鸡蛋");
    }

    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}
package com.dongguo.decorator;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:56
 * @description: 培根配料
 */
public class Bacon extends Garnish {

    public Bacon(FastFood fastFood) {

        super(fastFood,2,"培根");
    }

    @Override
    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}

package com.dongguo.decorator;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:56
 * @description:
 */
public class Client {
    public static void main(String[] args) {
        //点一份炒饭
        FastFood food = new FriedRice();
        //花费的价格
        System.out.println(food.getDesc() + " " + food.cost() + "元");

        System.out.println("========");
        //点一份加鸡蛋的炒饭
        FastFood food1 = new FriedRice();

        food1 = new Egg(food1);
        //花费的价格
        System.out.println(food1.getDesc() + " " + food1.cost() + "元");

        System.out.println("========");
        //点一份加培根的炒面
        FastFood food2 = new FriedNoodles();
        food2 = new Bacon(food2);
        //花费的价格
        System.out.println(food2.getDesc() + " " + food2.cost() + "元");
    }
}
运行结果:
炒饭 10.0元
========
鸡蛋炒饭 11.0元
========
培根炒面 14.0元

装饰模式的优点:
可以提供比继承更多的灵活性,可以通过一种动态地方式来扩展一个对象的功能,并通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,而且具体构建类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构建类和具体装饰类。

装饰模式的缺点
使用装饰模式进行系统设计时将产生很多小对象,而且装饰模式比继承更加易于出错,排错也困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐,尽量减少装饰类的数量,以便降低系统的复杂度。

适用情况:

装饰模式是对模式的有力补充。继承不是万能的,在项目中如果考虑到已维护、易扩展、易复用等,而在一些情况下,使用继承增加了很多子类,这样灵活性非常差,维护起来也很困难,那么我们可以采用装饰模式替代继承,解决子类过多的问题,继承是静态的给类增加功能,而装饰模式则是动态地增加功能。

装饰模式的扩展性非常好,在项目中,随时面临着需求变更,那么我们可以通过装饰模式重新封装一个类,而不是通过继承来完成,对源程序没有改变,通过扩展很好的完成了这次变更。

JDK源码解析

IO流中的包装类使用到了装饰者模式。BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。

我们以BufferedWriter举例来说明,先看看如何使用BufferedWriter

public class Demo {
    public static void main(String[] args) throws Exception{
        //创建BufferedWriter对象
        //创建FileWriter对象
        FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
        BufferedWriter bw = new BufferedWriter(fw);

        //写数据
        bw.write("hello Buffered");

        bw.close();
    }
}

使用起来感觉确实像是装饰者模式,接下来看它们的结构:

BufferedWriter使用装饰者模式对Writer子实现类进行了增强,添加了缓冲区,提高了写数据的效率。

代理和装饰者的区别

静态代理和装饰者模式的区别:

  • 相同点:

    • 都要实现与目标类相同的业务接口

    • 在两个类中都要声明目标对象

    • 都可以在不修改目标类的前提下增强目标方法

  • 不同点:

    • 目的不同 装饰者是为了增强目标对象 静态代理是为了保护和隐藏目标对象

    • 获取目标对象构建的地方不同 装饰者是由外界传递进来,可以通过构造方法传递 静态代理是在代理类内部创建,以此来隐藏目标对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失业找工作中

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值