黑匣子和遗产

时间:2016-08-05 15:44:13

标签: java oop inheritance

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类。

我正在寻找一种正确的方法来实现这一点,这将尊重黑盒编程概念。因为这个例子强制派生类的写者知道如何实现基类并知道每个更改

3 个答案:

答案 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 来决定不能覆盖该方法。

通过这种方式,您可以告诉其他开发人员,他们将使用您不希望扩展某些行为的代码。