大家好, 这里是K字的研究. 今天聊聊设计模式里的策略模式.
说实话, 我一直以为自己没使用过这个设计模式. 然后, 就去重新学习了一把…结果发现,哈哈哈.原来,这么简单的吗?策略模式一直在我们身边啊.
很多时候,并不是不存在.而是设计模式已经像空气一样, 渗透进了我们日常的编码中. 甚至,都难以察觉他的存在. 今天老K和你一起,揪出身边的策略模式.
策略模式解决什么问题
老规矩, 先确定一个模式解决什么问题.
先去翻GOF
原书
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Design Patterns: Elements of Reusable Object-Oriented Software.
换成人话就这么几个点
-
一家子算法
-
可互换
实际使用起来, 其实就是把实现了某个策略的可执行子类
, 当做参数传入
某个方法内.
寻找策略模式
AWT 相关
按照这个特征, 来找一下, JDK里符合这个特点的代码. 会发现, JDK 里用到策略模式最多的, 其实是awt
之类.
不过这东西吧,半死不活的样子,让人寒心. 不提他了. 喜欢的可以搜源码:Strategy
这个词儿,有惊喜.
RejectedExecutionHandler
还有一个非常灯下黑的地方:RejectedExecutionHandler
.
是滴, 线程池执行器的的4种拒绝策略
. 可不, 这都写到头上, 他是一种策略了. 你注意到了吗? 可以考虑试试看老K做的动画化的线程池执行器. 早期没做好, 回头会再重画的.
DiscardOldestPolicy, DiscardPolicy, AbortPolicy, CallerRunsPolicy
jkl,公众号:K字的研究线程池执行器执行过程可视化
除了这个,其实还有一个更防不胜防的.
java.util.Comparator
java.util.Comparator
提供了一系列的比较算法:
-
naturalOrder 自然顺序
-
comparingDouble 把对象转成
double
来比较 -
comparingInt 把对象转成
int
来比较 -
nullsFirst null放前面
-
nullsLast null放后面
当我们有一个对象的列表待比较, 比如: [“30”, “4”,”1″,”2″,”11″]
对他施加不同的排序比较算法.
List<String> list = Arrays.asList("30", "4", "1", "2", "11");
list.sort(Comparator.naturalOrder());
System.out.println("list naturalOrder = " + list);
list.sort(Comparator.comparingInt(Integer::parseInt));
System.out.println("list intOrder = " + list);
//按字符串排序
//list naturalOrder = [1, 11, 2, 30, 4]
//按数字排序
//list intOrder = [1, 2, 4, 11, 30]
你看,
-
这些比较算法都实现了
Comparator
-
可以执行
-
可以当做参数传入
sort()
方法里
这还真是一个策略模式.
相较于标准的策略模式示例, 这个策略模式的实现, 还有一点好, 他支持自定义策略. 除了原本提供的,自然序
,int序
,我们甚至可以自己来实现一个更复杂的排序规则.
到底能用多复杂的比较顺序, 可以看看这篇文章. 老K在这里甚至实现了一个, 按照在干支纪年中的先后顺序进行比较的Comparator
策略.
(x, y) -> (int) ((((x.v1.v2) * 6 – 5 * (x.v2.v2)) + 60) % 60 – (((y.v1.v2) * 6 – 5 * (y.v2.v2)) + 60) % 60));
J K L,公众号:K字的研究Java8 的 Stream 还能写成这样?
函数式接口+策略模式
策略模式的特点就是,
-
有一个上下文
-
一个定义好的算法族
-
一个接收
可执行对象
的方法 -
可以扩展策略(这个是可选的)
这个可执行对象,一般是抽象类
,抽象接口
的实现. 在支持了函数式接口的时代, 这个地方其实可以更简单, 按照函数式接口来收参数.那么很多时候, 函数式的策略模式可以定义成接受函数式接口.
实战策略模式
下面的是一个简单的策略模式, 将10以下数字转成String. 默认提供了,
-
二进制格式
-
罗马字格式
-
小写汉字
-
大写汉字
-
十六进制格式
当然, 因为接收的是函数式接口, 这里可以使用任何符合签名的内容来进行扩展.
package sh.now.afk.mp;
import java.util.function.Function;
public class Num {
public static void main(String[] args) {
System.out.println("new Num(3).toString(Num.HANS) = " + new Num(3).toString(Num.HANS));
}
int value;
public final static Function<Integer, String> ROMAN = Num::toRoman;
public final static Function<Integer, String> DEFAULT = (i) -> Integer.toString(i);
public final static Function<Integer, String> HEX = (i) -> Integer.toHexString(i);
public final static Function<Integer, String> BIN = (i) -> Integer.toBinaryString(i);
public final static Function<Integer, String> HANS = Num::toHanzi;
public final static Function<Integer, String> HANC = Num::toHanziComplex;
public Num(int i) {
this.value = i;
}
static String toHanzi(Integer num) {
return s[num];
}
static final String[] s = {"", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
static final String[] c = {"", "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
static final String[] r = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
static String toHanziComplex(Integer num) {
return c[num];
}
static String toRoman(Integer num) {
return r[num];
}
public String toString(Function<Integer, String> toStringStrategy) {
return "编号:" + toStringStrategy.apply(value);
}
}
为什么不写10以上的数字? 因为篇幅太小, 装不下那么多代码, 😄. 使用起来, 是这样的.
new Num(3).toString(Num.HANZIS);
//编号:三
总结
好了, 今天就到这里为止了. 策略模式其实并不难. 重点是数据和算法的关注点分离,将算法变量化,参数化. 在函数式编程大行其道的今天, 策略模式的算法, 可以由实现抽象类或者接口变的更为容易和简单.
不熟悉函数式接口的朋友, 可以简单听下我的总结,更细节的内容还是要自己学.
-
正常的Java方法, int foo(int bar) 是由,方法名+参数类型确定的
-
函数式接口, 由入参+返回值确定,因此非常适合当做参数传递.
我在一篇题到委托的文章里也写过, 这个和C#的委托有本质上的相似.可以参考.
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用. 在方法重载的上下文中,方法的签名不包括返回值。 但在委托的上下文中,签名包括返回值。 换句话说,方法和委托必须具有相同的返回类型
原文始发于微信公众号(K字的研究):设计模式之函数风策略模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24663.html