Eu tenho duas classes base, Operation
e Trigger
. Cada um possui várias subclasses especializadas em certos tipos de operações ou gatilhos. A Trigger
pode disparar um específico Operation
. Enquanto um Operation
pode ser acionado por um específico Trigger
.
Preciso escrever o código que mapeia um dado Operation
a um dado Trigger
(ou vice-versa), mas não tenho certeza de onde colocá-lo.
Nesse caso, o código não pertence claramente a uma classe ou a outra classe. Portanto, em termos de um princípio de responsabilidade única, não tenho certeza de onde o código deve pertencer.
Eu posso ver três opções que funcionariam. Enquanto 1 e 2 parecem ser apenas uma opção de semântica, 3 representa uma abordagem totalmente diferente.
- No gatilho, por exemplo
bool Triggers(Operation o)
. - Na operação, por exemplo
bool TriggeredBy(Trigger t)
. - Em uma classe inteiramente nova que gerencia o mapeamento, por exemplo
bool MappingExists(Trigger t, Operation o)
.
Como devo decidir onde colocar o código de mapeamento compartilhado em relação a um único princípio de responsabilidade?
Como gerenciar uma responsabilidade única quando a responsabilidade é compartilhada?
Editar 1.
Portanto, o código real se parece com isso. Todas as propriedades, são ou um string
, Guid
, collection<string>
, ou enum
. Eles são basicamente apenas pequenos pedaços de dados.
Editar 2.
O motivo do tipo de retorno de bool. Outra classe consumirá uma coleção de Trigger
e uma coleção de Operation
. Ele precisa saber onde existe um mapeamento entre a Trigger
e an Operation
. Ele usará essas informações para criar um relatório.
fonte
Respostas:
Eu pensaria dessa maneira: como é determinado qual operação faz com que qual acionador seja acionado. Tem que ser um algoritmo que pode mudar com o tempo ou evoluir para vários algoritmos. Colocá-lo nas classes Trigger ou Operation implica que essas classes serão capazes de lidar com esses cenários no futuro. Observe que eu não vejo isso tão simples quanto um mapeamento, pois pode haver mais do que isso.
Minha escolha seria criar uma classe com métodos apropriados, como GetOperationForTrigger (Trigger t). Isso permite que o código evolua para um conjunto de classes cuja escolha pode depender em tempo de execução ou outras variáveis (por exemplo, padrão de estratégia).
Observe que a principal suposição nesta linha de pensamento é escrever código mínimo (ou seja, três classes hoje), mas evitar grandes refatorações se a funcionalidade precisar ser estendida no futuro, não assumindo que sempre haverá exatamente uma maneira de determine qual acionador causa qual operação.
Espero que isto ajude. Embora a resposta seja semelhante a user61852, o raciocínio é diferente. Como resultado, a implementação será diferente (por exemplo, ter métodos explícitos em vez de substituir iguais, para que o número de métodos possa evoluir ao longo do tempo com base nas necessidades).
fonte
Estive lá, fiz isso.
Opção # 3.
Não sei qual linguagem você usará, mas usarei um pseudo-código muito semelhante ao Java. Se seu idioma é C #, você provavelmente possui interfaces e estruturas semelhantes.
Tenha uma classe ou interface de mapeamento:
equals()
métodoMapping
para que coleções deMapping
possam ser perguntadas se elas contêm um determinado mapeamento.equals()
métodos adequados também.Comparable
, para que você possa classificar os relatórios.Neles, você pode simplesmente colocar um mapeamento em uma coleção
Mais tarde você pode perguntar:
fonte
Atualmente, você tem conhecimento da classe A sobre a classe B e da classe B sobre a classe A. Isso acontece com muitos acoplamentos.
Por definição, A está executando pelo menos sua própria operação E verificando se B deve ser executado. O inverso é verdadeiro com B. Qualquer que seja a primeira classe chamada, ela deve ser capaz de examinar o resultado e verificar se outras coisas precisam ser executadas.
Tente quebrar esse acoplamento dividindo suas classes em componentes menores. Eu gosto de colocar um comentário no topo de cada aula explicando em inglês simples o que ele faz. Se você precisar usar palavras como AND ou passar por uma ou duas frases, considere dividi-las. Como regra geral, qualquer coisa após um "e" deve estar em sua própria classe
Veja também se você pode definir uma interface que cubra a funcionalidade de Disparo e Operação. Se você não puder, isso é outra indicação de que sua turma está ficando muito grande. Isso também interromperá o acoplamento entre as classes.
fonte
Trigger
seria acoplado a aOperation
. Mas é assim que os dados do mundo real se parecem. Eles são acoplados, porque existe um mapeamento, por exemplo, eles precisam conhecer um ao outro para ter significado.