定义
我觉得装饰者模式是在已有功能的基础之上,动态地添加更多 功能的一种方式,这些新加的代码装饰了原有类的 核心职责或主要行为。
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
实例
之前的项目中开发一款石油行业绘图软件,其中有图道和曲线绘制功能。基于同一组数据绘制的曲线略有不同。可以是线状图,也能是阶梯图装、杆状图,曲线还需要填充功能,可以左侧填充,右侧填充、及间填充等。填充的内容也有很多类,比如:石灰岩、白云岩、油气水等等。
如下图:
正好使用装饰者模式,在绘制曲线的同时,在不影响曲线绘制的前提下,根据用户需求动态增加各种额外的绘制功能。
废话不多说,上类图:
使用时候的代码:
代码语言:javascript复制 private ICurveDrawer GetCurveDrawerInstance()
{
ICurveDrawer loDrawer = null;
var curveDrawer = new CurveDrawer(this);
//曲线
switch (CurveStyle)
{
case ECurveStyle.Line:
loDrawer = curveDrawer;
switch (Overflow)
{
case EOverflowDrawType.Move://曲线移峰
loDrawer = new CurveMoveDrawer(this);
break;
case EOverflowDrawType.Mirror://曲线折峰
loDrawer = new CurveMirrorDrawer(this);
break;
}
break;
case ECurveStyle.Ladder:
loDrawer = new LadderCurveDrawer(this);
break;
case ECurveStyle.StickPlot:
loDrawer = new StickPlotCurveDrawer(this);
break;
case ECurveStyle.Point:
loDrawer = new PointCurveDrawer(this);
break;
case ECurveStyle.PointLine:
loDrawer = new PointCurveDrawer(this, curveDrawer);
break;
case ECurveStyle.StickPlotLine:
loDrawer = new StickPlotCurveDrawer(this, curveDrawer);
break;
case ECurveStyle.LeftFill:
loDrawer = new LeftFillCurveDrawer(this, curveDrawer);
break;
case ECurveStyle.RightFill:
loDrawer = new RightFillCurveDrawer(this, curveDrawer);
break;
case ECurveStyle.ClipFill:
loDrawer = new CurveFillCurveDrawer(this, curveDrawer);
break;
}
return loDrawer;
}
根据用户的选择不同,为用户添加不同的装饰者,进行曲线绘制。
总结一下该模式的使用:
当系统需要新功能的时候,要向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,这样会导致一些问题:主类中由于加了新的字段,新的方法以及新的逻辑,从而增加了主类的复杂度。
装饰模式提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为的时候,客户代码就可以在运行的时候根据需要有选择的、按顺序的使用装饰功能包装对象。
好处:能有有效的把类的核心职责和装饰功能区分开,而且可以去除相关类中重复的装饰逻辑,把类中的装饰功能从类中搬除,可以简化原来的类。
附代码:
代码语言:java复制/// <summary>
/// 曲线绘制装饰者
/// </summary>
public class CurveDrawerDecorator : CurveDrawerBase
{
private readonly ICurveDrawer _drawer;
protected CurveDrawerDecorator(Curve curve)
: base(curve)
{
}
protected CurveDrawerDecorator(Curve curve,ICurveDrawer drawer)
:base(curve)
{
this._drawer = drawer;
}
public override void Draw(Graphics g, PointF[] points, Pen pen, Brush brush)
{
if (_drawer != null)
{
var drawPoints = GetPointsInScrollBounds(points);
_drawer.Draw(g, drawPoints, pen, brush);
}
}
public override void DrawAlarm(Graphics g, PointF[] points, Pen pen, Brush brush)
{
if (_drawer != null)
{
var drawPoints = GetPointsInScrollBounds(points);
_drawer.DrawAlarm(g, drawPoints, pen, brush);
}
}
}
/// <summary>
/// 曲线间填充
/// </summary>
public class CurveFillCurveDrawer : CurveDrawerDecorator
{
public CurveFillCurveDrawer(Curve owner, ICurveDrawer drawer) :
base(owner, drawer)
{
}
public override void Draw(Graphics g, PointF[] points, Pen pen, Brush brush)
{
base.Draw(g, points, pen, brush);
var nextCurve = OwnerCurve.GetNextIndexCurve();
if (nextCurve == null || !nextCurve.Visible)
{
return;
}
var drawPoints = GetPointsInScrollBounds(points);
var rect = g.VisibleClipBounds;
rect.Intersect(OwnerCurve.Owner.Parent.CurShape.InnerRectangle);
var validPoints = OwnerCurve.GetVisiblePoints(drawPoints, rect, EFillType.Left);
using (var gp = new GraphicsPath())
{
gp.AddPolygon(validPoints.ToArray());
var nextValidPoints = OwnerCurve.GetVisiblePoints(GetPointsInScrollBounds(nextCurve.Points), rect, EFillType.Left);
using (var gpNext = new GraphicsPath())
{
gpNext.AddPolygon(nextValidPoints.ToArray());
var region = new Region(gp);
region.Xor(gpNext);
g.FillRegion(OwnerCurve.FillBrush, region);
region.Dispose();
gpNext.Dispose();
}
gp.Dispose();
}
}
}