62

我面临着一个持续的问题,将委派、组合和聚合彼此区分开来,并确定最好使用一个而不是另一个的情况。

我查阅了一本 Java OO Analysis and Design 书籍,但我的困惑仍然存在。主要的解释是这样的:

委派:当我的对象按原样使用另一个对象的功能而不更改它时。

组成:我的对象由其他对象组成,这些对象在我的对象被销毁 - 垃圾收集后又不能存在。

聚合:我的对象由其他对象组成,即使在我的对象被销毁后也可以存活。

是否有可能有一些简单的例子来展示每个案例,以及它们背后的推理?除了我的对象简单地引用另一个对象之外,这些示例还能如何演示?

4

5 回答 5

69

代表团

public class A {
  private B b = new B();

  public void methodA() {
    b.methodB();
  }
}

当客户A调用methodA时,类将调用A 委托B's methodB

基本原理。A 类暴露了属于其他地方的行为。这可能发生在单继承语言中,其中类 A 继承自一个类,但其客户端需要在不同类中实现的行为。进一步研究

混合委托

public class A {
  private B b = new B();

  public void methodA() {
    b.methodB( this );
  }
}

涉及简单转发的委托和替代继承的委托之间的区别在于,被调用者必须接受调用者的参数,例如:

    b.methodB( this );

基本原理。允许类B实例使用类提供的功能A,就像类B从类继承A一样——但没有继承。进一步研究

作品

public class A {
  private B b = new B();

  public A() {
  }
}

一旦不再存在对特定类实例的引用A,它的类实例B就会被销毁。

基本原理。允许类以模块化方式定义行为和属性。进一步研究

聚合

public class A {
  private B b;

  public A( B b ) {
    this.b = b;
  }
}

public class C {
  private B b = new B();

  public C() {
    A a = new A( this.b );
  }
}

一旦不再有对特定类实例的引用A,它的类实例B就不会被销毁。在这个例子中,两者都A必须C在被垃圾回收之前B被销毁。

基本原理。允许实例重用对象。进一步研究

没有参考的演示

这些简单模式的名称由它们的引用关系定义。

于 2009-09-06T00:11:46.477 回答
56

在所有三种情况下,您的对象都将引用另一个对象。区别在于引用对象的行为和/或生命周期。一些例子:

  1. 组成:房子包含一个或多个房间。Room 的生命周期由 House 控制,因为没有 House,Room 将不存在。

  2. 聚合:用积木建造的玩具屋。您可以拆卸它,但会保留块。

  3. 代表团:你的老板让你给他弄杯咖啡,你让实习生替你做。委派不是一种关联(就像组合/聚合一样)。后两者在 Stack Overflow 上讨论过很多次

在评论中,您询问在每种情况下实现将如何不同,并观察到在所有情况下我们都会调用相关对象上的方法。确实,在每种情况下,我们都会有如下代码

myRoom.doWork();

myBlock.doWork();

myMinion.doWork();

但不同之处在于相关对象的生命周期和基数。

对于组件,房间在房屋创建时就存在。所以我们可以在 House 的构造函数中创建它们。

在关联的情况下(我将使用轮胎和汽车)汽车可能会在其构造函数中添加轮胎,但稍后您可能想要移除和更换轮胎。所以你也有方法,比如

 removeTyre(FrontLeft)
 addNewTyre(aTyre, BackRight)

aTyre 对象很可能来自工厂——我们new在 Car 的任何方法中都没有。

在委托的情况下,您甚至可能没有一个成员变量来保存委托

 resourcingPool().getIntern().getCoffee(SkinnyLatte, workstation 7);

对象之间的关系仅在实习生拿咖啡时才持续。然后它返回到资源池。

于 2009-09-05T23:09:33.600 回答
17

你的书解释得很好,所以让我详细说明并为你提供一些例子。

委托:当我的对象按原样使用另一个对象的功能而不更改它时。

有时一个类在逻辑上可能需要很大。但是大类并不是一个好的编码实践。有时,一个类的某些功能可能以不止一种方式实现,您可能想在某个时候改变它。


class FeatureHolder {
 void feature() {
  // Big implementation of the feature that you dont want to put in the class Big
 }
}

class Big {
 private FeatureHolder FH = new FeatureHolder();

 void feature() {
  // Delegate to FeatureHolder.
  FH.feature();
 }

 //.. Other features
}

从上面的例子中,Big.feature() 调用 FH 的特征而不改变它。这样,Big 类就不需要包含特性的实现(分工)。此外,feature() 可以由其他类(如“NewFeatureHolder”)以不同方式实现,Big 可能会选择使用新的特征持有者。

组成:我的对象由其他对象组成,这些对象在我的对象被销毁垃圾收集后又不存在。

聚合:我的对象由其他对象组成,即使在我的对象被销毁后也可以存活。

从技术上讲,组合是“一部分”,聚合是“引用”关系。你的手臂是你的一部分。如果你不再活着,你的手臂也会死。你的衣服不是你的一部分,但你拥有它们;你可以做客,你的布不会随身携带。

在编程中,一些对象是另一个对象的一部分,没有它它们就没有逻辑意义。例如,一个按钮被组合成一个窗口框架。如果框架关闭,按钮就没有理由再存在了(组合)。一个按钮可能引用了一个数据库(比如刷新数据);当按钮被消除时,数据库可能仍然存在(聚合)。

对不起我的英语,希望这有帮助

于 2009-09-05T23:44:08.820 回答
1

1) 委派:人-司机-车示例。一个男人买了一辆车。但那个人不知道开车。所以他会指定一个会开车的司机。所以 Man 类想要使用汽车进行运输。但它不具备与汽车的交互功能/兼容性。所以他使用了一个与汽车兼容的类,即与人类兼容的驱动程序。假设司机能听懂男人说什么

2)组成:汽车模拟是一个例行的例子。为了使汽车移动,车轮会旋转。使用轮子类旋转功能作为其移动功能的一部分的汽车类,其中轮子是汽车的一部分。

3) 聚合:汽车及其颜色。汽车类对象法拉利将有一个颜色类对象红色。但是当用户搜索使用红色规范时,颜色类对象红色可以作为单独的类存在。

于 2013-12-24T13:16:36.607 回答
0

我可以用一个非常简单的句子说:

委托是:当您不想更改行为时,将行为委托给其他类。通过更改,我的意思是在运行时。例如,您将司机委派给司机在驾驶时不会改变的汽车类。

组合是:当您想要使用可能在运行时更改的类族(一个或多个实现接口的类)的行为时。但是您应该考虑这些类不能在没有主要类的情况下存在,例如酒店的房间。如果您删除酒店,酒店的所有房间都将不存在。

聚合是:与组合相同,但类可以在没有主类的情况下存在。

于 2020-03-30T11:41:45.070 回答