美颜相机中的设计模式——装饰者模式

栏目: 后端 · 发布时间: 4年前

内容简介:几乎所有的设计模式都是通过增加一层抽象来解决问题。上一篇中提到的三个设计模式通过相同的手段来达到相同的目的:它们通过接口和抽象方法来新增抽象层以应对变化。这一系列的后续几篇中会提到的四个设计模式通过相同的手段来达到不同的目的:它们通过新增一个类并持有原有类的方式实现对其扩展或限制。

几乎所有的 设计模式 都是通过增加一层抽象来解决问题。

上一篇中提到的三个设计模式通过相同的手段来达到相同的目的:它们通过接口和抽象方法来新增抽象层以应对变化。

这一系列的后续几篇中会提到的四个设计模式通过相同的手段来达到不同的目的:它们通过新增一个类并持有原有类的方式实现对其扩展或限制。

这一篇先来看看装饰者模式。

装饰者模式就好像美颜相机,通过添加不同的装饰品,它可以让你变成另一个你。(虽然可能面目全非,但本质上还是你)

只复用类型

假设有四种饰品:耳环、钻石、黄金、羽毛。不同装饰品有不同价格,通常我们会这样做抽象:

//抽象饰品
public abstract class Accessory {
    public abstract String name();//饰品名称
    public abstract int cost();//饰品价格
}

//耳环
public class Ring extends Accessory {
    @Override
    public String name() { return "Ring"; }
    @Override
    public int cost() { return 20; }
}

//钻石
public class Diamond extends Accessory {
    @Override
    public String name() { return "Diamond"; }
    @Override
    public int cost() { return 1000; }
}

//黄金
public class Gold extends Accessory {
    @Override
    public String name() { return "Gold"; }
    @Override
    public int cost() { return 300; }
}

//羽毛
public class Feather extends Accessory {
    @Override
    public String name() { return "Feather"; }
    @Override
    public int cost() { return 90; }
}
复制代码

现推出两款新饰品:黄金耳环,羽毛黄金耳环。同样的思路,使用继承可以解决问题:

public class GoldRing extends Accessory {
    @Override
    public String name() { return "GoldRing"; }
    @Override
    public int cost() { return 320; }
}

public class FeatherGoldRing extends Accessory {
    @Override
    public String name() {  "FeatherGoldRing"; }
    @Override
    public int cost() { return 1110; }
}
复制代码
  • 如果继续推出更多的新品,比如羽毛耳环,钻石耳环,羽毛钻石耳环。。。每个新产品都用一个新的类表示,这样就会遇到 子类膨胀 的问题。
  • 除此之外,继承还有一个更致命的缺点:对单个类型的饰品没有统一的控制力。如果黄金涨价了,我们需要分别修改 GoldRingFeatherGoldRing 的价格,如果和黄金相关的饰品有好几十个,那简直是一场噩梦。
  • 在计算 GoldRing 价格的时候,我们并没有复用现有代码,即没有复用 GoldRing 已经定义的 cost() 行为,而只是通过继承复用了类型( GoldRing 是一个 Accessory )。只复用类型而没有复用行为的后果是:当 Gold 涨价时, GoldRing 无感知。

有没有一种比继承更好的方案在现有饰品基础上扩展新的饰品?

既复用类型又复用行为

采用组合的方式就可以实现既复用类型又复用行为:

public class Gold extends Accessory {
    private Accessory accessory;
    public Gold(Accessory accessory) { this.accessory = accessory; }
    
    @Override
    public String name() {
        return "Gold " + accessory.name();
    }
    @Override
    public int cost() {
        return 300 + accessory.cost();
    }
}

public class Feather extends Accessory {
    private Accessory accessory;
    public Feather(Accessory accessory) { this.accessory = accessory; }

    @Override
    public String name() {
        return "Feather " + accessory.name();
    }
    @Override
    public int cost() {
        return 90 + accessory.cost();
    }
}
复制代码
Accessory

用组合的方式实现羽毛黄金耳环:

Accessory ring = new Gold(new Feather(new Ring()));
复制代码
  • 为了说明装饰与被装饰的关系,使用了带有俄罗斯套娃既视感的代码(虽然这样的代码可读性较差)。
  • Ring 作为基础饰品被 Feather 装饰成羽毛耳环,羽毛耳环接着被 Gold 装饰成换羽毛黄金耳环。
  • 过程中并没有为羽毛黄金耳环新增一个叫 FeatherGoldRing 的子类,而是复用了现有的 FeatherGold 的行为。这样就解决了子类泛滥和控制力的问题。如果黄金涨价,只需要修改 Gold.cost() ,所有被 Gold 装饰的饰品价格都会随之而涨。
  • 这个方案还有一个更有用的好处: 在运行时动态新增类型 。通过继承新增的类型都是在编译时定死的,而通过组合的方式只要新增一行俄罗斯套娃式的代码,程序运行起来后就新增了一个类型,比如要新增“双倍黄金羽毛耳环”这个类型,只需要如下的代码:
Accessory ring = new Gold(new Gold(new Feather(new Ring())));
复制代码

抽象的装饰者?

新的需求来了:基础饰品镶嵌附加饰品收取 10% 的一次性加工费。我们可以为所有附加饰品增加一层抽象:

public abstract class Decorator extends Accessory{
    private Accessory accessory;
    public Decorator(Accessory accessory) { this.accessory = accessory; }

    @Override
    public int cost() {
        return  1.1 * accessory.cost();
    }
}
复制代码
  • Decorator 通过组合持有超类型 Accessory 且规定了在构造时必须注入超类型,它还定义了镶嵌加工费的收费标准。
  • 现在就可以像这样重新定义附加饰品:
public class Gold extends Decorator {
    public Gold(Accessory accessory){ super(accessory); }

    @Override
    public String name() {
        return "Gold " + accessory.name();
    }
    @Override
    public int cost() {
        return 300 + super.cost();
    }
}
复制代码
  • 其实对于装饰者模式来说,为装饰者定义一个抽象的父类不是必须的,只要满足继承超类型,以及持有超类型引用这两点就是装饰者模式。除非需要统一操作所有装饰者,比如在美颜相机这个场景中,需要通过遍历找出所有附加饰品。

总结

  • 装饰者模式是一种复用原有类并对其进行扩展的方式,它是继承的替代方法。
  • 装饰者模式通过继承原有类型实现复用类型。这一点很重要,因为所有使用原有类型的地方不需要修改代码就可以替换成装饰者。
  • 装饰者模式通过组合持有原有类实例实现复用行为。
  • 装饰者模式通过在调用原有类方法的前后插入新的逻辑实现功能扩展。
  • 装饰者模式符合开闭原则,即在新增功能的时候没有修改原有代码。
  • 装饰者模式特别适用于子类型之间可以有随机组合的场景,比如美颜相机的各种道具组合之后形成新的道具。

运用组合的设计模式不止装饰者一个,该系列的后续文章会继续分析“组合”在设计模式中的运用。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java学习笔记

Java学习笔记

林信良 / 清华大学出版社 / 2015-3-1 / CNY 68.00

●本书是作者多年来教学实践经验的总结,汇集了学员在学习课程或认证考试中遇到的概念、操作、应用等问题及解决方案 ●针对Java SE 8新功能全面改版,无论是章节架构或范例程序代码,都做了重新编写与全面翻新 ●详细介绍了JVM、JRE、Java SE API、JDK与IDE之间的对照关系 ●从Java SE API的源代码分析,了解各种语法在Java SE API中的具体应用 ......一起来看看 《Java学习笔记》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具