/*第二章行为参数化2.2模板方法模式2.2.2
在2.2模板方法模式中,插入本节。直接用Sum为例,不需要搞新例子。*/
在Sum设计中,需要注意的问题是:抽象方法item(intx) 有无穷多或者足够多的实现方式;而且存在2个抽象方法的组合。
--------------------------------------------------------
道生一,一生二,二生三,三生万物。多次使用策略模式需要考虑两个问题:组合爆炸和无限可能。
单纯地使用如Netbeans的重构-提取接口是不够的,我们需要将提取的接口由父类型(is_a)修改成使用关系。
1. 避免类型爆炸
抽象类型Sum(或Context)有两个抽象方法——next()与item (),如果两个抽象方法分别有m和n种实现,则Sum因为方法实现的任意组合,将导致它可以有m*n个子类型。也就是说,方法实现的任意组合将导致Sum的子类型数量急剧膨胀。
为了避免类型(数量)的爆炸,需要对Sum进行如下步骤的重构。
(1)以INext和IItem分别封装next ()和item ()。显然,不可设计如下Sum的类层次,
public abstract class Sum implements INext, IItem
因为将Sum作为这两个接口的子类型,对于解决类型爆炸问题没有任何帮助
(2)以Sum作为环境类(context),Sum使用(has-s关系) 这两个接口。于是,原来的Sum的类层次(可能存在子类型m*n个) 转化为2个策略的类层次(共有子类型m+n个)。使用INext和IItem时,既可以将它们作为成员变量,也可以作为参数。
(3) 而Sum则从抽象类返璞归真为具体的工具类,仅仅提供final方法。
为了避免类型爆炸问题,通常多次使用策略模式——或者说使用多重策略。模板方法模式、桥接模式、装饰模式等等均面临类型爆炸问题,所以都使用了多重策略。
只要一个类有两个抽象方法,而且它们的实现方式不相互制约,就会导致该类子类型的数量爆炸。模板方法模式、桥接模式均面临这一问题,所以将它们作为n次策略的变体。
2.无限可能性和函数接口
Sum的设计者应该放弃对IItem变量的初始化,而将它作为方法的形参,要求使用者给出IItem对象(由实参)对形参进行赋值。特别的,如果能够使IItem只有一个抽象方法,将它设计成函数接口,以便用户方便地使用Java8的lambda表达式作为实参。
总之,应对无限可能的最佳选项是行为参数化。重构Sum得到新的类型Accumulate。
例程3-3的Person作为环境类,say(SayBehavior sayStyle)的参数则是策略。
-------------------------
而命令模式的典型例子java.lang.Runnable及其实现类,与本例程的结构相同。
【虽然说函数接口的基本目标是实现回调,但是在非框架场合,代码由客户类提供也是函数接口的正常应用。毕竟回调只是分层结构中的术语,在非分层结构中,其代码和普通多态没有任何差异。】