大话设计模式——装饰模式

需求

写一个给人模拟搭配不同服饰的程序,可以给人换各种各样的衣服裤子的形象。

初步实现

需求比较简单,直接上代码:

public class Person {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void wearTShirt(){
        System.out.print("大T恤 ");
    }

    public void wearBigTrouser(){
        System.out.print("垮裤 ");
    }

    public void wearSneakers(){
        System.out.print("破球鞋 ");
    }

    public void wearSuit(){
        System.out.print("西装 ");
    }

    public void wearTie(){
        System.out.print("领带 ");
    }

    public void wearLeatherShoes(){
        System.out.print("皮鞋 ");
    }

    public void show(){
        System.out.println("装扮的"+name);
    }
}

客户端代码:

public class Client {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小明");

        person.wearTShirt();
        person.wearBigTrouser();
        person.wearSneakers();
        person.show();

        person.wearSuit();
        person.wearTie();
        person.wearLeatherShoes();
        person.show();
    }
}

运行结果:


程序运行结果

分析

通过前面几节的介绍,可以发现这种写法的显著毛病:难以实现功能拓展。比如现在不仅仅只有这两种搭配方式,额外需要添加其他的搭配方式,该怎么办呢?这种写法必然需要对Person类进行手术,违反了开闭原则。

改进实现

很容易结合前面的实例,我们利用面向对象的程序设计方式,通过创造服饰抽象类,让其他具体的西装、领带、垮裤等来继承,实现程序功能的可扩展。
UML类图:

UML类图

Person类实现:

public class Person {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println("装扮的" + name);
    }
}

服饰基类:

public abstract class Finery {
    public abstract void show();
}

其他具体衣服的实现,以大T恤、西装为例:

public class TShirt extends Finery {
    @Override
    public void show() {
        System.out.println("大T恤");
    }
}
public class Suit extends Finery {
    @Override
    public void show() {
        System.out.println("西装");
    }
}

客户端代码:

public class Client {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小明");

        Finery xz = new Suit();
        Finery ld = new Tie();
        Finery px = new LeatherShoes();

        xz.show();
        ld.show();
        px.show();
        person.show();
    }
}

再次分析

<<大话设计模式>>49页中提及,上面这种改进方案虽然实现了服饰与人的分离,且利用面向对象程序设计的特性可以实现功能的扩展,但是仍然存在一个显著的问题——穿衣服的动作是一步一步完全暴露在外面的。

xz.show();
ld.show();
px.show();

通过在客户端,一步步的进行穿着服饰,这种设计方案是不够良好的。(但实际开发中不好在哪,我也没有深刻理解。)这些操作应该在内部组装完毕,同时也要将需要的功能(服饰)按照一定的顺序进行串联,不能先打领带后穿衬衫等...而装饰模式就是解决这一需求的。

装饰模式

  • 定义:动态给一个对象添加一些额外的职责,使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活。
  • 设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。

装饰模式的使用

UML类图:

UML类图

这里需要注意的是,装饰模式,装饰类与被装饰类都是继承自同一父类,究竟是为什么?这一点在客户端代码中可以体现,需要好好体会,这也是理解装饰模式的关键所在。

代码实现:

  1. Component抽象类——被装饰者的抽象类,实际中也不一定有此类:

    public abstract class Component {
        public abstract void meathod();
    }
    
  2. 具体的被装饰者类:

    public class ConcreteComponent extends Component {
        @Override
        public void meathod() {
            System.out.println("具体要被装饰的对象的方法");
        }
    }
    
  3. Decorator抽象类——装饰者抽象类:

    public abstract class Decorator extends Component {
        private Component component;
    
        public void setComponent(Component component) {
            this.component = component;
        }
    
        @Override
        public void meathod() {
            if (component != null)
                component.meathod();
        }
    }
    
  4. 具体的装饰类A,B:

    public class ConcreteDecoratorA extends Decorator {
    
        private void addNewMethodOfA(){
            //A装饰器的作用
            System.out.println("A装饰器的作用");
        }
    
        @Override
        public void meathod() {
            super.meathod();
            addNewMethodOfA();
        }
    }
    
    public class ConcreteDecoratorB extends Decorator {
    
        private void addNewMethodOfB(){
            //B装饰器的作用
            System.out.println("B装饰器的作用");
        }
    
        @Override
        public void meathod() {
            super.meathod();
            addNewMethodOfB();
        }
    }
    
  5. 客户端类:

    public class Client {
    
        public static void main(String[] args) {
            Component component = new ConcreteComponent();
            Decorator decoratorA = new ConcreteDecoratorA();
            Decorator decoratorB = new ConcreteDecoratorB();
    
            decoratorA.setComponent(component);
            decoratorB.setComponent(decoratorA);
            decoratorB.meathod();
        }
    }
    

总结:

  • 装饰模式是用setComponent()方法来进行装饰,其实也就是功能扩展;
  • 每个装饰类装饰什么样的功能和这个装饰类如何被调用进行装饰是分离的,即装饰类A/B能完成什么样的装饰与他们什么时候被调用、按照什么顺序是无关的。

利用装饰模式实现换衣

UML类图:

UML类图

代码实现:

  1. Person类——被装饰者;

    public class Person {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void show() {
            System.out.println("装扮的" + name);
        }
    }
    
  2. Finery类——抽象装饰类;

    public abstract class Finery extends Person {
    
        private Person person;
    
        public void setPerson(Person person) {
            this.person = person;
        }
    
        @Override
        public void show() {
            if (person != null)
                person.show();
        }
    }
    
  3. 其他具体装饰类;

    public class BigTrousers extends Finery {
        @Override
        public void show() {
            super.show();
            System.out.println("垮裤");
        }
    }
    
  4. 客户端类;

    public class Client {
        public static void main(String[] args) {
            Person person = new Person();
            person.setName("小明");
    
            Finery xz = new Suit();
            Finery ld = new Tie();
            Finery px = new LeatherShoes();
    
            xz.setPerson(person);
            ld.setPerson(xz);
            px.setPerson(ld);
            px.show();
        }
    }
    
  5. 运行结果:


    运行结果

装饰模式小结

  • 装饰模式是为已有功能动态的添加更多功能的一种方式;
  • 装饰模式把类中的装饰功能从类中搬移去除,这样可以简化原有的类。同时有效的把类的核心职责和装饰功能区分开,而且可以 去除相关类中重复的装饰逻辑 ;(后半句话怎么理解呢?望评论出你的看法!)
  • 当系统需要新功能时,是向旧的类中添加新的代码。这些新的代码通常装饰了原有类的核心职责或主要行为。但这种做法的问题在于,他们在主类中加入了新的字段、新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。装饰模式提供了一种非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以根据需要、有选择按顺序的使用装饰功能包装对象;
  • 对于前面所说为什么装饰模式,被装饰者与装饰者都要继承同一父类,这一点我觉得相当于给人穿了件衣服后返回的新对象毕竟本质还是人,只是多了件衣服,这样就可以不断的穿衣服不断的返回。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容