Estou lendo objetos, padrões e práticas do PHP . O autor está tentando modelar uma lição em uma faculdade. O objetivo é produzir o tipo de aula (palestra ou seminário) e os encargos da aula, dependendo se é uma aula de preço fixo ou por hora. Portanto, a saída deve ser
Lesson charge 20. Charge type: hourly rate. Lesson type: seminar.
Lesson charge 30. Charge type: fixed rate. Lesson type: lecture.
quando a entrada é a seguinte:
$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');
Eu escrevi isto:
class Lesson {
private $chargeType;
private $duration;
private $lessonType;
public function __construct($chargeType, $duration, $lessonType) {
$this->chargeType = $chargeType;
$this->duration = $duration;
$this->lessonType = $lessonType;
}
public function getChargeType() {
return $this->getChargeType;
}
public function getLessonType() {
return $this->getLessonType;
}
public function cost() {
if($this->chargeType == 'fixed rate') {
return "30";
} else {
return $this->duration * 5;
}
}
}
$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');
foreach($lessons as $lesson) {
print "Lesson charge {$lesson->cost()}.";
print " Charge type: {$lesson->getChargeType()}.";
print " Lesson type: {$lesson->getLessonType()}.";
print "<br />";
}
Mas, de acordo com o livro, estou errado (também tenho certeza). Em vez disso, o autor forneceu uma grande hierarquia de classes como solução. Em um capítulo anterior, o autor declarou os seguintes 'quatro sinais' como o momento em que eu deveria considerar alterar minha estrutura de classe:
- Duplicação de código
- A turma que sabia demais sobre seu contexto
- O valete de todos os negócios - classes que tentam fazer muitas coisas
- Instruções condicionais
O único problema que vejo são as declarações condicionais e isso também de uma maneira vaga - então, por que refatorar isso? Que problemas você acha que podem surgir no futuro que eu não previ?
Atualização : esqueci de mencionar - esta é a estrutura de classes que o autor forneceu como solução - o padrão de estratégia :
fonte
Respostas:
É engraçado que o livro não o exponha claramente, mas a razão pela qual favorece uma hierarquia de classes sobre se as instruções dentro da mesma classe é provavelmente o princípio Aberto / Fechado Esta regra de design de software amplamente conhecida afirma que uma classe deve ser fechada para modificação, mas aberto para extensão.
No seu exemplo, adicionar um novo tipo de lição significaria alterar o código-fonte da classe Lesson, tornando-o frágil e propenso a regressão. Se você tivesse uma classe base e uma derivada para cada tipo de lição, seria necessário adicionar outra subclasse, que geralmente é considerada mais limpa.
Você pode colocar essa regra na seção "Declarações condicionais", se quiser, no entanto, considero essa "placa de sinalização" um pouco vaga. Se as instruções geralmente são apenas códigos, cheiros, sintomas. Eles podem resultar de uma ampla variedade de más decisões de design.
fonte
Eu acho que o que o livro visa ensinar é evitar coisas como:
Minha interpretação do livro é que você deve ter três classes:
Você deve reimplementar a
cost
função nas duas subclasses.Minha opinião pessoal
para casos simples como esse: não. É estranho que seu livro seja a favor de uma hierarquia de classes mais profunda para evitar declarações condicionais simples. Além do mais, com suas especificações de entrada,
Lesson
terá que ser uma fábrica com um despacho que é uma declaração condicional. Portanto, com a solução do livro, você terá:Isso é mais complexo sem nenhum benefício adicional.
fonte
SemesterLesson
,MasterOneWeekLesson
etc. Uma classe raiz abstrata, ou melhor ainda, uma interface, é definitivamente o caminho a seguir. Mas quando você tem apenas dois casos, fica a critério do autor.O exemplo no livro é bem estranho. De sua pergunta, a declaração original é:
o que, no contexto de um capítulo sobre POO, significaria que o autor provavelmente deseja que você tenha a seguinte estrutura:
Mas então vem a entrada que você citou:
ou seja, algo que é chamado de código digitado de forma estrita e que, honestamente, nesse contexto, quando o leitor pretende aprender OOP, é horrível.
Isso também significa que, se eu estiver certo sobre a intenção do autor do livro (ou seja, criar as classes abstratas e herdadas listadas acima), isso levará a códigos duplicados e bastante ilegíveis e feios:
Quanto aos "letreiros":
Duplicação de código
Seu código não possui duplicação. O código que o autor espera que você escreva faz.
A classe que sabia demais sobre seu contexto
Sua classe apenas passa seqüências de caracteres da entrada para a saída e não sabe nada. O código esperado, por outro lado, pode ser criticado por saber demais.
The Jack of All Trades - Classes que tentam fazer muitas coisas
Novamente, você está apenas passando as cordas, nada mais.
Declarações condicionais
Há uma declaração condicional no seu código. É legível e fácil de entender.
Talvez, de fato, o autor esperasse que você escrevesse um código muito mais refatorado do que aquele com seis classes acima. Por exemplo, você pode usar o padrão de fábrica para lições e encargos, etc.
Nesse caso, você terminará com nove classes, que serão um exemplo perfeito de uma super arquitetura .
Em vez disso, você acabou com um código limpo e fácil de entender, que é dez vezes menor.
fonte
Antes de tudo, você pode alterar "números mágicos" como 30 e 5 na função cost () para variáveis mais significativas :)
E para ser sincero, acho que você não deve se preocupar com isso. Quando você precisar mudar de classe, então você mudará. Tente criar valor primeiro e depois passe para a refatoração.
E por que você está pensando que está errado? Essa aula não é "boa o suficiente" para você? Por que você está preocupado com algum livro;)
fonte
Não há absolutamente nenhuma necessidade de implementar quaisquer classes adicionais. Você tem um pouco de
MAGIC_NUMBER
problema em suacost()
função, mas é isso. Qualquer outra coisa é uma enorme engenharia excessiva. No entanto, infelizmente é comum que conselhos muito ruins sejam dados - o Circle herda Shape, por exemplo. Definitivamente, não é eficiente, de forma alguma, derivar uma classe para a herança simples de uma função. Você poderia usar uma abordagem funcional para personalizá-la.fonte