装饰模式

2019/10/15 posted in  算法

装饰模式简介

装饰模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),结构性设计模式之一,其使用一种对客户端透明的方式来动态地扩展对象的功能,同时它也是继承关系的一种替代方案之一。

装饰模式的定义

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

装饰模式的使用场景

需要透明且动态地扩展类的功能时。

模式的简单实现

人总是要穿衣服的,我们将人定义为一个抽象类,将其穿衣的行为定义为一个抽象方法。

public abstract class Person {
    /**
     *  Person 下有一个穿着的抽象方法
     */
     public abstract void dressed(); 
}

该类就是抽象组件类,也就是我们需要装饰的原始对象,接下里实现一个具体的实现类。

public class Boy extends Person {
    @Override
    public void dressed() {
        // Boy类下dressed方法的基本逻辑
        System.out.println("穿了内衣内裤");
    }
}

Boy类继承于Person类,该类仅对Person中的dressed方法作了具体的实现,而Boy类则是我们所要装饰的具体对象,现在需要一个装饰者来装饰我们的这个Boy对象,这里定义一个PersonCloth类来表示人所穿着的衣服。

public abstract class PersonCloth extends Person {
    protected Person mPerson; //保持一个Person类的引用
    
    public PersonCloth(Person mPerson) {
        this.mPerson = mPerson;
    }
    
    @Override
    public void dressed() {
        //调用Person类中的dressed方法
        mPerson.dressed();
    }
}

在PersonCloth类中我们保持了一个对Person类的引用,可以方便地调用具体被装饰对象中的方法,这也是为什么我们可以在不破坏原类层次结构的情况下为类增加一些功能,我们只需要在被装饰对象的相应方法前或后增加相应的功能逻辑即可。

使用一个类ExpensiveCloth表示高档衣服

public class ExpensiveCloth extends PersonCloth {
    public ExpensiveCloth(Person person) {
        super(person);
    }
    
    /**
     * 穿短袖
     */
     private void dressShirt() {
        System.out.println("穿件短袖");
     } 
     
     /**
     * 穿皮衣
     */
     private void dressLeather() {
        System.out.println("穿件皮衣");
     }
     
     /**
     * 穿牛仔裤
     */
     private void dressJean() {
        System.out.println("穿条牛仔裤");
     }
     
     @Override
     public void dressed() {
        super.dressed();
        dressShirt();
        dressLeather();
        dressJean();
     }
}

使用CheapCloth表示便宜的衣服

public class CheapCloth extends PersonCloth {
    public CheapCloth(Person person) {
        super(person);
    }
    
    /**
     * 穿短裤
     */
     private void dressShorts() {
        System.out.println("穿条短裤");
     } 
        
     @Override
     public void dressed() {
        super.dressed();
        dressShorts();
     }
}

这两个类本质上没有区别,都是为Boy类中的dressed方法提供功能扩展,不过这个扩展并非是直接修改原有的方法逻辑或结构,更恰当地说,仅仅是在另一个类中将原有的方法和新逻辑进行封装整合。

在客户端中调用

public class Main {
    public static void main(String[] args) {
        //首先我们要有一个Person男孩
        Person person = new Boy();
        
        //然后为他穿上便宜的衣服
        PersonCloth clothCheap = new CheapCloth(person);
        clothCheap.dressed();
        
        //或穿上比较上档次的衣服
        PersonCloth clothExpensive = new ExpensiveCloth(person);
        clothExpensive.dressed();
    }
}

总结

装饰模式和代理模式有点类似,容易把装饰看作时代理。装饰模式是以对客户端透明的方法扩展对象的功能,是继承关系的一个替代方案;而代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用。装饰模式应该为所装饰的对象增强功能;代理模式对代理的对象施加控制,但不对对象本身的功能进行增强。