依赖和组合的区别是什么?

32

这里得出的定义

依赖性

当一个类的结构或行为的变化影响到另一个相关类时,这两个类之间存在依赖关系。反之不一定成立。当一个类包含另一个类时,就会发生这种情况。

组合

组合是聚合的特殊情况。更具体地说,受限制的聚合称为组合。当一个对象包含另一个对象时,如果被包含的对象不能存在于没有容器对象的情况下,则称其为组合。

Java中的具体例子见这里这里

依赖性

class Employee {
    private Address address;

    // constructor 
    public Employee( Address newAddress ) {
        this.address = newAddress;
    }

    public Address getAddress() {
    return this.address;
    }
    public void setAddress( Address newAddress ) {
        this.address = newAddress;
    }
}

构图

final class Car {

  private final Engine engine;

  Car(EngineSpecs specs) {
    engine = new Engine(specs);
  }

  void move() {
    engine.work();
  }
}

这些定义的质量相当差。例如:“……这两个类之间的依赖关系”,“它不需要是反过来的”。但是,“between”是一种无向关系——根据“between”的定义,它是反过来的。 - Marko Topolnik
1
你能给我一个代码示例吗? :) - danihodovic
@MarkoTopolnik 一个依赖和组合的例子,我能够清楚地理解它们之间的区别。最好提供代码示例。 - danihodovic
4个回答

53

可以从两个构造函数中看出区别:

  • 依赖: Address对象来自外部,它在其他地方被分配。这意味着AddressEmployee对象是分开存在的,并且仅相互依赖

  • 组合: 在这里,你可以看到一个新的Engine是在Car内部创建的。这个Engine对象是Car的一部分。这意味着Car是由一个Engine 组成的。


3
所以依赖关系 == 聚合关系吗?https://dev59.com/kGct5IYBdhLWcg3wsfkZ#12431359依赖关系和聚合关系不是同一概念。在依赖关系中,一个对象依赖于另一个对象,但并不拥有它。而在聚合关系中,一个对象包含另一个对象,并且拥有对其进行操作的权力。这两种关系在面向对象编程中有着不同的应用场景和实现方式。 - danihodovic
1
@dani-h 不,聚合组合描述了事物的构建/结构方式,而依赖性更多是某种结构的属性。请参见@TheLostMind的精彩答案。 - meaning-matters

24

简单来说:

感谢 Marko Topolnik 的介绍...

  1. 依赖 是指一个对象“依赖”于另一个对象。这可以发生在两个对象之间存在或不存在关系的情况下。实际上,一个对象甚至可能不知道另一个对象的存在,但它们可能是相互依赖的。 例如:生产者-消费者问题。生产者不需要知道消费者的存在,但是它必须执行wait()和notify()。所以,“否”,依赖不是关联的子集。

  2. 组合:是一种关联类型,其中“子”对象无法在没有父类的情况下存在。也就是说,如果子对象存在,则必须在父对象中,并且不能在其他地方存在。

    例如:汽车(父级)具有燃油喷射系统(子级)。现在,在汽车外部拥有燃油喷射系统毫无意义(它将毫无用处)。也就是说,燃油喷射系统不能在没有汽车的情况下存在。

  3. 聚合:在这里,子对象可以存在于父对象之外。 汽车有一个司机。司机可以在汽车外面存在。


1
是的... 依赖是一个通用术语... 组合/聚合/继承会导致依赖。虽然我认为关联是一个比依赖更好的术语。 - TheLostMind
如果依赖关系的定义是“结构/行为的变化会影响一个类”,那么类之间就不能存在聚合或其他类型的关系。它们可能都访问同一个第三个对象,并且必须就它们使用该对象的方式达成一致。典型例子:生产者/消费者模式。 - Marko Topolnik
@TheLostMind 我在那份文件中使用了Ctrl+F搜索,没有提到依赖关系作为聚合/组合/关联的一般形式。 - danihodovic
@MarkoTopolnik - 我认为将组合/聚合/关联视为依赖关系是错误的,这样说对吗? - TheLostMind
3
引用我在上面评论中的话:“典型的例子:生产者/消费者模式”。生产者和消费者都聚合了一个“队列”对象,但除此之外它们完全解耦并且不相互了解。然而,更改生产者的行为将影响(并可能破坏)消费者的行为。 - Marko Topolnik
显示剩余9条评论

1
依赖性是指仅在函数范围内使用对象。换句话说,类的实例仅存在于包含类的函数(或方法)中,并且在函数退出后被销毁。
你给出的依赖性示例不是依赖性,因为Employee类包含一个Address的实例,这被称为聚合。聚合类似于组合,除了对象还可以存在于使用它的类之外(它可以位于类的范围之外)。在您的示例中,您将Address的副本传递给Employee构造函数。但是由于它是在Employee对象之外创建的,因此Address也可能存在于程序的其他位置。
与聚合一样,在组合中,组件对象可用于整个复合对象。这意味着复合对象的所有方法/函数都可以访问组件对象。聚合和组合之间唯一的区别在于,在组合中,组件对象仅存在于复合对象内部,而不在程序的任何其他地方。因此,当复合对象被销毁时,给定的组件对象也被销毁,并且不能存在于任何其他地方。在您的示例中,Car是一个复合对象,Engine是一个组件,因为Engine的实例仅存在于该特定Car中,而不在其他地方。

0
组合必然会使用依赖注入。
然而,可以在不构成组合的情况下进行依赖注入。
实际上,当你转到具体的代码时,是否发生组合取决于在实例化对象时是否将通过依赖注入传递的参数保存为成员变量。如果保存了,那就是组合。如果没有保存,那就不是。

定义

简而言之,依赖注入是将参数添加到封装的代码块(例如类、函数、块等)中,而不是使用没有参数的字面过程化代码。

组合是在实例化被组合对象时使用依赖注入的参数/参数。


使用组合实现依赖注入

Syringe类由Drug组成,因为Syringe将drug保存为成员变量。

顺便说一下:Syringe可以由多个依赖项组成,不仅仅是单个药物依赖(警告!),但即使是这种退化的情况下,只有一个依赖项的组合仍然构成了组合。如果有帮助的话,想象一下,如果你必须从一个Drug对象和一个Needle对象组合一个Syringe对象,其中needle对象具有许多可能的规格和长度中的特定值。

Class Drug
  def __init__(name)
    @name = name
  end

  def contents
    return @name
  end
end

Class Syringe
  def __init__(drug)
    @drug = drug
  end

  def inject()
    return @drug.name
  end
end

amoxicillin = Drug.new('amoxicillin')
#these two lines are the key difference. compare them with the 'same' lines in the other other example
syringe = Syringe.new(amoxicillin) 
syringe.inject() 

不使用组合的依赖注入

下一个示例并非使用组合,因为在实例化“注射器”时并未保存“药物”。

相反,直接在成员函数中使用“药物”。

Class Drug
  def __init__(name)
    @name = name
  end

  def contents
    return @name 
  end
end

Class Syringe
  def inject(drug)
    return drug.contents
  end
end

amoxicillin = Drug.new('amoxicillin')
#these two lines are the key difference. compare them with the 'same' lines in the other other example
syringe = Syringe.new()
syringe.inject(amoxicillin)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接