装饰器模式

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。
这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

介绍

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 

1、Component 类充当抽象角色,不应该具体实现。

2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 
1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。
2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。
在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
注意事项:可代替继承。

实现

我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。
然后我们创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape对象作为它的实例变量。
RedShapeDecorator 是实现了 ShapeDecorator 的实体类。
DecoratorPatternDemo,我们的演示类使用 RedShapeDecorator 来装饰 Shape 对象。

 

步骤 1

创建一个接口:

Shape.java

public interface Shape { 
    void draw(); 
}

步骤 2

创建实现接口的实体类。

Rectangle.java

public class Rectangle implements Shape {
    @Override 
    public void draw() { 
        System.out.println("Shape: Rectangle"); 
    } 
}

Circle.java

public class Circle implements Shape { 
    @Override 
    public void draw() { 
        System.out.println("Shape: Circle"); 
    }
}

步骤 3

创建实现了 Shape 接口的抽象装饰类。

ShapeDecorator.java

public abstract class ShapeDecorator implements Shape { 
    protected Shape decoratedShape; 
    public ShapeDecorator(Shape decoratedShape){ 
        this.decoratedShape = decoratedShape; 
    } 
    public void draw(){ 
        decoratedShape.draw(); 
    } 
}

步骤 4

创建扩展了 ShapeDecorator 类的实体装饰类。

RedShapeDecorator.java

public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { decoratedShape.draw(); setRedBorder(decoratedShape); } private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); } }

步骤 5

使用 RedShapeDecorator 来装饰 Shape 对象。

DecoratorPatternDemo.java

public class DecoratorPatternDemo { public static void main(String[] args) { Shape circle = new Circle(); Shape redCircle = new RedShapeDecorator(new Circle()); Shape redRectangle = new RedShapeDecorator(new Rectangle()); System.out.println("Circle with normal border"); circle.draw(); System.out.println("\nCircle of red border"); redCircle.draw(); System.out.println("\nRectangle of red border"); redRectangle.draw(); } }

步骤 6

执行程序,输出结果:

Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border Color: Red

 组合模式

外观模式 

 

4 篇笔记 写笔记

  1.    周霆

      598***063@qq.com

    一个更易理解的实例:

    装饰模式为已有类动态附加额外的功能就像LOL、王者荣耀等类Dota游戏中,英雄升级一样。每次英雄升级都会附加一个额外技能点学习技能。具体的英雄就是ConcreteComponent,技能栏就是装饰器Decorator,每个技能就是ConcreteDecorator;

    //Component 英雄接口 
    public interface Hero {
        //学习技能
        void learnSkills();
    }
    //ConcreteComponent 具体英雄盲僧
    public class BlindMonk implements Hero {
        
        private String name;
        
        public BlindMonk(String name) {
            this.name = name;
        }
    
        @Override
        public void learnSkills() {
            System.out.println(name + "学习了以上技能!");
        }
    }
    //Decorator 技能栏
    public class Skills implements Hero{
        
        //持有一个英雄对象接口
        private Hero hero;
        
        public Skills(Hero hero) {
            this.hero = hero;
        }
    
        @Override
        public void learnSkills() {
            if(hero != null)
                hero.learnSkills();
        }    
    }
    //ConreteDecorator 技能: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();
        }
    }
    //ConreteDecorator 技能: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();
        }
    }
    //ConreteDecorator 技能: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();
        }
    }
    //ConreteDecorator 技能: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();
        }
    }
    //客户端:召唤师
    public class Player {
        public static void main(String[] args) {
            //选择英雄
            Hero hero = new BlindMonk("李青");
            
            Skills skills = new Skills(hero);
            Skills r = new Skill_R(skills,"猛龙摆尾");
            Skills e = new Skill_E(r,"天雷破/摧筋断骨");
            Skills w = new Skill_W(e,"金钟罩/铁布衫");
            Skills q = new Skill_Q(w,"天音波/回音击");
            //学习技能
            q.learnSkills();
        }
    }

    输出:

    学习了技能Q:天音波/回音击
    学习了技能W:金钟罩/铁布衫
    学习了技能E:天雷破/摧筋断骨
    学习了技能R:猛龙摆尾
    李青学习了以上技能!
    周霆

       周霆

      598***063@qq.com

    2年前 (2017-10-02)
  2.    该用户昵称违规

      815***754@qq.com

    游戏里英雄皮肤的实现 是不是也比较适合装饰器模式

    public interface Hero {
        public void init();
    }
    
    public class victor implements Hero {
        @Override
        public void init() {
            System.out.println("维克托:输出型英雄 武器:步枪");
        }
    }
    
    public abstract class HeroDecorator implements Hero {
        private Hero heroDecorator;
    
        public HeroDecorator(Hero heroDecorator) {
            this.heroDecorator = heroDecorator;
        }
    
        @Override
        public void init() {
            heroDecorator.init();
        }
    }
    
    public class GalacticWarriors extends HeroDecorator {
        private Hero heroDecorator;
    
        public GalacticWarriors(Hero heroDecorator) {
            super(heroDecorator);
        }
    
        @Override
        public void init() {
            super.init();
            setSkin();
        }
    
        private void setSkin() {
            System.out.println("皮肤:银河战士套装");
        }
    }
    
    public class DecoratorPatternDemo {
        public static void main(String[] args) {
            Hero victor = new victor();
            GalacticWarriors galacticWarriors = new GalacticWarriors(victor);
            galacticWarriors.init();
        }
    }
    该用户昵称违规

       该用户昵称违规

      815***754@qq.com

    1年前 (2018-04-16)
  3.    郭为宇

      gos***02009986@gmail.com

    《HeadFirst 设计模式》里的装饰器模式的代码。

    package HeadFirst设计模式.装饰者模式_星巴兹订单系统;
     
    /**
     *区别于Shape,Beverage采用抽象类
     *通常装饰者模式是采用抽象类,但是在Java中可以使用接口
     */
    public abstract class Beverage {
        public String description = "Unknown Beverage";
     
        public String getDescription() {
            return description;
        }
     
        public abstract double cost();
    }
    package HeadFirst设计模式.装饰者模式_星巴兹订单系统.被装饰者;
    
    import HeadFirst设计模式.装饰者模式_星巴兹订单系统.Beverage;
    /**
     *被装饰类继承Beverage抽象类,最终会通过装饰者动态添加上新的行为
     */
    public class DarkRoast extends Beverage {
    
        public DarkRoast() {
            description = "DarkRoast";
        }
    
        @Override    
        public double cost() {
            return 0.99;
        }
    }
    package HeadFirst设计模式.装饰者模式_星巴兹订单系统.装饰者;
    import HeadFirst设计模式.装饰者模式_星巴兹订单系统.Beverage;
    
    /**
     *这是继承Beverage的抽象装饰者,接下来所有具体的装饰者都要继承CondimentDecorator
     */
    public abstract class CondimentDecorator extends Beverage {
    
        /**     
         *所有的调料装饰者都必须重新实现该方法,因为调料的该方法应该得到扩充,方法实现不同于原来方法     
         */    
        public abstract String getDescription();
    
    }
    package HeadFirst设计模式.装饰者模式_星巴兹订单系统.装饰者;
    
    import HeadFirst设计模式.装饰者模式_星巴兹订单系统.Beverage;
    
    /**
     *摩卡,是具体的装饰者
     *用一个实例变量记录饮料(被装饰者)
     *装饰者和被装饰者通过组合来增强功能,实现功能的扩展,用组合来替代继承
     */
    public class Mocha extends CondimentDecorator {
        Beverage beverage;
    
        public Mocha(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override    
        public String getDescription() {
            return beverage.getDescription() + ", Mocha";
        }
    
        @Override    
        public double cost() {
            return 0.20 + beverage.cost();
        }
    }

    测试:

    package HeadFirst设计模式.装饰者模式_星巴兹订单系统;
    
    import HeadFirst设计模式.装饰者模式_星巴兹订单系统.被装饰者.DarkRoast;
    import HeadFirst设计模式.装饰者模式_星巴兹订单系统.装饰者.Mocha;
    
    
    public class TestStarbuzzCoffee {
        public static void main(String[] args) {
            Beverage beverage1 = new DarkRoast();
            beverage1 = new Mocha(beverage1);
            beverage1 = new Mocha(beverage1);
            System.out.println(beverage1.getDescription()+ " $" + beverage1.cost());
        }
    }

    输出:

    /Library/.../Java/设计模式/bin HeadFirst设计模式.装饰者模式_星巴兹订单系统.TestStarbuzzCoffee
    DarkRoast, Mocha, Mocha $1.39
    
    Process finished with exit code 0
    郭为宇

       郭为宇

      gos***02009986@gmail.com

    6个月前 (11-06)
  4.    这不是小明

      xia***ng.chn@foxmail.com

    复杂些的实例

    在《绝地求生:刺激战场》游戏里面我们都知道。

    •  Kar 98K有5发子弹;
    •  装上弹匣后有10发子弹;
    •  装上4倍镜后可以进行4倍瞄准;
    •  装上8倍镜后可以进行4倍瞄准、8倍瞄准。

    枪具有开火功能:

    public interface Gun {
        /** * 开火直至打空子弹 */
        public void fire();
    }
    
    public class Kar98K implements Gun {
        @Override
        public void fire() {
            System.out.println("砰*5");
        }
    }

    装饰上弹匣变更枪开火功能:

    public abstract class AbstractMagazine implements Gun {
        private Gun gun;
    
        public AbstractMagazine(Gun gun) {
            this.gun = gun;
        }
    
        @Override
        public void fire() {
            gun.fire();
        }
    }
    
    public class Magazine extends AbstractMagazine {
        public Magazine(Gun gun) {
            super(gun);
        }
    
        @Override
        public void fire() {
            System.out.println("砰*10");
        }
    }
    测试:
    public class Demo {
        public static void main(String[] args) {
            System.out.println("[捡起一把98K]");
            Gun gun = new Kar98K();
            System.out.println("[开炮!]");
            gun.fire();
            System.out.println("[装饰上弹匣]");
            gun = new Magazine(gun);
            System.out.println("[开炮!]");
            gun.fire();
        }
    }

    输出:

    [捡起一把98K]
    [开炮!]
    砰*5
    [装饰上弹匣]
    [开炮!]
    砰*10

    现在我要装上4倍镜,使它具有4倍瞄准功能,这是枪支原本没有的功能。

    扩展枪支接口功能:

    public interface Aim4X extends Gun {
        public void aim4X();
    }
    
    public abstract class AbstractTelescope4X implements Aim4X {
        private Gun gun;
    
        public AbstractTelescope4X(Gun gun) {
            this.gun = gun;
        }
    
        @Override
        public void fire() {
            gun.fire();
        }
    }
    
    public class Telescope4X extends AbstractTelescope4X {
        public Telescope4X(Gun gun) {
            super(gun);
        }
    
        @Override
        public void aim4X() {
            System.out.println("已进入4倍瞄准模式");
        }
    }
    
    /** * 55式4倍镜 */
    public class Telescope4X55 extends AbstractTelescope4X {
        public Telescope4X55(Gun gun) {
            super(gun);
        }
    
        @Override
        public void aim4X() {
            System.out.println("4倍超级瞄准已部署");
        }
    }

    测试:

    public class Demo {
        public static void main(String[] args) {
            System.out.println("[捡起一把98K]");
            Gun gun = new Kar98K();
            System.out.println("[装饰上4倍镜]");
            Aim4X aim4X = new Telescope4X(gun);
            System.out.println("[4倍瞄准]");
            aim4X.aim4X();
            System.out.println("[开炮!]");
            aim4X.fire();
            System.out.println("[先装饰上弹匣]");
            gun = new Magazine(gun);
            System.out.println("[再装饰上4倍镜]");
            aim4X = new Telescope4X(gun);
            System.out.println("[4倍瞄准]");
            aim4X.aim4X();
            System.out.println("[开炮!]");
            aim4X.fire();
            System.out.println("[人体描边?换上我的55式4倍镜]");
            aim4X = new Telescope4X55(gun);
            System.out.println("[4倍瞄准]");
            aim4X.aim4X();
            System.out.println("[开炮!]");
            aim4X.fire();
        }
    }

    输出:

    [捡起一把98K]
    [装饰上4倍镜]
    [4倍瞄准]
    已进入4倍瞄准模式
    [开炮!]
    砰*5
    [先装饰上弹匣]
    [再装饰上4倍镜]
    [4倍瞄准]
    已进入4倍瞄准模式
    [开炮!]
    砰*10
    [人体描边?换上我的55式4倍镜]
    [4倍瞄准]
    4倍超级瞄准已部署
    [开炮!]
    砰*10

    现在我要装上8倍镜,它具有4倍瞄准功能,也具有8倍瞄准功能。

    扩展接口:

    public interface Aim8X extends Aim4X {
        public void aim8X();
    }
    
    public abstract class AbstractTelescope8X implements Aim8X {
        private Gun gun;
    
        public AbstractTelescope8X(Gun gun) {
            this.gun = gun;
        }
    
        @Override
        public void fire() {
            gun.fire();
        }
    }
    
    public class Telescope8X extends AbstractTelescope8X {
        public Telescope8X(Gun gun) {
            super(gun);
        }
    
        @Override
        public void aim8X() {
            System.out.println("进入8倍瞄准模式");
        }
    
        @Override
        public void aim4X() {
            System.out.println("进入4倍瞄准模式");
        }
    }

    测试:

    public class Demo {
        public static void main(String[] args) {
            System.out.println("[先装饰上弹匣]");
            gun = new Magazine(gun);
            System.out.println("[再装饰上8倍镜]");
            aim8X = new Telescope8X(gun);
            System.out.println("[8倍瞄准]");
            aim8X.aim8X();
            System.out.println("[敌人很近,换4倍]");
            aim8X.aim4X();
            System.out.println("[开炮!]");
            aim8X.fire();
        }
    }

    输出:

    [先装饰上弹匣]
    [再装饰上8倍镜]
    [8倍瞄准]
    进入8倍瞄准模式
    [敌人很近,换4倍]
    进入4倍瞄准模式 [开炮!] 砰*10这不是小明

       这不是小明

      xia***ng.chn@foxmail.com

    3个月前 (02-20)
posted @ 2019-09-19 22:49  grj001  阅读(50)  评论(0编辑  收藏  举报