学习设计模式之Strategy
2007-08-25 16:13 莫耶 阅读(2622) 评论(8) 编辑 收藏 举报
颠颠儿跑到书城,弄了本《软件架构设计》,打算好好学习一下这方面的知识
有一个章节关于恶性依赖的讲述非常精彩:
“
需求改变了,原先的良性依赖变成了恶性依赖,但我们“只受一次愚弄”。比如,在开发一个需求跟踪工具的时候,起初可能仅需要支持保存为专有格式的“项目”文件,但后来又需要导出为HTML格式的网页。
按照敏捷软件开发过程,来讲述这个故事。
最开始的设计如下图所示,CReqMartixDoc调用CProjectSaver来保存自己。此时,所有需求就是支持“保存为专有格式的项目文件”,而且我们并没有预见到将来还需要以更多的形式保存,所以类CProjectSaver此时是“不易变的”,CReqMartixDoc对CProjectSaver的依赖是良性依赖,整个设计也是个“稳定的”设计。顺便说明,按照开放-封闭原则(Open-Closed Principle),这并不是一个好的设计;但按照当前的需求,这个设计却“不多不少”刚刚好,因为当前它是满足良性依赖原则的。
后来需求发生了变化,这个工具需要支持“导出为HTML格式的网页”的特性。是的,这个需求不管是客户新提出来的,还是设计人员在上一个迭代有意忽略了,总之在这个迭代周期需求发生了变化。于是,设计人员意识到,需求跟踪工具可能需要支持多种保存策略;如果不改变原来的设计,那么CProjectSaver就是“易变的”,因为它要支持不只一种新的保存策略。好了,如下图所示,设计虽然没有改变,但由于需求的改变,原来设计中的良性依赖,现在变成了恶性依赖,这意味着CReqMartixDoc可能也要随着CProjectSaver的改变而改变,这不是一个灵活的设计。
是的,代码出现了臭味(Smell),需要重构(Refactoring)。让我们谨遵Martin Fowler的教诲——不要将重构和添加新功能同时进行——这一步我们仅进行重构。我们要做的就是去除这个恶性依赖,采用依赖倒置原则(Dependency-Inversion Principle)惯用的“用两个抽象依赖代替一个具体依赖”策略,重构之后的设计如下图所示。我们引入了一个接口CDocSaver,然后让CProjectSaver实现这个接口。一个设计良好的接口无疑是“不易变的”,所以不管是CReqMartixDoc对CDocSaver的调用,还是CProjectSaver对CDocSaver的实现,都是良性依赖。
重构完毕。
哈,新的设计非常易于扩充,我们只需要新写一个CHtmlSaver来实现接口CDocSaver,就离支持“导出为HTML格式的网页”不远了,如下图所示。咦?原来是策略模式。
”
Wonderful~~!!!
看着过瘾,不如实践,从最Easy的方式尝试起是我的习惯。设计这样一个抽象类,它只有一个抽象方法getTextboxColor(),返回一个Color结构的值,但可以有若干的继承类来实现——获取具体的颜色值,come on~
在宿主环境类中,就可以灵活的调用,改动无非是实例化时,调用不同的策略代码族~
有一个章节关于恶性依赖的讲述非常精彩:
“
需求改变了,原先的良性依赖变成了恶性依赖,但我们“只受一次愚弄”。比如,在开发一个需求跟踪工具的时候,起初可能仅需要支持保存为专有格式的“项目”文件,但后来又需要导出为HTML格式的网页。
按照敏捷软件开发过程,来讲述这个故事。
最开始的设计如下图所示,CReqMartixDoc调用CProjectSaver来保存自己。此时,所有需求就是支持“保存为专有格式的项目文件”,而且我们并没有预见到将来还需要以更多的形式保存,所以类CProjectSaver此时是“不易变的”,CReqMartixDoc对CProjectSaver的依赖是良性依赖,整个设计也是个“稳定的”设计。顺便说明,按照开放-封闭原则(Open-Closed Principle),这并不是一个好的设计;但按照当前的需求,这个设计却“不多不少”刚刚好,因为当前它是满足良性依赖原则的。
后来需求发生了变化,这个工具需要支持“导出为HTML格式的网页”的特性。是的,这个需求不管是客户新提出来的,还是设计人员在上一个迭代有意忽略了,总之在这个迭代周期需求发生了变化。于是,设计人员意识到,需求跟踪工具可能需要支持多种保存策略;如果不改变原来的设计,那么CProjectSaver就是“易变的”,因为它要支持不只一种新的保存策略。好了,如下图所示,设计虽然没有改变,但由于需求的改变,原来设计中的良性依赖,现在变成了恶性依赖,这意味着CReqMartixDoc可能也要随着CProjectSaver的改变而改变,这不是一个灵活的设计。
是的,代码出现了臭味(Smell),需要重构(Refactoring)。让我们谨遵Martin Fowler的教诲——不要将重构和添加新功能同时进行——这一步我们仅进行重构。我们要做的就是去除这个恶性依赖,采用依赖倒置原则(Dependency-Inversion Principle)惯用的“用两个抽象依赖代替一个具体依赖”策略,重构之后的设计如下图所示。我们引入了一个接口CDocSaver,然后让CProjectSaver实现这个接口。一个设计良好的接口无疑是“不易变的”,所以不管是CReqMartixDoc对CDocSaver的调用,还是CProjectSaver对CDocSaver的实现,都是良性依赖。
重构完毕。
哈,新的设计非常易于扩充,我们只需要新写一个CHtmlSaver来实现接口CDocSaver,就离支持“导出为HTML格式的网页”不远了,如下图所示。咦?原来是策略模式。
”
Wonderful~~!!!
看着过瘾,不如实践,从最Easy的方式尝试起是我的习惯。设计这样一个抽象类,它只有一个抽象方法getTextboxColor(),返回一个Color结构的值,但可以有若干的继承类来实现——获取具体的颜色值,come on~
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
/// 设计模式演练
namespace DesignPattern
{
/// <summary>
/// 策略模式演练
/// </summary>
public abstract class GetColor
{
public abstract Color getTextboxColor();
}
/// <summary>
/// 实现获取蓝色
/// </summary>
public class GetBlue : GetColor
{
public override Color getTextboxColor()
{
return Color.Blue;
}
}
/// <summary>
/// 实现获取红色
/// </summary>
public class GetRed : GetColor
{
public override Color getTextboxColor()
{
return Color.Red;
}
}
}
using System.Collections.Generic;
using System.Text;
using System.Drawing;
/// 设计模式演练
namespace DesignPattern
{
/// <summary>
/// 策略模式演练
/// </summary>
public abstract class GetColor
{
public abstract Color getTextboxColor();
}
/// <summary>
/// 实现获取蓝色
/// </summary>
public class GetBlue : GetColor
{
public override Color getTextboxColor()
{
return Color.Blue;
}
}
/// <summary>
/// 实现获取红色
/// </summary>
public class GetRed : GetColor
{
public override Color getTextboxColor()
{
return Color.Red;
}
}
}
在宿主环境类中,就可以灵活的调用,改动无非是实例化时,调用不同的策略代码族~
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using DesignPattern;
/// <summary>
/// 环境类
/// 运用策略模式调用代码族
/// </summary>
public partial class 设计模式_DesignPattern : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// 蓝色策略
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btColor_Click(object sender, EventArgs e)
{
GetColor getcolor = new GetBlue();
lblColor.ForeColor = getcolor.getTextboxColor();
}
/// <summary>
/// 红色策略
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btColor2_Click(object sender, EventArgs e)
{
GetColor getcolor = new GetRed();
lblColor.ForeColor = getcolor.getTextboxColor();
}
}
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using DesignPattern;
/// <summary>
/// 环境类
/// 运用策略模式调用代码族
/// </summary>
public partial class 设计模式_DesignPattern : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// 蓝色策略
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btColor_Click(object sender, EventArgs e)
{
GetColor getcolor = new GetBlue();
lblColor.ForeColor = getcolor.getTextboxColor();
}
/// <summary>
/// 红色策略
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btColor2_Click(object sender, EventArgs e)
{
GetColor getcolor = new GetRed();
lblColor.ForeColor = getcolor.getTextboxColor();
}
}
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 Unported许可协议
欢迎转载,但必须保留文章的署名老莫的帐本子
并保留此链接:http://moye.cnblogs.com/
如有疑问请发邮件:moyerock@gmail.com
欢迎转载,但必须保留文章的署名老莫的帐本子
并保留此链接:http://moye.cnblogs.com/
如有疑问请发邮件:moyerock@gmail.com