作者:姜敏 来源:博客园 酷勤网收集 2008-04-22
摘要
在OOP中代码应该遵守开放-关闭的原则:即对修改关闭,对扩展开放。策略模式是除了继承之外的一种弹性替代方案,如果你使用继承定义了一个类,下面有部分的派生类,此时你会让基类所困住,要想修改它特别不容易,而策略模式则可能通过组合不同的对象来改变行为。
本来园子里面已经有很多特别好的关于设计模式学习的文章,但一般都是概念性的或者都是些简单实例,
没有用过设计模式的朋友看过之后虽然明白怎么回事了,但是对于自己实际项目中何时用设计模式就不
太清楚了,本人借jillzhang开发的控件中的部分代码与大家讨论下模式在具体项目中的应用,好的代码应该
与大家分享.
本人并不想为了模式而谈模式,这次有机会在jillzhang 的一篇文章中看到有类似策略模式的应用,虽然不是特别的应用,但是思路应该是一样的。我们好多朋友看到别人用设计模式自己也想用,但是有的时候并不是你想用就能用到的,只有特定的项目用上设计模式才能发挥它的作用,否则只能说是杀鸡焉用牛刀。
在他的文章中给GRIDVIEW添加上数据导出成EXCEL,WORD,PDF文件的功能,这三类导出虽然具体操作略有不同,但是大部分都相同。
在OOP中代码应该遵守开放---- 关闭的原则:即对修改关闭,对扩展开放。
策略模式是除了继承之外的一种弹性替代方案,如果你使用继承定义了一个类,下面有部分的派生类,此时你会让基类所困住,要想修改它特别不容易,而策略模式则可能通过组合不同的对象来改变行为。
具体策略模式的类图:

具体代码如下:
第一:数据导出的接口类:IExporter
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

Code

/**//// <summary>
/// 将GridView导出到其他文件的接口
/// </summary>
public interface IExporter

{

/**//// <summary>
/// 输出编码
/// </summary>

Encoding ResponseEncoding
{ get;}


/**//// <summary>
/// 导出文件的名称
/// </summary>

string ExportFileName
{ get;}


/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
void Export(GridViewEx grid);

}
下面为实现了此接口的抽象类

Code
public abstract class Exporter:IExporter

{

private fields#region private fields
Encoding _responseEncoding;
string _exportFileName;
#endregion


my constructors#region my constructors
public Exporter(Encoding encoding, string outName)

{
this._responseEncoding = encoding;
this._exportFileName = outName;
}
#endregion


public property#region public property

/**//// <summary>
/// 输出编码
/// </summary>
public Encoding ResponseEncoding

{
get

{
return _responseEncoding;
}
}

/**//// <summary>
/// 导出文件的名称
/// </summary>
public string ExportFileName

{
get

{
return _exportFileName;
}
}
#endregion



public methods#region public methods

/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
public abstract void Export(GridViewEx grid);
#endregion


private methods#region private methods
protected string ReplaceHref(string html)

{
string content = Regex.Replace(html, "(<a[^>]+>)|(</a>)", "");
return content;
}
protected virtual void Export(GridViewEx grid, string extension, string contentType)

{
//具体代码略
}
#endregion
}
上面抽象策略(Strategy)角色用了一个接口和一个抽象类来共同完成.也可以合并成一个.
第二:context
环境(Context)角色:持有一个Strategy类的引用。
Code
DropDownList drop = dropList;
if (drop != null)

{
if (drop.SelectedIndex > 0)

{
switch (drop.SelectedValue)

{
case "Excel":

{
IExporter exporter = new ExcelExporter(Encoding.GetEncoding("gb2312"), this._exportFileName);
ExporterHelper.Export(Owner, exporter);
break;
}
case "Word":

{
IExporter exporter = new WordExporter(Encoding.GetEncoding("gb2312"), this._exportFileName);
ExporterHelper.Export(Owner, exporter);
break;
}
case "Pdf":

{
IExporter exporter = new PdfExporter(Encoding.GetEncoding("gb2312"), this._exportFileName);
ExporterHelper.Export(Owner, exporter);
break;
}
}
}
}
大家看过这个方法后一定会说:这哪是context啊,哈哈,的确,它并像一般的策略模式中的客户调用,我只是就原文章中的代码讨论而已,在这个方法中,只不过是把三个客户调用放在了一个方法中,由下拉控件来集成。可以说是一个加强的context类。
第三:具体实现类
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
下面是三个具体操作类

Code

/**//// <summary>
/// Excel导出
/// </summary>
public class ExcelExporter:Exporter

{

private fields#region private fields
static readonly string extension = ".xls";
static readonly string contentType = "application/vnd.xls";
#endregion


my constructors#region my constructors
public ExcelExporter(Encoding encoding,string outName):base(encoding,outName)

{
}
#endregion



public methods#region public methods

/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
public override void Export(GridViewEx grid)

{
Export(grid, extension, contentType);
}
#endregion
PdfExporter:

Code
public class PdfExporter : Exporter

{
static readonly string extension = ".pdf";
static readonly string contentType = "application/pdf";
public PdfExporter(Encoding encoding, string outName)
: base(encoding, outName)

{

}
public override void Export(GridViewEx grid)

{
//具体代码略
}
}
WordExporter:

Code

/**//// <summary>
/// Word导出
/// </summary>
public class WordExporter:Exporter

{

private fields#region private fields
static readonly string extension = ".doc";
static readonly string contentType = "application/ms-word";
#endregion


my constructors#region my constructors
public WordExporter(Encoding encoding, string outName)
: base(encoding, outName)

{
}
#endregion


public methods#region public methods

/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
public override void Export(GridViewEx grid)

{
Export(grid, extension, contentType);
}
#endregion

}
没有用过设计模式的朋友看过之后虽然明白怎么回事了,但是对于自己实际项目中何时用设计模式就不
太清楚了,本人借jillzhang开发的控件中的部分代码与大家讨论下模式在具体项目中的应用,好的代码应该
与大家分享.
本人并不想为了模式而谈模式,这次有机会在jillzhang 的一篇文章中看到有类似策略模式的应用,虽然不是特别的应用,但是思路应该是一样的。我们好多朋友看到别人用设计模式自己也想用,但是有的时候并不是你想用就能用到的,只有特定的项目用上设计模式才能发挥它的作用,否则只能说是杀鸡焉用牛刀。
在他的文章中给GRIDVIEW添加上数据导出成EXCEL,WORD,PDF文件的功能,这三类导出虽然具体操作略有不同,但是大部分都相同。
在OOP中代码应该遵守开放---- 关闭的原则:即对修改关闭,对扩展开放。
策略模式是除了继承之外的一种弹性替代方案,如果你使用继承定义了一个类,下面有部分的派生类,此时你会让基类所困住,要想修改它特别不容易,而策略模式则可能通过组合不同的对象来改变行为。
具体策略模式的类图:

具体代码如下:
第一:数据导出的接口类:IExporter
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

/**//// <summary>
/// 将GridView导出到其他文件的接口
/// </summary>
public interface IExporter
{
/**//// <summary>
/// 输出编码
/// </summary>
Encoding ResponseEncoding
{ get;}

/**//// <summary>
/// 导出文件的名称
/// </summary>
string ExportFileName
{ get;}

/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
void Export(GridViewEx grid);

}
public abstract class Exporter:IExporter
{
private fields#region private fields
Encoding _responseEncoding;
string _exportFileName;
#endregion

my constructors#region my constructors
public Exporter(Encoding encoding, string outName)
{
this._responseEncoding = encoding;
this._exportFileName = outName;
}
#endregion

public property#region public property
/**//// <summary>
/// 输出编码
/// </summary>
public Encoding ResponseEncoding
{
get
{
return _responseEncoding;
}
}
/**//// <summary>
/// 导出文件的名称
/// </summary>
public string ExportFileName
{
get
{
return _exportFileName;
}
}
#endregion


public methods#region public methods
/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
public abstract void Export(GridViewEx grid);
#endregion

private methods#region private methods
protected string ReplaceHref(string html)
{
string content = Regex.Replace(html, "(<a[^>]+>)|(</a>)", "");
return content;
}
protected virtual void Export(GridViewEx grid, string extension, string contentType)
{
//具体代码略
}
#endregion
}第二:context
环境(Context)角色:持有一个Strategy类的引用。
DropDownList drop = dropList;
if (drop != null)
{
if (drop.SelectedIndex > 0)
{
switch (drop.SelectedValue)
{
case "Excel":
{
IExporter exporter = new ExcelExporter(Encoding.GetEncoding("gb2312"), this._exportFileName);
ExporterHelper.Export(Owner, exporter);
break;
}
case "Word":
{
IExporter exporter = new WordExporter(Encoding.GetEncoding("gb2312"), this._exportFileName);
ExporterHelper.Export(Owner, exporter);
break;
}
case "Pdf":
{
IExporter exporter = new PdfExporter(Encoding.GetEncoding("gb2312"), this._exportFileName);
ExporterHelper.Export(Owner, exporter);
break;
}
}
}
}大家看过这个方法后一定会说:这哪是context啊,哈哈,的确,它并像一般的策略模式中的客户调用,我只是就原文章中的代码讨论而已,在这个方法中,只不过是把三个客户调用放在了一个方法中,由下拉控件来集成。可以说是一个加强的context类。
第三:具体实现类
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
下面是三个具体操作类

/**//// <summary>
/// Excel导出
/// </summary>
public class ExcelExporter:Exporter
{
private fields#region private fields
static readonly string extension = ".xls";
static readonly string contentType = "application/vnd.xls";
#endregion

my constructors#region my constructors
public ExcelExporter(Encoding encoding,string outName):base(encoding,outName)
{
}
#endregion


public methods#region public methods
/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
public override void Export(GridViewEx grid)
{
Export(grid, extension, contentType);
}
#endregionPdfExporter:
public class PdfExporter : Exporter
{
static readonly string extension = ".pdf";
static readonly string contentType = "application/pdf";
public PdfExporter(Encoding encoding, string outName)
: base(encoding, outName)
{
}
public override void Export(GridViewEx grid)
{
//具体代码略
}
}WordExporter:

/**//// <summary>
/// Word导出
/// </summary>
public class WordExporter:Exporter
{
private fields#region private fields
static readonly string extension = ".doc";
static readonly string contentType = "application/ms-word";
#endregion

my constructors#region my constructors
public WordExporter(Encoding encoding, string outName)
: base(encoding, outName)
{
}
#endregion

public methods#region public methods
/**//// <summary>
/// 将GirdView导出为其他格式的文件
/// </summary>
/// <param name="grid">要导出的GridView对象</param>
/// <param name="unExportedColumnNames">不导出的列名称集合</param>
public override void Export(GridViewEx grid)
{
Export(grid, extension, contentType);
}
#endregion
} 是不是在项目中一定要用过模式才算是好项目呢?我的答案是否定的.
个人理由:
第一:你的项目在此功能上是不是会有改动的可能。
小项目一般开发成本有限,开发周期短,如果不存在在修改扩展,那么用设计模式只是形式上的,并无实际好处。
第二:设计模式应用上较一般用法要有难度,不容易掌握,除非实在解决不了,才考虑用设计模式,否则越简单的
实现方法越是有效。
总之:设计模式的有效应用能够给我们的开发带来效率,同时也是把双刃剑,滥用设计模式往往会费力不计好。本人在实际开发中对于模式的应用还不是特别多,如果有什么地方说错了,还望大家谅解.
来自:实际项目中的策略模式应用
延伸阅读:大话设计模式(小菜编程成长记系列)

