Como posso projetar isso de maneira mais eficiente de acordo com o design pretendido, garantindo que eu esteja atendendo aos princípios de design do SOLID?

5

Eu sei o que é SRP, mas estou questionando meu design atual de um objeto que eu chamo de entidade. Abaixo está uma foto do desenho a que me refiro. Se eu mudar o código dos objetos GeometricInformation e TransactionInformation para os abstracts GeometricEntityBase e TranslactionalEntityBase respectivamente, isso seria considerado uma violação grave do SRP? Eu estou esperando que os especialistas aqui possam ajudar a criar buracos neste projeto ou afirmá-lo, se for o caso.

Para resumir a intenção, tenho uma API do CAD em que estou trabalhando. Cada entidade no domínio do CAD é representada por uma Entidade no código. Para permitir que as Entidades carreguem somente os atributos apropriados ao seu propósito, há três níveis de herança que a Entidade pode usar. Por exemplo, se apenas os recursos muito básicos da Entidade (como ter um ID, etc), ele pode herdar apenas do EntityBase. Se requerer propriedades geométricas adicionais para identificar limites, inserir ponto, matriz de transformação, etc, então herdaria de GeometricEntityBase. Se for para ser uma entidade totalmente transacionada (por meio de uma estrutura transacional personalizada), é necessário que ela contenha os atributos de ambos, além de transacional (como o estado da entidade (novo, limpo, sujo), documento etc.).

Para garantir o SRP, usei o padrão do adaptador e configurei os atributos geométricos e as operações para um objeto chamado GeometricInformation e, da mesma forma, o material transacional para TransactionInformation. Isso está funcionando muito bem, mas agora estou começando a perceber que, para permitir alterações nas propriedades geométricas, preciso validar o estado da entidade. Em outras palavras, o objeto GeometricInformation deve conhecer os atributos do objeto TransactionInformation. As soluções parecem envolver um padrão de observador tal que a GeomtetricInformation poderia observar a TransactionInformation (que parece vigorosa) ou asseverar uma propriedade "CanModify" da XXXEntityBase que pode ser alterada e observada pelo objeto XXXInformation. Infelizmente isso criaria uma dependência bidirecional, pois cada objeto XXXInformation teria que saber que é pai.

Devido ao fato adicional de que o WCF é usado para comunicar as entidades a uma camada de dados de nível inferior, há também um processo de conversão para convertê-las em equivalentes da DTO. No final, estou pensando em mover o código dos objetos XXXInformation para seus equivalentes abstratos, que têm muito pouco neles de qualquer maneira, mas estou preocupado que a quebra do SRP também poderia causar problemas de downstream. Por outro lado, isso simplificaria mais as entidades e permitiria que o processo de tradução melhorasse em termos de desempenho também, de modo que, definitivamente, algum tipo de troca de ideias.

    
por bjhuffine 01.02.2016 / 20:21
fonte

1 resposta

5

É precisamente por isso que a herança nunca deve ser a opção padrão para criar recursos / funcionalidades reutilizáveis.

Seu problema se resume a você ter uma hierarquia de herança que pode, em certos cenários, precisar de conhecimento em partes do gráfico de herança, fazendo com que o gráfico tenha ainda mais relacionamentos entre cada nó nele. Essas relações são entre tipos e são uma forma de acoplamento. Um que é melhor evitar, simplesmente não envolvendo herança para uma grande maioria dos cenários.

Olhe desta maneira, ao usar a herança, você acaba empilhando dependências umas em cima das outras, de tal forma que elas não podem ser alteradas independentemente. Qualquer alteração na classe base afetará todas as classes derivadas, qualquer alteração na classe derivada mudará sua relação com a classe base e com todas as outras classes derivadas dela. Sua situação agora é que você quer fazer com que duas classes derivadas tenham recursos que a classe base não faz, mas não faz sentido promover isso porque ela é especializada para essas duas classes.

É aí que entra a composição. Em vez de fazer uma hierarquia de heranças de coisas que são iguais, geralmente é melhor abordá-lo do ponto de vista de que você tem partes de funcionalidade desejadas. Ao isolar as unidades em partes da funcionalidade, você pode escolher quais peças usar e como.

No seu exemplo, por exemplo, você quer comportamentos transacionais e deseja a validação de limite. Essas devem provavelmente ser duas unidades não relacionadas totalmente independentes. Então, quando você tem uma classe que deseja um comportamento de transação, ela obtém um campo ou uma propriedade transacional de classe / módulo / what-have-you como membro. Se quiser também a validação de limite, ele puxa uma unidade de validação de limite. Em seguida, ele pode compor os dois passando informações entre as atividades transacionais e a validação de limites.

Outra grande falha que você está enfrentando aqui parece ser uma strong aderência aos padrões de design como soluções. Elas não são soluções porque não resolvem problemas, elas simplesmente fornecem idéias conceituais sobre como você pode encontrar algumas características de design. A realidade é que você deseja preencher um conjunto de boas características de design (como o SRP e o resto do SOLID, por exemplo), mas você quer fazê-lo em uma solução sob medida para o seu domínio de problema. Não busque um "observador", apenas tente projetar fundamentalmente uma solução ideal para o problema de separar as preocupações que você tem, de uma maneira que atenda a tantas características positivas quanto possível. Se você achar que um padrão de design é o resultado, então seja, mas não comece do ponto de vista de usar um.

Sempre comece a resolver um problema dividindo-o em subproblemas até que você tenha os mais fáceis de resolver. Então, quando você reintegrar essas soluções em um todo coeso, você deseja que essas integrações sejam feitas de uma maneira que atenda aos princípios do projeto SOLID, tanto quanto possível, bem como quaisquer outras características de projeto e implementação importantes, dadas as demandas de sua solução. Talvez seja necessário executar rápido e você desista de integrar as soluções de forma abstrata / limpa o quanto quiser, por exemplo, talvez precise de confiabilidade, para que você adicione muito mais. Os padrões são apenas ideias conceituais , que não devem ser seguidos à risca ou tomados como soluções literais.

    
por 01.02.2016 / 20:46
fonte