策略模式
本篇博客将介绍策略模式,策略模式用于算法的自由切换和扩展,他是使用较为广泛的设计模式之一。基本上凡是涉及到父类和父类之间的交互都会或多或少的涉及到这个模式。策略模式提供了一种对象在面对多项选择时的解决方案。
模式分类
行为型设计模式。
模式产生的原因
在软件开发中,我们常常会遇到一种情况:实现一个功能需要用到多种算法或者途径,一种常用的方式是我们通过硬编码将所有的算法集中在一个类中,在该类中通过多个方法,每一种方法对应一个具体的算法;当然也可以将这些算法封装在一个统一的方法中,并使用分支语句来判断选择。这两种的方法都被称之为硬编码,如果增加一种新的算法此时我们就需要修改我们写好的代码,非常的不利于维护和拓展。
此时我们可以选择一种设计模式来灵活的选择算法,还能够方便的增加新的算法。该设计模式就是策略模式。可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类都可以是一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做算法的声明,而每种算法则对应一个具体策略类。
模式类图
由上图可知,经典策略模式包括以下3个对象:
Context(环境类):
环境类是使用算法的角色,他在解决某个问题是可以采用多种策略。在环境类中维持一个对抽象策略类的引用,用于定义所采用的策略。
Strategy(抽象策略类):
抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类,他可以是抽象类或者具体类,也可以是接口。
ConcreteStrategy(具体策略类):
具体抽象类实现了在抽象策略类中声明的算法,在运行时,具体策略类对象将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务的功能。
代码实现
某软件公司为某电影院开发了一套影院售票系统,在该系统中需要为不同类型的用户提供不同的电影票打折方式,具体打折方案如下:
- 学生凭学生证可以享受电影票8折优惠。
- 年龄在10周岁及以下的儿童可享受每张票减10元的优惠。
- 影院VIP享受半折优惠。
请使用策略模式设计这个系统。
电影票类:
namespace Strategy.Strategy.Example
{
public class MovieTicket
{
private Discount _discount;
private int _ticketMoney;
public MovieTicket(int money)
{
_ticketMoney = money;
}
public void SetDiscount(string name)
{
_discount = DisCountFactory.Create(name);
}
public float Calculate()
{
return _discount.Calculate(_ticketMoney);
}
}
}
抽象打折类:
namespace Strategy.Strategy.Example
{
public abstract class Discount
{
public const string NAME = "";
public abstract float Calculate(int money);
}
}
打折工厂:
using System;
namespace Strategy.Strategy.Example
{
public static class DisCountFactory
{
public static Discount Create(string name)
{
Type type = Type.GetType($"Strategy.Strategy.Example.MyDiscount.{name}");
if (type == null)
{
return null;
}
return Activator.CreateInstance(type) as Discount;
}
}
}
儿童折扣:
namespace Strategy.Strategy.Example.MyDiscount
{
public class ChildDiscount : Discount
{
public new const string NAME = "ChildDiscount";
public override float Calculate(int money)
{
if (money >= 20)
{
return money - 10;
}
return money;
}
}
}
学生折扣:
namespace Strategy.Strategy.Example.MyDiscount
{
public class StudentDiscount : Discount
{
public new const string NAME = "StudentDiscount";
public override float Calculate(int money)
{
return money * 0.8f;
}
}
}
VIP折扣:
namespace Strategy.Strategy.Example.MyDiscount
{
public class VipDiscount : Discount
{
public new const string NAME = "VipDiscount";
public override float Calculate(int money)
{
return money * 0.5f;
}
}
}
Program类:
using System;
using Strategy.Strategy.Example;
using Strategy.Strategy.Example.MyDiscount;
namespace Strategy
{
internal class Program
{
public static void Main(string[] args)
{
MovieTicket movieTicket = new MovieTicket(30);
movieTicket.SetDiscount(ChildDiscount.NAME);
Console.WriteLine(movieTicket.Calculate());
movieTicket.SetDiscount(StudentDiscount.NAME);
Console.WriteLine(movieTicket.Calculate());
movieTicket.SetDiscount(VipDiscount.NAME);
Console.WriteLine(movieTicket.Calculate());
}
}
}
策略模式总结
策略模式的优点:
- 策略模式提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或者行为,也可以灵活的添加新的算法和行为。
- 使用策略模式可以避免多重条件选择语句。
- 策略模式可以让被选择的算法可以得到复用。
策略模式的缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类,可以通过引入策略代理来解决这个问题。
- 策略模式将造成系统产生很多具体策略类。