relaciones objetos entre ejemplos diagrama dependencia composicion clases asociacion agregacion c++ oop associations aggregation ooad

c++ - ejemplos - relaciones entre clases y objetos



C++: Asociación, Agregación y Composición (1)

Estoy empezando a estudiar OOAD y tengo dificultades para encontrar un ejemplo de código en C++ que ilustre cómo la Association , la Aggregation y la Composition se implementan mediante programación. (Hay varias publicaciones en todas partes, pero están relacionadas con C # o Java). Encontré un ejemplo o dos, pero todos están en conflicto con las instrucciones de mi instructor y estoy confundido.

Mi entendimiento es que en:

  • Asociación: Foo tiene un puntero al objeto Bar como miembro de datos
  • Agregación: Foo tiene un puntero al objeto Barra y los datos de la barra se copian en profundidad en ese puntero.
  • Composición: Foo tiene un objeto Bar como miembro de datos.

Y así es como lo he implementado:

//ASSOCIATION class Bar { Baz baz; }; class Foo { Bar* bar; void setBar(Bar* _bar) { bar=_bar; } }; //AGGREGATION class Bar { Baz baz; }; class Foo { Bar* bar; void setBar(Bar* _bar) { bar = new Bar; bar->baz=_bar->baz; } }; //COMPOSTION class Bar { Baz baz; }; class Foo { Bar bar; Foo(Baz baz) { bar.baz=baz; } };

¿Es esto correcto? Si no, entonces, ¿cómo debería hacerse en su lugar? Se agradecería que también me diera una referencia de un código de un libro (para que pueda hablar con mi instructor)


Voy a ignorar la Agregación. No es un concepto muy claramente definido y, en mi opinión, causa más confusión de la que vale la pena. La composición y la asociación son suficientes, Craig Larman me lo dijo. Puede que no sea la respuesta que su instructor estaba buscando, pero es poco probable que se implemente en C ++ de manera diferente.

No hay una sola manera de implementar Composición y Asociación. La forma en que los implemente dependerá de sus requisitos, por ejemplo, la multiplicidad de la relación.

Composición

La forma más sencilla de implementar la composición es utilizar una variable simple de miembro Bar , como usted sugirió. El único cambio que haría es inicializar la bar en la lista de inicializadores de miembros constructores:

// COMPOSITION - with simple member variable class Foo { private: Bar bar; public: Foo(int baz) : bar(baz) {} };

En general, es una buena idea inicializar las variables miembro utilizando la lista de inicialización del constructor, puede ser más rápida y, en algunos casos, como las variables miembro const es la única forma de inicializarlas.

También puede haber una razón para implementar la composición utilizando un puntero. Por ejemplo, Bar podría ser de tipo polimórfico y no conoce el tipo concreto en el momento de la compilación. O quizás desee reenviar declarar Bar para minimizar las dependencias de compilación (ver lenguaje PIMPL ). O quizás la multiplicidad de esta relación es de 1 a 0..1 y necesitas tener una Bar nula. Por supuesto, debido a que esta es la Composición, Foo debería poseer la Bar y en el mundo moderno de C ++ 11 / C ++ 14 preferimos usar punteros inteligentes en lugar de poseer punteros sin formato:

// COMPOSITION - with unique_ptr class Foo { private: std::unique_ptr<Bar> bar; public: Foo(int baz) : bar(barFactory(baz)) {} };

He usado std::unique_ptr aquí porque Foo es el único propietario de Bar pero es posible que desee usar std::shared_ptr si algún otro objeto necesita un std::weak_ptr para Bar .

Asociación

La asociación normalmente se implementaría usando un puntero como lo ha hecho:

// Association - with non-owning raw pointer class Foo { private: Bar* bar; public: void setBar(Bar* b) { bar = b; } };

Por supuesto, debe estar seguro de que Bar estará vivo mientras Foo esté utilizando, de lo contrario tendrá un puntero colgante. Si la vida útil de Bar es menos clara, entonces una std::weak_ptr puede ser más apropiada:

// Association - with weak pointer class Foo { private: std::weak_ptr<Bar> bar; public: void setBar(std::weak_ptr<Bar> b) { bar = std::move(b); } void useBar() { auto b = bar.lock(); if (b) std::cout << b->baz << "/n"; } };

Ahora Foo puede usar la Bar sin temor a quedarse con un puntero colgante:

Foo foo; { auto bar = std::make_shared<Bar>(3); foo.setBar(bar); foo.useBar(); // ok } foo.useBar(); // bar has gone but it is ok

En algunos casos en los que la propiedad de Bar está realmente clara, se podría implementar una Asociación utilizando std::shared_ptr pero creo que debería ser un último recurso.

Con respecto a su implementación de Agregación con una copia profunda de un puntero de Bar . No hubiera dicho que esa era una implementación típica pero, como dije, depende de sus requisitos. Debe asegurarse de llamar a delete en el puntero de miembro de la bar en el destructor Foo ya que de lo contrario tiene una pérdida de memoria (o usa un puntero inteligente).