随笔 - 116  文章 - 27  评论 - 710 
一、模式概述
      策略模式(Strategy Pattern)在外形上与状态模式很相似,但在意图上有些不同。其意图是使这些算法可以相互替换,并提供一种方法来选择最合适的算法。
      在我应用OOP的设计过程演化(三)这篇文章里应用到了策略模式,在图书的租金计算上分多种情况,每一种不同类型的图书的租金是不一样的,而站在用户的角度来看,不同类型用户的租金收取又是不一样的,见下面分析:
      计算机类图书:会员租借打5折,普通顾客租借打6折。
      小说类图书:会员租借打6折,普通顾客租借打8折。
      生活类图书:会员租借打9折,普通顾客租借打9折。
     
      从上面分析可知,在租金计算这块是非常复杂的,由于有这样复杂的折扣算法,使得价格计算问题需要系统地解决。
     书店经营一段时间后发现生活类的图书出租量很小,或许是因为租金的原因,很少有人来租借,于是老板决定降低租金,将生活类图书的租金按照计算机类的租金价格收取。这个时候怎么办?是不是已经有现存的计算机类图书租金计算策略,此时我们只需要把生活来图书的租金计算策略换为计算机类图书的租金计算策略便OK。通过这样的分析,策略模式(Strategy)正是解决这样的问题的模式,它的定义:"准备一组算法,并将每一个算法封装起来,使得它们可以互换。"

二、模式定义
     定义:"准备一组算法,并将每一个算法封装起来,使得它们可以互换。"

三、模式意图
      策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。

四、模式UML图
                        
五、模式参与者
     根据上面UML,不难看出,在策略模式里主要有三种角色:环境角色、抽象策略角色和具体策略角色。
  环境(Context)角色:持有一个抽象策略(Strategy)角色的引用。也叫上下文。
  抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或一个抽象类来实现。
  具体策略(ConcreteStrategy)角色:包装了相应的算法和行为。

六、简单示例
      比如我们有这样一个需求,当我们的系统在记录操作日志的时候,客户要求提供多种日志记录策略,通过设置可以将日志记录到不同的地方(文本文件、XML文件以及数据库等),那么根据策略模式的定义,我们完全可以把不同的日志记录算法定义为一个独立的策略对象。
 1namespace DesignPattern.Strategy
 2{
 3    /// <summary>
 4    /// 抽象策略角色:这是一个抽象角色,通常由一个接口或抽象类实现。
 5    /// 此角色给出所有的具体策略类所需的接口。
 6    /// </summary>

 7    public abstract class Strategy
 8    {
 9        public abstract void WriteLog();
10    }

11}

 1/// <summary>
 2/// 具体策略角色:包装了相关的算法或行为。
 3/// </summary>

 4public class DBStrategy:Strategy
 5{
 6    public override void WriteLog()
 7    {
 8        Console.WriteLine("日志被记录到数据库!");
 9    }

10}

11
12/// <summary>
13/// 具体策略角色:包装了相关的算法或行为。
14/// </summary>

15public class TXTStrategy:Strategy
16{
17    public override void WriteLog()
18    {
19        Console.WriteLine("日志被记录到TXT文件!");
20    }

21}

22
23/// <summary>
24/// 具体策略角色:包装了相关的算法或行为。
25/// </summary>

26public class XMLStrategy:Strategy
27{
28    public override void WriteLog()
29    {
30        Console.WriteLine("日志被记录到XML文件!");
31    }

32}

 1namespace DesignPattern.Strategy
 2{
 3    /// <summary>
 4    /// 环境角色:持有一个Strategy类的引用
 5    /// </summary>

 6    public class Context
 7    {
 8        Strategy strategy;
 9        /// <summary>
10        /// 初始化时传入具体的策略对象
11        /// </summary>
12        /// <param name="strategy"></param>

13        public Context(Strategy strategy)
14        {
15            this.strategy = strategy;
16        }

17
18        public void ContextStrategy()
19        {
20            strategy.WriteLog();
21        }

22
23        /// <summary>
24        /// 动态设置策略
25        /// </summary>
26        /// <param name="strategy"></param>

27        public void SetStrategy(Strategy strategy)
28        {
29            this.strategy = strategy;
30        }

31    }

32}

      定义了一个抽象策略角色(Strategy),三种不同的日志记录策略(TXTStrategy、XMLStrategy和DBStrategy),也就是模式参与者中的具体策略角色,在环境角色里持有一抽象策略角色的引用,并通过构造器传入具体的策略对象初始化策略角色的引用,我们还定义了一动态设置策略的方法(SetStrategy),UML如下:
                        
      那么客户端就可以这样来使用这个策略:
 1namespace DesignPattern.Strategy
 2{
 3    class Program
 4    {
 5        static void Main(string[] args)
 6        {
 7            //初始化时传入具体的策略对象
 8            Context context = new Context(new XMLStrategy());
 9            context.ContextStrategy();
10
11            //动态设置策略对象--改变策略
12            context.SetStrategy(new DBStrategy());
13            context.ContextStrategy();
14
15            context.SetStrategy(new TXTStrategy());
16            context.ContextStrategy();
17        }

18    }

19}

      策略模式是一个提倡“针对接口编程”的模式,而使用接口的目的是为了统一标准或着说是制定一种强行的规定。此处抽象策略角色就担任了制定规定的角色,它制定了一种写日志(WriteLog方法)的规定。只要系统中需要记录日志就得使用这个规定(调用WriteLog方法),直接就使用他是不行的,就如去商场购物,要把商品带走不付费怎么行?

      策略模式的使用是由用户发起的,根据用户的操作决定使用什么具体的策略角色。策略模式仅仅封装算法,提供新的算法加入到已有系统和算法间的相互替换,以及老算法从系统中“退休”的方便。策略模式并不决定在何时使用何种算法。因为使用何种算法决定权在用户,也就是说我们需要调用策略模式中定义好的算法方法之前,必须得选择一种具体的策略算法,上例中,客户端需要调用ContextStrategy方法,则调用前得为其设置一个具体的策略算法,如下:
1//初始化时传入具体的策略对象
2Context context = new Context(new XMLStrategy());
3context.ContextStrategy();

七、PetShop 4.0中的策略模式
     PetShop 4.0的体系结构非常庞大,在订单处理上设计了两种处理策略,这里也是策略模式的一个应用,IOrderStrategy接口作为订单策略的高层抽象(抽象策略角色),实现不同订单处理的具体策略去实现它,UML如下:
                         

示意性代码:
1namespace PetShop.IBLLStrategy
2{
3    public interface IOrderStrategy
4    {
5        void Insert(OrderInfo order);
6    }

7}

 1namespace PetShop.BLL
 2{
 3    public class OrderSynchronous:IOrderStrategy
 4    {
 5        private static readonly IOrder asynchOrder = QueueAccess.CreateOrder();
 6
 7        public void Insert(OrderInfo order)
 8        {
 9            asynchOrder.Send(order);
10        }

11    }

12}

13namespace PetShop.BLL
14{
15    public class OrderAsynchronous:IOrderStrategy
16    {
17       //
18    }

19}

      从上面UML和代码就可以看出,订单策略接口下有两种实现,使用了抽象工厂模式来完成相应的订单策略对象的创建 。关于定单策略对象的创建详细可查阅ASP.NET中进行消息处理(MSMQ) 三 这篇文章,我在这里作了详细介绍。

八、模式的相关优点及缺点
   定义了一系列的可供重用的算法和行为,继承有助于析取出这些算法中的公共功能。
   Strategy模式可以提供相同行为的不同实现,客户可以根据不同的需求选择不同的Strategy。
   客户必须了解不同的Strategy,一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。
   Strategy模式会增加对象的数目(具体的策略对象),可以将Strategy实现为可供各Context共享的无状态的对象来减少这一开销。任何其与的状态都由Context维护。

九、参考资料
    张逸 《软件设计精要与模式》
    GOF 《设计模式--可复用面向对象软件的基础》
    MS开源项目PetShop 4.0

注:原创文章,转载请注明出处:http://beniao.cnblogs.com  或 http://www.cnblogs.com   作者:beniao
posted on 2008-07-08 14:24 Bēniaǒ 阅读(2006) 评论(13)  编辑 收藏 网摘 所属分类: 设计模式

  回复  引用  查看    
2008-07-08 14:47 | thriving.country      
楼主的这一系列文章很好,我基本都看了,收获不小,用例比较好!支持
  回复  引用  查看    
2008-07-08 19:20 | Bēniaǒ      
@thriving.country
这文章的示例很简单,对新手入门还可以,要求高点就不行了.
  回复  引用  查看    
2008-07-08 20:09 | 金色海洋(jyk)      
这样的文章,怎么回复的人这么少呢?
  回复  引用  查看    
2008-07-08 20:23 | Bēniaǒ      
@金色海洋(jyk)
呵呵,不知道呢.可能是文章不受欢迎吧.^.^
  回复  引用    
2008-07-08 21:00 | nh022 [未注册用户]
呵呵,好文章迟早会被发现的,楼主要坚持哦!
  回复  引用  查看    
2008-07-08 22:36 | T2噬菌体      
文章很不错!!!

至于回复少的原因,我觉得可能是因为博客园中关于设计模式的系列文章太多了,而且不乏经典之作,这样就会使得此系列文章相比之下没有太多突出的东西。

不过我支持楼主,一定要继续写完!!!
  回复  引用  查看    
2008-07-08 23:53 | landylee      
I just like it !
please go on!
文中以PetShop4.0的項目中相應部分舉例,太好 了,我正在研究PetShop4.0,有很多地方不懂,如果以后別的模式也舉其中例,那就優待我了!
  回复  引用  查看    
2008-07-08 23:56 | Bēniaǒ      
@nh022
谢谢支持.
  回复  引用  查看    
2008-07-09 00:00 | Bēniaǒ      
@landylee
PS4是个很不错的项目,如果以后文章里能够用到PS4里的现有模块作案例,我不会错过.大家一起学习.
  回复  引用  查看    
2008-07-09 00:06 | Bēniaǒ      
@T2噬菌体
我一直也在关注你的基于.NET平台的分层架构实战系列,写得很不错.期待后续文章.

  回复  引用  查看    
2008-07-09 11:35 | 陳龑      
好文,顶一个!
  回复  引用  查看    
2008-07-14 17:09 | zendevelop      
楼主的文章不错,受教了!
  回复  引用  查看    
2008-07-14 18:41 | Bēniaǒ      
@陳龑
@zendevelop
一起学习。

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-07-09 13:38 编辑过
Google站内搜索
[推荐职位]上海盛大网络招聘架构师



China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
近千种 9-95 新二手计算图书火热销售中!
开发者征途系统新作:《设计模式——基于C#的工程化实现及扩展》

相关文章:


相关搜索:
设计模式 Design Pattern Strategy

相关链接: