应用场景:现在超市需要一套超市收银系统,我们需要做一套收银系统出来。需求会很简单,我们一想,那不就是几个文本框的事吗,单价和数量输入,然后一算就好啦。但是,如果后期超市推出折扣活动,那我们这几个文本框还扛得住需求吗?这时候肯定有小伙伴儿说,加个下拉列表就行啦,那如果日后又有其他活动呢?岂不是要把代码改来改去喽?
这时候,就用到策略模式啦。
我用的是winform程序。
一:我们拟定现在有三种结算方式:无折扣,八折或者是满三百减一百。
分析一下,这三种折扣方式肯定需要三个类(基础策略类)来分别实现。然后我们再深入思考一下,既然他们三个的作用都是为了来满足不同的折扣运算,那我们是否可以将这三个类找一个共同点抽象出来呢?这时候就出现了抽象策略类(因为三个基础策略类只是为了实现活动需求的,但是超市经常会有各种活动,所以这些类就会被换来换去,这就是变化点,而封装变化点就是我们面向对象的一种很重要的思维方式)。
二:定义一个抽象策略类
抽象类就好比一个模具,你用A类实例化他,那他就是A实体,你用B类实例化他,那他就是B实体,但前提是A或者B两个类均继承过此抽象类并且进行了代码实现。
//抽象类是什么呢?其实就好比一个模具,你给他注入什么材质(类)他就变成什么材质(类)。
public abstract class CashSuper//抽象类,此类就是抽象策略类,折扣具体策略类均会继承(实现)此类
{
/// <summary>
/// 入参为折扣前总金额,出参为折扣后金额
/// </summary>
/// <param name="Total"></param>
/// <returns></returns>
public abstract double CashFuncInterFace(double total);//抽象方法,此方法会在具体策略类继承(实现)抽象类后被实现。
}
三:既然抽象类已经有了,那我们就来创建不同的折扣类(基础策略类)来实现此抽象类吧。
public class CashFullPay : CashSuper//全额支付具体类
{
public override double CashFuncInterFace(double total)
{
return total;
}
}
public class CashEightDiscount : CashSuper//打八折的基础策略类
{
public override double CashFuncInterFace(double total)
{
return total * 0.8;
}
}
class CashThreeMinusOne : CashSuper//满300减100基础策略类
{
public override double CashFuncInterFace(double total)
{
return total - Math.Floor(total / 300) * 100;
}
}
四:此时,抽象策略类和三个基础策略类都分别建好了,但我们在哪里实例化抽象策略类呢?总不要在后台代码中吧?那样写的代码也太乱七八糟啦,所以我们给他一个统一的入口(上下文类:Context)
public class Context//上下文类,这个类的作用就是通过传入的参数,从而将抽象策略类(模型)实例化为各个基础策略类来满足场景需求
{
CashSuper cashSuper;//抽象策略类
public Context(CashSuper cashSuper)//通过构造函数来实例化我们的抽象策略类
{
this.cashSuper = cashSuper;//通过构造函数中的基础策略类来实例化抽象策略类。就好比往模具中灌输不同的材质
}
//实例化的是什么基础策略类,这里返回的就是什么基础策略类所实现的方法
public double CashFuncInterFace(double total)
{
return cashSuper.CashFuncInterFace(total);
}
}
五:实例化策略类的入口类也有啦,那我们就在后台代码中来根据不同的折扣来实例化不同的策略类吧。
private void btn_Confirm_Click(object sender, EventArgs e)
{
Context = null;
double total = 0;
switch (comBox_Mode.SelectedValue)
{
case "1":
Context = new Context(new CashFullPay());//无折扣
break;
case "0.8":
Context = new Context(new CashEightDiscount());//八折
break;
case "300-100":
Context = new Context(new CashThreeMinusOne());//满三百减一百
break;
default://默认无折扣
Context = new Context(new CashFullPay());//无折扣
break;
}
total = Context.CashFuncInterFace(double.Parse(txt_Num.Text) * double.Parse(txt_Price.Text));//计算不同折扣后的金额
txt_TotalInfo.Text += $"{txt_Name.Text}购买了{txt_Num.Text}个,每个{txt_Price.Text}元,优惠活动:{comBox_Mode.Text},共{total.ToString()}元\r\n";
lab_TotalMoney.Text = (double.Parse(lab_TotalMoney.Text) + total).ToString();
}
六:最后是效果图
到这里,策略模式就算完事儿啦。但是细心的小伙伴一定发现了一个问题,那就是如果商场有新的活动,那我们就必须修改下拉列表并且在后端代码的switch中新增分支来满足需求,所以本质上我们还是得修改代码来重新编译,并没有解决实质性的问题。所以,这时候就又一次用到了我们上节提到的设计模式之一:简单工厂模式。这里如果运用简单工厂模式+策略模式就能完美地解决掉修改代码且重新编译的问题。
具体实现方法,我们下节见。