Agora que você explicou mais detalhadamente, parece que realmente não há lógica específica para subclasses. Nesse caso, a herança não é a escolha certa. Se realmente existem apenas maneiras pequenas em que outras partes do código tratam os produtos, colocar o código nas classes de produto seria errado - uma violação da separação de interesses que sobrecarregaria as classes de produto com o conhecimento de outras partes do sistema. O exemplo de classificação deixa isso claro. Os produtos não devem saber como são classificados, armazenados ou apresentados; se o código que classifica ou apresenta objetos for alterado, a classe do produto não precisará ser reescrita.
Peço desculpas por duvidar de você sobre isso. Vemos tantas pessoas aqui apresentando perguntas baseadas em conhecimentos inadequados de OO, e presumi que esse era o caso quando eu deveria ter solicitado mais informações primeiro.
Na verdade ...
A opção de sobrecarga de método, conforme mostrado em seu código de exemplo, está realmente indo contra princípios como a separação de interesses e DRY. Qual é a coisa comum sobre o método add_product ? Adiciona um produto à coleção apropriada. Mas no código de exemplo, essa única tarefa fundamentalmente idêntica é duplicada em três locais diferentes. Isso realmente não é muito diferente de uma cadeia de lógica if..then..else .
- A lógica é duplicada e quaisquer alterações no comportamento comum terão que ser duplicadas em cada lugar. Os erros são prováveis e não serão detectados.
- O código de adição de produtos não precisa ser para saber quais tipos de produtos estão disponíveis. Tudo o que precisa saber é que há uma variedade de produtos, cada um dos quais deve ser adicionado a uma coleção apropriada. Torná-lo ciente significa que você precisa alterar esta seção do código sempre que adicionar um novo produto.
Uma maneira melhor de fazer isso, se você realmente precisa de uma coleta separada para cada tipo de produto, seria ter um mapa de coleções de produtos. A chave seria o tipo de produto. Em seguida, você pode ter um único método product_add , que consulta esse mapa e adiciona o produto ao local correto. Mesmo se você descobrir que existe alguma necessidade de diferentes subclasses de produtos, esse método não precisa ser conhecido. A classe de objeto em si pode ser a chave para o mapa, por exemplo.
Como é provável que mais de um método deseje atualizar ou ler essas coleções, provavelmente é melhor ter um objeto Produtos que armazena os objetos. Se ele contém um mapa de N coleções diferentes ou uma coleção que ele sabe filtrar / pesquisar por tipo é um detalhe de implementação que não precisa se preocupar com outras partes do seu código.
Em resumo
Como realmente parece não haver código específico do produto, classes extras vazias não são apenas inúteis, elas complicam seu código e o tornam mais frágil. Um membro "tipo" é melhor.
- A maioria do seu código nem precisa saber que existe tal membro.
- Em cada domínio (as seções claramente separadas de seu aplicativo), identifique os poucos bits que realmente precisam diferenciar entre os tipos de produto.
- Isole-os em classes auxiliares especiais ou funções que retornarão o produto apropriado (ou faça a coisa apropriada que corresponda ao tipo)
- Os mapas são muito úteis (podem conter não apenas coleções, mas outros objetos auxiliares / funções para retornar a algum outro código para fazer a coisa apropriada com este objeto), mas devem estar ocultos de qualquer coisa, exceto do código privilegiado que realmente precisa ter conhecimento do tipo de produto.
Se você fizer isso, não apenas seu código será mais limpo e mais elegante (livre de feias if..then..else cadeias, por uma coisa), se você alguma vez encontrar um real necessidade de subclasses de produtos, quase nenhum dos seus códigos existentes terá que mudar. E até mesmo eles precisam apenas alterar sua implementação interna, porque o código deles ainda não é uma preocupação da classe de produto ou de qualquer uma de suas subclasses.
Ainda há muita diversão com a adição de novas classes nos respectivos domínios para encapsular qualquer "Eu sei que tipo de produtos existem". Um desafio é diferenciar entre os bits daqueles códigos que são específicos do domínio (por exemplo, classificação em uma coleção que é relevante somente em um domínio) daqueles que são realmente gerais. A lista de todos os tipos de produto válidos, por exemplo, deve ser um enum que pode ser buscado por qualquer código específico do domínio com reconhecimento de produto. Qualquer mapa deve usar esse enum como o tipo para sua chave. Qualquer função que tenha um "tipo" de produto como um parâmetro deve ter um parâmetro apenas desse tipo de enum e assim por diante. Agora, onde no seu código você vai definir esse enum?