首页 > 代码库 > 3.2.4 方法类型化

3.2.4 方法类型化

方法类型化——将某个或某些方法(准确的说,是方法头或接口) 提升为类型,是方法型模式的共同技术基础和设计思路

Java中以抽象类或接口类型封装该接口。Java接口的作用在此被充分展示。

策略模式中,排序策略int[]sort(int[] arr)被抽象类IntSort封装。更多的方法类型化例子在随后的章节中介绍。本节将介绍方法对象化的两个重要应用。

1. 避免类型爆炸

假定类Person有eat()方法——大家全部用筷子(final方法);有say()方法——每人说自己的方言。于是,类Person代码如3-5所示。按照“地域”可以设计Person的各种子类如BeiJin、Wuhan等——假设程序员可以容忍这样的30个子类。

再假定Person还有walk()方法——慢条斯理、风风火火等七八种实现方式。

请问,你如何面对慢条斯理的武汉人、风风火火的上海人……say()与walk()的任意组合将导致至少30*7即210个以上的Person的子类。这是不可容忍的设计。

例程3-5组合的数目
package method.strategy;
public abstract class Person{
    public final void eat(){
        System.out.println("用筷子");
    }    
abstract public void say();//每人说自己的方言
abstract public void walk();//慢条斯理、风风火火等七八种实现方式
}

为了避免类型(数量)的爆炸,可以将say()和walk()类型化,作为两个策略类层次各自封装,取名SayBehavior和WalkBehavior。于是类Person的类层次转化为方法类型或策略类WalkBehavior、SayBehavior的类层次。重构后,系统中类的数量从m*n降低为m+n+2。

例程 3 6 重构Person类
package method.strategy;
import tool.God;
public class  Person{
    private WalkBehavior walk = null;
    private SayBehavior say = null;
    public Person(){
        walk = (WalkBehavior)God.create("3-6-Walk");
        say = (SayBehavior)God.create("3-6-Say");
    }
    public final void eat(){
        System.out.println("用筷子");
    }
    public void say(){
        this.say.say();
    }
    public void walk(){
        this.walk.walk();
    }
}
此时,Person是两个策略类的环境类,它的用户App仅依赖环境类。

package method.strategy;

public class App{
   public static void main(String args[]){
     Person one = new Person();  //上下文对象
     one.say();
     one.walk();
    }
}


由这个例子,大家联想到什么模式?

2.无限可能性和函数接口

如果Person只有一个需要多态化的方法如say(),通常Person本身作为策略类而非定义一个SayBehavior。但是,如果say()有无穷多或者足够大的变化呢?

这时将有无穷变化的say()设计成接口类型——Java8中设计为函数接口则是合理的选择。

例程 3 7无限可能
package method.strategy;
@FunctionalInterface
public interface SayStyle{
   void say();
}
package method.strategy;
public class Man{
    private SayStyle say = null;
    public void setSayStyle(SayStyle say){
        this.say = say;
    }
    public final void eat(){
        System.out.println("用筷子");
    }
    //每人说自己的方言
    public void say(){
        this.say.say();
    }
}

package method.strategy;
public class App{
    public static void test(){
     Man one = new Man();  //上下文对象
     SayStyle say = ()->{System.out.println("格老子");};
     one.setSayStyle( say);
     one.say();
    }
}

此时,Man是环境类,它的用户App需要依赖环境类Man和函数接口SayStyle。

虽然说函数接口的基本目标是实现回调,但是在非框架场合,代码由客户类提供也是函数接口的正常应用。毕竟回调只是分层结构中的术语,在非分层结构中,其代码和普通多态没有任何差异。

3.2.4 方法类型化