正确设计Java类层次结构以实现代码共享和封装

Rea*_*ted 2 java oop encapsulation design-patterns interface

在布置类层次结构时,我经常发现自己对能够封装功能同时共享代码之间的差距感到沮丧.当然,部分问题是缺少多重继承,但接口有所帮助.在我看来,无法在接口上定义受保护的方法是一个更大的问题.

标准解决方案似乎是拥有一个由受保护的抽象基类实现的公共接口.问题是我们有以下情况

public interface Foo {
    public String getName();
}

abstract protected BaseFoo implements Foo {
    abstract protected int getId();

    private String name;
    protected BaseFoo(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

public class ConcreteFoo extends BaseFoo {
    public ConcreteFoo (String name) {
        super(name);
    }

    @Override
    protected int getId() {
        return 4; // chosen by fair dice roll.
                  // guaranteed to be random.
    }
}

// in the foo package with the classes above
public class FooCollection {
    private static Map<Integer, Foo> foos = new HashMap();
    public static void add(Foo foo) {
        synchronized(foos) {
            foos.put(foo.getId(), foo); // can't call foo.getId()
        }
    }
}

// client code, not in the foo package
FooCollection.add(new ConcreteFoo("hello world"));
Run Code Online (Sandbox Code Playgroud)

也就是说,我们将一个封装好的对象返回给调用者,但是任何获取该对象的方法都需要能够依赖于某些内部功能.内部功能不能成为接口的一部分(这将破坏封装),但要使其成为抽象基类的一部分,需要我们使用转换.

我们不能将Foo作为抽象类,因为其他接口需要扩展它以将可选的正交功能添加到比此处显示的更复杂的层次结构中.

解决这个问题的标准方法是什么?您是否将getId添加到Foo界面,即使客户端不应该使用它?你是否在FooCollection.add中对BaseFoo执行了不安全的转换?如果你在投射之前检查,当你的类型不匹配时你会怎么做,即使它们总是应该用于所有意图和目的?

您在这种情况下获得的最佳实践信息将非常有用.

编辑:如果不清楚,此示例有意过度简化.关键点在于,有时您会返回对象的"界面视图".当"接口视图"传递回特定于包的类时,传递给它的方法可能需要在其实现中使用内部功能.如何管理内部和公共功能之间的不匹配?

Cha*_*tin 6

好的,这里有几点:

  1. 与流行的观点相反,继承实际上不是关于共享代码.您在继承层次结构中创建的是一组共享一些常见抽象行为的事物; 它有时会产生重用一些代码的效果.

  2. 在过去几年中,时尚已经发生了相当大的变化,因此深层次和复杂的继承层次结构不再被认为是良好的形式.一般用Java.你应该

    • 在实现接口之前使用聚合
    • 使用接口来表达"混合"合同
    • 仅当类描述具有自然继承的东西时才使用继承.
  3. 如果您真的想要多重继承的效果,请为您的接口构建实现类,然后聚合它们.

  4. 特别是,通过使用接口和实现类定义类,可以使构建测试变得更加容易; 如果您的界面是独立的,那么为该界面构建模拟几乎是微不足道的.

  • 实际上是四点或七点.没有人期待西班牙的调查. (2认同)