Hello Stackoverflowers,
想象一下以下的基类
import java.util.ArrayList;
public class Array
{
private ArrayList<Object> a = new ArrayList<Object>();
public void add(Object element)
{
a.add(element);
}
public void addAll(Object elements[])
{
for (int i = 0; i < elements.length; ++i)
a.add(elements[i]); // this line is going to be changed
}
}
这是Derived类:
public class ArrayCount extends Array
{
private int count = 0;
@Override
public void add(Object element)
{
super.add(element);
++count;
}
@Override
public void addAll(Object elements[])
{
super.addAll(elements);
count += elements.length;
}
}
Array add()将一个元素添加到本地ArrayList。 Array addAll()为每个元素调用本地ArrayList。
ArrayCount add()调用其父级的add(),然后递增计数。 ArrayCount addAll()调用其父级的addAll(),然后将计数增加元素数。
现在进行重大改变。 Base类中注释的代码行更改为以下内容:
public void addAll(Object elements[])
{
for (int i = 0; i < elements.length; ++i)
add(elements[i]); // this line was changed
}
现在,ArrayCount addAll()调用其父级的addAll(),它在内部调用已被Derived类覆盖的add()。
Derived类的作者必须知道Base类是如何实现的。并且他们必须被告知Base类中的每一个变化,因为它可能以不可预测的方式打破他们的Derived类。
我正在寻找一种正确的方法来实现这一点,这将尊重黑盒编程概念。因为这个例子强制派生类的写者知道如何实现基类并知道每个更改
答案 0 :(得分:2)
我假设“Black Box”是指:“派生类的实现者必须不知道基类Array的实现细节”。我会选择使用委托的装饰者,因为这可能是更好的方法:
Section GroupTheory.
Variable G: Set.
Variable operation: G -> G -> G.
Variable e : G.
Variable inv : G -> G.
Infix "*" := operation.
Hypothesis associativity : forall x y z : G, (x * y) * z = x * (y * z).
Hypothesis identity : forall x : G, exists e : G, (x * e = x) /\ (e * x = x).
Hypothesis inverse : forall x : G, (x * inv x = e) /\ (inv x * x = e).
Theorem latin_square_property :
forall a b : G, exists x : G, a * x = b.
proof.
let a : G, b : G.
take (inv a * b).
have H1:(a * (inv a * b) = (a * inv a) * b) by associativity.
have H2:(a * inv a = e) by inverse.
have H3:(e * b = b) by identity.
have (a * (inv a * b) = (a * inv a) * b) by H1.
~= (e * b) by H2.
~= (b) by H3.
hence thesis.
end proof.
Qed.
End GroupTheory.
注意:为简洁起见,我省略了输入参数检查。
如果public class ArrayCount {
private int count = 0;
private Array a;
public ArrayCount(Array a) {
this.a = a;
}
public void add(Object element) {
a.add(element);
++count;
}
public void addAll(Object elements[]) {
a.addAll(elements);
count += elments.length;
}
}
和Array
同时实现相同的界面,例如ArrayCount
,您仍然可以互换使用这些类:
IArray
答案 1 :(得分:0)
将基类方法标记为final,但包括支持扩展的方法。
import java.util.ArrayList;
public class Array {
private final ArrayList<Object> a = new ArrayList<Object>();
public final void add(Object element) {
a.add(element);
afterAdd(element);
}
public final void addAll(Object[] elements) {
for (Object element : elements) {
a.add(element);
}
afterAddAll(elements);
}
protected void afterAddAll(Object[] elements) {
return;
}
protected void afterAdd(Object element) {
return;
}
}
所以子类只能覆盖支持方法
public class ArrayCount extends Array {
private int count = 0;
@Override
protected void afterAdd(Object element) {
++count;
}
@Override
protected void afterAddAll(Object[] elements) {
count += elements.length;
}
}
答案 2 :(得分:0)
当您使用黑匣子方法时,您只需调用对象的方法,因此您不关心它是如何实现的。
Class的公共方法是契约,用于定义您提供的内容和获得的内容。
维护Class的开发人员可以更改除合同或方法签名之外的任何内容。
如果你扩展一个类,并且你要覆盖一个或多个方法,那么你应该关注类的实现,因为你将专门研究类的一些行为。
要清楚地分离具有相同接口的不同类,您应该使用java 接口,这样每个开发人员都可以自己实现业务逻辑。
当你知道某些方法可以专门化时,你可以设计一个可以从其他人扩展的类。
但是如果你不想那样,你可以通过定义那些方法 final 来决定不能覆盖该方法。
通过这种方式,您可以告诉其他开发人员,他们将使用您不希望扩展某些行为的代码。