策略模式【设计模式学习-02】

      策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如网购计算订单的邮费,不同的快递不同的价格。

      策略模式的UML类图如下: 

策略模式的组成

      —抽象策略角色: 策略类,通常由一个接口或者抽象类实现。 

  —具体策略角色:包装了相关的算法和行为。 

  —环境角色:持有一个策略类的引用,最终给客户端调用。

概念

      策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.) 

  Context(应用场景): 

  1、需要使用ConcreteStrategy提供的算法。 

  2、 内部维护一个Strategy的实例。 

  3、 负责动态设置运行时Strategy具体的实现算法。 

  4、负责跟Strategy之间的交互和数据传递。 

  Strategy(抽象策略类): 

  1、 定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。 

  ConcreteStrategy(具体策略类): 

  2、 实现了Strategy定义的接口,提供具体的算法实现。

应用场景

      应用场景: 

  1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。

  2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。

  3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

优缺点

      优点: 

  1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。 

  2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。 

  3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。 

  缺点: 

  1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。

示例代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _02策略模式
{
class Program
{
static void Main()
{
Context context;

// Three contexts following different strategies
context = new Context(new ConcreteStrategyA());
context.Execute();

context = new Context(new ConcreteStrategyB());
context.Execute();

context = new Context(new ConcreteStrategyC());
context.Execute();

}
}
// The classes that implement a concrete strategy should implement this
// The context class uses this to call the concrete strategy
interface IStrategy
{
void Execute();
}

// Implements the algorithm using the strategy interface
class ConcreteStrategyA : IStrategy
{
public void Execute()
{
Console.WriteLine("Called ConcreteStrategyA.Execute()");
}
}

class ConcreteStrategyB : IStrategy
{
public void Execute()
{
Console.WriteLine("Called ConcreteStrategyB.Execute()");
}
}

class ConcreteStrategyC : IStrategy
{
public void Execute()
{
Console.WriteLine("Called ConcreteStrategyC.Execute()");
}
}

// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context
{
IStrategy strategy;

// Constructor
public Context(IStrategy strategy)
{
this.strategy = strategy;
}

public void Execute()
{
strategy.Execute();
}
}
}

 

 

以计算订单邮费为例子,来看下我们应该如何重构我们的代码。

策略模式想要解决的问题:

namespace Strategy_Before.Domain
{
    public class Address
    {
        public string ContactName { get; set; }
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string AddressLine3 { get; set; }
        public string City { get; set; }
        public string Region { get; set; }
        public string Country { get; set; }

        public string PostalCode { get; set; }
    }
}

namespace Strategy_Before.Domain
{
    public class Order
    {
        public enum ShippingOptions
        {
            UPS = 100,
            FedEx = 200,
            USPS = 300,
        };

        public ShippingOptions ShippingMethod { get; set; }
        
        public Address Destination { get; set; }
        public Address Origin { get; set; }
    }
}
View Code
namespace Strategy_Before.Domain
{
    public class ShippingCostCalculatorService
    {
        public double CalculateShippingCost(Order order)
        {
            switch (order.ShippingMethod)
            {
                case Order.ShippingOptions.FedEx:
                    return CalculateForFedEx(order);

                case Order.ShippingOptions.UPS:
                    return CalculateForUPS(order);

                case Order.ShippingOptions.USPS:
                    return CalculateForUSPS(order);

                default:
                    throw new UnknownOrderShippingMethodException();
            }
        }

        double CalculateForUSPS(Order order)
        {
            return 3.00d;
        }

        double CalculateForUPS(Order order)
        {
            return 4.25d;
        }

        double CalculateForFedEx(Order order)
        {
            return 5.00d;
        }
    }
}

 

使用策略模式来重构以上代码:(寻找变化点,封装变化点)

namespace Strategy_After.Domain.ShippingService
{
    public interface IShippingCostStrategy
    {
        double Calculate(Order order);
    }
}

namespace Strategy_After.Domain.ShippingService
{
    public class FedExShippingCostStrategy : IShippingCostStrategy
    {
        public double Calculate(Order order)
        {
            return 5.00d;
        }
    }
}

namespace Strategy_After.Domain.ShippingService
{
    public class UPSShippingCostStrategy : IShippingCostStrategy
    {
        public double Calculate(Order order)
        {
            return 4.25d;
        }
    }
}

namespace Strategy_After.Domain.ShippingService
{
    public class USPSShippingCostStrategy : IShippingCostStrategy
    {
        public double Calculate(Order order)
        {
            return 3.00d;
        }
    }
}

//
namespace Strategy_After.Domain.ShippingService
{
    public class ShippingCostCalculatorService
    {
        readonly IShippingCostStrategy shippingCostStrategy;

        public ShippingCostCalculatorService(IShippingCostStrategy shippingCostStrategy)
        {
            this.shippingCostStrategy = shippingCostStrategy;
        }

        public double CalculateShippingCost(Order order)
        {
           return shippingCostStrategy.Calculate(order);
        }
    }
}

 

可能的一些演变:

Property Injection、Delegate等。

using System;

namespace Strategy_After_Variation.Domain
{
    public class ShippingCostCalculatorService
    {
        public double CalculateShippingCost(Order order, Func<Order, double> shippingCostStrategy)
        {
           return shippingCostStrategy(order);
        }
    }
}


using System;
using Strategy_After_Variation.Domain;

namespace Strategy_After_Variation
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Func<Order, double> fedExStrategy = CalcForFedEx;
            Func<Order, double> upsStrategy = delegate(Order order) { return 4.00d; };
            Func<Order, double> uspsStrategy = order => 3.25d;

            Order theOrder = Mother.CreateOrder();
            
            var calculatorService = new ShippingCostCalculatorService();
            Console.WriteLine("FedEx Shipping Cost: " + 
                calculatorService.CalculateShippingCost(theOrder, fedExStrategy));

            Console.WriteLine("UPS Shipping Cost: " +
                calculatorService.CalculateShippingCost(theOrder, upsStrategy));

            Console.WriteLine("USPS Shipping Cost: " +
                calculatorService.CalculateShippingCost(theOrder, uspsStrategy));
            
            Console.ReadKey();
        }

        internal static double CalcForFedEx(Order arg)
        {
            return 5.00d;
        }
    }
}

 

结束语:      

      希望能对各位博友有点帮助~

posted @ 2012-03-04 10:39  DebugLZQ  阅读(1224)  评论(0编辑  收藏  举报