Я давно работаю разработчиком (мне 49 лет), но я довольно новичок в объектно-ориентированной разработке. Я читал об ОО со времен Эйфеля Бертранда Мейера, но мало занимался программированием.
Дело в том, что каждая книга по ОО-дизайну начинается с примера лодки, автомобиля или какого-либо обычного объекта, который мы часто используем, и они начинают добавлять атрибуты и методы, а также объясняют, как они моделируют состояние объекта и что можно сделать с помощью Это.
Таким образом, они обычно выглядят так: «чем лучше модель, тем лучше она представляет объект в приложении и тем лучше все получается».
Пока все хорошо, но, с другой стороны, я нашел несколько авторов, которые дают рецепты, такие как «класс должен умещаться всего на одной странице» (я бы добавил «на каком размере монитора?», Теперь, когда мы пытаемся не напечатать код!).
Возьмем, к примеру, PurchaseOrder
класс с конечным автоматом, управляющим его поведением, и коллекцией PurchaseOrderItem
, одним из аргументов которого здесь является то, что мы должны использовать PurchaseOrder
простой класс с некоторыми методами (немного больше, чем класс данных), и иметь PurchaseOrderFSM
«эксперт класс» , который обрабатывает конечный автомат для PurchaseOrder
.
Я бы сказал, что подпадает под классификацию «Зависть к особенностям» или «Неприемлемая близость» поста Джеффа Этвуда « Запахи кода» на тему «Кодирующий ужас». Я бы назвал это здравым смыслом. Если я могу оформить, утвердить или отменить свой заказ в режиме реальной покупки, то PurchaseOrder
класс должен иметь issuePO
, approvePO
и cancelPO
методу.
Разве это не идет с принципами «максимизировать сплоченность» и «минимизировать сцепление», которые я понимаю как краеугольные камни ОО?
Кроме того, разве это не помогает в обслуживании класса?
источник
PurchaseOrder
, вы могли бы просто назвать свои методыissue
,approve
иcancel
.PO
Суффикс добавляет только отрицательное значение.Ответы:
Класс должен использовать принцип единой ответственности. Большинство очень больших классов, которые я видел, делают для многих вещей, поэтому они слишком большие. Посмотрите на каждый метод и код, решите, должен ли он быть в этом классе или отдельно, дублированный код является подсказкой. Возможно, у вас есть метод issuePO, но он содержит, например, 10 строк кода доступа к данным? Этот код, вероятно, не должно быть там.
источник
На мой взгляд, размер класса не имеет значения, поскольку переменные, функции и методы имеют отношение к рассматриваемому классу.
источник
Самая большая проблема с большими классами - когда дело доходит до тестирования этих классов или их взаимодействия с другими классами. Большой размер класса сам по себе не является проблемой, но, как и все запахи, он указывает на потенциальную проблему. Вообще говоря, когда класс большой, это указывает на то, что класс делает больше, чем должен делать один класс.
По аналогии с вашим автомобилем, если класс
car
слишком велик, это, вероятно, указывает на то, что его нужно разделить на классengine
, классdoor
s, классwindshield
s и т. Д.источник
Я не думаю, что есть конкретное определение слишком большого класса. Тем не менее, я бы использовал следующие рекомендации, чтобы определить, следует ли мне реорганизовать мой класс или переопределить область действия класса:
Что касается 1, представьте, что вы определяете свой размер лобового стекла, размер колеса, модель лампы заднего фонаря и 100 других деталей в своем классе
car
. Хотя все они «актуальны» для автомобиля, очень трудно отследить поведение такого классаcar
. И когда однажды ваш коллега случайно (или, в некоторых случаях, преднамеренно) изменит размер лобового стекла в зависимости от модели лампы заднего фонаря, вам понадобится целая вечность, чтобы выяснить, почему лобовое стекло больше не подходит к вашему автомобилю.Что касается пункта 2, представьте, что вы пытаетесь определить все варианты поведения автомобиля в классе
car
, ноcar::open_roof()
он будет доступен только для кабриолетов иcar::open_rear_door()
не применяется к этим 2-дверным автомобилям. Хотя кабриолеты и 2-дверные машины по определению являются «автомобилями», их реализация в классеcar
усложняет класс. Класс становится все труднее использовать и труднее поддерживать.Помимо этих двух рекомендаций, я бы предложил четкое определение области действия класса. Как только вы определите область действия, реализуйте класс строго на основании определения. Большую часть времени люди получают «слишком большой» класс из-за плохого определения области действия или из-за произвольных функций, добавленных в класс
источник
Скажите, что вам нужно в 300 строк
Примечание. Это практическое правило предполагает, что вы придерживаетесь подхода «один класс на файл», который сам по себе является хорошей идеей в отношении удобства сопровождения.
Это не абсолютное правило, но если я вижу более 200 строк кода в одном классе, я подозреваю. 300 линий и предупреждающих звонков. Когда я открываю чужой код и нахожу 2000 строк, я знаю, что пришло время рефакторинга, даже не читая первую страницу.
В основном это сводится к ремонтопригодности. Если ваш объект настолько сложен, что вы не можете выразить его поведение в 300 строк, это будет очень трудно понять кому-то еще.
Я обнаружил, что изгибаю это правило в Model -> ViewModel -> View world, где я создаю «объект поведения» для страницы пользовательского интерфейса, потому что для описания полной серии поведения часто требуется более 300 строк. страница. Однако я все еще новичок в этой модели программирования и нахожу хорошие способы рефакторинга / повторного использования поведения между страницами / моделями представления, что постепенно уменьшает размер моих ViewModels.
источник
Давайте вернемся назад:
На самом деле, это краеугольный камень программирования, из которого ОО является лишь одной парадигмой.
Я позволю Антуану де Сент-Экзюпери ответить на ваш вопрос (потому что я ленивый;)):
Чем проще класс, тем увереннее вы будете правы, тем проще будет его полностью протестировать.
источник
Я с ОП по классификации Feature Envy. Ничто не раздражает меня больше, чем набор классов с набором методов, которые работают с внутренностями некоторых других классов. ОО предлагает вам смоделировать данные и операции с этими данными. Это не значит, что вы помещаете операции в другое место, чем данные! Он пытается заставить вас собрать данные и эти методы вместе .
С
car
иperson
модели настолько распространены, что было бы , как положить код для создания электрического соединения, или даже вращение стартера, вperson
классе. Я надеюсь, что это будет явно неправильно для любого опытного программиста.У меня нет идеального размера класса или метода, но я предпочитаю, чтобы мои методы и функции помещались на одном экране, чтобы я мог видеть их все в одном месте. (У меня есть Vim, настроенный на открытие окна из 60 строк, которое точно соответствует числу строк на печатной странице.) Есть места, где это не имеет большого смысла, но это работает для меня. Мне нравится следовать одному и тому же руководству для определений классов, и стараюсь держать мои файлы под несколькими "страницами" долго. Что-то более 300, 400 строк начинает казаться громоздким (в основном потому, что класс начал брать на себя слишком много прямых обязанностей).
источник
Когда в определении класса есть пара сотен методов, он слишком велик.
источник
Я полностью согласен с использованием принципа единой ответственности для классов, но если он соблюдается и приводит к большим классам, то пусть будет так. Реальное внимание должно быть уделено тому, чтобы отдельные методы и свойства не были слишком большими, цикломатическая сложность должна быть там руководством, а последующее может привести ко многим частным методам, которые увеличат общий размер класса, но сделают его читаемым и тестируемым до детального уровня. Если код остается читаемым, то размер не имеет значения.
источник
Оформление заказа на покупку часто является сложным процессом, который включает в себя не только заказ на покупку. В этом случае, вероятно, правильное сохранение бизнес-логики в отдельном классе и упрощение заказа на покупку. Но в целом вы правы - вам не нужны классы «структура данных». Например, ваш метод бизнес-класса "POManager"
issue()
должен, вероятно, вызывать метод ПОissue()
. Затем ПО может установить свой статус на «Выдано» и отметить дату выпуска, в то время как POManager может затем отправить уведомление счетам к оплате, чтобы они знали, ожидать счета-фактуры, и другое уведомление инвентаризации, чтобы они знали, что что-то поступает и ожидаемая дата.Любой, кто выдвигает «правила» для размера метода / класса, очевидно, не провел достаточно времени в реальном мире. Как уже упоминали другие, это часто показатель рефакторинга, но иногда (особенно в работе типа «обработка данных») вам действительно нужно написать класс с одним методом, который охватывает более 500 строк. Не зацикливайтесь на этом.
источник
С точки зрения только физического размера класса, вид большого класса является признаком запаха кода, но не обязательно означает, что всегда есть запахи. (Обычно они есть) Как сказали бы пираты в Пиратах Карибского моря, это скорее то, что вы бы назвали «руководящими принципами», чем фактическими правилами. Я старался строго следовать «не более ста LOC в классе» один раз, и в результате получилось МНОГО ненужного чрезмерного проектирования.
Я обычно начинаю свои проекты с этого. Что этот класс делает в реальном мире? Как мой класс должен отражать этот объект, как указано в его требованиях? Мы добавим немного состояния здесь, поле там, метод для работы с этими полями, добавим еще несколько и вуаля! У нас есть рабочий класс. Теперь заполните подписи правильными реализациями, и мы готовы, или вы так думаете. Теперь у нас есть хороший большой класс со всей необходимой нам функциональностью. Но проблема в том, что он не принимает во внимание то, как работает реальный мир. И под этим я подразумеваю, что это не учитывает "ИЗМЕНЕНИЕ".
Причина, по которой классы предпочтительно делятся на более мелкие части, заключается в том, что позже трудно изменить большие классы, не затрагивая существующую логику, не вводя новую или две ошибки непреднамеренно или просто усложняя повторное использование. Это где рефакторинг, шаблоны проектирования, SOLID и т. Д. Вступают в игру, и конечным результатом обычно является маленький класс, работающий с другими более мелкими, более детальными подклассами.
Кроме того, в вашем примере добавление IssuePO, ApprovePO и Cancel PO в класс PurchaseOrder кажется нелогичным. Заказы на покупку не выдают, не утверждают и не отменяют сами. Если у PO должны быть методы, то методы должны работать в своем состоянии, а не в классе в целом. Это действительно классы данных / записей, над которыми работают другие классы.
источник
Достаточно большой, чтобы содержать всю необходимую логику для выполнения своей работы.
Достаточно маленький, чтобы его можно было обслуживать и следовать принципу единой ответственности .
источник