avatar

35 принципов объектно-ориентированного дизайна

Рейтинг 3.5 из 5. Голосов: 381
Den 6 лет назад
1
,

0


Ответы (1)

Den
avatar
Модератор За 6 месяцев на форуме За год на форуме За два года на форуме за 10 сообщений За 100 сообщений за 500 сообщений За 700 сообщений
1089 Mar 10
6 лет назад
Вольный перевод фрагмента книги Брюса Эккеля «Thinking in Java.» Изящество всегда вознаграждается. Может показаться, что слишком долго искать действительно красивое решение проблемы, но когда вы сможете легко адапатировать его к новой ситуации, избежав долгих часов, дней, а то и месяцев борьбы с собственным кодом, вы будете вознаграждены(даже если со стороны это будет незаметно). Это позволит вам создать программу, которую легко не только скомпилировать и отладить, но и понимать и изменять, что, собственно, и составляет коммерческую ценность. Понимание этого пункта требует некоторого опыта, поскольку, пока вы делаете фрагмент кода элегантным, кажется, что вы мало продуктивны. Не поддавайтесь спешке и суете, они только замедлят вашу работу. Сначала заставь работать, потом ускоряй. Это верно, даже если вы уверены, что фрагмент кода действительно важен и будет основным узким местом в вашей системе. Не торопитесь. Сперва заставьте систему работать с настолько простым решением задачи, насколько это возможно. Уже потом, если решение оказалось не достаточно быстрым, профилируйте его. Почти всегда вы обнаружите, что ваше мнимое узкое место не проблема. Потратьте время на более важные вещи. Помните принцип «Разделяй и властвуй». Если проблема, которой вы занимаетесь, слишком сложна, попробуйте вообразить, как должна работать программа, если некий черный ящик скроет все сложности. Этот черный ящик — объект. Напишите сначала код, который использует объект, а потом рассмотрите проблемый объект еще раз и инкапсулируйте его сложности в другие объекты. Отделите создателя класса, от его пользователя(программиста-клиента). Пользователь класса — это своего рода «покупатель», и ему не интересно, что происходит внутри класса. Создатель класса должен быть экспертом в своем деле и создавать код так, чтобы даже использование его начинающим программистом, было работоспособным и эффективным. Библиотеку использовать легко, только если способ ее использования прозрачен. Когда создаете класс, постарайтесь использовать такие имена, чтобы комментарии не понадобились. Ваша цель сделать программный интерфейс модуля концептуально простым. Для этого используйте, по возможности, перегрузку методов. Анализ и дизайн должны определить, как минимум, классы вашей системы, их открытые интерфейсы и их отношения с другими классами, в особенности — базовыми. Если ваш способ разработки производит более того, спросите себя, все ли произведенное имеет значение на протяжении жизненного цикла программы. Если нет, поддержка лишнего влетит вам в копеечку. Участники групп разработки стараются не поддерживать ничего, что не способствует их продуктивности. Фактически, многие методологии разработки этого не учитывают. Автоматизируйте все. Пишите тестовый код первым(до написания самого класса) и храните его вместе с классом. Автоматизируйте прогон тестов вместе со сборкой. Возможно, вы выберете для этого Ant — стандарт дефакто для сборки Java программ. Таким образом, все изменения будут автоматически проверены прогоном тестового кода и вы немедленно обнаружите ошибки. Благодаря сети безопасности вашей тестовой оболочки, вы будете более уверенно вносить изменения, когда обнаружится такая необходимость. Помните, что величайшие улучшения в языках произошли именно из встроенных тестов, предоставляемых контролем типов, системой исключений и пр., но только до сих пор. Остальной путь вы должны проделать сами, создавая надежную систему исполняющую тесты, которые проверят основные специфические свойства вашего класса или программы. Пишите тестовый код первым(прежде чем написать класс) для того, чтобы проверить, что разработка класса завершена. Если вы не способны написать тестовый код, вы не знаете, на что похож ваш класс. Вдобавок, написание тестового кода часто позволяет выявить все дополнительные свойства и ограничения, которые вы могли бы заложить в класс. Эти свойства и ограничения не всегда появляются на стадии анализа и разработки. Кроме того, тесты представляют собой пример кода, показывающий, как следует использовать ваш класс. Все проблемы разработки ПО могут быть упрощены добавлением дополнительного уровня концептуального обобщения. Это фундаментальное правило разработки ПО является основой абстракции, наиважнейшей особенности объектно-ориентированного программирования. Обобщение должно иметь значение. Это значение может быть столь простым как «совмещение часто используемого кода в одном методе». Если вы добавляете уровни обобщения (абстракции, инкапсуляции и пр.), которые не имеют значения, это столь же пл***хо, как не иметь их вообще. Делайте классы настолько атомарными, насколько это возможно. Давайте каждому классу единственное, четкое предназначение. Если ваши классы или вся система становятся слишком сложными, разбейте сложные классы на более простые. Самый простой индикатор — размер, если класс слишком большой, есть вероятность, что он делает слишком много и должен быть декомпозирован. Ключом к переработке класса может быть: Сложный switch(оператор выбора): попробуйте использовать полиморфизм. Большое количество методов, охватывающих очень разные типы операций: следует использовать несколько классов. Большое количество переменных-членов класса, которые определяют очень разные параметры: следует использовать несколько классов. Следите за длинными списками параметров. Вызов метода становится труднее читать, записывать и поддерживать при росте списка параметров. Вместо этого, попробуйте разместить метод в соответствующем классе и/или передавать параметры в виде объектов. Не повторяйтесь. Если фрагмент кода повторяется во многих методах производных классов, поместите этот код в один метод базового класса и вызывайте этот метод из методов производных классов. Следите за операторами switch или цепочками ветвлений if-else. Это типичный индикатор проверки типа — вы пытаетесь выбрать, какой код исполнять в зависимости от того, каков тип данных (в точности тип может быть и неизвестен поначалу). Обычно подобный код можно заменить наследованием и полиморфизмом. Полиморфный метод производит проверку типа вместо вас и обладает лучшей расширяемостью. С точки зрения дизайна, осмотрите и отделите вещи, которые меняются, от вещей, которые остаются неизменными. То есть отыщите элементы системы, которые вы можете захотеть изменить без редизайна, затем инкапсулируйте такие элементы в классах. Сведения о шаблонах проектирования, вы можете почерпнуть в книге «Thinking in Patterns with Java», доступной для скачивания на сайте автора Не расширяйте базовую функциональность с помощью подклассов. Если элемент интерфейса важен для класса, он должен находиться в базовом классе, а не добавляться при наследовании. Если вы добавляете методы в производных классах, возможно, стоит переосмыслить весь дизайн. Меньшее — это большее.Начните с минимального интерфейса класса, настолько маленького и простого, насколько вообще позволяет решение текущей задачи. Не пытайтесь предвидеть все способы, которыми ваш класс может быть использован. Напротив, используя класс, вы обнаружите, как еще стоит расширить интерфейс. Однако, если класс уже используется, невозможно расширить интерфейс без изменения клиентского кода. Если нужно добавить новые методы — клиентский код не изменится. Но если новые методы заменят функциональность старых, лучше оставить старый интерфейс (вы можете объединить функциональность в последующей реализации, если захотите). Если нужно расширить интерфейс существующего метода добавлением новых аргументов, создайте перегруженный метод с новыми аргументами. Такой способ никак не помешает существующим вызовам того метода. Прочтите ваши классы вслух, чтобы убедиться, что они логичны. Отношение между производным и базовым классом произносите как «есть», а между классом и объектами-членами как «имеет, содержит». Когда выбираете между наследованием и композицией, спросите себя требуется ли приведение к базовому типу. Если нет, выбирайте композицию(объекты-члены). Это устранит необходимость в существовании множества базовых типов. Если выберете наследование, пользователи будут думать, что предполагается приведение к базовому типу. Используйте члены класса для изменения значения и перекрытие методов для изменения поведения. То есть, если вы нашли класс, который использует переменные состояния вместе с методами, которые изменяют поведение на основе значения этих переменных, возможно, следует переработать его, отразив различия в поведении в подклассах и перекрытых методах. Следите за перегрузкой. Метод не должен исполнять код условно в зависимости от типа значения аргумента. В этом случае следует создать два или более перегруженных метода взамен. Используйте иерархии исключений, предпочитетльно, производных от специфичных классов исключений в стандартной иерархии Java. Тогда ваши специфичные исключения можно будет поймать, как исключения базового типа. Если вы добавляете новые производные исключения, существующий клиентский код будет по прежнему ловить исключения через базовый тип. Иногда помогает простая агрегация. Система комфорта пассажиров на авиалиниях состоит из разрозненных элементов: сидения, кондиционера, видео и т.п., и таких комплектов в самолете много. Вы создали частные(private) члены класса и построили полностью новый интерфейс? Не в этом случае. Компоненты так же являются частью открытого интерфейса, так что необходимо создавать открытые(public) члены класса. Такие объекты имеют собственные частные реализации, которыые по прежнему безопасны. Простую агрегацию не следует применять часто, но иногда без нее не обойтись. Смотрите с точки зрения программиста-клиента и человека, поддерживающего код. Делайте свои классы столь понятными, насколько это возможно. Предугадывайте изменения, которые могут быть внесены и разрабатывайте класс так, чтобы вносить изменения было легко. Остерегайтесь «синдрома гигантских объектов». Это частый недуг процедурных программистов, которым вновинку ООП и которые создают процедурную программу, и запирают ее в один-два гигантских объекта. За исключением каркаса приложения, объекты представляют концепцию заложенную в ваше приложение, а не его элементы. Если надо сделать что-нибудь безобразное, покрайней мере локализуйте безобразие внутри класса. Если надо сделать что-нибудь непереносимое, создайте абстракцию для этой задачи и локализуйте ее в классе. Этот дополнительный уровень абстракции предотвратит распространение непереносимости по программе. Эта идиома заложена в шаблон проектирования «Мост» Объекты не должны просто хранить данные. Они должны иметь также четко определенное поведение. Кстати, «объекты данных» — это правильное решение, но только когда используется для упаковки и транспортировки группы элементов в случае, если их обобщенный контейнер неадекватен задаче. Сперва попробуйте композицию, прежде чем создать новые классы из существующих. Наследование следует использовать только, если это необходимо. Если вы используете наследование там, где могла бы работать композиция, ваш дизайн становится неоправданно сложным. Используйте наследование и перекрытие методов для выражения разницы в поведении и поля для выражения различий состояний.Предельный пример того, чего не следует делать — это наследование различных классов для определения цветов вместо использования поля «цвет». Избегайте несогласованности. Два семантически разных объекта могут иметь идентичные действия, или задачи и естественно будет искушение попытаться сделать одно подклассом другого, просто чтобы воспользоваться наследованием. Это называется несогласованностью, но нет никакого оправдания созданию связки суперкласс-подкласс там, где она не существует. Лучшим решением будет создать общий базовый класс, который предоставит интерфейс для обоих производных классов. Это займет чуть больше места, но наследование будет полезным и возможно позволит обнаружить нечто важное в дизайне. Избегайте ограничений при наследовании. Наиболее ясные решения добавляют новые возможности к унаследованным. Подозрительно решение, убирающее прежние возможности при наследовании без добавления новых. Но правило может быть и нарушено, и если вы работаете со старой библиотекой классов, может оказаться более эффективным ограничить существующий класс в подклассе, чем реструктурировать иерархию так, чтобы новый класс разместился как следует. Используйте шаблоны проектирования для уничтожения «голой функциональности». То есть, если только один объект вашего класса должен быть создан, вставляйте код в приложение с комментарием «сделать только раз». Оберните его в шаблон «Одиночку». Если у вас много беспорядочного кода, который создает ваши объекты, в главной программе, поищите созидательный шаблон, вроде «фабричного метода», в который вы сможете инкапсулировать создание объектов. Устранение «голой функциональности» не только сделает код гораздо более понятным и поддерживаемым, кроме того оно защитит его от «благих намерений» тех, ктоо будет заниматься его поддержкой после вас. Остерегайтесь «аналитического паралича». Помните, что нужно постоянно двигаться вперед в разработке, до тех пор, пока не станете знать о проекте все, и зачастую лучший и самый быстрый способ узнать что-нибудь о неучтенных факторах — это перейти на следующий этап, вместо того, чтобы пытаться сформулировать все мысленно. Вы не можете знать решения, пока не найдете его. В Java есть встроенные файрволлы, позвольте им работать на вас. Ваши ошибки в классе или множестве классов не разрушат целостности всей системы. Когда вам кажется, что у вас все проанализировано, разработано или реализовано, проведите сквозной контроль. Позовите кого-нибудь извне вашей группы — это не должен быть консультант, а например, кто-нибудь из другой группы в вашей компании. Осмотр вашей работы свежей парой глаз может обнаружить проблемы на стадии, когда их легче устранить, и пользы от этого будет больше, чем затрат времени и средств. примечание : Несмотря на то, что эти принципы описаны автором в книге, посвященной языку Java, в большинстве своем, они являются принципами объектно-ориентированного проектирования вообще. Если вы пишете на C++ или, тем более, С# — вышеупомянутые советы адресованы и вам.
отправить
+ 0


Чтобы оставить комментарий войдите или зарегистрируйтесь