Я читаю PHP объекты, шаблоны и практики . Автор пытается смоделировать урок в колледже. Цель состоит в том, чтобы вывести тип урока (лекция или семинар), а также плату за урок в зависимости от того, является ли это почасовым или фиксированным уроком. Таким образом, вывод должен быть
Lesson charge 20. Charge type: hourly rate. Lesson type: seminar.
Lesson charge 30. Charge type: fixed rate. Lesson type: lecture.
когда вход выглядит следующим образом:
$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');
Я написал это:
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 />";
}
Но, согласно книге, я ошибаюсь (я тоже почти уверен). Вместо этого автор дал большую иерархию классов в качестве решения. В предыдущей главе автор указал следующие «четыре указателя» как время, когда мне следует подумать об изменении структуры моего класса:
- Дублирование кода
- Класс, который слишком много знал о его контексте
- Мастер на все руки - классы, которые пытаются делать много вещей
- Условные заявления
Единственная проблема, которую я вижу, - это условные утверждения, и это тоже расплывчато - так зачем же рефакторинг? Как вы думаете, какие проблемы могут возникнуть в будущем, чего я не предвидел?
Обновление : я забыл упомянуть - это структура класса, которую автор предоставил в качестве решения - шаблон стратегии :
источник
Ответы:
Забавно, что в книге не говорится об этом четко, но причина, по которой она предпочитает иерархию классов, если операторы внутри одного класса, вероятно, является принципом Open / Closed. Это широко известное правило разработки программного обеспечения гласит, что класс должен быть закрыт для модификация, но открытая для расширения.
В вашем примере добавление нового типа урока будет означать изменение исходного кода класса урока, делая его хрупким и подверженным регрессии. Если бы у вас был базовый класс и одна производная для каждого типа урока, вам просто нужно добавить еще один подкласс, который обычно считается более чистым.
Вы можете поместить это правило в раздел «Условные заявления», если хотите, однако я считаю этот «указатель» немного расплывчатым. Если заявления, как правило, просто запах кода, симптомы. Они могут возникнуть в результате широкого спектра плохих дизайнерских решений.
источник
Я предполагаю, что целью книги является обучение таким вещам, как:
Моя интерпретация книги такова, что у вас должно быть три класса:
Затем вы должны переопределить
cost
функцию в обоих подклассах.Мое личное мнение
для простых случаев, подобных этому: не надо. Странно, что ваша книга предпочитает более глубокую иерархию классов, чтобы избежать простых условных выражений. Более того, с вашей входной спецификацией
Lesson
должна быть фабрика с отправкой, которая является условным оператором. Так что с книжным решением у вас будет:Это больше сложности без каких-либо дополнительных преимуществ.
источник
SemesterLesson
,MasterOneWeekLesson
и т. Д. В этом случае определенно стоит использовать абстрактный корневой класс или, что еще лучше, интерфейс. Но когда у вас есть только два случая, это остается на усмотрение автора.Пример в книге довольно странный. Исходя из вашего вопроса, оригинальное утверждение:
что в контексте главы о ООП будет означать, что автор, вероятно, хочет, чтобы вы имели следующую структуру:
Но потом приходит информация, которую вы цитировали:
то есть то, что называется строго типизированным кодом, и которое, честно говоря, в этом контексте, когда читатель намеревается изучить ООП, ужасно.
Это также означает, что если я прав в отношении намерения автора книги (т.е. создания тех абстрактных и унаследованных классов, которые я перечислил выше), это приведет к дублированию и довольно нечитаемому и уродливому коду:
Что касается «указателей»:
Дублирование кода
Ваш код не имеет дублирования. Код, который автор ожидает от вас, делает.
Класс, который слишком много знал о его контексте
Ваш класс просто передает строки из входных данных в выходные и вообще ничего не знает. С другой стороны, ожидаемый код может быть подвергнут критике за то, что он знает слишком много.
Мастер на все руки - Классы, которые пытаются делать много вещей
Опять же, вы просто пропускаете строки, не более того.
Условные заявления
В вашем коде есть одно условное утверждение. Это читабельно и легко понять.
Возможно, на самом деле, автор ожидал, что вы напишите гораздо более переработанный код, чем тот, который содержит шесть классов выше. Например, вы можете использовать фабричный шаблон для уроков и сборов и т. Д.
В этом случае вы получите девять классов, которые станут отличным примером чрезмерной архитектуры .
Вместо этого вы получили простой для понимания, чистый код, который в десять раз короче.
источник
Прежде всего вы можете изменить «магические числа», такие как 30 и 5 в функции cost (), на более значимые переменные :)
И, честно говоря, я думаю, что вы не должны беспокоиться об этом. Когда вам нужно будет изменить класс, тогда вы измените его. Попробуйте сначала создать ценность, а затем перейти к рефакторизации.
И почему ты думаешь, что ты не прав? Разве этот класс не достаточно хорош для вас? Почему вы беспокоитесь о какой-то книге;)
источник
Совершенно не нужно реализовывать какие-либо дополнительные классы. У вас есть небольшая
MAGIC_NUMBER
проблема в вашейcost()
функции, но это все. Все остальное - это огромная чрезмерная инженерия. Однако, к сожалению, очень часто дается очень плохой совет - например, Circle наследует Shape. Определенно неэффективно выводить класс для простого наследования одной функции. Вы могли бы использовать функциональный подход, чтобы настроить его вместо этого.источник