Quando usar herança, quando usar 'apenas um campo booleano'?

18

Em nosso aplicativo Rails, estamos adicionando notificações. Alguns deles são blocking: Eles interrompem o progresso de qualquer recurso em que foram adicionados, porque faltam algumas informações sobre esse recurso.

Outras notificações são simples e fornecem apenas informações.

Hoje tive uma discussão com outro programador de nossa equipe. Eu criei a estrutura de herança como esta:

insira a descrição da imagem aqui

Ele, no entanto, prefere que eu adicione blockingcomo método de retorno booleano em cada Notificação e especifique uma lista de subclasses que estão bloqueando dentro da classe pai da Notificação.

A diferença entre essas abordagens não é muito grande; na minha abordagem, não é necessário especificar essa lista, mantendo a classe raiz mais limpa. Por outro lado, a lógica especial que acontece Notification::Blockingagora também não é muito grande.

Que tipo de abstração é mais adequado para esse problema?

Qqwy
fonte
11
Uma turma de pais nunca deve saber sobre seus filhos. Por que você precisa manter uma lista de subclasses?
Coteyr 23/05
Como eles são adicionados a um recurso e como eles interrompem seu progresso?
null
3
Por que você precisa de tantas classes de notificação? Parece-me que você poderia criar uma classe de notificação e deixar os dados direcionarem as ações em vez dos tipos de dados.
Trisped
1
@Trisped: certo, se você se mover para um aspecto do comportamento na classe base com uma lista exaustiva de casos, tenha a coragem de suas convicções, admita que não está realmente criando uma classe base utilizável para personalização por extensão e mova todo o comportamento para a classe base!
Steve Jessop

Respostas:

35

Você deseja evitar que as classes base conheçam as classes derivadas. Ele apresenta acoplamento rígido e é uma dor de cabeça para manutenção, porque você deve se lembrar de adicionar à lista sempre que criar uma nova classe derivada.

Também impedirá que você seja capaz de colocar a classe Notification em um pacote / conjunto reutilizável, se você quiser usar essa classe em vários projetos.

Se você realmente deseja usar uma única classe base, outra maneira de resolver isso é adicionar uma propriedade virtual ou método IsBlocking na classe base Notification. Classes derivadas podem então substituir isso para retornar verdadeiro ou falso. Você teria uma solução de classe única sem a classe base sabendo sobre classes derivadas.

17 de 26
fonte
3
Este. Tomar decisões em um só lugar. Não espalhe o conhecimento de quais classes bloqueiam entre a classe e a lista.
Candied_orange 23/05
É o que faço, funciona muito bem e é muito reutilizável.
Coteyr 23/05
13

e especifique uma lista de subclasses que estão bloqueando dentro da classe pai da Notificação.

Isso parece muito peculiar e é um cheiro de código específico.

Eu forneceria subclasses se você tiver diferenças de comportamento entre as classes e quiser tratar todas essas notificações da mesma maneira (por exemplo, usando polimorfismo ).

Brian Agnew
fonte
1
Eu acho que "comportamento" é a chave aqui: quando são apenas dados, o campo deve ser um discriminador suficiente. O comportamento é a melhor razão para usar o polimorfismo, no entanto, sempre se deve considerar a complexidade da manutenção ao criar hierarquias de herança. Leia en.wikipedia.org/wiki/Composition_over_inheritance
cottsak
7

Como uma conversa com as respostas existentes, sugiro que a propriedade booleana seja a melhor opção se for necessário alterar dinamicamente o modo a ser usado (por exemplo, através de um arquivo de configuração que fornece uma lista de quais tipos devem ser bloqueados). e que não são).

Dito isto, um design melhor, mesmo nessa situação, pode ser o uso de um objeto Decorator.

Jules
fonte
1

Eu diria que depende de quanto mais é especial sobre uma notificação de bloqueio, embora meu primeiro pensamento seja o de "ambos":

class Notification
 virtual Boolean Blocking{get return false;}

class BlockingNotification inherits Notification
 virtual overrides Boolean Blocking{get return true;}

Dessa forma, você pode usar n.Blockingor n is BlockingNotification(all in pseudo-code), embora, se você permitir que uma classe implemente um Blockingvalor sensível ao contexto , visto que você precisará verificar esse valor toda vez que a BlockingNotificationclasse se tornar menos útil.

De qualquer forma, concordo com as outras respostas das quais você não deseja que a implementação da classe base Blockingprecise saber sobre as classes derivadas.

Mark Hurd
fonte
0

Em vez de criar duas classes base e várias instâncias de cada uma, faça uma classe de notificação com um booleano para indicar se a notificação está bloqueando e qualquer outra informação necessária para comunicar a notificação ao usuário.

Isso permite que você use um conjunto de códigos para processar e apresentar notificações e reduz a complexidade do seu código.

Trisped
fonte