“组件协作”模式----策略模式(Strategy Pattern)

1.“组件协作”模式

“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合。 典型模式:
Strategy
Template Method
Observer/Event

2.策略模式动机(Motivation)

在运行时根据需要透明地更改对象的算法,将算法与对象本身解耦

3.问题引入

商场收银软件
输入单价和数量
算出每种商品的小计
记录商品的清单
记录总计

4.商场收银系统1.0

import java.util.Scanner;
public class Cash{
   public String list=" ";
   public Double totalPrice=0.00;   //声明一个double变量totalPrice来计算总计
   public void buttonOK(){
       Scanner scan=new Scanner(System.in);
       System.out.println("输入单价:");
       String price=scan.nextLine();
       scan=new Scanner(System.in);
       System.out.println("输入数量:");
       String num=scan.nextLine();
       Double xiaoji=Double.parseDouble(price)*Integer.parseInt(num);
       //声明一个xiaoji来计算每个商品的单价*数量后的合计
       list+="单价:"+price+",数量:"+num+",小计:"+xiaoji+"\n";
       //清单
       totalPrice+=xiaoji;
       //每个商品计入小计
     }

public static void main(String[] args){
      Cash cash=new Cash();
      boolean flag=true;
      while(flag){           //循环终止条件可改
              cash.buttonOK();
              if(cash.totalPrice>10){
              flag=false;
              }
      }
      System.out.println("=============");
      System.out.println("清单:\n"+cash.list);
      System.out.println("总价:"+cash.totalPrice);
//如果商场商品打折
//加下拉选择框或输入折扣可以解决

//即在buttonOK类中输入数量的下面加上
scan =new scanner(Systen.in);
System.out.println("输入折扣:");
String zhekou=scan.nextLine();
//并且小计的计算中需要改变如下:
Double xiaoji=Double.parseDouble(price)*Integer.parseInt(num)*Double.parseDouble(zhekou)/10;



//如果增加满减促销活动或则折扣种类过多将会难以实现

5. 使用简单工厂模式

用简单工厂模式:
现金收费抽象类CashSuper,里有收取现金的方法acceptCash()
有三种方案:正常收费,打折收费,满减收费
工厂类,封装现金收费类

import java.util.Scanner;
//现金收取父类
abstract class CashSuper{
   public abstract double acceptCash(double money);
}
//正常收费类
class CashNormal extends CashSuper{
  @Override
  public double acceptCash(double money){
    return money;
  }
}
//打折收费类,继承CashSuper
class CashRebate extends CashSuper{
   private double discount=0.00;
   //初始化时,必须输入折扣率,如八折就是0.8
   public CashRebate(double discount){
      this.discount=discount/10;
   }
   @override
   public double acceptCash(double money){
      return this.discount*monwy;

//返利收费,继承CashSuper
class CashReturn extends CashSuper{
   private double baseCash;//基础金额
   private double returnCash;//返现金额
   //初始化时必须要输入返利条件和返回值,比如满300反100,则baseCash为300,returnCash为100
   public CashReturn(double baseCash,double reurnCash){
       this.baseCash=baseCash;
       this.returnCash=returnCash;
   }
   @overrride
   public double acceptCash(double money){
       double result=money;
       if(money>=baseCash)//若大于返利条件,则需要减去返利值
         result=money-Math.floor(money/baseCash)*returnCash;
         //函数Math.floor(x)返回小于x的最大整数值
         return result;  
     }
     public double getBaseCash(){   return baseCash;   }
     public void setBaseCash(double baseCash)  {   this.baseCash=baseCash;   }
     public double getReturnCash() {  return returnCash;   }
     public void setReturnCash(double returnCash) {  this.returnCash=returnCash;  }
}

//现金收取工厂
class CashAcceptFactory{
    //根据条件生成相应的对象
    //静态方法特点可以使用类名调用
    public static CashSuper createCashAccept(String type,double discount,double basePrice,double returnPrice){
       CashSuper cs=null;
       if("1".equals(type)) cs=new CashNormal();
       else 
           if("2".equals(type)) cs=new CashRebate(discount);
           else
               if("3".equals(type)) cs=new CashReturn(basePrice,returnPrice);
     }
}

//客户端程序
public class program{
    public static void main(String[] args){
        boolean flag=true;
        String list="";
        Double totalPrice=0.00;
        while(flag){
          scanner scan=new Scanner(System.in);
          System.out.println("输入单价:");
          String price=scan.nextLine();
  
          scan=new Scanner(System.in);
          System.out.println("输入数量:");
          String num=scan.nextLine();

          scan=new Scanner(System.in);
          System.out.println("输入折扣类型(1无折扣 2打折 3满减):");
          String type=scan.nextLine();

          double discount=0.0d;
          double basePrice=0;
          double returnPrice=0;
          if("2".equals(type)){
               Scan=new Scanner(System.in);
               System.out.println("输入折扣:");
               discount=Double.parseDouble(scan.nextLine());
           }
           if("3".equals(type)){
                scan=new Scanner(System.in);
                System.out.println("返回基础金额:");
                basePrice=Double.parseDouble(scan.nextLine());
                scan=new Scanner(System.in);
                System.out.println("返现金额:");
                returnPrice=Double.parseDouble(scan.nextLine());
            }
            Double xiaoji=Double.parseDouble(pric。e)*Integer.parseInt(num);
            CashSuper cs=CashAcceptFactory.createCashAccept(type,discount,basePrice,returnPrice);   //类名调用静态方法
            double xiaoji=cs.accpetCash(xianjin);
            list+="单价:"+price+",数量:"+num+",折扣:"+discount+",小计:"+xiaoji+"\n";
            totalPrice+=xiaoji;
            if(totalPrice>10){
                flag=false;
                    }
            }
            System.out.println(list);
            System.out.println("总价:"+totalPrice);
       }
}

6.简单工厂模式的缺陷

简单工厂模式只是解决对象的创建问题,虽然工厂可能已经包括了所有的收费方式,但商场可能经常地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需要重新编译部署
不符合开闭原则,所以不是最好地办法。

原因是因为现金收取工厂与具体收费类是紧耦合的,需要改进成现金收取工厂中不要出现具体收费类,
而且现金工厂实际是现金收取控制类,可以用收费类的父类来代替具体收费类,并为其传入具体收费方式对象。
代码如下


//现金收取控制类
class CashContext{
   private CashSuper cs; //声明一个现金收费父类对象
   public CashContext(CashSuper csuper){
      //通过构造方法,传入具体的收费策略对象(正常
打折或返利)
       this.cs=csuper;  }
   public double GetResult(double money){
       //根据不同收费策略获得合计结果
       return cs.acceptCash(money);    }
}

//改造后的客户端代码
public class program{
  public static void main(String[] args){
       boolean flag=true;
       String list="";
       Double totalPrice=0.00;
       while(flag){
          Scanner scan=new Scanner(System.in);
          System.out.println("输入单价:");
          String price=scan.nextLine();

          scan=new Scanner(System.in);
          System.out.println("输入数量:");
          String num=scan.nextLine();

          scan=new Scanner(System.in);
          System.out.println("输入折扣类型(1无折扣2打折3满减):");
          String type=scan.nextLine();

          double discount=0.0d;
          double basePrice=0;
          double returnPrice=0;
          CashSuper cs=null;       //改造加入的

          if("1".equals(type))cs=new CashNormal();
          else 
               if("2".equals(type)){
                   scan=new Scanner(System.in);
                   System.out.println("输入折扣:");
                   discount=Double.parseDouble(scan.nextLine());
                   cs=new CashRebate(discount);
                   }
                   else if("3".equals(type)){
                       scan=new Scanner(System.in);
                       System.out.println("返现基础金额:");
                       basePrice=Double.parseDouble(scan.nextLine());
                       scan=new Scanner(System.in);
                       System.out.println("返现金额:");
                       returnPrice=Double.parseDouble(scan.nextLine());
                       cs=new CashReturn(basePrice,returnPrice);
                      }
                      Double xianjin=Double.parseDouble(price)*Integer.parse(num);
                      CashContext cc=new CashContext(cs);
                        //将相应的策略对象作为参数传入CashContext构造函数中

                      double xiaoji=cc.GetResult(xianjin);
                      list+="单价:"+price+",数量:"+num+",折扣:"+discount+",小计:"+xiaoji+"\n";
                      totalPrice+=xiaoji;
                      if(totalPrice>10){
                         flag=false;
                               }
                       }
                       System.out.println(list);
                       System.out.println("总价:"+totalPrice);
          }
}

策略模式下加入新的方法
只需要增加新的方法类
以及修改客户端即可

7. 简单工厂模式和策略模式

简单工厂模式:传条件进工厂类,工厂类就会返回一个对象给调用者,供调用者使用——最终给用户使用的是工厂返回的类
策略模式:创建一个Context类,(可以看作是工厂模式中工厂类的替代品)的对象context,传一个要使用的策略实例对象A个context,然后使用context调用A中的某些方法——最终给用户的是Context类。

8.策略模式的优缺点

策略类之间可以自由切换(由于策略类都实现一个接口)
避免使用多重条件选择语句,充分体现面向对象设计思想
扩展性良好,增加一个新的策略只需要添加一个具体的策略类,不需要改变原有的代码,符合“开闭原则”
缺点:
把分支判断又放回到客户端,需要改变需求算法时,还是要改变客户端程序
缺乏工厂模式的优势,客户端必须知道所有策略类,并自行决定使用哪一个策略类。
策略类增多

9.策略与简单工厂结合

针对策略模式的缺点:分支判断在客户端,改变算法时,要更改客户端的程序,意味着暴露了所有具体策略类
解决:其他不变,改变CashContext类,把分支判断放到环境角色(CashContext 类)中

class CashContext{
  private CashSuper cs=null;//声明一个现金收费父类对象
  public CashContext()//注意构造函数的参数不再是一个具体策略对象
  {
       Scanner scan=new Scanner(System.in);
       System.out.println("输入折扣类型(1无折扣2打折3满减):");
       String type=scan.nextLine();
       double discount=0.0d;
       double basePrice=0;
       double returnPrice=0;
       if("1".equals(type))  cs=new CashNormal();    //此处不同
         else{  //将实例化具体策略的过程移到Context类中,简单工厂的应用
         if("2".equals(type)){
          scan=new Scanner(System.in);
          System.out.println("输入折扣:");
          discount=Double.parseDouble(scan.nextLine());
          cs=new CashRebate(discount);     //此处不同
          }else
              if("3".equals(type)){
                  scan=new Scanner(System.in);
                  System.out.println("返现基础金额:");
                  basePrice=Double.parseDouble(scan.nextLine());
                  scan=new Scanner(System.in);
                  System.out.println("返现金额:");
                  returnPrice=Double.parseDouble(scan.nextLine());
                  cs=new CashReturn(basePrice,returnPrice);  //**此处不同**
                  }
   }
   public double GetResult(double money){  return cs.acceptCash(money);  }
}

//客户端
public class program{
   public static void main(String[] args){
       boolean flag=true;
       String list="";
       Double totalPrice=0.00;
       while(flag){
           Scanner scan=new Scanner(System.in);
           System.out.println("输入单价:");
           String price=scan.nextLine();
           scan=new Scanner(System.in);
           System.out.println("输入数量:");
           String price=scan.nextLine();
           Double xianjin=Double.parseDouble(price)*Integer.parseInt(num);
           CashContext cc=new CashContext();     //**此处不同**
           double xiaoji=cc.GetResult(xianjin);     //
           list+="单价:"+price+",数量:"+num+",小计:"+xiaoji+"\n";
           totalPrice+=xiaoji;
           if(totalPrice>10){  flag=false;   }  //标志可根据需要更改
           System.out.println(list);
           System.out.println("总价:"+totalPrice);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值