一.模式解说
以客户端透明的方式动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案。装饰者模式用意是要保持对象接口,增强对象性能.
实际生活中经常发现装饰者模式.比如,你需要装裱挂画,你不想讲画和画框定死以便更换不同的画框.下图就是一个装裱挂画的装饰者模式.
装饰者模式的使用时机:
1. 在不影响其他对象的情况下,动态透明的增加责任到一个对象.
2.希望责任和功能可以随时增加或取消.
3.当无法通过类继承来扩展功能时.
二.结构和用法
2.1 模式结构
装饰者模式结构如下图:
包含以下参与者:
1. 抽象部件(TComonent):定义一个接口可以动态的附加责任到其他对象.
2.具体部件(TConcreteComponent): 定义可以被附加责任的对象.
3.装饰者(TDecorator): 维护一个抽象部件对象的引用,并定义与抽象部件接口一致的接口,以便装饰抽象部件对象接口.
4.具体装饰者(TConcreteDecorator): 附加责任到抽象部件,完成具体的装饰.
2.2 代码模板(Delphi)
unit Decorator; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type Tcomponent = class(TObject) public procedure Operation; virtual; abstract; end; TConcreteComponent = class(Tcomponent) public procedure Operation; override; end; TDecorator = class(Tcomponent) private FComponent: TComponent; public constructor Create(Component: TComponent); procedure Operation;overload; override; end; TConcreteDecoratorA = class(TDecorator) private FAddedState: integer; public procedure Operation;override; end; TConcreteDecoratorB = class(TDecorator) public procedure AddedBehavior; procedure Operation;override; end; implementation { TConcreteComponent } procedure TConcreteComponent.Operation; begin //被装饰者部件的操作代码 end; { TDecorator } constructor TDecorator.Create(Component: TComponent); begin FComponent := Component; end; procedure TDecorator.Operation; begin if FComponent <> nil then FComponent.Operation; end; { TConcreteDecoratorA } procedure TConcreteDecoratorA.Operation; begin //装饰部件操作代码 end; { TConcreteDecoratorB } procedure TConcreteDecoratorB.AddedBehavior; begin //装饰者新增操作的代码 end; procedure TConcreteDecoratorB.Operation; begin //装饰部件操作代码 // 注意这里是装饰后已经扩展了对象的功能 inherited Operation; AddedBehavior; end; end.
2.3 问题讨论
三. 范例与实践
3.1 装饰者模式在图片观赏器中的应用
部分示例代码:
unit PicDecorator; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, ComCtrls, Jpeg; type TPicShow = class(TObject) public procedure Display(Owner: TForm; ImgFile: string); virtual; abstract; end; TPic = class(TPicShow) public procedure Display(Owner: TForm; ImgFile: string); override; end; TDecoratedPic = class(TPicShow) private FComponent: TPicShow; public constructor Create(PicShow: TPicShow); procedure Display(Owner: TForm; ImgFile: string); overload; override; end; TPicWithFrame = class(TDecoratedPic) private FAddedFrame: integer; public procedure Display(Owner: TForm; ImgFile: string); override; destructor Destroy; override; end; TPicWithMusic = class(TDecoratedPic) public destructor Destroy; override; procedure AddMusic; procedure Display(Owner: TForm; ImgFile: string); override; end; implementation { TPic } procedure TPic.Display(Owner: TForm; ImgFile: string); var Img : TImage; begin Img := TImage.Create(Owner); Img.Picture.LoadFromFile(ImgFile); Img.AutoSize := True; Img.Stretch := True; Owner.Width := Img.Width + 32; Owner.Height := Img.Height + 64; Owner.Caption := ImgFile; Img.Left := 11; Img.Top := 13; Img.Parent := Owner; end; { TDecoratedPic } constructor TDecoratedPic.Create(PicShow: TPicShow); begin Self.FComponent := PicShow; end; procedure TDecoratedPic.Display(Owner: TForm; ImgFile: string); begin if FComponent <> nil then FComponent.Display(Owner, ImgFile); end; { TPicWithFrame } destructor TPicWithFrame.Destroy; begin if FComponent <> nil then FComponent.Free; end; procedure TPicWithFrame.Display(Owner: TForm; ImgFile: string); var FrmOut: TBevel; FrmIn: TBevel; begin inherited Display(Owner, ImgFile); FrmIn := Tbevel.Create(Owner); FrmIn.Parent := Owner; FrmIn.Width := Owner.Width - 30; FrmIn.Height := Owner.Height - 62; FrmIn.Left := 10; FrmIn.Top := 12; FrmIn.Shape := bsBox; FrmIn.Style := bsLowered; FrmOut := Tbevel.Create(Owner); FrmOut.Parent := Owner; FrmOut.Width := Owner.Width - 18; FrmOut.Height := Owner.Height - 48; FrmOut.Left := 4; FrmOut.Top := 6; FrmOut.Shape := bsBox; FrmOut.Style := bsRaised; end; { TPicWithMusic } procedure TPicWithMusic.AddMusic; begin SndPlaySound('SONG.wav', SND_ASYNC or SND_NODEFAULT); end; destructor TPicWithMusic.Destroy; begin if Self.FComponent <> nil then Self.FComponent.Free; end; procedure TPicWithMusic.Display(Owner: TForm; ImgFile: string); begin inherited Display(Owner, ImgFile); AddMusic; end; end.