热门问题
时间线
聊天
视角
修飾模式
来自维基百科,自由的百科全书
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