乐趣区

关于java:设计模式12-搞定最近大火的策略模式

开局还是那种图,最近策略模式貌似很火,各位客官往下看 …

策略模式到底是什么?

后面咱们其实曾经将结构型模式解说完了,剩下的全都是行为型模式,三种模式的辨别:

  • 创立型模式:如何创立一个对象
  • 结构型模式:对象外部的结构是如何结构的
  • 行为型模式:对象是如何运行(能够做什么)

而提到策略模式,咱们该如何了解呢?

  • 从北京到上海,能够坐飞机,也能够坐动车,也能够坐绿皮,甚至能够骑自行车,这些不同的形式,就是策略。
  • 一件商品,能够间接打 5 折,也能够加 100 再打 6 折,也能够打 5.5 折之后返回现金券,这些也是策略。
  • 带了 200 去超市买货色,能够买零食,也能够买生活用品,也能够啥也不买,这些也是策略。

下面的例子,其实咱们能够发现不同的策略其实是能够调换的,甚至策略细节之间能够混搭,那么很多时候咱们为了不便拓展,不便替换策略,对调用方屏蔽不同的策略细节,就能够应用策略模式。假使所有的策略全副写在一个类外面,能够是能够,这样保护代码会越来越简单,保护只会越来越艰难,外面会充斥着各种if-else,算法如果日益简单,动一发而牵全身,那就只剩下提桶跑路了。

策略模式是指有肯定口头内容的绝对稳固的策略名称。策略模式在现代中又称“计谋”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计策、策略。策略模式具备绝对稳固的模式,如“就事论事”、“出奇制胜”等。肯定的策略模式,既可利用于战略决策,也可利用于战术决策;既可施行于大零碎的全局性口头,也可施行于大零碎的局部性口头。

再说一个打工人都晓得的例子,比方每个人都要交个人所得税,咱们晓得个人所得税的计算形式是很简单的,税法计算就是一个行为,而可能存在不同实现算法,那每一种计算算法都是一种策略。

这些策略可能随时被替换掉,那么咱们为了好替换,互相隔离,就定义一系列算法,封装起来,这就叫策略模式。

策略模式的角色

策略模式个别有三种角色:

  • 形象的策略类(Strategy):将策略的行为形象出公共接口
  • 具体的策略类(ConcreteStrategy): 以 Strategy 接口实现某些具体的算法,具体的策略可能多个,也能够互相替换
  • 上下文(Context): 个别是封装了策略,以及策略的应用办法,对下层调用屏蔽了细节,间接调用即可。

策略模式的 demo

创立一个形象的策略接口:

public interface Strategy {int doStrategy();
}

创立 2 个具体的策略类,别离执行策略:

public class ConcreteStrategy1 implements Strategy{
    @Override
    public int doStrategy() {System.out.println("执行策略 1...");
        return 1;
    }
}

public class ConcreteStrategy2 implements Strategy{
    @Override
    public int doStrategy() {System.out.println("执行策略 2...");
        return 2;
    }
}

创立上下文类,封装策略类:

public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {this.strategy = strategy;}

    public void executeStrategy(){strategy.doStrategy();
    }
}

测试类:

public class Test {public static void main(String[] args) {Context context1 = new Context(new ConcreteStrategy1());
        context1.executeStrategy();

        context1 = new Context(new ConcreteStrategy2());
        context1.executeStrategy();}
}

测试后果:

执行策略 1
执行策略 2 

能够看到只有应用不同的策略,就能够执行不同的后果。

分红包策略实战

比方,工作中有波及到调配的策略,场景就是有红包,调配给若干个人,然而会有不同调配策略。这里不同的调配策略其实就是用策略模式来实现。能够随机调配,也能够平均分配,还能够依据各种权重来调配等等。这里只演示两种调配:

首先定义一个红包类,蕴含红包金额(咱们以分为单位,保留整数,防止金额小数不精确问题

public class RedPackage {
    public int remainSize;

    public int remainMoney;

    public RedPackage(int remainSize, int remainMoney) {
        this.remainSize = remainSize;
        this.remainMoney = remainMoney;
    }
}

定义一个调配策略的抽象类:

import java.util.List;

public interface AllocateStrategy {List<Integer> allocate(RedPackage redPackage);

}

平均分配的时候,如果不能均匀,那么第一位会有所增减:

import java.util.ArrayList;
import java.util.List;

public class AverageAllocateStrategy implements AllocateStrategy {
    @Override
    public List<Integer> allocate(RedPackage redPackage) {List<Integer> results = new ArrayList<>();
        Integer sum = redPackage.remainMoney;
        Integer average = sum / redPackage.remainSize;
        for (int i = 0; i < redPackage.remainSize; i++) {
            sum = sum - average;
            results.add(average);
        }
        if (sum != 0) {results.set(0, results.get(0) + sum);
        }
        return results;
    }
}

随机调配的时候,咱们将每一份随机增加到红包外面去,然而这样不能保障每一份都有金额(留神这个不要在生产应用,仅限测试


import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class RandomAllocateStrategy implements AllocateStrategy {
    @Override
    public List<Integer> allocate(RedPackage redPackage) {return ranRedPackage(redPackage.remainSize, redPackage.remainMoney);
    }

    public List<Integer> ranRedPackage(Integer count, Integer money) {List<Integer> result = new ArrayList<>();
        for (int i = 0; i < count; i++) {result.add(0);
        }
        for (int i = 1; i <= money; i++) {int n = new Random().nextInt(count);
            result.set(n, result.get(n) + 1);
        }
        return result;
    }

}

定义上下文类,封装不同的策略:

import java.util.List;

public class Context {
    private AllocateStrategy strategy;

    public Context(AllocateStrategy strategy) {this.strategy = strategy;}

    public List<Integer> executeStrategy(RedPackage redPackage){return strategy.allocate(redPackage);
    }
}

测试类:

import java.util.List;

public class Test {public static void main(String[] args) {RedPackage redPackage = new RedPackage(10,102);
        Context context = new Context(new RandomAllocateStrategy());
        List<Integer> list =context.executeStrategy(redPackage);
        list.forEach(l-> System.out.print(l+" "));

        System.out.println("");
        context = new Context(new AverageAllocateStrategy());
        list =context.executeStrategy(redPackage);
        list.forEach(l-> System.out.print(l+" "));
    }
}

能够看到调配的金额的确会随着策略类的不同产生不同的变动:

9 10 16 8 14 8 7 15 9 6 
12 10 10 10 10 10 10 10 10 10 

留神这个不能在生产应用!!!

优缺点

策略模式的长处:

  • 打消 if-else 语句,不同策略互相隔离,不便保护
  • 切换自在
  • 拓展性好,实现接口即可

策略模式的毛病:

  • 策略一旦数量过多,保护也会绝对比拟难,复用代码比拟难
  • 所有的策略类都对外裸露了,尽管个别通过 Context 上下文调用

策略模式比拟罕用,可能有些同学会混同策略模式和状态模式:

绝对于状态模式:策略模式只会执行一次办法,而状态模式会随着状态变动不停的执行状态变更办法。举个例子,咱们从 A 中央去 B 中央,策略就是飞机或者火车,状态模式则是可能到某一个中央,换了一种交通形式,到另外一个中央又换了一种交通形式。

小结

策略模式比拟罕用,外围就是隔离不同的策略,将具体的算法封装在策略外面,形象出一个策略抽象类,把不同具体策略类注入到上下文类中,达到抉择不同策略的目标。

然而如果策略很多,须要思考复用,策略类对外裸露的时候须要思考滥用危险,会毁坏封装性。

【作者简介】
秦怀,公众号【秦怀杂货店】作者,集体网站:http://aphysia.cn,技术之路不在一时,山高水长,纵使迟缓,驰而不息。

设计模式系列:

  • 设计模式【1】– 单例模式到底几种写法?
  • 设计模式【1.1】– 你想如何毁坏单例模式?
  • 设计模式【1.2】– 枚举式单例有那么好用么?
  • 设计模式【1.3】– 为什么饿汉式单例是线程平安的?
  • 设计模式【2】– 简略工厂模式理解一下?
  • 设计模式【2.1】– 简略工厂模式怎么演变成工厂办法模式?
  • 设计模式【2.2】– 工厂模式怎么演变成形象工厂模式?
  • 设计模式【3.1】– 浅谈代理模式之动态、动静、cglib 代理
  • 设计模式【3.2】– JDK 动静代理源码剖析有多香?
  • 设计模式【3.3】– CGLIB 动静代理源码解读
  • 设计模式【4】– 建造者模式详解
  • 设计模式【5】– 原型模式
  • 设计模式【6.1】– 初探适配器模式
  • 设计模式【6.2】– 再聊聊适配器模式
  • 设计模式【7】– 摸索一下桥接模式
  • 设计模式【8】– 手工耿教我写装璜器模式
  • 设计模式【9】– 外观模式?没那么高大上
  • 设计模式【10】– 顺便看看享元模式
  • 设计模式【11】– 搞定组合模式
退出移动版