轉載---Dephi狀態模式(State模式)

uuxa發表於2007-09-10

對於物件導向開發來說,很長的函式和過程是不容易維護的,同樣冗長的if else 以及case of的條件判斷語句會使得程式碼不夠清晰,特別是當條件判斷很多時,比如有幾十條甚至十幾條條件判斷語句,會使得程式碼難以修改和維護。

在早期windows開發中,程式設計師經常需要編寫巨大的訊息判斷語句,根據訊息的不同呼叫不同的訊息處理函式,後來為了改善訊息處理的複雜程度,Delphi引入了message關鍵字,從編譯器一級對訊息處理進行了對映,減少了條件判斷語句的大量使用,這一案例就是巨大的條件判斷語句弊端的典型體現。

通常說來,if else 以及case 語句都是對物件的某個狀態或者屬性進行判斷,根據物件的狀態或屬性的不同,執行不同的操作。實際上就是一個有限狀態機,為了消除這些if else條件判斷語句,我們可以使用State模式來解決。

[@more@]

所謂狀態模式就是將宿主物件中每一種可能的狀態抽象成一個狀態類,當宿主物件的狀態發生變化時,宿主物件改變自己的狀態,並執行不同狀態類對應的不同操作。State模式的缺點,就是如果宿主物件的狀態比較多的話,會產生大量的小粒度的物件,顯得物件過多,體系不緊湊。

另外,State模式的模型圖和策略模式的模型圖幾乎一樣,區別在於狀態模式的特點是狀態會經常發生變化,而策略模式一般來說在使用的時候通常是固定的,不會頻繁變化。另外兩個模式的目的也不同,對狀態改變時,物件具有不同的行為進行抽象,對應於State模式。而策略模式則是為了解決演算法的互換問題的。

狀態模式使用示例

在Delphi的demosdocgraphex目錄下,提供了一個畫圖程式的例子,執行後可以根據當前繪圖工具的不同,繪製不同的形狀,其中核心的繪圖部分就是一個大的新增判斷過程DrawShape,根據當前繪圖工具的不同(線、圓、矩形、圓角矩形),繪製不同的形狀,程式碼如下:

procedure TForm1.DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);

begin
  with Image.Canvas do
  begin
    Pen.Mode := AMode;
    case DrawingTool of
      dtLine:
        begin
          Image.Canvas.MoveTo(TopLeft.X, TopLeft.Y);
          Image.Canvas.LineTo(BottomRight.X, BottomRight.Y);
        end;
      dtRectangle: Image.Canvas.Rectangle(TopLeft.X, TopLeft.Y, BottomRight.X,
        BottomRight.Y);
      dtEllipse: Image.Canvas.Ellipse(Topleft.X, TopLeft.Y, BottomRight.X,
        BottomRight.Y);
      dtRoundRect: Image.Canvas.RoundRect(TopLeft.X, TopLeft.Y, BottomRight.X,
        BottomRight.Y, (TopLeft.X - BottomRight.X) div 2,
        (TopLeft.Y - BottomRight.Y) div 2);
    end;
  end;
end;
下面我們就對這一例子使用State模式進行改造,消除Case判斷

修改後的程式碼如下:

type
 
  TDrawingTool = class(TObject)
  private
    FImage: TImage;
  public
    constructor Create(AImage: TImage);
    procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode); virtual;
      abstract;
  end;
 
  TLineTool = class(TDrawingTool)
    procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
      override;
  end;
 
  TRectTool = class(TDrawingTool)
    procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
      override;
  end;
 
  TRoundRectTool = class(TDrawingTool)
    procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
      override;
  end;
 
  TEllipseTool = class(TDrawingTool)
    procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
      override;
  end;
 
  TForm1 = class(TForm)
  private
    FDrawingTool: TDrawingTool;
    procedure SetDrawingTool(const Value: TDrawingTool);
    { Private declarations }
  public
    { Public declarations }
    procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
    property DrawingTool: TDrawingTool read FDrawingTool write SetDrawingTool;
  end;
 
 
procedure TForm1.LineButtonClick(Sender: TObject);
begin
  FreeAndNil(FDrawingTool);
  DrawingTool := TLineTool.Create(Image);
end;
 
procedure TForm1.RectangleButtonClick(Sender: TObject);
begin
  FreeAndNil(FDrawingTool);
  DrawingTool := TRectTool.Create(Image);
end;
 
procedure TForm1.EllipseButtonClick(Sender: TObject);
begin
  FreeAndNil(FDrawingTool);
  DrawingTool := TEllipseTool.Create(Image);
end;
 
procedure TForm1.RoundRectButtonClick(Sender: TObject);
begin
  FreeAndNil(FDrawingTool);
  DrawingTool := TRoundRectTool.Create(Image);
end;
 
procedure TForm1.DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
begin
  DrawingTool.DrawShape(TopLeft, BottomRight, AMode);
end;
 
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  LineButton.OnClick(nil);
end;
 
 
{ TEllipseTool }
 
procedure TEllipseTool.DrawShape(TopLeft, BottomRight: TPoint;
  AMode: TPenMode);
begin
  with FImage.Canvas do
  begin
    Pen.Mode := AMode;
    Ellipse(Topleft.X, TopLeft.Y, BottomRight.X,
      BottomRight.Y);
  end;
end;
 
{ TRoundRectTool }
 
procedure TRoundRectTool.DrawShape(TopLeft, BottomRight: TPoint;
  AMode: TPenMode);
begin
  with FImage.Canvas do
  begin
    Pen.Mode := AMode;
    RoundRect(TopLeft.X, TopLeft.Y, BottomRight.X,
      BottomRight.Y, (TopLeft.X - BottomRight.X) div 2,
      (TopLeft.Y - BottomRight.Y) div 2);
  end;
end;
 
{ TRectTool }
 
procedure TRectTool.DrawShape(TopLeft, BottomRight: TPoint;
  AMode: TPenMode);
begin
  with FImage.Canvas do
  begin
    Pen.Mode := AMode;
    Rectangle(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);
  end;
end;
 
{ TLineTool }
 
procedure TLineTool.DrawShape(TopLeft, BottomRight: TPoint;
  AMode: TPenMode);
begin
  with FImage.Canvas do
  begin
    Pen.Mode := AMode;
    MoveTo(TopLeft.X, TopLeft.Y);
    LineTo(BottomRight.X, BottomRight.Y);
  end;
end;
 
procedure TForm1.SetDrawingTool(const Value: TDrawingTool);
begin
  FDrawingTool := Value;
end;
 
{ TDrawingTool }
 
constructor TDrawingTool.Create(AImage: TImage);
begin
  FImage := AImage;
end;
 
end.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7416120/viewspace-968816/,如需轉載,請註明出處,否則將追究法律責任。

相關文章