Não há necessidade de ter subclasses específicas para cada pessoa.
Você está certo, essas devem ser instâncias.
Meta de subclasses: para estender classes pai
As subclasses são usadas para estender a funcionalidade fornecida pela classe pai. Por exemplo, você pode ter:
-
Uma classe pai
Battery
que podePower()
something e pode ter uma propriedadeVoltage
, -
E uma subclasse
RechargeableBattery
, que pode herdarPower()
eVoltage
, mas também pode serRecharge()
d.
Observe que você pode passar uma instância de RechargeableBattery
class como um parâmetro para qualquer método que aceite Battery
como argumento. Isso é chamado de Princípio da substituição de Liskov , um dos cinco princípios do SOLID. Da mesma forma, na vida real, se meu MP3 player aceitar duas baterias AA, posso substituí-las por duas pilhas AA recarregáveis.
Observe que às vezes é difícil determinar se você precisa usar um campo ou uma subclasse para representar uma diferença entre algo. Por exemplo, se você tiver que lidar com pilhas AA, AAA e 9 volts, você criaria três subclasses ou usaria um enum? "Substituir subclasse por campos" em Refatoração por Martin Fowler, página 232, pode fornecer você algumas idéias e como passar de um para outro.
No seu exemplo, Karl
e John
não estendem nada, nem fornecem nenhum valor adicional: você pode ter exatamente a mesma funcionalidade usando a classe Person
diretamente. Ter mais linhas de código sem valor adicional nunca é bom.
Um exemplo de um caso de negócios
O que poderia ser um caso de negócio em que faria sentido criar uma subclasse para uma pessoa específica?
Digamos que criemos um aplicativo que gerencie pessoas trabalhando em uma empresa. O aplicativo também gerencia permissões, portanto, Helen, o contador, não pode acessar o repositório SVN, mas Thomas e Mary, dois programadores, não podem acessar documentos relacionados à contabilidade.
Jimmy, o grande chefe (fundador e CEO da empresa) tem privilégios muito específicos que ninguém tem. Ele pode, por exemplo, desligar todo o sistema ou demitir uma pessoa. Você entendeu a ideia.
O modelo mais pobre para tal aplicação é ter classes como:
porqueaduplicaçãodecódigosurgirámuitorapidamente.Mesmonoexemplobásicodequatrofuncionários,vocêduplicaráocódigoentreasclassesThomaseMary.IssofarácomquevocêcrieumaclassepaicomumProgrammer
.Comovocêtambémpodetervárioscontadores,provavelmentetambémcriaráAccountant
class.
Agora, você percebe que ter a classe Helen
não é muito útil, bem como manter Thomas
e Mary
: a maior parte do seu código funciona no nível superior de qualquer maneira - no nível de contadores, programadores e Jimmy. O servidor SVN não se importa se é Thomas ou Mary quem precisa acessar o log - ele só precisa saber se é um programador ou um contador.
if (person is Programmer)
{
this.AccessGranted = true;
}
Então você acaba removendo as classes que não usa:
"Mas eu posso manter Jimmy como está, já que sempre haveria apenas um CEO, um grande chefe - Jimmy", você pensa. Além disso, o Jimmy é muito usado em seu código, que na verdade parece mais com isso, e não como no exemplo anterior:
if (person is Jimmy)
{
this.GiveUnrestrictedAccess(); // Because Jimmy should do whatever he wants.
}
else if (person is Programmer)
{
this.AccessGranted = true;
}
O problema com essa abordagem é que Jimmy ainda pode ser atropelado por um ônibus, e haveria um novo CEO. Ou a diretoria pode decidir que Mary é tão boa que ela deveria ser uma nova CEO, e Jimmy seria rebaixado a cargo de vendedor, então agora você precisa percorrer todo seu código e mudar tudo.