zoukankan      html  css  js  c++  java
  • 设计模式-----策略模式

    策略模式

    定义

    所谓策略模式就是定义了算法族,分别封装起来,让他们之前可以互相转换,此模式让该算法的变化独立于使用算法的客户

    理解

    **策略这个词应该怎么理解,打个比方说,我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略 **

    **再比如我们去逛商场,商场现在正在搞活动,有打折的、有满减的、有返利的等等,其实不管商场如何进行促销,说到底都是一些算法,这些算法本身只是一种策略,并且这些算法是随时都可能互相替换的,比如针对同一件商品,今天打八折、明天满100减30,这些策略间是可以互换的 **

    UML类图

    **其中,Context是上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用;Strategy是策略类,用于定义所有支持算法的公共接口;ConcreteStrategy是具体策略类,封装了具体的算法或行为,继承于Strategy **

    解释

    1.Context 上下文

    Context上下文角色,也叫Context封装角色,起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化

    public class Context {
    
        Strategy strategy;
    
        public Context(Strategy strategy){
            this.strategy = strategy;
        }
    
        /**
         * 上下文接口
         */
        public void contextInterface(){
            strategy.algorithmInterface();
        }
    }
    

    2.Strategy 策略角色

    抽象策略角色,是对策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。algorithm是“运算法则”的意思

    public interface Strategy {
        /**
         * 算法方法
         */
        public void algorithmInterface();
    }
    

    3.具体策略角色

    用于实现抽象策略中的操作,即实现具体的算法,下方用print代替。测试类共3个ConcreteStrategy,其它两个类与ConcreteStrategyA同理,就不再赘述了

    class ConcreteStrategyA implements Strategy{
    
        @Override
        public void algorithmInterface() {
            System.out.println("策略A的具体算法实现");
        }
    }
    

    4.Client 客户端

    下面依次更换策略,测试一下策略模式

    public class StrategyClient {
        public static void main(String[] args) {
            Context context;
    
            context = new Context(new ConcreteStrategyA());
            context.contextInterface();
    
            context = new Context(new ConcreteStrategyB());
            context.contextInterface();
    
            context = new Context(new ConcreteStrategyC());
            context.contextInterface();
        }
    }
    

    运行结果如下:

    实例一:

    我们都知道排序算法有很多种,但是什么时候选择冒泡排序,什么时候选择选择排序,什么时候选择插入排序,所以这里用排序算法来演示策略模式的实现

    public void selectSort(String type){
            if("type1".equals(type)){
                //选择快速排序
            }
            else if("type2".equals(type)){
                //选择插入排序
            }
            else if("type3".equals(type)){
                //选择冒泡排序
            }
            else if("type4".equals(type)){
                //选择选择排序
            }
            ......
        }
    

    对于这样的代码实现,除了代码中充斥着大量的if…else if…else,导致程序可维护性很差,而且系统的可扩展性不好,如果某个排序模块进行更改了,有可能需要修改源代码。所以在对于这样的情景是非常合适使用策略模式的。

    那么如何使用策略模式呢?首先我们需要定义一个接口,该接口提供排序算法,然后定义想要的排序算法,实现给接口即可。如下:

    首先是Sort接口,该接口定义了排序算法,所有的排序算法都应该实现该接口

    public interface Sort{
        public abstract int[] sort(int arr[]);
    }
    

    然后是三个具体的排序算法,他们实现Sort接口。

    **冒泡排序:BubbleSort.java **

    public class BubbleSort implements Sort{
        public int[] sort(int arr[]){
           int len=arr.length;
           for(int i=0;i<len;i++){
               for(int j=i+1;j<len;j++){
                  int temp;
                  if(arr[i]>arr[j]){
                      temp=arr[j];
                      arr[j]=arr[i];
                      arr[i]=temp;
                  }             
               }
            }
            System.out.println("冒泡排序");
            return arr;
        }
    }
    

    **插入排序:InsertionSort.java **

    public class InsertionSort implements Sort {
        public int[] sort(int arr[]) {
            int len = arr.length;
            for (int i = 1; i < len; i++) {
                int j;
                int temp = arr[i];
                for (j = i; j > 0; j--) {
                    if (arr[j - 1] > temp) {
                        arr[j] = arr[j - 1];
    
                    } else
                        break;
                }
                arr[j] = temp;
            }
            System.out.println("插入排序");
            return arr;
        }
    }
    

    **选择排序:SelectSort.java **

    public class SelectionSort implements Sort {
        public int[] sort(int arr[]) {
            int len = arr.length;
            int temp;
            for (int i = 0; i < len; i++) {
                temp = arr[i];
                int j;
                int samllestLocation = i;
                for (j = i + 1; j < len; j++) {
                    if (arr[j] < temp) {
                        temp = arr[j];
                        samllestLocation = j;
                    }
                }
                arr[samllestLocation] = arr[i];
                arr[i] = temp;
            }
            System.out.println("选择排序");
            return arr;
        }
    }
    

    **最后就是测试类客户端了Client.java **

    public class ArrayHandler
    {
        private Sort sortObj;
        
        public int[] sort(int arr[])
        {
            sortObj.sort(arr);
            return arr;
        }
    
        public void setSortObj(Sort sortObj) {
            this.sortObj = sortObj; 
        }
    }
    
    public class Client
    {
        public static void main(String args[])
        {
           int arr[]={1,4,6,2,5,3,7,10,9};
           int result[];
           ArrayHandler ah=new ArrayHandler();
           
           Sort sort = new SelectionSort();    //使用选择排序
           
           ah.setSortObj(sort); //设置具体策略
           result=ah.sort(arr);
           
           for(int i=0;i<result.length;i++)
           {
                   System.out.print(result[i] + ",");
           }
        }
    }
    

    运行结果:

    选择排序

    1,2,3,4,5,6,7,9,10,

    实例二:

    **以商场促销为例使用策略模式实现商场促销算法 **

    1. 上下文类

      首先声明一个CashSuper对象,通过构造方法,传入具体的收费策略,getResult()方法的功能为根据收费策略的不同获得计算结果。

    public class CashContext {
        
        private CashSuper cashSuper;
        
        public CashContext(CashSuper cashSuper) {
            this.cashSuper = cashSuper;
        }
        
        public double getResult(double money) {
            return cashSuper.acceptCash(money);
        }
    
    }
    

    2. 现金收费抽象类

      策略类,为抽象类,抽象出收费的方法供子类实现。

    public abstract class CashSuper {
        
    	public abstract double acceptCash(double money);
    
    }
    

    3. 正常收费子类

      没有任何活动的情况,正常收费,返回原价。

    public class CashNormal extends CashSuper {
    
    	@Override
    	public double acceptCash(double money) {
    		return money;
    	}
    }
    

    4. 打折收费子类

      打折活动,根据折扣返回打折后的价格。

    public class CashRebate extends CashSuper {
        
        private double moneyRebate = 1;    //折扣
        
        public CashRebate(double moneyRebate) {
            this.moneyRebate = moneyRebate;
        }
    
        @Override
        public double acceptCash(double money) {
            return money * moneyRebate;
        }
    }
    

    5. 返利收费子类

      返利活动,输入返利条件和返利值,比如满300返100,moneyCoditation为300,moneyReturn为100。

      result = money - Math.floor(money / moneyConditation) * moneyReturn; 的意思为,如果当前金额大于等于返利条件,则使用当前金额减去返利值

    public class CashReturn extends CashSuper {
    
        private double moneyConditation = 0.0;    //返利条件
        private double moneyReturn = 0.0d;    //返利值
        
        public CashReturn(double moneyConditation, double moneyReturn) {
            this.moneyConditation = moneyConditation;
            this.moneyReturn = moneyReturn;
        }
    
        @Override
        public double acceptCash(double money) {
            double result = money;
            
            if (money >= moneyConditation) {
                result = money - Math.floor(money / moneyConditation) * moneyReturn;
            }
            
            return result;
        }
    }
    

    6. Client客户端

      下面写一个简单的程序测试一下上方编写的代码。

     public class Client {
        
        public static void main(String[] args) {
            CashContext cashContext = null;
            
            Scanner scanner = new Scanner(System.in);
            System.out.print("请输入打折方式(1/2/3):");
            int in = scanner.nextInt();
            String type = "";
            
            switch (in) {
                case 1:
                    cashContext = new CashContext(new CashNormal());
                    type += "正常收费";
                    break;
                    
                case 2:
                    cashContext = new CashContext(new CashReturn(300, 100));
                    type += "满300返100";
                    break;
                    
                case 3:
                    cashContext = new CashContext(new CashRebate(0.8));
                    type += "打8折";
                    break;
        
                default:
                    System.out.println("请输入1/2/3");
                    break;
            }
            
            double totalPrices = 0;
            
            System.out.print("请输入单价:");
            double price = scanner.nextDouble();
            System.out.print("请输入数量:");
            double num = scanner.nextDouble();
            totalPrices = cashContext.getResult(price * num);
            
            System.out.println("单价:" + price + ",数量:" + num + ",类型:" + type + ",合计:" + totalPrices);
            
            scanner.close();
        }
    
    }
    

    正常收费结果如下:

    应用

    1.何时使用

    • 一个系统有许多类,而区分它们的只是他们直接的行为时

    2.方法

    • 将这些算法封装成一个一个的类,任意的替换

    3.优点

    • 算法可以自由切换
    • 避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护)
    • 扩展性良好,增加一个策略只需实现接口即可

    4.缺点

    • 策略类数量会增多,每个策略都是一个类,复用的可能性很小
    • 所有的策略类都需要对外暴露

    5.使用场景

    • 多个类只有算法或行为上稍有不同的场景
    • 算法需要自由切换的场景
    • 需要屏蔽算法规则的场景

    6.应用实例

    • 出行方式,自行车、汽车等,每一种出行方式都是一个策略
    • 商场促销方式,打折、满减等
    • Java AWT中的LayoutManager,即布局管理器

    7.注意事项

    • 如果一个系统的策略多于四个,就需要考虑使用混合模式来解决策略类膨胀的问题
  • 相关阅读:
    react ts axios 配置跨域
    npm run eject“Remove untracked files, stash or commit any changes, and try again.”错误
    java 进程的参数和list的线程安全
    帆软报表 大屏列表跑马灯效果JS
    帆软报表 快速复用数据集,避免重复劳动
    分析云 OA中部门分级思路和实现方法
    分析云 分段器 只显示一个块的数据
    分析云 更改服务默认的端口号
    分析云U8项目配置方法新版本(2)
    Oracle 创建时间维度表并更新是否工作日字段
  • 原文地址:https://www.cnblogs.com/MessiXiaoMo3334/p/11779509.html
Copyright © 2011-2022 走看看