修飾模式

修飾模式,是物件導向程式領域中,一種動態地往一個類別中添加新的行為的設計模式。就功能而言,修飾模式相比生成子類別更為靈活,這樣可以給某個對象而不是整個類別添加一些功能。[1]

UML 修飾模式的UML類圖

介紹

通過使用修飾模式,可以在運行時擴充一個類別的功能。原理是:增加一個修飾類包裹原來的類別,包裹的方式是在修飾類的構造函數中將原來的類以參數的形式傳入。裝飾類實現新的功能,但是,在不需要用到新功能的地方,它可以直接調用原來的類別中的方法。修飾類必須和原來的類別有相同的接口。

修飾模式是類別繼承的另外一種選擇。類繼承在編譯時候增加行為,而裝飾模式是在運行時增加行為。

當有幾個相互獨立的功能需要擴充時,這個區別就變得很重要。在有些物件導向的程式語言中,類別不能在運行時被創建,通常在設計的時候也不能預測到有哪幾種功能組合。這就意味著要為每一種組合創建一個新類別。相反,修飾模式是面向運行時候的對象實例的,這樣就可以在運行時根據需要進行組合。一個修飾模式的示例是JAVA里的Java I/O Streams的實現。

動機

舉個例子來說,在一個視窗軟體系統中呈現的一個視窗,為了讓視窗中呈現的內容資料能夠捲動,我們希望給它添加水平或垂直滾動條。假設窗口通過「Window」類實例來表示,並且假設它沒有添加滾動條功能。我們可以創建一個子類「ScrollingWindow」來提供,或者我們可以創建一個ScrollingWindowDecorator來為已存在的Window對象添加這個功能。在這點上,只要是解決方案就可以了。 現在我們假設希望選擇給我們的窗口添加邊框,同樣,我們的原始Window類不支持。ScrollingWindow子類現在會造成一個問題,因為它會有效的創建一種新的窗口。如果我們想要給所有窗口添加邊框,我們必須創建WindowWithBorderScrollingWindowWithBorder子類。顯然,這個問題由於被添加類而變得更糟了。對於修飾模式,我們簡單的創建一個新類BorderedWindowDecorator,在運行時,我們能夠使用ScrollingWindowDecoratorBorderedWindowDecorator或兩者結合來修飾已存在的窗口。 一個修飾能夠被應用的另一個好例子是當有需要根據某套規則或者幾個平行的規則集(不同的用戶憑據等)限制訪問對象的屬性或方法時。

一個對象的屬性或方法按照某組規則或幾個並行規則(不同用戶證書等)需要限制訪問時,在這種情況下,不是在原始對象中實現訪問控制而是在他的使用中不變或不知道任何限制,並且他被包裝在一個訪問控制修飾對象中,這個對象能夠對允許的原始對象的接口子集服務。

應用

Java IO 流為典型的裝飾模式。

代碼示例

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++代碼示例:

#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;
}

C#

C#代碼示例:

namespace GSL_Decorator_pattern
{
	interface IWindowObject
	{
		void Draw(); // draws the object
		string GetDescription(); // returns a description of the object
	}

	class ControlComponent : IWindowObject
	{
		public ControlComponent()
		{
		}

		public void Draw() // draws the object
		{
			Console.WriteLine( "ControlComponent::draw()" ); 
		}

		public string GetDescription() // returns a description of the object
		{
			return "ControlComponent::getDescription()";
		}
	}

	abstract class Decorator : IWindowObject
	{
		protected IWindowObject _decoratedWindow = null; // the object being decorated

		public Decorator( IWindowObject decoratedWindow)
		{
			_decoratedWindow = decoratedWindow;
		}

		public virtual void Draw()
		{
			_decoratedWindow.Draw();
			Console.WriteLine("\tDecorator::draw() ");
		}

		public virtual string GetDescription() // returns a description of the object
		{
			return _decoratedWindow.GetDescription() + "\n\t" + "Decorator::getDescription() ";
		}
	}

	// the first decorator 
	class DecorationA : Decorator
	{
		public DecorationA(IWindowObject decoratedWindow) : base(decoratedWindow)
		{
		}

		public override void Draw()
		{
			base.Draw();
			DecorationAStuff();
		}

		private void DecorationAStuff()
		{
			Console.WriteLine("\t\tdoing DecorationA things");
		}

		public override string GetDescription()
		{
			return base.GetDescription() + "\n\t\tincluding " + this.ToString();
		}

	}// end  class ConcreteDecoratorA : Decorator

	class DecorationB : Decorator
	{
		public DecorationB(IWindowObject decoratedWindow) : base(decoratedWindow)
		{
		}

		public override void Draw()
		{
			base.Draw();
			DecorationBStuff();
		}

		private void DecorationBStuff()
		{
			Console.WriteLine("\t\tdoing DecorationB things");
		}

		public override string GetDescription()
		{
			return base.GetDescription() + "\n\t\tincluding " + this.ToString();
		}

	}// end  class DecorationB : Decorator

	class DecorationC : Decorator
	{
		public DecorationC(IWindowObject decoratedWindow) : base(decoratedWindow)
		{
		}

		public override void Draw()
		{
			base.Draw();
			DecorationCStuff();
		}

		private void DecorationCStuff()
		{
			Console.WriteLine("\t\tdoing DecorationC things");
		}

		public override string GetDescription()
		{
			return base.GetDescription() + "\n\t\tincluding " + this.ToString();
		}

	}// end  class DecorationC : Decorator

}// end of namespace GSL_Decorator_patternpublic class MyClass

// Main
IWindowObject controlComponent = new ControlComponent();
controlComponent = new DecorationA(controlComponent);
controlComponent = new DecorationB(controlComponent);
controlComponent.Draw();

Go

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())
}

參考資料

  1. ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. 设计模式:可复用面向对象软件的基础. 北京: 機械工業出版社. 2000: 115 [2013-02-17]. ISBN 9787111075752 (中文). 

外部連結