结构型模式-装饰者模式

Posted vbirdbest

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构型模式-装饰者模式相关的知识,希望对你有一定的参考价值。

一:装饰者模式定义

在不改变现有对象结构的情况下,动态的给改对象增加一些职责(即增加其额外功能)的模式。
装饰者即继承某个类,又持有某个类的引用。

装饰(Decorator)模式中的角色:

  • 抽象构件
  • 具体构件
  • 抽象装饰角色
  • 具体装饰角色

二:案例

FastFood(快餐):抽象类,拥有价格、描述两个属性和一个抽象计算价格的方法。

/**
 * 快餐(抽象构件角色)
 */
public abstract class FastFood 
    /** 价格 */
    private float price;
    /** 描述 */
    private String desc;

    public FastFood() 
    

    public FastFood(float price, String desc) 
        this.price = price;
        this.desc = desc;
    

    /**
     * 计算价格
     */
    public abstract float cost();


    public float getPrice() 
        return price;
    

    public void setPrice(float price) 
        this.price = price;
    

    public String getDesc() 
        return desc;
    

    public void setDesc(String desc) 
        this.desc = desc;
    



FriedRice:炒饭,属于快餐的一种,一个炒饭定义单价10元。

/**
 * 炒饭(具体构件角色)
 */
public class FriedRice extends FastFood 

    public FriedRice() 
        super(10, "炒饭");
    

    @Override
    public float cost() 
        return getPrice();
    

FastFood fastFood = new FriedRice();
// 炒饭10.0元
System.out.println(fastFood.getDesc() + fastFood.cost() + "元");
  • new FriedRice() : 调用父类构造函数 super(10, "炒饭"),将价格和描述传到FastFood中的price属性和desc属性。
  • fastFood.cost() -> FriedRice#cost() -> FastFood#getPrice() 最终获取到10元。

Gamish:装饰者类,即继承了快餐类FastFood,同时也持有快餐类的引用fastFood,该类也是一个抽象类没有实现快餐类的抽象方法cost()。

/**
 * 装饰者类(抽象装饰者角色)
 */
public abstract class Gamish extends FastFood 

    private FastFood fastFood;

    public Gamish(FastFood fastFood, float price, String desc) 
        super(price, desc);
        this.fastFood = fastFood;
    

    public FastFood getFastFood() 
        return fastFood;
    

    public void setFastFood(FastFood fastFood) 
        this.fastFood = fastFood;
    


Egg:鸡蛋,一个鸡蛋1元,继承装饰者类,提供一个有参构造函数,参数为快餐类。

/**
 * 鸡蛋(具体的装饰者角色)
 */
public class Egg extends Gamish 

	// 有参构造函数需要提供快餐的主食
    public Egg(FastFood fastFood) 
        super(fastFood, 1, "鸡蛋");
    

    @Override
    public float cost() 
        // 快餐价格 + 鸡蛋价格
        return this.getFastFood().cost() + getPrice();
    

    @Override
    public String getDesc() 
        return super.getDesc() + this.getFastFood().getDesc();
    


public static void main(String[] args) 
    FastFood fastFood = new FriedRice();
    // 炒饭10.0元
    System.out.println(fastFood.getDesc() + fastFood.cost() + "元");

    fastFood = new Egg(fastFood);
    // 鸡蛋炒饭11.0元
    System.out.println(fastFood.getDesc() + fastFood.cost() + "元");

  1. new Egg(fastFood) -> super(fastFood, 1, "鸡蛋"):将fastFood作为属性值保存下来,继续调用FastFood的构造方法super(price, desc):将价格和描述保存到FastFood类的price和desc属性上。
  2. fastFood.cost() -> this.getFastFood().cost() + getPrice():获取fastFood即FriedRice.cost()=10元 + getPrice() = 1元 = 11元。

Bacon:培根,一个培根2元,在程序上也属于快餐的一种。

/**
 * 培根(具体的装饰者角色)
 */
public class Bacon extends Gamish 
    public Bacon(FastFood fastFood) 
        super(fastFood, 2, "培根");
    

    @Override
    public float cost() 
        // 快餐价格 + 鸡蛋价格
        return getFastFood().cost() + getPrice();
    

    @Override
    public String getDesc() 
        return super.getDesc() + getFastFood().getDesc();
    


public class Client 
    public static void main(String[] args) 
        FastFood fastFood = new FriedRice();
        // 炒饭10.0元
        System.out.println(fastFood.getDesc() + fastFood.cost() + "元");

        fastFood = new Egg(fastFood);
        // 鸡蛋炒饭11.0元
        System.out.println(fastFood.getDesc() + fastFood.cost() + "元");

        fastFood = new Bacon(fastFood);
        // 培根鸡蛋炒饭13.0元
        System.out.println(fastFood.getDesc() + fastFood.cost() + "元");
    

public class Bacon extends Gamish 
    @Override
    public String getDesc() 
    	// super.getDesc()=培根 + getFastFood().getDesc()鸡蛋炒饭
    	// 此时getFastFood()=Egg
        return super.getDesc() + getFastFood().getDesc();
    


public class Egg extends Gamish 

    @Override
    public String getDesc() 
    	// super.getDesc()=鸡蛋 + this.getFastFood().getDesc()=炒饭
    	// 此时this.getFastFood()=FriedRice
        return super.getDesc() + this.getFastFood().getDesc();
    


public abstract class FastFood 
    /** 描述 */
    private String desc;

    public String getDesc() 
        return desc;
     


三:装饰者模式的好处

  • 装饰者模式可以带来比集成更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承具有更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
  • 装饰类和被装饰类可以独立发展,不会相互耦合(例如增加一个炒河粉和增加一个胡萝卜都互不影响),装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

四:使用场景

  • 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时,不能采用继承的情况主要有两类:
    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,是的子类数目成爆炸性增长。
    • 第二类是因为类定义不能继承(如 final).
  • 在不影响其它对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 当对象的功能要求可以动态的添加,也可以再动态的撤销时。

五:装饰模式在JDK中的使用

public static void main(String[] args) throws Exception 
     FileWriter fileWriter = new FileWriter("/text.txt");
     BufferedWriter writer = new BufferedWriter(fileWriter);
 

BufferedWriter 继承Writer,并持有Writer的引用,BufferedWriter使用装饰者模式对Writer子实现进行了增强,添加了缓冲器,提高了写数据的效率。

public class BufferedWriter extends Writer 
    private Writer out;
    

六:装饰者和静态代理的区别

  • 相同点:
    • 都要实现与目标类相同的业务接口。
    • 在两个类中都要声明目标对象。
    • 都可以在不修改目标类的 前提下增强目标方法。
  • 不同点:
    • 目的不同:
      • 装饰者是为增强目标对象, 可以无限制的层层增强;
      • 静态代理是为了保护和隐藏目标对象。
    • 获取目标对象构建的地方不同:
      • 装饰者是由外界传递进来的,可以通过构造方法传递;
      • 静态代理是在代理类内部创建的,以此来隐藏目标对象。
    • 类之间的关系不一样
      • 静态代理对象和目标对象没有什么继承关系;
      • 装饰者一般用在多个类,并且所有类都继承同一个父类。

以上是关于结构型模式-装饰者模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式 结构型模式 -- 装饰者模式(概述 & 快餐店案例 & 模式优点 & 使用场景 & 源码解析 & 和代理模式的区别)

设计模式----结构型模式之代理模式和装饰者模式的区别

深入理解设计模式-装饰者模式

深入理解设计模式-装饰者模式

设计模式之装饰者模式

装饰者模式