代码改变世界

学习设计模式之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~

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