美文网首页设计模式
装饰者模式(动态组合)

装饰者模式(动态组合)

作者: 幺鹿 | 来源:发表于2017-02-05 16:07 被阅读28次

    公告

    如果您是第一次阅读我的设计模式系列文章,建议先阅读设计模式开篇,希望能得到您宝贵的建议。

    定义

    装饰者模式:即动态的给一个对象添加一些额外的职责。

    场景举例

    现在是2187年,智能机器人已经发展到可以一个新的高度。全球知名厂商 XX 正在推广新一代“机器人”,这次它的宣传口号是 “除了不能生孩子其它都可以做”,这满足了无数宅男们无尽的幻想。

    某天顾客Alice 进入你的店铺,正在准备挑选她的下一任“机器人女友”。Alice首先购买了机器人的原型,选择价格99999元的价位的机器人原型。机器人原型皮肤表面很光滑(想象下母鸡褪了毛的样子~~)。于是你顺理成章的向Alice介绍了,本店新推出的“外形套餐”。

    Alice希望他的机器人拥有 “大胸”、“肤白”、“瓜子脸”、“大眼睛”……(省略300个要求)。好吧,Alice并不算本店遇到的最挑剔的客户,但幸好我们拥有多于3000个可选方案,可供Alice挑选。

    在一番深思熟虑之后,Alice最终选择了,“C罩杯”、“瓜子脸”、“丰满”、“翘臀”的组合套餐,该“套餐包”额外收费3000元。确定付款之后。

    后台的3D打印机正在迅速的制造中,经历10分钟的漫长等待。Alice取到了他满意的机器人女友。

    程序员视角

    拆分角色

    • 被装饰的对象(被装饰者) —— 机器人原型
    • 可用于装饰的对象(装饰者) —— 肤色、胸型、身型、脸型等
    • 客户端 —— 顾客Alice

    重新描述该场景

    客户端(Alice)被装饰的对象(机器人原型) 添加了许多 装饰对象(肤色、胸型、脸型、身型)

    如何实现

    Bob看到了你的门店生意火爆,同样是做机器人生意的他觉得有必要改良下他的传统售卖模式。

    方案1 —— 最粗暴方式的实现

    Bob一番思考后心想,暂时想不到啥好办法。我先做出来,再慢慢思考有没有好办法。不然客人都要被你抢光了。

    
    public class Customer {
    
        public static void main(String[] args) {
            Machine machine = new Machine();
            machine.add(new Body("纤细"));
            machine.add(new Chest("C罩杯"));
            machine.add(new Butt("翘臀"));
            machine.add(new Face("瓜子脸"));
        }
    }
    
    public class Machine {
    
        public void add(Body value) {
            System.out.println(value);
        }
    
        public void add(Chest value) {
            System.out.println(value);
        }
    
        public void add(Butt value) {
            System.out.println(value);
        }
    
        public void add(Face value) {
            System.out.println(value);
        }
    
    }
    
    // 此处示意举例一个装饰对象,其他装饰对象与其结构一致
    public class Body {
    
        public String value;
    
        public Body(String value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "身型{" +
                    "value='" + value + '\'' +
                    '}';
        }
    }
    
    

    运行结果:

    添加身型{value='纤细'}
    添加胸型{value='C罩杯'}
    添加臀型{value='翘臀'}
    添加脸型{value='瓜子脸'}
    

    方案2 —— 隔离变化,抽象类型

    Bob很高兴的实现了方案1,但是总是有客户会提出新的奇葩的需求。每次客户提出新的需求的时候,Bob都要修改“机器人”的生产程序,来匹配外观的修改。才能把机器人的外观,预览给客户看。这样下来一天Bob只能服务几个客人,而对面的你的门店每天人进人出。

    于是Bob心想是否可以不改变机器人的源代码程序,修改其外观元素

    方案2有一个思想需要注意:把装饰物 装饰到 机器人身上,那么被装饰过的机器人依然是机器人。

    所以无论是装饰物,还是被装饰物都需要实现同一个接口IDecorComponent

    // 抽象装饰组件类型
    public interface IDecorComponent {
    
    }
    
    public class Machine {
    
        public void add(MachineContainer container) {
            System.out.println("TODO:遍历容器,并Machine添加功能");
        }
    
    }
    
    // 提取装饰容器,并去掉了对具体类型的耦合
    public class MachineContainer implements IDecorComponent {
    
        public void add(IDecorComponent component) {
            System.out.println("添加了" + component);
        }
    
    }
    // 装饰物实现了无方法的接口
    public class Body implements IDecorComponent {
    
        public String value;
    
        public Body(String value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "身型{" +
                    "value='" + value + '\'' +
                    '}';
        }
    
    }
    

    运行结果:

    添加了身型{value='纤细'}
    添加了胸型{value='C罩杯'}
    添加了臀型{value='翘臀'}
    添加了脸型{value='瓜子脸'}
    TODO:遍历容器,并Machine添加功能 // 纳尼?这里怎么还没做?
    机器人组装完成
    

    方案3 —— 装饰者模式

    Bob编译执行看了下结果,发现自己还遗漏了一个TODO还未做。
    他意识到一个重要的问题:

    无论是委托给 装饰容器还是机器人本身都避免不了要为机器人添加装饰物这事。

    既然在劫难逃,那就只能勇敢面对了。

    最终的方案:

    public class Customer {
    
        public static void main(String[] args) {
    
            Machine machine = new Machine();
            // 配置装饰容器
            IDecorComponent component = new MachineContainer(machine);
            component = new Body(component, "纤细");
            component = new Butt(component, "翘臀");
            component = new Chest(component, "C罩杯");
            component = new Face(component, "瓜子脸");
            // 触发添加行为
            component.addBehiavor();
        }
    }
    
    public interface IDecorComponent {
    
        void addBehiavor();
    }
    
    public class Machine {
        // 对Machine 没有任何侵入
    }
    
    public class MachineContainer implements IDecorComponent {
    
        private Machine machine;
    
        public MachineContainer(Machine machine) {
            this.machine = machine;
        }
    
        public void add(IDecorComponent component) {
            System.out.println("添加了" + component);
        }
    
        @Override
        public void addBehiavor() {
            System.out.println("===触发了装饰物的行为===");
        }
    }
    
    // 新增的类,用于委托递归操作
    public class Decor implements IDecorComponent {
    
        IDecorComponent component;
    
        public Decor(IDecorComponent component) {
            this.component = component;
        }
    
        @Override
        public void addBehiavor() {
            // 委托装饰的组件继续执行相关的行为
            this.component.addBehiavor();
        }
    }
    
    public class Body extends Decor {
    
        public String value;
    
        public Body(IDecorComponent component, String value) {
            super(component);
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "身型{" +
                    "value='" + value + '\'' +
                    '}';
        }
    
        @Override
        public void addBehiavor() {
            // 触发了递归操作
            super.addBehiavor();
            System.out.println("添加了" + toString());
        }
    }
    
    

    运行结果如下:

    ===触发了装饰物的行为===
    添加了身型{value='纤细'}
    添加了臀型{value='翘臀'}
    添加了胸型{value='C罩杯'}
    添加了脸型{value='瓜子脸'}
    

    总结

    装饰者模式

    装饰模式的本质:动态组合。

    应用装饰模式的注意点:

    各个装饰器之间最好是完全独立的功能,不要有依赖,这样在进行组合的时候才没有先后顺序的限制。否则会大大降低装饰组合的灵活度。

    装饰模式不仅可以增加功能,可以增加功能的访问(这点可以参考:职责链模式)。也可以限制功能的执行的先后顺序(递归的思想)。

    总之装饰模式是通过把复杂的功能简单化、分散化(注意:会产生比较多的子类)。然后再根据需求动态的组合这些功能(子类)。

    建议用法:在不影响其他对象的情况下,透明的添加职责时。


    相关文章

      网友评论

      • 幺鹿:想找群小伙伴互相勘误彼此的文章,站在不同的角度提点建议、批判,有意向的朋友可以私信我:smile:

      本文标题:装饰者模式(动态组合)

      本文链接:https://www.haomeiwen.com/subject/yjseittx.html