聚合、关联和组合

5
我有一个简单的例子:

我有一个如此简单的例子:

public class Order 
{

 private ArrayList<Product> orders = new ArrayList<Product>();

 public void add(Product p)
 {
  orders.add(p);
 }
}

这是聚合还是组合?我猜是组合,因为在删除订单后订单将被删除,对吗? 不幸的是,答案与我的猜测不同;/ 你知道为什么吗?

第二个问题:

public class Client extends Person 
{
    String adress = "";

    Orders orders = new Orders();

    public Client(String n, String sn)
    {
     name = n;
     surName = sn;
    }

    public String getAddress()
    {
     return adress;
    }
    public Orders getOrders()
    {
     return this.orders; 
    }
}

这是客户和订单之间的关联吗?我的老师告诉我这是关联,但我想知道为什么它不是聚合/组合 - 他告诉我聚合或组合仅在一个类包含少量不同类的实例时发生 - 这是正确的吗?我想不是,因为例如汽车只包含一个轮子,它是聚合吧?

这是什么类型的关系,为什么?

5个回答

6
你的第一个例子是聚合。变量orders在Order实例被删除时可能会被删除,但每个Product仍然有意义并且可以存在于Order类之外。
你在第二个例子中是正确的。因为Client包含对Orders的引用(has-a),所以这是组合(因为没有Client就不存在orders)。
更新以回应您的评论:
聚合和组合都是不同类型的关联,但它们是具体的关联类型。为了使两个类之间只有关联而没有聚合或组合,它们需要比给定的示例更弱的链接。以下是一个(人为制造的)示例:
class A {
    String phrase = "These pretzels are making me thirsty.";

    public String process(B b) {
        // use a B object to do something
        String tmp = b.doSomething(phrase);

        // do more processing...
        return tmp;
    }
}

class B {
    public String doSomething(String s) {
        // do something with the input string and return
        ...
    }
}

这里没有组合或聚合(A没有自己对B对象的引用),但是由于B的一个实例被A中的一个方法使用,所以存在关联关系。


第二个例子有没有可能被视为关联?我的意思是,这种构造:Orders orders = new Orders(); 是否意味着这已经是聚合了?如果A类和B类之间存在关联,那么是否只有当我们在A中有一个方法接受B的实例,但A没有对B类对象的引用时才存在关联?(B object = new B())。 - Bukocen
@Bukocen:我的回复太长了,无法在评论中展示。请查看我对答案的编辑。 - Bill the Lizard
在您的编辑示例中,A和B之间存在依赖关系,是吗? - Alex Turbin
@Alex:是的,A依赖于B来完成其部分工作。 - Bill the Lizard
问题中的第二个例子应该是组合关系,因为当客户端销毁时,订单对象也会被销毁。 - user20358

2

我认为,第一个例子并不是聚合而是组合。因为这里订单(Order)组成了产品(Product)。如果订单被删除,那么产品也会自动被删除。

class Product;

class Factory {
    Product *product;
    product = new Product();
    product createProduct() {
        return new(product);
    }
};

class Product {
     ArrayList<Order> *orders = new ArrayList<Order>();

     void createOrder() {
         orders.add(OrderInfomation);
     }
}

class Order {
    string orderID;
    string orderName;
    Customer customer;
};

class Customer {
    string cusID;
    string cusName;
    string cusAddress;
};

这里的产品可以具有相同的订单类型。如果产品被删除,则订单也将被删除。因此,它是一种组合关系(高度耦合或死亡关系)。

在您的情况下,订单与产品相关联。一个订单可以拥有任何产品订单。因此,它是一种关联关系。如果订单被删除,则产品仍将存在(轻度耦合)。类似地,客户和订单之间存在关联关系。

工厂和产品是聚合的。即使产品被删除,工厂也将存在。


1

在Java中,实际上没有组合,一切都是聚合。组合可以存在于像C++这样的语言中,其中对象可以在堆栈上声明,并随父对象一起生存和死亡。在Java中,一切都存在于堆上。

对于您的第二个问题,订单与产品具有聚合关系。关联关系可以是当我们将订单作为参数传递给产品方法时。产品使用传递的引用完成工作,但不会将该引用缓存到某个子引用中。


嗯,如果我们故意杀掉Order实例会怎样呢?那么我们就失去了对它的字段(以及“orders”...)的引用,即使它仍然存在于内存中,这是否与删除所有产品(从程序员的角度来看)相同呢?因为我们无法再引用它们。 - Bukocen
我相信你的意思是在另一个(对象)对象中将内部对象声明为普通类/结构成员,即不是指针也不是引用。实际上,如果您在堆上分配外部对象的内存(malloc/new),它们所有的成员也会与之一起存储在堆上,但您仍然不需要手动管理它们的生命周期,并且它们占用单个连续的内存块。 - Przemek Kryger
1
@Bukocen 说得没错,但是你可能会有一些“产品”(Product)是某个“订单”(Order)的成员,并从其他地方引用,因此它们的生命周期与“订单”的生命周期无关。@lalit 的意思是使用C/C++中另一种类型的定义对象来将成员的生命周期与定义的类/结构绑定。 - Przemek Kryger
@Przemek,你说得对。我的意思是内部对象是普通的类/成员。那么它在堆栈上还是在堆上都无所谓。感谢进一步澄清。 - lalit

1

聚合和组合都是关联。聚合意味着整体/部分的关系。组合是具有生命周期责任的聚合: http://ootips.org/uml-hasa.html

在你的例子中,答案可能是聚合,因为存在整体/部分的关系。

通常,您的订单会有订单行项目(而不仅仅是产品),这些订单行项目将被视为组合。


0

@Bill the Lizard

感谢您的解释,但是在您的关联示例中,仍然没有一个类对另一个类的引用/指针...

我想知道是否可以在A类中有一个字段: B instanceOfB = new B() 并告诉A和B处于关联状态(但不是聚合或组合!)

我正在考虑这个问题,因为根据这里所说,似乎不可能,但是... 我在这里找到了关联、聚合和组合之间的区别这样的例子:

[Example:]

|A|----------->|B|

class A
{
  private:
    B* itsB;
};

这是一个关联的示例,并且有指向其他类的指针...(我想我们可以更多或少地将Java类中的引用视为在此处处理指针) 因此,在Java中,它看起来像:

[Example:]

|A|----------->|B|

class A
{
  private:
    B itsB;
};

这是否意味着在某些情况下我们可以在上面的示例中进行关联?这是我们思考类的方式不同吗?如果A是汽车,B是轮子,那么它将是聚合,因为汽车有轮子。如果A是例如人,B是例如国家,那么它将是关联,因为人没有国家,但我们可能想记住这个人最近访问的国家的名称?或者如果在类A中有像“B itsB;”这样的东西,无论我们如何思考这些类,它都意味着这是聚合或组合吗?

我的思考方式是否正确,还是我在胡说八道,这很有可能,因为我只是猜测;)


如果类A有类B的实例,则可能是组合或聚合关系。我的示例是为了表明您可以在没有任何情况下进行关联。请注意,我的类A没有在任何地方存储类B的实例,它只是使用传递给它的实例。 - Bill the Lizard
是的,我理解它不存储类B的实例...但这是否意味着在那个主题中提供的示例不是真正的关联而是聚合?(我的意思是带有B* itsB的那个)?它是一个指针,因此可以存储(好吧-指向)实例。 - Bukocen

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