组合与继承(如何理解Java中的思考)

时间:2014-06-28 08:10:31

标签: java inheritance

你能帮我理解下面的内容。这是来自Thinking in Java book。

  

因为你正在从现有的类中编写一个新类,所以这个   概念称为组合(如果组合动态发生,   它通常被称为聚合)。

     

组合通常被称为" has-a"关系,如在" A   汽车有发动机。"

     

组合具有很大的灵活性。成员对象   您的新课程通常是私人的,使他们无法访问   正在使用该类的客户端程序员。这允许你   在不干扰现有客户端代码的情况下更改这些成员。您可以   还可以在运行时更改成员对象,以动态更改   你的程序的行为。接下来描述的继承确实如此   没有这种灵活性,因为编译器必须放置编译时   对继承创建的类的限制。

     

因为继承在面向对象编程中非常重要,所以它   经常被高度重视,新程序员可以得到这个想法   继承应该在任何地方使用。这可能导致   尴尬和过于复杂的设计。相反,你应该先   在创建新类时查看组合,因为它更简单   而且更灵活。

没有例子,我不能抓住这种果冻。你能给我提供一些例子:

  1. 聚合,即动态合成。

  2. 继承面临编译时限制。

  3. 我的意思是我正在寻找揭示构图优于继承的优点的例子。

    稍后添加:我需要代码示例(不是描述)。

2 个答案:

答案 0 :(得分:1)

  

我的意思是我正在寻找揭示构图优于继承的优点的例子。

我们假设你有一个顶级抽象AbstractCar。如果要创建强大的继承关系,则可以使用

abstract class AbstractCar {
    protected abstract void startEngine();
}

class FastCar extends AbstractCar {
    @Override
    public void startEngine() {
        System.out.println("turn key... vroom");
    }
}

就功能而言,这似乎符合要求,因为汽车应具备启动引擎的功能。但是,如果您想要关闭引擎或将引入启动功能安装到引擎,该怎么办?你必须改变实施。

更好的方法是将引擎功能提取到Engine类,并将 封装到AbstractCar中。 Engine将是一个顶级抽象,其实现可能会发生变化,您可以使用Engine作为类型,同时允许引擎实现更改。例如

interface Engine {
    void startEngine();
} 

class PushStartEngine implements Engine {
    @Override
    public void startEngine() {
        System.out.println("push button.. vrooom");
    }
}

abstract class AbstractCar {
    protected Engine engine;
    protected void setEngine(Engine engine) {
        this.engine = engine;
    }
    protected void getEngine() {
        return engine;
    }
}

class FastCar extends AbstractCar {
    public FastCar() {
        setEngine(new KeyStartEngine());
    }
}

这可能不是最好的例子(因为它仍然强加紧密类型耦合 - 还有其他设计模式来解决这个问题),但它应该给你一个组合优于继承的优点的例子。提取各种不同的基本前提,在这种情况下是引擎。在字符串继承关系中,您可能在顶级抽象中定义了更多功能,但是当您喜欢继承的组合时,您将该功能封装到其他对象中。这样可以更容易地进行扩展和更改。

因此,通过上面的示例,您现在可以轻松切换引擎,这在继承的情况下是不可能的

public static void main(String[] args) {
    // let's say you have another OlderCar implmentation
    // that has a KeyStartEngine
    OlderCar car = new OlderCar();
    car.getEngine().startEngine();

    // I just hit the lotto, lets upgrade!
    PushStartEngine engine = store.buyPushStartEngine();
    car.setEngine(engine);
    car.getEngine().startEngine();
}

答案 1 :(得分:0)

有一个很好的例子:

  • 组成:大学是部门(和其他静态装置)的组合
  • 聚合:一个部门有一个教授的聚合:他们可能会来去,甚至与另一个部门(在另一所大学)相关联。

组合相比,继承的讨论在这里需要太长时间。我们只说继承有利于代表

  • 细化:根据子类添加更多不同的属性,从而产生不同的特征或功能集
  • 抽象:你意识到一组东西有一些共同的属性或特征,你把它们“分解”成一个基类
  • 变化:一组事物具有相同的特征,但这些特征的“内部工作”是不同的,需要不同的实现,这些实现将被放入不同的子类中

通过组合各种组件来表达不同的特征是有意义的,如果这些组件可以以各种方式使用,或者已经可用,或者类型层次结构不能容易地表达可能的组合的数量。此外,考虑到抽象类型组件的组合在实际组成根对象时使用这些抽象组件的不同实现(子类)打开了广泛的组合。 / p>

网上有很多文章讨论这个基本话题。这是一个article。请注意,在软件设计中,没有任何规则在每种情况下都很好,所以要准备好使用多个设计原则......