Design com subclasses sendo agregados de classes que implementam interfaces
Eu modelei uma estrutura de classe, onde subclasses Rectangle e Circleherdam de uma superclasse abstrata Figure. Todas as subclasses compartilham uma interface de IGeometry que prevê getArea()e getPerimeter()e precisa de uma implementação diferente para cada subclasse. Várias classes concretas contêm os respectivos métodos, por exemplo CircleGeometry:
Por exemplo, a classe Circleagora herda de Figuree tem CircleGeometrycomo atributo.
Qual é a maneira mais razoável de modelar esse relacionamento?
- Se eu modelar este relacionamento como um agregado-relacionamento, onde
Figuretem umaIGeometry-object, e, em seguida, os agregados de betão para as subclasses são modeladas de novo, isto é,CircletemCircleGeometry, a agregação seria com efeito modelado duas vezes nas subclasses, uma vez herdado, uma vez betão ( veja meu diagrama). - Se eu apenas modelar a agregação na superclasse, mas já souber qual subclasse manterá qual classe de implementação, não sei como descrever seu relacionamento.
E se as classes de implementação forem abstract, e os métodos para calcular a área e o perímetro forem static?
Respostas
Qual é a sua intenção de design?
Em primeiro lugar, seu design geral está muito próximo de um padrão de design Bridge , Figuresendo uma abstração, e as IGeometryimplementações, os implementadores:
O objetivo desse padrão é desacoplar a abstração e sua implementação de uma forma que ambas possam variar independentemente. Um exemplo típico seria ter suas figuras abstratas, mas ter diferentes implementações, por exemplo, um conjunto de geometrias 2D e um conjunto de geometrias 3D.
O padrão não requer uma especialização de
IGeometrypara cada tipo de figura, mas é seu direito fazer isso se quiser reduzir o risco de usar o implementador errado.
É mesmo isto que queres? Essa grande flexibilidade vem com o custo de uma grande complexidade com muitos códigos clichê que encaminha as chamadas de abstração para o implementador, e o risco de engenharia excessiva ao traçar a fronteira entre a abstração abstrata e sua implementação abstrata.
GoF é claro: se houver apenas um implementador (ou seja, no seu caso, cada figura pode sempre ter um), então é " um caso degenerado de padrão de ponte ". Portanto, pense duas vezes antes de seguir esse caminho: poderia ser muito mais simples se Figure fosse uma classe abstrata e cada tipo Figureapenas teria que implementar a IGeometryinterface diretamente.
Como expressar isso em UML?
- Se
Figurefor uma superclasse, você deve usar a notação de especialização no diagrama: uma linha simples da classe para sua superclasse em vez de uma linha pontilhada e um triângulo branco no final em vez de uma ponta de seta aberta! - No lado da interface, a linha pontilhada com o triângulo está ok: significa a implementação de uma interface.
- Há duas vezes a agregação , porque em cada forma, você
geometryherdou da superclasse, bem comogeoadicionou na classe. Esta não pode ser sua intenção: você tem que deixar claro que é a mesma simplesmente escrevendogeo { redefines Geometry } - Você pode modelar a relação entre a figura e a forma como uma agregação; é uma prática comum. Mas isso não é recomendado; Expliquei em detalhes o porquê dessa outra resposta não relacionada ao SO . Uma simples associação com o ponto de propriedade é suficiente.
Além das relações de associação, agregação e composição, a UML também tem uma relação de dependência geral, que é mostrada como uma seta tracejada com uma ponta de seta aberta (e geralmente um estereótipo indicando o tipo de dependência).
Supondo que haja uma razão pela qual a hierarquia de geometria não pode ser mesclada com a hierarquia de figuras, eu provavelmente a desenharia como uma composição entre Figuree e IGeometry, em seguida, uma <<creates>>dependência entre as figuras concretas (isto é Circle) e geometrias ( CircleGeometry).
Isso implica que, após a criação, as geometrias concretas só podem ser usadas por meio de sua interface, portanto, você não pode ter métodos específicos de círculo em CircleGeometry.
Se os métodos de geometria são estáticos e você nunca instancia um objeto de geometria, pode modelá-lo com uma <<uses>>dependência.
Acho que você está perdendo o ponto das interfaces, já que o uso da interface IGeometry parece bastante inútil. Ao implementar a interface para cada figura em um objeto específico de figura separado, você está derrotando o propósito da interface.
A interface deve servir como um tipo comum para figuras. Você quer tratar uma figura como uma IGeometria. Com sua implementação você não pode fazer isso, para chamar um método IGeometry você precisa saber exatamente com qual figura está lidando e então usar a implementação específica de figura de IGeometry. Isso é inútil.
Os métodos IGeometry precisam ser implementados diretamente nas classes de figura e a classe abstrata Figura deve declarar a implementação de IGeometry. Então você terá ganho algo: você pode chamar métodos IGeometry em um tipo de figura sem saber com qual figura está lidando.