• 设计模式第三篇-装饰者模式


    一、引言

    先看一个开发问题,很多人都玩过英雄联盟这款游戏:里面有各种英雄,每个英雄都有各自的技能(一般是4个主动技能),每升一级可以升级一个技能,但是可升级的技能不固定。我们需要通过技能状态来计算伤害,这个时候组合就非常多了(理论上是英雄数*技能数)。如果用继承来解决的话,那么子类就爆炸多了。

    除了继承还有一种设计,就是在基类上增加布尔变量,如Q,E等,然后提供一些has(get)和set方法来设置这些布尔值,子类里通过扩展计算伤害值,这个看起来是一个可行的设计,但这个设计也会有一些问题。

    1.每个技能可以多次加点,单纯靠布尔值是处理不了的。

    2.技能有可能会进行调整,那么我们就必须通过修改基类来处理。

    3.游戏里面还有装备这种情况,增加装备也是相当于多了技能(貌似用装备来做例子更合适)。。。

    我们用更好的方法来解决这个问题

    这个问题的本质是扩展,我们想要扩展一些功能,但是不想用继承。装饰者模式可以解决这个问题

    二、装饰者模式

    定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

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

    主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

    何时使用:在不想增加很多子类的情况下扩展类。

    如何解决:将具体功能职责划分,同时继承装饰者模式。

    装饰者通用类图:

    三、实现

    英雄联盟游戏实现

    //抽象基类
    public abstract class Hero {
        //学习技能
        public abstract void learnSkills();
    }
    
    //具体英雄兰博,需要被扩展的类
    public class Lanbo extends Hero {
        //英雄属性
        private String name;
    
        public Lanbo(String name){
            this.name = name;
        }
        @Override
        public void learnSkills() {
            System.out.println(name + "学习了以上技能!");
        }
    }
    
    //抽象技能类,装饰者的抽象基类
    public abstract class Skills extends Hero {
        private Hero hero;
    
        public Skills(Hero hero){
            this.hero = hero;
        }
    
        @Override
        public void learnSkills() {
            if(hero!=null){
                hero.learnSkills();
            }
        }
    }
    
    //具体装饰子类,用来装饰 Q技能
    public class Skill_Q extends Skills {
        private  String skillName;
    
        public Skill_Q(Hero hero,String skillName) {
            super(hero);
            this.skillName=skillName;
        }
        @Override
        public void learnSkills() {
            System.out.println("学习了技能Q:" +skillName);
            super.learnSkills();
        }
    }
    //具体装饰子类,用来装饰 W技能
    public class Skill_W extends Skills {
        private  String skillName;
    
        public Skill_W(Hero hero,String skillName) {
            super(hero);
            this.skillName=skillName;
        }
        @Override
        public void learnSkills() {
            System.out.println("学习了技能W:" +skillName);
            super.learnSkills();
        }
    }
    //具体装饰子类,用来装饰E技能
    public class Skill_E extends Skills {
        private  String skillName;
        public Skill_E(Hero hero,String skillName) {
            super(hero);
            this.skillName=skillName;
        }
        @Override
        public void learnSkills() {
            System.out.println("学习了技能E:" +skillName);
            super.learnSkills();
        }
    }
    //具体装饰子类,用来装饰R技能
    public class Skill_R extends Skills {
        private  String skillName;
    
        public Skill_R(Hero hero,String skillName) {
            super(hero);
            this.skillName=skillName;
        }
        @Override
        public void learnSkills() {
            System.out.println("学习了技能R:" +skillName);
            super.learnSkills();
        }
    }

    运行:

            //选择英雄
            Hero hero = new Lanbo("兰博");
            Skills q = new Skill_Q(hero,"纵火盛宴");
            Skills w = new Skill_W(q,"破碎护盾");
            Skills e = new Skill_E(w,"电子鱼叉");
            Skills r = new Skill_R(e,"恒温灼烧");
            //学习技能
            r.learnSkills();

    运行结果:

    四、总结

    优点

    1. 装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活
    2. 通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合
    3. 装饰者模式有很好地可扩展性

    缺点:装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。

    java.io类就是用的装饰者模式

  • 相关阅读:
    xcode 4.2 运行 4.3 simulator出错, dyld: Library not loaded: /usr/lib/libc++abi.dylib
    Mathematica 进阶 自定义抽象矩阵运算
    php正则表达式函数 preg_replace用法
    jQuery DIV圆角插件之jquery.corner.js jQuery插件怎么用?
    JQuery获取和设置Select选项方法
    javascript+css无刷新实现页面样式的更换
    Nginx 高性能的 HTTP 和 反向代理 服务器
    240多个jquery插件
    imgPreview在线预览
    Linux下查看apache连接数
  • 原文地址:https://www.cnblogs.com/yuanqinnan/p/10155806.html
Copyright © 2020-2023  润新知