在说明柔性多态之前先看看下面多态的设计
一般常规的多态程序设计,首先定义多态接口
public interface IShape3 { public float getArea(); }
然后在实体类中重写多态函数
public class Circle3 implements IShape3 { float r; public Circle3(float r){ this.r = r; } public float getArea(){ return r*r*(float)Math.PI; } }
public class Rect3 implements IShape3 { private float width,height; public Rect3(float width,float height){ this.width = width; this.height = height; } public float getArea(){ return width*height; } }
客户端通过动态绑定对接口编程实现了求圆或长方形面积功能。但是考虑一个问题,如果以后需要求图形的周长,那上面这个例子该如何修改?
思路是:重新定义接口IShape3,添加所需要的接口函数(求周长),然后再在继承该接口的对象(Circle3和Rect3)中再实现函数功能。
这样做的后果就是使接口以及实现模块,客户端程序都需要重新修改并且重新编译。但是在实际开发维护中,我们只希望仅仅在底层具体模块功能可以修改并编译,
而接口,上层模块以及客户端程序不需要更改或重新编写。
由此可以看到普通多态的局限性:如果接口函数内容发生变化,那么相应的各实现子类必须发生变化,导致相关联的各级模块必须重新编写和编译。而造成原因莫过于父类与自己的多态函数关联过强。
柔性多态的设计就是正是为了消除这种局限性,下面对上面提到的例子加以“改造”。
定义柔性多态接口
public interface IShape2 { public Object dispatch(int nID,Object in); }
具体实现类
public class Circle2 implements IShape2{ public float r; public Circle2(float r){ this.r = r; } public Object dispatch(int nId,Object in){ Object obj = null; switch (nId){ case 0: obj = getArea(in) ;break; case 1: obj = getPerimeter(in);break; } return obj; } Object getArea(Object in){ float area = (float)Math.PI*r*r; return new Float(area); } Object getPerimeter(Object in){ float len = (float)Math.PI*r*2.0f; return new Float(len); } }
柔性多态的设计思想如下:
1.接口内容固定,如上面IShape2中仅仅定义一个多态接口方法dispatch()
2.子类中重写动态函数dispatch仅仅起到转发作用,且转发的具体函数都不是多态函数。这与第一个例子中多态接口编程思想不一致。
假若现在需要添加新的功能,如求内接三角形的周长。现在只需要在Circle2类中添加一个普通方法getTriLength(),并且在多态函数中添加一个case开关,调用getTriLength()就可以了。
而对接口IShape2根本没有修改。从而削弱了接口类和实现类的强关联,这就是实现柔性多态的关键。
package interfaceAndAbstract.Polymorphism; /** * Created by lenovo on 2017/4/15. */ public class Circle2 implements IShape2{ public float r; public Circle2(float r){ this.r = r; } public Object dispatch(int nId,Object in){ Object obj = null; switch (nId){ case 0: obj = getArea(in) ;break; case 1: obj = getPerimeter(in);break; case 2: obj = getTriLength(in);break; } return obj; } Object getArea(Object in){ float area = (float)Math.PI*r*r; return new Float(area); } Object getPerimeter(Object in){ float len = (float)Math.PI*r*2.0f; return new Float(len); } Object getTriLength(Object in){ //具体实现代码 return null; } }
下面分析一下dispatch()方法的参数理解
1.上面例子中2个参数,整型的普通功能号nID,输入参数in,类型是Object,相当于泛型编程,使程序更加灵活。
2.改函数的返回值是Object对象,若为null,表明计算失败;若not null,则在强调方用强制类型转换才能得到需要的结果。
测试类
public class Test { public static void main(String[] args) { IShape2 obj = new Circle2(10.0f); Float result = (Float)obj.dispatch(1,null); System.out.println("半径10圆形面积:" + result.floatValue()); } }
完善:根据nID来获得执行函数并不友好,对于使用者不好去记住每个功能的ID。因此可以在接口中定义一个方法public int query(String strID)
在子类中实现,通过方法名获得nID,再由nID来获得执行函数。
总结:固化父类接口函数定义,子类通过重写多态派发函数,这是柔性多态的基本设计思想。