设计模式之装饰者模式

什么是装饰者模式

装饰者模式是动态地给一个对象添加一些额外的职责,就增加功能来说,装饰者模式比生成子类更为灵活。

原理类图:

在这里插入图片描述

需要说明的一点是,不必一定按照上面的形式,如果没有抽象出Component的需求,可以直接让装饰者继承具体组件,毕竟,本来装饰者就是给具体组件装饰的。

为什么要用装饰者模式

假设有一位女生想要出门,这出门要想精致一点,就得化化妆,化妆这件事用代码怎么模拟呢?

基本实现如下:

首先得有一个人:

public class Person {
    public void foundation() {
        System.out.println("打粉底...");
    }

    public void shadow() {
        System.out.println("画眼影...");
    }

    public void lipstick() {
        System.out.println("涂口红...");
    }
}

现在出门:

public class GoOut {
    public static void main(String[] args) {
        Person person = new Person();
        person.foundation();
        person.shadow();
        person.lipstick();
    }
}

很好,现在就完成了出门化妆的工作。

但是,这似乎是违背开闭原则的吧。如果再想要涂个腮红,岂不是要修改Person类了?

那么就来修改一下,人不再具有这些动作,而是让各个化妆品独立出来,成为一个个独立的类:

Person:

public class Person {
    public void makeUp(){
        System.out.println("Person make up...");
    }
}

化妆品:

public abstract class Cosmetic {
    public abstract void makeUp();
}

具体化妆品:

public class Foundation extends Cosmetic {
    @Override
    public void makeUp() {
        System.out.println("打粉底...");
    }
}

public class Lipstick extends Cosmetic{
    @Override
    public void makeUp() {
        System.out.println("涂口红...");
    }
}

public class Shadow extends Cosmetic {
    @Override
    public void makeUp() {
        System.out.println("画眼影...");
    }
}

现在出门:

public class GoOut {
    public static void main(String[] args) {
        Person person = new Person();
        Foundation foundation = new Foundation();
        Lipstick lipstick = new Lipstick();
        Shadow shadow = new Shadow();

        person.makeUp();
        foundation.makeUp();
        lipstick.makeUp();
        shadow.makeUp();
    }
}

非常好,现在我们不光完成了出门化妆的动作,还遵循了开闭原则,多一个化妆动作就多来一个类即可。

但是,再仔细想一想这样的结果,这是出了门了,然后打粉底、画眼影、涂口红,这不就是素颜出了门,然后在路上化的妆吗。这似乎有些与日常不符,日常往往都是在家里化完妆再出门。


我们不妨换一个角度想,化妆不是自己在脸上画,不再是一个自己主观的一个动作,而是一个化妆品在脸上装饰的过程。

但是这化妆品怎么自己跑到脸上啊,还化妆?没糊成一坨就不错了。

很容易啊,请个化妆师(装饰者)啊,让化妆师来化妆,那化妆品可不就来装饰脸了吗。

那么实现一下吧:(Person类与之前相同,就不重复代码了)

化妆师:(这里叫类名还是Cosmetic,只是代表化妆师这个位置,并非平常所说的化妆师,假设化妆品自带装饰属性就可以)

public class Cosmetic extends Person {
    protected Person person; //要知道是给"谁"化妆(装饰)

    public void decorate(Person person) {
        this.person = person;
    }

    public void makeUp() {
        if (person != null) {
            person.makeUp(); //这将会是一个递归的过程,直至一个没有装饰过的对象
        }
    }
}

各个化妆品:

public class Lipstick extends Cosmetic{
    @Override
    public void makeUp() {
        super.makeUp();
        System.out.println("涂口红..."); 
        //在已经装饰过的对象的基础上,再进行一次装饰,这么说有一些不准确,makeUp()主要是用来展示用的。
    }
}

public class Shadow extends Cosmetic {
    @Override
    public void makeUp() {
        super.makeUp();
        System.out.println("画眼影...");
    }
}

public class Foundation extends Cosmetic {
    @Override
    public void makeUp() {
        super.makeUp();
        System.out.println("打粉底...");
    }
}

现在出门吧:

public class GoOut {
    public static void main(String[] args) {
        Person person = new Person();
        Foundation foundation = new Foundation();
        Lipstick lipstick = new Lipstick();
        Shadow shadow = new Shadow();

        foundation.decorate(person); //用粉底装饰一个素颜的人
        lipstick.decorate(foundation); //用口红装饰一个打过粉底的人
        shadow.decorate(lipstick); //用眼影装饰一个涂过口红(也打过粉底)的人
        shadow.makeUp(); //化上妆整体展示一下
    }
}

这样,就完成了出门的化妆过程。

当前类图:

在这里插入图片描述

和建造者模式很像?

我们来看一下这两个模式:

  • 建造者模式:开发商找了一个施工队的负责人来负责盖房子
  • 装饰者模式:女生找了一个化妆师来给自己化妆

嘶,这似乎还真有点像,但也仅仅是一点了。

回想一下建造者模式(可以点击这里跳转),我们要建的东西是有一套固定流程的,先打地基,再砌墙,最后盖房顶,这几步对于一个型号的房子来说,是固定的,不会因为今天施工队心情好多干一步或心情不好少干一步,是一个固定的流程(或者说是一个静态的过程),如果不按这个流程走,很可能会出现难以预料的问题。

但现在的装饰者模式不一样,可以根据自己的心情来装饰:今天不得不出门,可以不化妆;出门不见什么人,随随便便涂点防晒好了;今天要见的人很重要,要好好打扮一下,打个粉底、涂个口红、画个眼影,甚至还有更多的步骤;这些都没有一套固定的流程,比如画眼影和涂口红,谁先谁后也都无所谓(不过某些步骤还是要注意先后顺序的哈),这是一个动态的过程。

另外需要提到的一点是,建造者模式是创造型模式,而装饰者模式是结构型模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值