①基本介绍
②互联网电商购物车支付
③策略模式的优缺点
④适用场景
策略模式:定义一系列算法类,将每一个算法封装起来,并让他们可以相互替换。
每一个封装的算法可以被称之为一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类作为规则的定义,每种具体算法对应于一个具体策略类。
策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合DIP(依赖倒转原则)。出现新算法时只需要定义一个新的具体策略类即可。
策略模式分为三种角色
①Context(环境类)
使用算法的角色,解决了某个问题时可以采用的多种策略,在环境类维持一个抽象策略类的引用实例,用于定义所采用的策略
②Strategy(抽象策略类)
为支持的算法声明了抽象方法,是所有策略类的父类,可以是抽象类或具体类,也可以是接口,环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法
③ConcreteStrategy(具体策略类)
实现了抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理
public class ProductPaymentNormal
{
public double acceptProductPayment(int price, int qty)
{
return price * qty;
}
}
ProductPaymentNormal p = new ProductPaymentNormal();
var total = 0.0d;
total+= p.acceptProductPayment(10, 20);
Console.WriteLine($"总费用:{total}");
分析:简单的相乘法相加就实现了,但是如果此时需要打8折,该如何处理
public class ProductPaymentDiscount
{
private double moneyDiscount = 1d;
public ProductPaymentDiscount(double _moneyDiscount)
{
this.moneyDiscount = _moneyDiscount;
}
public double acceptProductPayment(int price, int qty)
{
return price* qty * moneyDiscount;
}
}
ProductPaymentDiscount p = new ProductPaymentDiscount(0.8);
var total = 0.0d;
total+= p.acceptProductPayment(10, 20);
Console.WriteLine($"总费用:{total}");
分析:在以前的基础上添加了一个打折类。如果继续再添加满减返利,该如何处理呢。
①抽象基类与正常,返利,打折实现实例
public abstract class ProductPaymentBase
{
public abstract double acceptProductPayment(int price, int qty);
}
public class ProductPaymentNormal : ProductPaymentBase
{
public override double acceptProductPayment(int price, int qty)
{
return price * qty;
}
}
public class ProductPaymentDiscount: ProductPaymentBase
{
private double moneyDiscount = 1d;
public ProductPaymentDiscount(double _moneyDiscount)
{
this.moneyDiscount = _moneyDiscount;
}
public override double acceptProductPayment(int price, int qty)
{
return price* qty * moneyDiscount;
}
}
public class ProductPaymentReturn : ProductPaymentBase
{
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
public ProductPaymentReturn(double _moneyCondition,double moneyReturn)
{
this.moneyCondition = _moneyCondition;
this.moneyReturn = moneyReturn;
}
public override double acceptProductPayment(int price, int qty)
{
double result = price * qty;
if (result > this.moneyCondition)
{
result = result - Math.Floor(result / this.moneyCondition) * this.moneyReturn;
}
return result;
}
}
②简单工厂类封装支付规则
public class ProductPaymentFactory
{
public static ProductPaymentBase CreateProductPaymentAccept(string type)
{
ProductPaymentBase productPaymentBase = null;
switch (type)
{
case "正常收费":
productPaymentBase = new ProductPaymentNormal();
break;
case "满300返100":
productPaymentBase = new ProductPaymentReturn(300,100);
break;
case "打8折":
productPaymentBase = new ProductPaymentDiscount(0.8);
break;
}
return productPaymentBase;
}
}
③上端调用
var total = 0.0d;
ProductPaymentBase p = ProductPaymentFactory.CreateProductPaymentAccept("type");
total += p.acceptProductPayment(10, 20);
Console.WriteLine($"总费用:{total}");
分析:如果我们继续再添加积分返利等操作呢?添加返利的代码,然后继续更改工厂类吗
①上下文对象
public class ProductPaymentContext
{
private ProductPaymentBase productPaymentBase;
public ProductPaymentContext(ProductPaymentBase _productPaymentBase)
{
this.productPaymentBase = _productPaymentBase;
}
public double GetResult(int price, int qty)
{
return productPaymentBase.acceptProductPayment(price, qty);
}
}
②上端调用
ProductPaymentContext ppc = null;
var total = 0.0d;
var userType = "type";
switch (userType)
{
case "正常收费":
ppc = new ProductPaymentContext(new ProductPaymentNormal());
break;
case "满300返100":
ppc = new ProductPaymentContext(new ProductPaymentReturn(300,100));
break;
case "打8折":
ppc = new ProductPaymentContext(new ProductPaymentDiscount(30.8));
break;
}
total += ppc.GetResult(100, 20);
Console.WriteLine($"总费用:{total}");
分析:实现了不同的规则直接替换,但是上端调用的逻辑暴露太多。
①改造后的上下文对象
public class ProductPaymentContext
{
private ProductPaymentBase productPaymentBase;
public ProductPaymentContext(string type)
{
switch (type)
{
case "正常收费":
productPaymentBase = new ProductPaymentNormal();
break;
case "满300返100":
productPaymentBase = new ProductPaymentReturn(300, 100);
break;
case "打8折":
productPaymentBase = new ProductPaymentDiscount(0.8);
break;
}
}
public double GetResult(int price, int qty)
{
return productPaymentBase.acceptProductPayment(price, qty);
}
}
②上端调用
ProductPaymentContext ppc = new ProductPaymentContext("type");
var total = 0.0d;
total += ppc.GetResult(100, 20);
Console.WriteLine($"总费用:{total}");
分析:比较发现,简单工厂模式需要让上端实例化两个类,即ProductPaymentFactory与ProductPaymentBase,而策略模式与简单工厂结合之后,上端只需要依赖上下文对象ProductPaymentContext,耦合更加降低。
①完美支持OCP
策略模式提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或者行为,也可以灵活提供新的算法或行为。
②易于管理和复用算法
策略模式提供了管理相关的算法族的办法,策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共代码移到抽象策略类中以简化代码,同时由于算法单独封装在具体策略类中,可以方便复用这些算法。
③替换继承
策略模式提供了一种替换继承关系的方法,不使用策略模式的话环境类可能有子类,造成算法的使用和定义混在一起,而且使用继承的话无法实现算法或行为在运行时动态切换。
④避免多重else if
多重选择语句不易维护,因为将选择算法的逻辑以及算法本事实现逻辑混在一起,硬编码在一个巨大的if/else if中,使用策略模式可以避免这种结构。
①策略类需要对客户端透明
客户端必须知道所有的策略类,并自行决定哪一个策略类,也就是客户端需要理解这些算法的区别以便选择适当的算法。
②策略类数量多
策略模式会造成系统产生很多具体策略类,任何细小的变化都会导致系统增加一个新的具体策略类。
③客户端无法使用多个策略类
客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩下的功能。
①一个系统需要动态在几种算法中选择一种,这些算法类均有统一的接口。
②一个对象有很多行为,使用策略模式可以将这些行为转移到相应具体策略类中。
③不希望客户端知道复杂的,与算法相关的数据结构,在具体策略类中对其进行封装。