热门问题
时间线
聊天
视角
修饰模式
来自维基百科,自由的百科全书
Remove ads
在面向对象编程领域中,修饰模式(英語:decorator pattern),是一种动态地往一个類別中添加新的行为的设计模式。就功能而言,修饰模式相比生成子類別更为灵活,这样可以给某个对象而不是整个類別添加一些功能。[1]

介绍
通过使用修饰模式,可以在运行时扩充一个類別的功能。原理是:增加一个修饰类包裹原来的類別,包裹的方式是在修饰类的构造函数中将原来的类以参数的形式传入。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的類別中的方法。修饰类必须和原来的類別有相同的接口。
修饰模式是類別继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。
当有几个相互独立的功能需要扩充时,这个区别就变得很重要。在有些面向对象的编程语言中,類別不能在运行时被创建,通常在设计的时候也不能预测到有哪几种功能组合。这就意味著要为每一种组合创建一个新類別。相反,修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。修饰模式的典型的应用,是Java里的I/O Streams的实现。
动机

舉個例子來說,在一個視窗軟體系統中呈現的一個視窗,為了讓視窗中呈現的內容資料能夠捲動,人们希望给它添加水平或垂直滚动条。假设窗口通过Window
类实例来表示,并且假设它没有添加滚动条功能。人们可以创建一个子类ScrollingWindow
来提供,或者可以创建一个ScrollingWindowDecorator
来为已存在的Window
对象添加这个功能。在这点上,只要是解决方案就可以了。
假设人们希望选择给的窗口添加边框,同样,原始Window
类不支持。ScrollingWindow
子类现在会造成一个问题,因为它会有效的创建一种新的窗口。如果人们想要给所有窗口添加边框,必须创建WindowWithBorder
和ScrollingWindowWithBorder
子类。显然,这个问题由于被添加类而变得更糟了。对于修饰模式,简单的创建一个新类BorderedWindowDecorator
,在运行时,能够使用ScrollingWindowDecorator
或BorderedWindowDecorator
或两者结合来修饰已存在的窗口。
一个修饰能够被应用的另一个好例子是当有需要根据某套规则或者几个平行的规则集(不同的用户凭据等)限制访问对象的属性或方法时。一个对象的属性或方法按照某组规则或几个并行规则(不同用户证书等)需要限制访问时,在这种情况下,不是在原始对象中实现访问控制而是在他的使用中不变或不知道任何限制,并且他被包装在一个访问控制修饰对象中,这个对象能够对允许的原始对象的接口子集服务。
Remove ads
结构

- 抽象类
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++代码示例:
#include <iostream>
using namespace std;
/* Component (interface) */
class Widget {
public:
virtual void draw() = 0;
virtual ~Widget() {}
};
/* ConcreteComponent */
class TextField : public Widget {
private:
int width, height;
public:
TextField(int w, int h){
width = w;
height = h;
}
void draw() {
cout << "TextField: " << width << ", " << height << '\n';
}
};
/* Decorator (interface) */
class Decorator : public Widget {
private:
Widget* wid; // reference to Widget
public:
Decorator(Widget* w) {
wid = w;
}
void draw() {
wid->draw();
}
~Decorator() {
delete wid;
}
};
/* ConcreteDecoratorA */
class BorderDecorator : public Decorator {
public:
BorderDecorator(Widget* w) : Decorator(w) { }
void draw() {
Decorator::draw();
cout << " BorderDecorator" << '\n';
}
};
/* ConcreteDecoratorB */
class ScrollDecorator : public Decorator {
public:
ScrollDecorator(Widget* w) : Decorator(w) { }
void draw() {
Decorator::draw();
cout << " ScrollDecorator" << '\n';
}
};
int main(void)
{
Widget* aWidget =
new BorderDecorator(
new BorderDecorator(
new ScrollDecorator(
new TextField(80, 24))));
aWidget->draw();
delete aWidget;
}
Remove ads
这个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
Remove ads
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
Remove ads
下面的Python例子,通过咖啡制作场景展示如何使用修饰者,这个例子的场景中仅包含费用和配料。
from abc import ABC, abstractmethod
class Coffee(ABC):
@abstractmethod
def cost(self): pass
@abstractmethod
def ingredients(self): pass
def info(self):
print(f'Cost: {self.cost()}; '
+ f'Ingredients: {self.ingredients()}')
class SimpleCoffee(Coffee):
def cost(self):
return 1
def ingredients(self):
return 'Coffee'
class CoffeeDecorator(Coffee):
def __init__(self, coffee):
self.decorated_coffee = coffee
def cost(self):
return self.decorated_coffee.cost()
def ingredients(self):
return self.decorated_coffee.ingredients()
class WithMilk(CoffeeDecorator):
def cost(self):
return super().cost() + 0.5
def ingredients(self):
return f'{super().ingredients()}, Milk'
class WithSprinkles(CoffeeDecorator):
def cost(self):
return super().cost() + 0.2
def ingredients(self):
return f'{super().ingredients()}, Sprinkles'
其执行:
>>> coffee = SimpleCoffee()
>>> 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例子[3],在0-255的随机数的8x10方阵中,展示了通过修饰者流水线,给对象动态的附加上行为:
from abc import ABC, abstractmethod
from weakref import ref
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, prev):
self.prev = prev
self.decor = (prev.decor
if hasattr(prev, 'decor') else ref(prev))
@abstractmethod
def __call__(self): pass
class CacheDecorator(Decorator):
def __init__(self, prev):
super().__init__(prev)
n = self.decor().col*self.decor().row
self.cache = [None]*n
def __call__(self, x, y):
i = self.decor().col*x + y
r = self.cache[i]
if r is None:
r = self.prev(x, y)
self.cache[i] = r
return r
class MinMaxDecorator(Decorator):
def __init__(self, prev, minval, maxval):
super().__init__(prev)
self.minval = minval
self.maxval = maxval
def __call__(self, x, y):
r = self.prev(x, y)
r = (r if r > self.minval else self.minval)
return (r if r < self.maxval else self.maxval)
class VisibilityDecorator(Decorator):
def __call__(self, x, y):
return self.prev(x, y)
def draw(self):
for x in range(self.decor().row):
for y in range(self.decor().col):
print(f"{self.__call__(x, y):3d}", end=' ')
print()
square = RandomSquare(8, 10)
square = CacheDecorator(square)
square = MinMaxDecorator(square, 55, 200)
square = VisibilityDecorator(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
Remove ads
参考资料
外部链接
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads