看下面算术类:
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());
}
综上所述,我们要用组合来代替继承。