上篇我们学习了简单工厂模式,想必大家如果按照我写的步骤一步一步下来的话肯定会有印象(尤其是像我一样刚刚接触设计模式的小白白们)。这次我们继续学习策略模式,例子还是用作者给的吧~
问题提出:
需要设计一个商场的收银软件,营业员根据用户购买商品的单价和数量来计算总额。(考虑商场有多个促销活动(打折活动,满减活动)等,且由于我们是用java写的,写图形化界面不方便,所以把main方法看作是客户端)
大家看到这个想必第一个想到的就是我们学过的简单工厂模式(我也是)那么大家试着用简单工厂模式实现以下上述需求,顺便复习以下简单工厂咯~
以下是我写的简单工厂模式:
import java.util.Scanner;
public class Main {
public static void main(String args[]) {
Scanner scan = new Scanner(System.in);
int kinds;
Activity activity;
while(scan.hasNext())
{
kinds = scan.nextInt();
activity = ActivityFactory.getActivity(kinds);//根据输入的参数来决定使用哪种促销方式
}
System.out.println(activity2.money(50, 2));
}
}
interface Activity {//定义活动的接口
double money(double price, int amount);//输入单价和数量返回需要交多少钱
}
class A implements Activity// 活动A,打折活动
{
private double rate;
public A(double rate)
{
this.rate = rate;
}
public double money(double price, int amount) {
return price * amount * rate;
}
}
class B implements Activity// 活动B,满减活动
{
private double moneyLimit;//达到多少满减
private double moneyReturn;//满减多少
public B(double moneyLimit,double monetReturn)
{
this.moneyLimit = moneyLimit;
this.moneyReturn = monetReturn;
}
public double money(double price, int amount) {
double cost = price * amount;
if (cost >= moneyLimit) {
cost -= moneyReturn;
}
return cost;
}
}
class C implements Activity// 无活动,正常收费
{
public double money(double price, int amount) {
return price * amount;
}
}
class ActivityFactory// 商家促销活动的工厂
{
public static Activity getActivity(int kinds) {//根据输入活动的类型号来生成活动
switch (kinds) {
case 1://八折
return new A(0.8);
case 2://满100-50
return new B(100,50);
default://无活动
return new C();
}
}
}
把简单工厂写出来以后大家想必美滋滋了吧~。但是简单工厂的侧重点是生产对象(工厂嘛~),但是其实客户端得到对象并没有什么用,客户端只需要得到最后的结果就可以了。所以我们需要把算法封装好,减少客户端的工作量。省去activity.money()这一步。
下面来看策略模式的实现:
import java.util.Scanner;
public class Main {
public static void main(String args[]) {
Scanner scan = new Scanner(System.in);
int kinds;
Context context;
double money;
while(scan.hasNext())
{
kinds = scan.nextInt();
switch (kinds) {
case 1:
context = new Context(new A(0.8),50,2);
money = context.getMoney();
break;
case 2:
context = new Context(new B(100,50),50,2);
money = context.getMoney();
break;
default:
context = new Context(new C(),50,2);
money = context.getMoney();
break;
}
System.out.println(money);
}
}
}
interface Activity {// 定义活动的接口
double money(double price, int amount);// 输入单价和数量返回需要交多少钱
}
class A implements Activity// 活动A,打折活动
{
private double rate;
public A(double rate) {
this.rate = rate;
}
public double money(double price, int amount) {
return price * amount * rate;
}
}
class B implements Activity// 活动B,满减活动
{
private double moneyLimit;// 达到多少满减
private double moneyReturn;// 满减多少
public B(double moneyLimit, double monetReturn) {
this.moneyLimit = moneyLimit;
this.moneyReturn = monetReturn;
}
public double money(double price, int amount) {
double cost = price * amount;
if (cost >= moneyLimit) {
cost -= moneyReturn;
}
return cost;
}
}
class C implements Activity// 无活动,正常收费
{
public double money(double price, int amount) {
return price * amount;
}
}
class Context {
Activity activity;
double price;
int amount;
public Context(Activity activity,double price,int amount) {
this.activity = activity;
this.price = price;
this.amount = amount;
}
public double getMoney() {
return activity.money(price, amount);
}
}
大家看代码应该能看到多了一个Context()类,里面存放了一个Activity类的对象activity。构造方法传入一个Activity参数并且初始化activity。然后再提供一个对外获取activity的接口。
大家可能看完策略模式以后会感觉跟简单工厂模式差不太多,但是细看就会发现客户端是得不到我们的算法对象的,也就是我们完成了算法的封装。让不同的算法之间可以相互替换。
UML图代码为:
* @startuml
* interface Activity{
* + money():double
* }
* Activity<|..A
* class A{
* - rate:double
* + A(double)
* + money():double
* }
* Activity<|..B
* class B{
* - moneyLimit:double
* - moneyReturn:double
* + B(double,double)
* + money():double
* }
* Activity<|..C
* class C{
* + money():double
* }
* Context-->Activity
* class Context{
* -activity:Activity
* -price:double
* -amount:int
* +Context(Activity,double,int)
* +getMoney():double
* }
* @enduml
UML图为:
注:书中认为Context与Activity关系为聚合关系,但我感觉应该是关联关系。因为在Context里面只是保存了Activity的一个引用而已。
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户端。
但还有一个问题,我们在策略模式里面又把switch这么一大块的代码搬进了客户端里面(main方法)。这可不是什么好习惯,所以我们应该再进一步把这坨东西从客户端搬出来。
策略模式与简单工厂的结合
public class Main {
public static void main(String args[]) {
Scanner scan = new Scanner(System.in);
int kinds;
Context context;
double money;
while(scan.hasNext())
{
kinds = scan.nextInt();
context = new Context(kinds, 50, 2);
money = context.getMoney();
System.out.println(money);
}
}
}
class Context {
Activity activity;
double price;
int amount;
public Context(int kinds,double price,int amount) {
switch (kinds) {
case 1:
activity = new A(0.8);
break;
case 2:
activity = new B(100, 50);
break;
default:
activity = new C();
break;
}
this.price = price;
this.amount = amount;
}
public double getMoney() {
return activity.money(price, amount);
}
}
需要改的就只有客户端代码和Context代码。大家应该都能看懂吧,就是从客户端传入一个参数然后在Context里面进行判断。
最后摘抄一段书中的策略模式的好处:策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少各种算法类与使用算法类之间的耦合。简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。