Como gerenciar uma responsabilidade única quando a responsabilidade é compartilhada?

10

Eu tenho duas classes base, Operatione Trigger. Cada um possui várias subclasses especializadas em certos tipos de operações ou gatilhos. A Triggerpode disparar um específico Operation. Enquanto um Operationpode ser acionado por um específico Trigger.

Preciso escrever o código que mapeia um dado Operationa 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.

  1. No gatilho, por exemplo bool Triggers(Operation o).
  2. Na operação, por exemplo bool TriggeredBy(Trigger t).
  3. 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.

insira a descrição da imagem aqui

Editar 2.

O motivo do tipo de retorno de bool. Outra classe consumirá uma coleção de Triggere uma coleção de Operation. Ele precisa saber onde existe um mapeamento entre a Triggere an Operation. Ele usará essas informações para criar um relatório.

James Wood
fonte
Por que o tipo bool?
Tulains Córdova
@ user61852 para retornar um resultado para o código de chamada
James Wood
1
O que o código de chamada faz com o booleano? Dependendo do que você responder a esta pergunta, eu posso ter a solução.
Tulains Córdova
@ user61852, veja minhas edições.
James Wood
1
Portanto, não tem nada a ver com a execução do gatilho da operação?
Tulains Córdova

Respostas:

4

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).

Omer Iqbal
fonte
5

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:

public interface Mapping {
    public void setObject1(Object o);
    public void setObject2(Object o);
    public Object getObjecto1();
    public Object getObjecto2();
}
  • Substitua o equals()método Mappingpara que coleções de Mappingpossam ser perguntadas se elas contêm um determinado mapeamento.
  • Os objetos especializados devem ter equals()métodos adequados também.
  • Implemente também a interface Comparable, para que você possa classificar os relatórios.

Neles, você pode simplesmente colocar um mapeamento em uma coleção

List<Mapping> list = new ArrayList<Mapping>();
Hat hat = new Hat();
Bag bag = new Bag();
list.add(new Mapping(hat,bag));

Mais tarde você pode perguntar:

// let's say you have a variable named x which is of type Mapping

if ( list.contains(x) ){
    // do some thing
}
Tulains Córdova
fonte
0
  1. Divida seu código em bits menores.

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.

Tom Squires
fonte
1
Com toda a honestidade, não tenho certeza se posso decifrar ainda mais meu código. Atualizei minha pergunta com as assinaturas de classe para que você possa ver, mas basicamente eles são objetos de dados bem leves que armazenam algumas propriedades cada. Em termos de acoplamento, sim, isso é um pouco problemático, pois efetivamente a Triggerseria acoplado a a Operation. 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.
James Wood