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

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

举个例子来说,在一个视窗软件系统中呈现的一个视窗,为了让视窗中呈现的内容资料能够卷动,我们希望给它添加水平或垂直滚动条。假设窗口通过Window
类实例来表示,并且假设它没有添加滚动条功能。我们可以创建一个子类ScrollingWindow
来提供,或者我们可以创建一个ScrollingWindowDecorator
来为已存在的Window
对象添加这个功能。在这点上,只要是解决方案就可以了。
现在我们假设希望选择给我们的窗口添加边框,同样,我们的原始Window
类不支持。ScrollingWindow
子类现在会造成一个问题,因为它会有效的创建一种新的窗口。如果我们想要给所有窗口添加边框,我们必须创建WindowWithBorder
和ScrollingWindowWithBorder
子类。显然,这个问题由于被添加类而变得更糟了。对于修饰模式,我们简单的创建一个新类BorderedWindowDecorator
,在运行时,我们能够使用ScrollingWindowDecorator
或BorderedWindowDecorator
或两者结合来修饰已存在的窗口。
一个修饰能够被应用的另一个好例子是当有需要根据某套规则或者几个平行的规则集(不同的用户凭据等)限制访问对象的属性或方法时。
一个对象的属性或方法按照某组规则或几个并行规则(不同用户证书等)需要限制访问时,在这种情况下,不是在原始对象中实现访问控制而是在他的使用中不变或不知道任何限制,并且他被包装在一个访问控制修饰对象中,这个对象能够对允许的原始对象的接口子集服务。
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++代码示例:
#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
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
Go代码示例:
package main
import "fmt"
// step1: 编写基础功能,刚开始不需要定义接口
type Base struct {
}
func (b *Base) Call() string {
return "base is called"
}
// step2: 将上面的方法声明为接口类型,基础功能中的 Call() 调用自动满足下面的接口
type DecoratorI interface {
Call() string
}
// step3: 编写新增功能,结构中保存接口类型的参数
type Decorator struct {
derorator DecoratorI
}
func (d *Decorator) Call() string {
return "decorator: " + d.derorator.Call()
}
func main() {
base := &Base{}
fmt.Println(base.Call())
decorator := Decorator{base}
fmt.Println(decorator.Call())
}
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 __init__(self, coffee):
super().__init__(coffee)
def cost(self):
return super().cost() + 0.5
def ingredients(self):
return f'{super().ingredients()}, Milk'
class WithSprinkles(CoffeeDecorator):
def __init__(self, coffee):
super().__init__(coffee)
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例子[2],在0-255的随机数的8x10方阵中,展示了通过修饰者流水线,给对象动态的附加上行为:
import random
class RandomSquare():
def __init__(self, seed=None):
random.seed(seed)
def get(self, x, y):
assert 0 <= x < 8 and 0 <= y <10
return random.randint(0, 255)
class CacheDecorator():
def __init__(self, decorated):
self.decorated = decorated
self.cache = [None]*10*8
def get(self, x, y):
r = self.cache[x*10 + y]
if r == None:
r = self.decorated.get(x, y)
self.cache[x*10 + y] = r
return r
class MinMaxDecorator():
def __init__(self, decorated, minval, maxval):
self.decorated = decorated
self.minval = minval
self.maxval = maxval
def get(self, x, y):
r = self.decorated.get(x, y)
r = r if r > self.minval else self.minval
return r if r < self.maxval else self.maxval
class VisibilityDecorator():
def __init__(self, decorated):
self.decorated = decorated
def get(self, x, y):
return self.decorated.get(x, y)
def draw(self):
for x in range(8):
for y in range(10):
print(f"{self.get(x, y):3d}", end=' ')
print()
square = RandomSquare()
square = CacheDecorator(square)
square = MinMaxDecorator(square, 55, 200)
square = VisibilityDecorator(square)
其执行:
>>> square.draw()
192 55 58 55 107 59 129 200 55 200
87 181 55 197 55 55 113 58 87 165
196 55 200 87 200 100 200 149 143 200
164 158 178 55 200 200 195 94 200 191
187 90 159 200 153 65 191 180 200 200
200 55 151 200 162 89 188 55 55 89
200 55 82 83 64 119 200 55 55 200
129 200 55 136 97 160 200 55 132 108
Remove ads
参考资料
外部链接
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads