设计模式之函数风策略模式

大家好, 这里是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.

换成人话就这么几个点

  1. 一家子算法

  2. 可互换

实际使用起来, 其实就是把实现了某个策略的可执行子类, 当做参数传入某个方法内.

寻找策略模式

AWT 相关

按照这个特征, 来找一下, JDK里符合这个特点的代码. 会发现, JDK 里用到策略模式最多的, 其实是awt之类.

不过这东西吧,半死不活的样子,让人寒心. 不提他了. 喜欢的可以搜源码:Strategy这个词儿,有惊喜.

RejectedExecutionHandler

还有一个非常灯下黑的地方:RejectedExecutionHandler.

是滴, 线程池执行器的的4种拒绝策略. 可不, 这都写到头上, 他是一种策略了. 你注意到了吗? 可以考虑试试看老K做的动画化的线程池执行器. 早期没做好, 回头会再重画的.

DiscardOldestPolicy, DiscardPolicy, AbortPolicy, CallerRunsPolicy

jkl,公众号:K字的研究线程池执行器执行过程可视化

除了这个,其实还有一个更防不胜防的.

java.util.Comparator

java.util.Comparator 提供了一系列的比较算法:

  1. naturalOrder 自然顺序

  2. comparingDouble 把对象转成double来比较

  3. comparingInt 把对象转成int来比较

  4. nullsFirst null放前面

  5. 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 还能写成这样?

函数式接口+策略模式

策略模式的特点就是,

  1. 有一个上下文

  2. 一个定义好的算法族

  3. 一个接收可执行对象的方法

  4. 可以扩展策略(这个是可选的)

这个可执行对象,一般是抽象类,抽象接口的实现. 在支持了函数式接口的时代, 这个地方其实可以更简单, 按照函数式接口来收参数.那么很多时候, 函数式的策略模式可以定义成接受函数式接口.

实战策略模式

下面的是一个简单的策略模式, 将10以下数字转成String. 默认提供了,

  1. 二进制格式

  2. 罗马字格式

  3. 小写汉字

  4. 大写汉字

  5. 十六进制格式

当然, 因为接收的是函数式接口, 这里可以使用任何符合签名的内容来进行扩展.

 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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!