修饰模式

在面向对象编程领域中，修饰模式（），是一种动态地往一个類別中添加新的行为的设计模式。就功能而言，修饰模式相比生成子類別更为灵活，这样可以给某个对象而不是整个類別添加一些功能。

 介绍 
通过使用修饰模式，可以在运行时扩充一个類別的功能。原理是：增加一个修饰类包裹原来的類別，包裹的方式是在修饰类的构造函数中将原来的类以参数的形式传入。装饰类实现新的功能，但是，在不需要用到新功能的地方，它可以直接调用原来的類別中的方法。修饰类必须和原来的類別有相同的接口。

修饰模式是類別继承的另外一种选择。类继承在编译时候增加行为，而装饰模式是在运行时增加行为。

当有几个相互独立的功能需要扩充时，这个区别就变得很重要。在有些面向对象的编程语言中，類別不能在运行时被创建，通常在设计的时候也不能预测到有哪几种功能组合。这就意味著要为每一种组合创建一个新類別。相反，修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。修饰模式的典型的应用，是Java里的I/O Streams的实现。

 动机 
舉個例子來說，在一個視窗軟體系統中呈現的一個視窗，為了讓視窗中呈現的內容資料能夠捲動，人们希望给它添加水平或垂直滚动条。假设窗口通过Window类实例来表示，并且假设它没有添加滚动条功能。人们可以创建一个子类ScrollingWindow来提供，或者可以创建一个ScrollingWindowDecorator来为已存在的Window对象添加这个功能。在这点上，只要是解决方案就可以了。

假设人们希望选择给的窗口添加边框，同样，原始Window类不支持。ScrollingWindow子类现在会造成一个问题，因为它会有效的创建一种新的窗口。如果人们想要给所有窗口添加边框，必须创建WindowWithBorder和ScrollingWindowWithBorder子类。显然，这个问题由于被添加类而变得更糟了。对于修饰模式，简单的创建一个新类BorderedWindowDecorator，在运行时，能够使用ScrollingWindowDecorator或BorderedWindowDecorator或两者结合来修饰已存在的窗口。

一个修饰能够被应用的另一个好例子是当有需要根据某套规则或者几个平行的规则集（不同的用户凭据等）限制访问对象的属性或方法时。一个对象的属性或方法按照某组规则或几个并行规则(不同用户证书等)需要限制访问时，在这种情况下，不是在原始对象中实现访问控制而是在他的使用中不变或不知道任何限制，并且他被包装在一个访问控制修饰对象中，这个对象能够对允许的原始对象的接口子集服务。

结构

在上面的UML类图中： 
抽象类Decorator维护到被修饰的对象（Component）的引用（component）并转发请求至它（component.operation()）。这使得Decorator透明（不可见）于Component的客户。
子类（Decorator1,Decorator2），实现应当增加到Component的额外行为（addBehavior()）于转发请求给它之前/之后。

序类图展示运行时交互：
Client对象协作于Decorator1和Decorator2对象来扩展Component1对象的功能。
Client调用operation()于Decorator1之上，它转发请求至Decorator2。Decorator2在转发请求至Component1之后进行进行addBehavior()，接着返回到Decorator1，它进行addBehavior()并返回到Client。

代码示例
C++
下面是基于《设计模式》书中前C++98实现的C++23实现例子：

import std;

using std::unique_ptr;

// Beverage interface.
class Beverage {
public:
    virtual void drink() = 0;
    virtual ~Beverage() = default;
};

// Drinks which can be decorated.
class Coffee: public Beverage {
public:
    virtual void drink() override {
        std::print("Drinking Coffee");
    }
};

class Soda: public Beverage {
public:
    virtual void drink() override {
        std::print("Drinking Soda");
    }
};

class BeverageDecorator: public Beverage {
private:
    unique_ptr<Beverage> component;
protected:
    void callComponentDrink() {
        if (component) {
            component->drink();
        }
    }
public:
    BeverageDecorator() = delete;

    explicit BeverageDecorator(unique_ptr<Beverage> component): 
        component{std::move(component)} {}

    virtual void drink() = 0;
};

class Milk: public BeverageDecorator {
private:
    float percentage;
public:
    Milk(unique_ptr<Beverage> component, float percentage):
        BeverageDecorator(std::move(component)), percentage{percentage} {}

    virtual void drink() override {
        callComponentDrink();
        std::print(", with milk of richness {}%", percentage);
    }
};

class IceCubes: public BeverageDecorator {
private:
    int count;
public:
    IceCubes(unique_ptr<Beverage> component, int count):
        BeverageDecorator(std::move(component)), count{count} {}

    virtual void drink() override {
        callComponentDrink();
        std::print(", with {} ice cubes", count);
    }
};

class Sugar: public BeverageDecorator {
private:
    int spoons = 1;
public:
    Sugar(unique_ptr<Beverage> component, int spoons):
        BeverageDecorator(std::move(component)), spoons{spoons} {}

    virtual void drink() override {
        callComponentDrink();
        std::print(", with {} spoons of sugar", spoons);
    }
};

int main(int argc, char* argv[]) { 
    unique_ptr<Beverage> soda = std::make_unique<Soda>();
    soda = std::make_unique<IceCubes>(std::move(soda), 3);
    soda = std::make_unique<Sugar>(std::move(soda), 1);

    soda->drink();
    std::println();

    unique_ptr<Beverage> coffee = std::make_unique<Coffee>();
    coffee = std::make_unique<IceCubes>(std::move(coffee), 16);
    coffee = std::make_unique<Milk>(std::move(coffee), 3.);
    coffee = std::make_unique<Sugar>(std::move(coffee), 2);

    coffee->drink();

    return 0;
}

程序的输出如下：

Drinking Soda, with 3 ice cubes, with 1 spoons of sugar
Drinking Coffee, with 16 ice cubes, with milk of richness 3%, with 2 spoons of sugar

Java
这个Java示例使用window/scrolling情境。
// The Window interface class
public interface Window {
	public void draw(); // Draws the Window
	public String getDescription(); // Returns a description of the Window
}

// implementation of a simple Window without any scrollbars
public class SimpleWindow implements Window {
	public void draw() {
		// Draw window
	}

	public String getDescription() {
		return "simple window";
	}
}

以下类包含所有Window类的decorator，以及修饰类本身。
// abstract decorator class - note that it implements Window
public abstract class WindowDecorator implements Window {
    protected Window decoratedWindow; // the Window being decorated

    public WindowDecorator (Window decoratedWindow) {
        this.decoratedWindow = decoratedWindow;
    }

    @Override
    public void draw() {
        decoratedWindow.draw();
    }

    @Override
    public String getDescription() {
        return decoratedWindow.getDescription();
    }
}

// The first concrete decorator which adds vertical scrollbar functionality
public class VerticalScrollBar extends WindowDecorator {
	public VerticalScrollBar(Window windowToBeDecorated) {
		super(windowToBeDecorated);
	}

	@Override
	public void draw() {
		super.draw();
		drawVerticalScrollBar();
	}

	private void drawVerticalScrollBar() {
		// Draw the vertical scrollbar
	}

	@Override
	public String getDescription() {
		return super.getDescription() + ", including vertical scrollbars";
	}
}

// The second concrete decorator which adds horizontal scrollbar functionality
public class HorizontalScrollBar extends WindowDecorator {
	public HorizontalScrollBar (Window windowToBeDecorated) {
		super(windowToBeDecorated);
	}

	@Override
	public void draw() {
		super.draw();
		drawHorizontalScrollBar();
	}

	private void drawHorizontalScrollBar() {
		// Draw the horizontal scrollbar
	}

	@Override
	public String getDescription() {
		return super.getDescription() + ", including horizontal scrollbars";
	}
}

以下是一个测试程序，它创建了一个包含多重装饰的Window实例(如,包含了垂直的和水平的滚动条),然后输出它的描述：

public class Main {
	// for print descriptions of the window subclasses
	static void printInfo(Window w) {
		System.out.println("description:"+w.getDescription());
	}
	public static void main(String[] args) {
		// original SimpleWindow
		SimpleWindow sw = new SimpleWindow();
		printInfo(sw);
		// HorizontalScrollBar  mixed Window
		HorizontalScrollBar hbw = new HorizontalScrollBar(sw);
		printInfo(hbw);
		// VerticalScrollBar mixed Window
		VerticalScrollBar vbw = new VerticalScrollBar(hbw);
		printInfo(vbw);
	}
}

以下是SimpleWindow及添加了组件HorizontalScrollBar和VerticalScrollBar后的Window测试结果：

description:simple window
description:simple window, including horizontal scrollbars
description:simple window, including horizontal scrollbars, including vertical scrollbars

C#
C#代码示例:
namespace DecoratorExample;

interface IBike {
    string GetDetails();
    double GetPrice();
}

class AluminiumBike : IBike {
    public double GetPrice() => 100.0;

    public string GetDetails() => "Aluminium Bike";
}

class CarbonBike : IBike {
    public double GetPrice() => 1000.0;

    public string GetDetails() => "Carbon";
}

abstract class BikeAccessories : IBike {
    private readonly IBike _bike;

    public BikeAccessories(IBike bike) {
        _bike = bike;
    }

    public virtual double GetPrice() => _bike.GetPrice();

    public virtual string GetDetails() => _bike.GetDetails();
}

class SecurityPackage : BikeAccessories {
    public SecurityPackage(IBike bike) : base(bike) {
        // ...
    }

    public override string GetDetails() => $"{base.GetDetails()} + Security Package";

    public override double GetPrice() => base.GetPrice() + 1;
}

class SportPackage : BikeAccessories {
    public SportPackage(IBike bike) : base(bike) {
        // ...
    }

    public override string GetDetails() => $"{base.GetDetails()} + Sport Package";

    public override double GetPrice() => base.GetPrice() + 10;
}

public class BikeShop {
    public void UpgradeBike() {
        AluminiumBike basicBike = new AluminiumBike();
        BikeAccessories upgraded = new SportPackage(basicBike);
        upgraded = new SecurityPackage(upgraded);

        Console.WriteLine($"Bike: '{upgraded.GetDetails()}' Cost: {upgraded.GetPrice()}");
    }

    static void Main(string[] args) {
        UpgradeBike();
    }
}
其输出：
Bike: 'Aluminium Bike + Sport Package + Security Package' Cost: 111

 Python 
下面的Python例子，通过咖啡制作场景展示如何使用修饰者，这个例子的场景中仅包含费用和配料。
from abc import ABC, abstractmethod

class Component(ABC):
    @abstractmethod
    def cost(self): pass
    @abstractmethod
    def ingredients(self): pass
    def info(self):
        print(f'Cost: {self.cost()}; '
            + f'Ingredients: {self.ingredients()}')

class Coffee(Component):
    def cost(self):
        return 1
    def ingredients(self):
        return 'Coffee'

class Decorator(Component):
    def __init__(self, component):
        self.component = component
    def cost(self):
        return self.component.cost()
    def ingredients(self):
        return self.component.ingredients()

class WithMilk(Decorator):
    def cost(self):
        return super().cost() + 0.5
    def ingredients(self):
        return f'{super().ingredients()}, Milk'

class WithSprinkles(Decorator):
    def cost(self):
        return super().cost() + 0.2
    def ingredients(self):
        return f'{super().ingredients()}, Sprinkles'
其执行：
>>> coffee = Coffee()
>>> coffee.info()
Cost: 1; Ingredients: Coffee
>>> coffee = WithMilk(coffee)
>>> coffee.info()
Cost: 1.5; Ingredients: Coffee, Milk
>>> coffee = WithSprinkles(coffee)
>>> coffee.info()
Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles

下面的Python例子，在0-255的随机数的8x10方阵中，展示了通过修饰者流水线，给对象动态的附加上行为：
from abc import ABC, abstractmethod
from random import Random

class RandomSquare():
    def __init__(self, row, col, seed=None):
        self.random = Random(seed)
        self.row = row
        self.col = col
    def __call__(self, x, y):
        assert 0 <= x < self.row and 0 <= y < self.col
        return self.random.randint(0, 255)

class Decorator(ABC):
    def __init__(self, previous):
        self.previous = previous
        self.component = (previous.component
            if hasattr(previous, 'component') else previous)
    @abstractmethod
    def __call__(self): pass

class CacheValue(Decorator):
    def __init__(self, previous):
        super().__init__(previous)
        n = self.component.col * self.component.row
        self.cache = [None]*n
    def __call__(self, x, y):
        i = self.component.col*x + y
        r = self.cache[i]
        if r is None:
            r = self.previous(x, y)
            self.cache[i] = r
        return r

class MinMaxValue(Decorator):
    def __init__(self, previous, minval, maxval):
        super().__init__(previous)
        self.minval = minval
        self.maxval = maxval
    def __call__(self, x, y):
        r = self.previous(x, y)
        r = (r if r > self.minval else self.minval)
        return (r if r < self.maxval else self.maxval)

class Visibility(Decorator):
    def __call__(self, x, y):
        return self.previous(x, y)
    def draw(self):
        for x in range(self.component.row):
            for y in range(self.component.col):
                print(f"{self.__call__(x, y):3d}", end=' ')
            print()

square = RandomSquare(8, 10)
square = CacheValue(square)
square = MinMaxValue(square, 55, 200)
square = Visibility(square)
其执行：
>>> square(0, 0)
62
>>> square.draw()
 62 115  55 200  55 200 119 179  94 200 
200  88 115 200 200 200 200 113  55 157 
 55 200 200 200 124 183 195  55 200 188 
 55 200  55 200 183 150 127 200 200 200 
 90 200  55  89 168 183 165 112 200 110 
 55 200  55  88 103 200  55 200 157 114 
 55  55  81 194  83  59 200 200 112 200 
177  55  55 167 200 126 200 178  55 161 

参见
责任链模式
适配器模式

参考资料

 外部链接
 Article "The Decorator Design Pattern" (Java) by Antonio García and Stephen Wong
 Article "Using the Decorator Pattern" (Java) by Budi Kurniawan
 Sample Chapter "C# Design Patterns: The Decorator Pattern" (C#) by James W. Cooper
 A Delphi approach (Delphi)
 A JavaScript implementation
 Jt J2EE Pattern Oriented Framework
 Decorator Pattern with Ruby in 8 Lines (Ruby)

Category:软件设计模式