找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1564|回复: 0
收起左侧

设计模式之策略模式——Strategy Pattern

[复制链接]
ID:107189 发表于 2016-3-5 23:45 | 显示全部楼层 |阅读模式
*
策略设计模式隶属行为模式,行为模式关注于算法和算法间的通讯。策略是在给定输入条件下,实现某个目标的计划或方案。策略与算法类似,算法是定义好的过程,由多个操作组成,操作可分散实现于不同的类中,我们需要管理和维护这些类。行为模式捕捉操作在类间的划分方式,并优化如何处理其通讯。策略是一个计划,它也涉及从一组输入获得一组输出。通常,与算法相比策略是更大范围的可选方案,策略通常代表一组或一簇可以相互替换的方案。(通常情况下可认为其同义),策略也可以是被使用的数据。
当有多个可用的策略时,会存在一个策略选择的问题。当你把策略看成到达目的地的道路时,策略选择就像一个路由器了,它内部封装路由算法(策略选择逻辑,一般策略的选择会有一个依据,比如根据上下文环境(自身的或外部的),或是随机选择一个)。
*
**
意图:
策略模式的意图在于,从算法的宿主(host)类中,将算法分离出来放到一个独立的类里。对于一个问题的解决可能有若干个算法(策略)可行。如果算法保留在宿主类中,会产生一些混乱的条件语句(路由选择)。策略模式允许客户端(client)从一个算法族中选择一个算法并给其一个简单的方式去访问它。这些被分离出来的算法会实现一个共同的操作(操作定义于接口中)。策略操作定义了策略的输入与输出,策略实现的工作会留给各个类,这些类以不同的方案实现同一操作,他为宿主类提供了统一的接口,因而这些类是可替换的。这符合类原理类的原理中的: 依赖性倒置和Liskov替代原理。
**
***
应用策略模式的例子。
经典的策略模式例子就是排序算法。有很多的排序算法,如冒泡,归并(java集合排序用此算法),快速,线性排序,shell排序等。这些算法在特定情形下其中的一些会比另一些更“合适”,在时空性价比上更好些。这些算法的输入都是一样的(乱序或有序集合)输出也是一样的(已排序的集合)。但其内部实现不同,在什么条件下选择那个算法就是一个策略选择问题。每个算法都有相同的操作如:Collection Sort(Collection SomeArray);
关于排序算法请参考数据结构方面的书籍或资料。
***
****
模式图示(点击看大图):

在给定上下文中,合适的策略会从可用的策略簇中选出。
策略的角色:
1.上下文(context):一个对策略将要工作其中的上下文(即环境)信息的维护者。(Host类)
2.策略接口(IStrategy):为所有的策略定义公共的操作接口。
3.具体策略(concreteStratety):实现策略接口中的操作,该类含有具体的算法。
****
*****
所有的设计模式中的类图都是角色。我们不必拘泥于其名称,其中的方法签名也是可改变的,在我们的环境中我们需要一些指派(assignment),由我们自定义的类来承担那些角色。
以下给出一个该模式实现的示例:
描述:有一个人“小明”或是“小王”它们将去旅游,由于资金的问题,他们会选择不同的交通工具;
第一种Person版本中会根据自己拥有的钱来选择不同的旅游策略:坐飞机,坐高客,或是坐火车。
使用策略模式后。将旅游的策略部分提到Person外部,仅让Person依赖一个抽象的交通工具(IVehicle)现在
Person中的Travel方法仅调用IVehicle的TravelTo方法。至于具体交通工具的决策逻辑已经提到Person类外了。
来看我的练习代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StrategyPatternDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Console.WriteLine("<<--start 策略模式示例...."+'\n');
            //********测试*******************************************************************
            Test1();
            Test2();
            //********测试*******************************************************************
            System.Console.WriteLine();
            System.Console.WriteLine(" 策略模式示例....--end>>");
            System.Console.ReadLine();
        }
        static void Test1() {
            //不使用策略设计模式的示例
            Person p = new Person();
            p.Name = "小明";
            p.Money = 1500;
            System.Console.WriteLine("{0}有{1}元",p.Name ,p.Money );
            p.Travel("北京");
        }
        static void Test2() {
            System.Console.WriteLine();
            System.Console.WriteLine("<<--startTest2: 重构为策略模式后");
            Person_V1 p = new Person_V1();
            p.Name = "小王";
            p.Money = 900;
            IVehicle decisionVehicle;
            if (p.Money >= 2000) {
                decisionVehicle = new Airliner();
            }
            else if (p.Money >= 1000)
            {
                decisionVehicle = new Coach();
            }
            else {
                decisionVehicle = new Train();
            }
            p.RecommendedTravelBy(decisionVehicle);
            System.Console.WriteLine("{0}有{1}元",p.Name ,p.Money );
            p.Travel("上海");
            System.Console.WriteLine(" 重构为策略模式后 endTest2-->>");
        }
    }
//*****************原始类********************************************************************
    class Person{
        private string name;
        private double money;
        public Person() {
            this.name = "[SomePerson]";
            this.money = 0.0D;
        }
        public string Name {
            set { this.name = value; }
            get { return this.name; }
        }
        public double Money {
            set { this.money = value; }
            get { return this.money; }
        }
        public void Travel(string where) {
            if (money >= 2000) {
                this.TravelByairliner(where);
            }
            else if (money >= 1000)
            {//暗含逻辑小于2000
                this.TravelByCoach(where );
            }
            else {
                this.TravelByTrain(where );
            }
        }
        #region TravelBySomeVehicle
        private void TravelByairliner(string toSomewhere)
        {
            System.Console.WriteLine("坐飞机去{0} ", toSomewhere);
        }
        private void TravelByTrain(string toSomewhere)
        {
            System.Console.WriteLine("坐火车去{0}", toSomewhere);
        }
        private void TravelByCoach(string toSomewhere)
        {
            System.Console.WriteLine("坐高客去{0} ", toSomewhere);
        }
        #endregion
    }
    //*****************原始类********************************************************************
    //*****************重构后********************************************************************
    class Person_V1
    {
        private string name;
        private double money;
        public Person_V1()
        {
            this.name = "[SomePerson]";
            this.money = 0.0D;
        }
        public string Name
        {
            set { this.name = value; }
            get { return this.name; }
        }
        public double Money
        {
            set { this.money = value; }
            get { return this.money; }
        }
        //*********策略对象可由外部传入*************************************
        private IVehicle vehicle;//某交通工具
        public void RecommendedTravelBy(IVehicle someVehicle){
            this.vehicle = someVehicle;//交通工具由外部决定
         }
        //*********策略对象可由外部传入*************************************
        public void Travel(string where)
        {
            if (this.vehicle == null)
            {
                System.Console.WriteLine("并未指定交通工具!");
            }
            else
            {
                this.vehicle.TravelTo(where);
            }
        }
      
    }
    #region IStrategy_Interface   策略接口
    public interface IVehicle {
        //策略接口,定义了一个操作TravelTo
        void TravelTo(string where);
    }
    #endregion
   
    #region ConcreteStrategy_Classes 策略实现者类
   
    public class Airliner : IVehicle {
        #region IVehicle 成员
        public void TravelTo(string where)
        {
            //对操作TravelTo的实现
            System.Console.WriteLine("坐飞机去{0} ", where);
        }
        #endregion
    }
    public class Train : IVehicle {

        #region IVehicle 成员
        public void TravelTo(string where)
        {
            System.Console.WriteLine("坐火车去{0}", where);
        }
        #endregion
    }

    public class Coach : IVehicle {

        #region IVehicle 成员
        public void TravelTo(string where)
        {
            System.Console.WriteLine("坐高客去{0} ", where);
        }
        #endregion
    }
    #endregion
    //******************重构后*******************************************************************
}
上面策略模式的应用为我们带来的好处:Person类不再亲自决定到底使用那种交通工具了,策略选择逻辑已被分离出去了,任何实现策略接口的具体类现在都可被传入到Person类中。这样体现了可扩展性。Person类只执行策略就可以了。
就向这样的情形。一个企业中,高层指定战略计划,中层负责执行战略,中层不需要选择战略计划塔们只知道执行计划,决策逻辑交给高层去吧!
*****
******
策略设计模式的UML图示很简单,它们只是角色示意而已,到底谁来实现这个角色,模式并没有明说,我们其实也可以将决策逻辑仍放在策略执行者之内(这里就是指Person类,),它甚至可有一个策略列表,用来存放可用的策略,如果你在提供增加,删除,或替换策略的公共接口方法,那就更帅了!
策略模式的图示,与状态模式的结构是一样的,它们都符合类原理:依赖抽象。
但与策略模式不同的是。状态模式中存在对象的状态转移,它是对状态机的建模。
在本例中,Person有一个属性Money,它也可以表示Person当下的状态,但是执行某个操作后我们后续并没有改变它的状态(比如说,让他的钱财减少)。因为策略的选择总是有些依据的,这里就依赖了Person内部的状态,但是我们还可以依赖外部的状态或环境(就是角色中的那个Context上下文)。状态模式中,操作的具体行为总是依赖系统内部的具体状态对象,操作完备后必要时还需要置换当前的状态对象。但无论如何它们都能改善我们的设计,我们需认真选择什么环境下使用那个模式。
另外提一点,《重构》一书提到,如果代码中出现了过多的选择语句,IF..if else..if...else。Switch(){case:case:case:....}。在面向对象语境下都是代码坏味道的体现,这些结构典型的属于面向过程的编程方式,在面向对象中都能用多态去除。我们上面的示例中仍然有这样的结构,有兴趣的朋友可以试试想法去掉它们。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表