设计模式(七)-装饰模式

定义

装饰模式又叫装饰者模式,它属于一种结构型模式。装饰模式可以在不改变原对象的情况下动态的扩展一个对象的功能

场景

以往我们在扩展一个对象的方式往往是通过使用继承从写对象的相关方法来扩展对象的功能。例如日常生活中我们喝的牛奶,我们可以在牛奶中添加糖让它变成甜牛奶,如果加咖啡就成了咖啡牛奶,如果加果汁则成了果汁牛奶。如果用程序代码来表示,我们的设计如下:

设计模式(七)-装饰模式
类结构设计

这么设计看上去好像是没什么问题,但是如果我想喝加了糖又加了咖啡的牛奶,那该怎么办呢?甚至更过分的是这三个我都想加,那你该怎么办呢?如果你还想通过继续继承来实现,这将会变得非常麻烦。因为可能会出现的情况有很多种,例如加了糖咖啡果汁的牛奶、加了糖果汁的牛奶、加了果汁咖啡的牛奶等等。随着可加的东西越来越多,类的数量也会越来越多,很显然这不是一个好的设计。面对上面的这种情况,使用装饰模式能很简单的去解决子类太多的问题,同时还能实现我们想要的效果。

对象分析

在装饰模式中,一般存在以下这么几种对象:

抽象构件

它是具体构件和抽象装饰类的共同父类,声明了需要具体构件中实现的业务方法。

public interface Drinks {
    void taste();
}

具体构件

它是抽象构件的子类,用于定义具体构件对象。

public class Milk implements Drinks{
    @Override
    public void taste(){
        System.out.println("有奶味");
    }
}

抽象装饰类

它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。

public abstract class Decorator implements Drinks{
    protected Drinks drinks;

    protected Decorator(Drinks drinks) {
        this.drinks = drinks;
    }
}

具体装饰类

它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

public class CoffeeDecorator extends Decorator{
    
    public CoffeeDecorator(Drinks drinks) {
        super(drinks);
    }
    
    @Override
    public void taste() {
        drinks.taste();
        System.out.println("加了咖啡有苦味");
    }
}

public class JuiceDecorator extends Decorator{

    public JuiceDecorator(Drinks drinks) {
        super(drinks);
    }

    @Override
    public void taste() {
        drinks.taste();
        System.out.println("加了果汁有果汁味");
    }
}

public class SugarDecorator extends Decorator{

    public SugarDecorator(Drinks drinks) {
        super(drinks);
    }

    @Override
    public void taste() {
        drinks.taste();
        System.out.println("加了糖有甜味");
    }
}

客户端调用

上面我们已经做了对象分析和具体代码实现,那么客户端怎么使用呢?

public class App {
    public static void main(String[] args) {
        System.out.println("====== 纯牛奶 ======");
        Drinks drinks = new Milk();
        drinks.taste();

        System.out.println("====== 加糖 ======");
        drinks = new SugarDecorator(drinks);
        drinks.taste();

        System.out.println("====== 加糖和咖啡 ======");
        drinks = new CoffeeDecorator(drinks);
        drinks.taste();

        System.out.println("====== 加糖、咖啡、果汁 ======");
        drinks = new JuiceDecorator(drinks);
        drinks.taste();

    }
}

运行代码结果如下:

====== 纯牛奶 ======
有奶味
====== 加糖 ======
有奶味
加了糖有甜味
====== 加糖和咖啡 ======
有奶味
加了糖有甜味
加了咖啡有苦味
====== 加糖、咖啡、果汁 ======
有奶味
加了糖有甜味
加了咖啡有苦味
加了果汁有果汁味

通过客户端的调用代码我们可以发现,通过不同的组合我们能够实现不同味道的牛奶。对于每一种味道的牛奶,我们只需要实现一个对应的装饰类,而客户端调用时只要根据需求组装调用即可很轻松地实现各种组合。

总结

装饰模式降低了系统的耦合度,可以动态的增加或删除对象的职责,并且使得具体构件类和装饰类可以独立变化。它的优点如下:

  • 对于扩展一个对象的功能,装饰模式会比直接使用继承更加灵活,而且不会导致类的数量急剧增多。
  • 一个对象可以进行多次装饰,通过不同的组合实现不同的功能增强。
  • 具体的构件类和装饰类可以独立的变化,互不影响,符合开闭原则

上面这些都是装饰模式的优点,但是它还存在以下缺点:

  • 装饰模式比继承模式更加灵活,特别是经过多层装饰,要排查问题时会增加难度。

本文示例代码地址:https://gitee.com/zengchao_workspace/design-pattern


原文始发于微信公众号(一只菜鸟程序员):设计模式(七)-装饰模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72881.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!