zoukankan      html  css  js  c++  java
  • 装饰器模式

    装饰器模式

    装饰器模式(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)
  • 相关阅读:
    兑奖
    杨辉三角
    偶数求和
    进制转化
    填词
    等值数目
    Spring框架的七个模块
    数据库中的第1、2、3范式 (昨天没睡好,因为那个蚊子~~)
    关于eclipse 不编译或者找不到*.class的问题
    servlet生命周期的理解
  • 原文地址:https://www.cnblogs.com/grj001/p/12223971.html
Copyright © 2011-2022 走看看