看下面算术类:
public class Arithmetica { public int sumTwo(int one, int second) { return one + second; } public int sumAll(int... numbers) { if (numbers.length == 0) { return 0; } int sum = numbers[0]; for (int i = 1; i < numbers.length; i++) { sum = sumTwo(sum, numbers[i]); } return sum; } public int sumRange(int from, int to) { int len = to - from; if (len < 0) { len = -len; from = to; } int[] array = new int[len + 1]; for (int i = 0; i <= len; i++) { array[i] = from + i; } return sumAll(array); } }当这个类发布出去后,使用这个类的人发现继承这个类,可以很容易地进行阶乘计算,于是定义类如下:
public final class Factorial extends Arithmetica { public static int factorial(int n) { return new Factorial().sumRange(1, n); } @Override public int sumTwo(int one, int second) { return one * second; } }后来,再下一个版本中,API开发者升级了这个API的某个方法,性能得以提升,如下:
public int sumRange(int from, int to) { return (from + to) * (Math.abs(to - from) + 1) / 2; }
但是我们会看到,升级后的API对于使用该API的开发者写的子类Factorial就不适用了。
为了解决以上的问题,可以使用组合来代替继承。
先来看下表,下表中是java不同访问级别的声明方式的不同语义,有的只有一种语义,有的有多种。
表格中中间一列指的是主要的语义,右边一列指的是额外的含义。我们要用只要主要含义,没有额外含义的访问级别。
看下表,左列是有不同含义的访问级别,右列是改写的只有一种含义的。
如上,现在我们知道对于类中的方法如果存在多种语义,我们如何来重写这个类。
下面要做进一步转换,来使类中的方法有更清晰的语义。
看下面的代码:
public abstract class MixedClass { private int counter; private int sum; protected MixedClass() { super(); } public final int apiForClients() { int subclass = toBeImplementedBySubclass(); sum += subclass; return sum / counter; } protected abstract int toBeImplementedBySubclass(); protected final void toBeCalledBySubclass() { counter++; } }
如果一个类是由单一语义的方法组成,那么我们可以用两个类和一个接口来代替原来的抽象类。如下:
public final class NonMixed { private int counter; private int sum; private final Provider impl; private NonMixed(Provider impl) { this.impl = impl; } public static NonMixed create(Provider impl) { NonMixed api = new NonMixed(impl); Callback callback = new Callback(api); impl.initialize(callback); return api; } public final int apiForClients() { int subclass = impl.toBeImplementedBySubclass(); sum += subclass; return sum / counter; } public interface Provider { public void initialize(Callback c); public int toBeImplementedBySubclass(); } public static final class Callback { NonMixed api; Callback(NonMixed api) { this.api = api; } public final void toBeCalledBySubclass() { api.counter++; } } }这样,使用该API的人就可以如下使用:
@Test public void useWithoutMixedMeanings() { class AddFiveMixedCounter implements NonMixed.Provider { private Callback callback; public int toBeImplementedBySubclass() { callback.toBeCalledBySubclass(); return 5; } public void initialize(Callback c) { callback = c; } } NonMixed add5 = NonMixed.create(new AddFiveMixedCounter()); assertEquals("5/1 = 5", 5, add5.apiForClients()); assertEquals("10/2 = 5", 5, add5.apiForClients()); assertEquals("15/3 = 5", 5, add5.apiForClients()); }综上所述,我们要用组合来代替继承。