共计 4693 个字符,预计需要花费 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】– 搞定组合模式