Java设计模式(八)结构型- 装饰模式Decorator(史上最全装饰模式继承与装饰器模式方式)与使用场景以及优缺点

  1. 装饰模式

装饰模式,是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)

装饰(Decorator)模式中的角色:

  • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

1.1 继承方式

举一个简单的例子,假如现在有一碟炒饭,每个人的口味不一样,有些人喜欢加鸡蛋,有些人喜欢加鸡蛋火腿,有些人喜欢加鸡蛋火腿胡萝卜等,那么就会发现,如果采用继承的方式去实现这个例子,那么没加一个配料,都需要创建新的配料类去继承上一个旧的配料类,那么久而久之,就会产生很多类了,而且还不利于扩展,代码如下:

// 炒饭类
public class FriedRice {
    String getDesc() {
        return "炒饭";
    }
    Integer getPrice() {
        return 5;
    }
}
// 炒饭加鸡蛋类
public class FriedRiceAddEgg extends FriedRice{
    String getDesc() {
        return super.getDesc() + "+鸡蛋";
    }
    Integer getPrice() {
        return super.getPrice() + 2;
    }
}
// 炒饭加鸡蛋加火腿类
public class FriedRiceAddEggAndHam extends FriedRiceAddEgg {
    String getDesc() {
        return super.getDesc() + "+火腿";
    }
    Integer getPrice() {
        return super.getPrice() + 3;
    }
}
    // 测试方法
    public static void main(String[] args) {
        FriedRice friedRice = new FriedRice();
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭5元
        FriedRice friedRiceAddEgg = new FriedRiceAddEgg();
        System.out.println(friedRiceAddEgg.getDesc() + friedRiceAddEgg.getPrice() + "元"); // 炒饭+鸡蛋7元
        FriedRice friedRiceAddEggAndHam = new FriedRiceAddEggAndHam();
        System.out.println(friedRiceAddEggAndHam.getDesc() + friedRiceAddEggAndHam.getPrice() + "元");// 炒饭+鸡蛋+火腿10元
    }

可以从上面看到,如果我们只需要炒饭加火腿,那么我们还需要创建一个FriedRiceAddHam类去继承FriedRice类,所以继承的方式扩展性非常不好,且需要定义非常多的子类,下面就可以用装饰器模式去改进它

1.2 装饰器模式方式

// 炒饭类
public class FriedRice {
    String getDesc() {
        return "炒饭";
    }
    Integer getPrice() {
        return 5;
    }
}
// 配料表
public abstract class Ingredients extends FriedRice{
    private FriedRice friedRice;
    public Ingredients(FriedRice friedRice) {
        this.friedRice = friedRice;
    }
    String getDesc() {
        return this.friedRice.getDesc();
    }
    Integer getPrice() {
        return this.friedRice.getPrice();
    }
}
// 鸡蛋配料
public class Egg extends Ingredients {
    public Egg(FriedRice friedRice) {
        super(friedRice);
    }
    String getDesc() {
        return super.getDesc() + "+鸡蛋";
    }
    Integer getPrice() {
        return super.getPrice() + 2;
    }
}
// 火腿配料
public class Ham extends Ingredients {
    public Ham(FriedRice friedRice){
        super(friedRice);
    }
    String getDesc() {
        return super.getDesc() + "+火腿";
    }
    Integer getPrice() {
        return super.getPrice() + 3;
    }
}
    // 测试方法
    public static void main(String[] args) {
        FriedRice friedRice = new FriedRice();
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭5元
        friedRice = new Egg(friedRice);
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭+鸡蛋7元
        friedRice = new Egg(friedRice);
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋9元
        friedRice = new Ham(friedRice);
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋+火腿12元
    }

可以看到,使用装饰器模式的方法实现,与普通的继承方法实现,最大的区别就是一种配料只有一个类,而且在加配料的时候,也可以直接想加多少就加多少,不需要说一个鸡蛋一个类,两个鸡蛋也要创建一个类,这样可以带来比继承更加灵活的扩展功能,使用也更加方便。

1.3 总结

装饰器模式与代理模式对比:

  • 装饰器模式就是一种特殊的代理模式。
  • 装饰器模式强调自身的功能扩展,用自己说了算的透明扩展,可动态定制的扩展;代理模式强调代理过程的控制。
  • 获取目标对象构建的地方不同,装饰者是从外界传递进来的,可以通过构造方法传递;静态代理是在代理类内部创建,以此来隐藏目标对象。

适用场景:

  • 用于扩展一个类的功能或者给一个类添加附加职责。
  • 动态的给一个对象添加功能,这些功能同样也可以再动态的撤销。

优点:

  • 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
  • 通过使用不同装饰类以及这些装饰类的排列组合,可实现不同效果。
  • 装饰器完全遵守开闭原则。

缺点:

  • 会出现更多的代码,更多的类,增加程序的复杂性。
  • 动态装饰时,多层装饰会更复杂。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

刘了个刘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值