Что должно быть в контрольном списке, который поможет кому-то разработать хорошее программное обеспечение для ОО? - PullRequest
25 голосов
/ 27 мая 2009

Я использовал языки и методы ОО-программирования несколько лет назад (в основном на C ++), но за прошедшее время мало что сделал с ОО.

Я начинаю делать небольшую утилиту в C #. Я мог бы просто запрограммировать все это без использования хорошей практики ОО, но для меня было бы неплохо освежить применение ОО-методов.

Как и уровни нормализации базы данных, я ищу контрольный список, который будет напоминать мне различные практические правила для «хорошей» объектно-ориентированной программы - краткий список да / нет, который я иногда могу прочитать во время проектирования и реализации чтобы я не думал и не работал процедурно. Было бы еще полезнее, если бы в нем содержались правильные термины и понятия ОО, чтобы любой элемент проверки можно было легко найти для получения дополнительной информации.

Что должно быть в контрольном списке, который поможет кому-то разработать хорошее программное обеспечение для ОО?

И наоборот, какие «тесты» могут быть применены, чтобы показать, что программное обеспечение не является ОО?

Ответы [ 12 ]

36 голосов
/ 30 октября 2009
  • Объекты делают вещи. (Самый важный момент во всей ООП!) Не думайте о них как о «держателях данных» - отправьте им сообщение, чтобы что-то сделать. Какие глаголы должен иметь мой класс? Школа мышления «Дизайн, основанный на ответственности» великолепна в этом. (См. Проектирование объектов: роли, обязанности и совместная работа , Ребекка Уирфс-Брок и Алан Маккин, Addison-Wesley 2003, ISBN 0201379430.)
  • Для каждой вещи, которую должна сделать система, предлагает набор конкретных сценариев, описывающих, как объекты общаются друг с другом, чтобы выполнить работу . Это означает думать в терминах диаграмм взаимодействия и разыгрывать вызовы методов. - Не начинайте с диаграммы классов - это мышление SQL, а не OO-мышление.
  • Изучите управляемую тестами разработку. Никто не получает их объектную модель сразу, но если вы делаете TDD, вы закладываете основу, чтобы убедиться, что ваша объектная модель делает то, что делает необходимо и сделать его безопасным для рефакторинга, когда все изменится позже.
  • Сборка только для тех требований, которые у вас сейчас есть - не зацикливайтесь на «повторном использовании» или вещах, которые будут «полезны позже». Если вы создаете только то, что вам нужно прямо сейчас, вы оставляете пространство для вещей, которые вы можете сделать позже, гораздо более открытым.
  • Забудьте о наследовании при моделировании объектов. Это всего лишь один из способов реализации общего кода. Когда вы моделируете объекты, просто притворяйтесь, что вы смотрите на каждый объект через интерфейс, который описывает, что его можно попросить сделать.
  • Если метод принимает множество параметров или если вам нужно многократно вызывать группу объектов для получения большого количества данных, метод может находиться в неправильном классе. Лучшее место для метода - правильный рядом с большинством полей, которые он использует в том же классе (или суперклассе ...)
  • Прочитайте книгу «Шаблоны проектирования» для вашего языка. Если это C #, попробуйте «Дизайн шаблонов в C #» Стива Метскера. Это научит вас ряду приемов, которые можно использовать для разделения работы между объектами.
  • Не проверяйте объект на предмет его типа, а затем выполняйте действия, основанные на этом типе - это запах кода, который, вероятно, должен выполнять объект. Это подсказка, что вы должны вызвать объект и попросить его выполнить работу. (Если работают только некоторые виды объектов, вы можете просто иметь реализации «ничего не делать» в некоторых объектах ... Это законно ООП.)
  • Помещение методов и данных в правильные классы ускоряет выполнение ОО-кода (и дает возможность виртуальным машинам лучше оптимизироваться) - это не просто эстетическое или теоретическое значение. Исследование Sharble и Cohen указывает на это - см. http://portal.acm.org/citation.cfm?doid=159420.155839 (см. график показателей «количество выполненных инструкций за сценарий»)
11 голосов
/ 06 ноября 2009

Звучит так, будто вы хотите задать себе несколько базовых вопросов «да / нет». Каждый дал несколько великолепных списков «делай это» и «думай так», так что вот мой пример с некоторыми простыми ответами да / нет.

Могу ли я ответить "да" на все эти вопросы?

  • Представляют ли мои классы существительные, которые меня интересуют?
  • Предоставляют ли мои классы методы для действий / глаголов, которые он может выполнять?

Могу ли я ответить нет на все эти вопросы?

  • Есть ли у меня глобальные данные о состоянии, которые могут быть либо помещены в одноэлементный файл, либо сохранены в реализациях классов, которые работают с ним?
  • Могу ли я удалить какие-либо открытые методы класса и добавить их в интерфейс или сделать их частными / защищенными для лучшей инкапсуляции поведения?
  • Должен ли я использовать интерфейс для отделения поведения от других интерфейсов или класса реализации?
  • Есть ли у меня код, который повторяется между связанными классами, который я могу переместить в базовый класс для лучшего повторного использования кода и абстракции?
  • Проверяю ли я тип чего-либо, чтобы решить, какое действие предпринять? Если это так, может ли это поведение быть включено в базовый тип или интерфейс, который используется в рассматриваемом коде, чтобы обеспечить более эффективное использование абстракции, или следует реорганизовать рассматриваемый код для использования лучшего базового класса или интерфейса?
  • Проверяю ли я некоторые данные контекста, чтобы решить, какой тип создавать? Если это так, можно ли абстрагироваться в шаблон проектирования фабрики для лучшей абстракции логики и повторного использования кода?
  • Является ли класс очень большим с несколькими фокусами функциональности? Если да, могу ли я разделить его на несколько классов, каждый со своей единственной целью?
  • Есть ли у меня несвязанные классы, наследуемые от одного базового класса? Если это так, могу ли я разделить базовый класс на лучшие абстракции или использовать композицию для получения доступа к функциональности?
  • Становится ли моя иерархия наследования страшно глубокой? Если да, могу ли я сгладить это или разделить вещи с помощью интерфейсов или функций разделения?
  • Я слишком сильно переживаю из-за своей иерархии наследования?
  • Когда я объясняю дизайн резиновой уточке, чувствую ли я себя глупо по поводу дизайна или глупо разговаривать с уткой?

Просто несколько быстрых из головы. Надеюсь, это поможет, ООП может сойти с ума. Я не включил никаких да / нет для более продвинутых вещей, которые обычно касаются больших приложений, таких как внедрение зависимостей или если вам нужно разделить что-то на разные сервисные / логические уровни / сборки ... конечно, я надеюсь, что вы по крайней мере отделите ваш интерфейс от вашей логики.

10 голосов
/ 02 ноября 2009

Собран из различных книг, известных программистов на C # и общих советов (не так много, если что-то из этого принадлежит мне; в том смысле, что это разные вопросы, которые я задаю себе во время разработки, но это так):

  • Структуры или классы? Является ли элемент, который вы создаете, своим собственным значением, сделайте его структурой. Если это «объект» с атрибутами и вспомогательными значениями, методами и, возможно, состоянием, тогда сделайте его объектом.
  • Запечатанные классы : Если вы собираетесь создавать класс и вам явно не нужно иметь возможность наследовать от него, сделайте его запечатанным. (Я делаю это для предполагаемого увеличения производительности)
  • Не повторяйте себя : если вы обнаружите, что копируете (/ /) код, то вам, вероятно, следует (, но не всегда ) переосмыслить свой дизайн, чтобы свести к минимуму дублирование кода.
  • Если вам не нужно предоставлять базовую реализацию для данного абстрактного класса, превратите его в интерфейс.
  • Принцип специализации : Каждый ваш предмет должен делать только одно. Это помогает избежать «бога-объекта».
  • Использовать свойства для публичного доступа : Это обсуждается снова и снова, но это действительно лучшее, что можно сделать. Свойства позволяют вам делать то, что вы обычно не можете делать с полями, и это также дает вам больше контроля над тем, как объект получен и установлен.
  • Singletons : еще одна спорная тема, и вот идея: используйте их только тогда, когда вам это абсолютно необходимо. Большую часть времени группа статических методов может служить цели синглтона. (Хотя, если вам абсолютно необходим одноэлементный паттерн, используйте Jon Skeet's превосходный )
  • Слабая связь : убедитесь, что ваши уроки зависят друг от друга как можно меньше; убедитесь, что пользователям вашей библиотеки легко обмениваться частями вашей библиотеки с другими (или созданными пользователем частями). Это включает в себя использование интерфейсов, где это необходимо, инкапсуляцию (другие упоминали об этом) и большинство остальных принципов в этом ответе.
  • Дизайн с учетом простоты : В отличие от глазури для тортов, проще создать нечто простое сейчас и добавить позже, чем сложное сейчас, а удалить позже.

Я мог бы выбросить часть или все это за дверь, если я:

  • Написание личного проекта
  • очень торопится что-то сделать (но я вернусь к этому позже .... когда-нибудь .....;))

Эти принципы помогают ориентироваться в моем повседневном кодировании и значительно улучшают качество моего кодирования в некоторых областях! надеюсь, что это поможет!

7 голосов
/ 03 ноября 2009
  • Я четко определил требования? Официальная документация требований может не потребоваться, но вы должны иметь четкое видение, прежде чем начать кодирование. Инструменты Mind-Mapping и прототипы или эскизы дизайна могут стать хорошей альтернативой, если вам не нужна формальная документация. Работайте с конечными пользователями и заинтересованными сторонами как можно раньше в процессе разработки программного обеспечения, чтобы убедиться, что вы реализуете то, что им нужно.

  • Я заново изобретаю колесо? Если вы кодируете для решения общей проблемы, ищите надежную библиотеку, которая уже решает эту проблему. Если вы считаете, что, возможно, уже решили проблему в другом месте своего кода (или это может сделать коллега), сначала найдите существующее решение.

  • Имеет ли мой объект четкую единственную цель? Следуя принципу инкапсуляции, объект должен вести себя вместе с данными, с которыми он работает. У объекта должна быть только одна главная ответственность.

  • Могу ли я написать код для интерфейса? Проектирование по контракту - отличный способ включить модульное тестирование, задокументировать подробные требования класса и поощрить повторное использование кода.

  • Могу ли я проверить свой код? Разработка через тестирование (TDD) не всегда легка; но модульные тесты неоценимы для рефакторинга и проверки регрессионного поведения после внесения изменений. Идет рука об руку с Проектом по контракту.

  • Я перезаписываю? Не пытайтесь закодировать повторно используемый компонент. Не пытайтесь предвидеть будущие требования. Эти вещи могут показаться нелогичными, но они ведут к лучшему дизайну. В первый раз, когда вы что-то кодируете, просто реализуйте это как можно проще и заставьте работать. Во второй раз вы используете ту же логику, скопируйте и вставьте. Если у вас есть два рабочих раздела кода с общей логикой, вы можете легко выполнить рефакторинг, не пытаясь предвидеть будущие требования.

  • Я ввожу избыточный код? Не повторяйте себя (СУХОЙ) - это основной принцип рефакторинга. Используйте копирование и вставку только в качестве первого шага к рефакторингу. Не кодируйте одно и то же в разных местах, это кошмар обслуживания.

  • Это обычный шаблон проектирования, анти-шаблон или запах кода? Ознакомьтесь с общими решениями проблем проектирования ОО и ищите их при кодировании, но не пытайтесь заставить проблему соответствовать определенному образцу. Не упустите код, который попадает в общий шаблон "плохой практики".

  • Не слишком ли тесно связан мой код? Слабая связь - это принцип, который пытается уменьшить взаимозависимости между двумя или более классами. Некоторые зависимости необходимы; но чем больше вы зависите от другого класса, тем больше вам нужно исправить, когда этот класс изменится. Не позволяйте коду в одном классе зависеть от деталей реализации другого класса - используйте объект только в соответствии с его контрактом.

  • Я раскрываю слишком много информации? Практикуйтесь в сокрытии информации. Если метод или поле не должны быть публичными, сделайте их приватными. Предоставьте только минимальный API, необходимый для выполнения контрактом объекта. Не делайте детали реализации доступными для клиентских объектов.

  • Защитно ли я пишу код? Проверьте наличие ошибок и Fail Fast. Не бойтесь использовать исключения, и пусть они распространяются. В случае, если ваша программа достигает неожиданного состояния, намного, намного лучше прервать операцию, записать трассировку стека, с которой вы можете работать, и избежать непредсказуемого поведения в нижестоящем коде. Следуйте рекомендациям по очистке ресурсов, таким как оператор using() {}.

  • Смогу ли я прочитать этот код через шесть месяцев? Хороший код читается при минимальной документации. Оставляйте комментарии там, где это необходимо; но также пишите интуитивно понятный код и используйте значимые имена классов, методов и переменных. Практикуйте хороший стиль кодирования; если вы работаете над командным проектом, каждый член команды должен написать код, который выглядит одинаково.

  • Это все еще работает? Тестируйте рано, тестируйте часто. После введения новых функций вернитесь назад и коснитесь любого существующего поведения, которое могло быть затронуто. Сделайте так, чтобы другие члены команды провели рецензирование и протестировали свой код. Повторно запустите юнит-тесты после внесения изменений и обновляйте их.

7 голосов
/ 27 мая 2009
  • Данные принадлежат коду, который обрабатывает их (то есть в том же классе). Это повышает удобство обслуживания, поскольку многие поля и методы могут быть закрытыми ( инкапсуляция ) и, таким образом, в некоторой степени исключаются из рассмотрения при рассмотрении взаимодействия между компонентами.
  • Использовать полиморфизм вместо условий - всякий раз, когда вам нужно делать разные вещи в зависимости от того, к какому классу относится объект, попробуйте поместить это поведение в метод, который разные классы реализуют по-разному, так что все, что вам нужно сделать это вызов этого метода
  • Используйте наследование экономно, предпочитайте композицию - Наследование является отличительной чертой ОО-программирования и часто рассматривается как "сущность" ООП. На самом деле он серьезно злоупотребляет и должен классифицироваться как наименее важный признак
7 голосов
/ 27 мая 2009

Стив Макконнелла Code Complete 2 содержит множество готовых к использованию контрольных списков для хорошей разработки программного обеспечения.

Agile Принципы, Шаблоны и Практики в C # Роберта Мартина содержат много принципов для правильной разработки ОО.

И то, и другое даст вам прочную основу для начала.

6 голосов
/ 05 июня 2009

Одним из лучших источников была бы книга «Рефакторинг» Мартина Фаулера, в которой содержится список (и вспомогательные сведения) запахов объектно-ориентированного кода, которые вы, возможно, захотите рассмотреть в качестве рефакторинга.

Я бы также порекомендовал контрольные списки в "Чистом коде" Роберта Мартина.

5 голосов
/ 04 ноября 2009
  • SOLID
  • DRY
  • TDD
  • Композиция на наследство
3 голосов
/ 27 мая 2009

Убедитесь, что вы прочитали и поняли следующее

  • Инкапсуляция
    • (Убедитесь, что вы предоставляете только минимальное состояние и функциональность, чтобы выполнить работу)
  • Полиморфизм
    • (способность производных объектов вести себя как их родители)
  • Разница между интерфейсом и абстрактным классом
    • (абстрактный класс позволяет функциональность и состояние для совместного использования с его потомками, интерфейс это только обещание, что функциональность будет реализована)
1 голос
/ 03 ноября 2009

Некоторые правила не зависят от языка, некоторые правила различаются от языка к языку.

Вот несколько правил и комментариев, которые противоречат некоторым другим ранее опубликованным правилам:

  • ОО имеет 4 принципа: Абстракция, Инкапсуляция, Полиморфизм и Наследование.
    Читайте о них и имейте в виду.

  • Моделирование. Предполагается, что ваши классы моделируют объекты в проблемной области:
    Разделите проблему на субдомены (пакеты / пространства имен / сборки)
    затем разделите сущности в каждом поддомене на классы.
    Классы должны содержать методы, которые моделируют, что делают объекты этого типа.

  • Используйте UML, подумайте о требованиях, сценариях использования, затем диаграммах классов и их последовательностях. (применимо в основном для проектирования высокого уровня - основные классы и процессы.)

  • Шаблоны проектирования - хорошая концепция для любого языка, реализация в разных языках различается.

  • Структура против класса - в C # это вопрос передачи данных по значению или по ссылке.

  • Интерфейс против базового класса, является базовым классом, делает интерфейс.

  • TDD - это не OO, на самом деле, это может привести к недостатку дизайна и привести к значительному переписыванию. (Scrum, например, рекомендует против этого, для XP это обязательно).

  • Отражение C # в некоторых отношениях переопределяет OO (например, сериализацию на основе отражения), но это необходимо для продвинутых фреймворков.

  • Убедитесь, что классы «не знают» о других классах, если только они не обязаны это делать, деление на несколько сборок и недостаток ссылок помогает.

  • AOP (Аспектно-ориентированное программирование) улучшает ООП (см., Например, PostSharp) настолько революционно, что вы должны хотя бы посмотреть его и посмотреть их клип.

  • Для C # прочитайте рекомендации MS (см. Рекомендации в справочнике MSDN справки VS), там у них много полезных руководств и условностей

  • Рекомендуемые книги:
    Рекомендации по разработке структуры: условные обозначения, идиомы и шаблоны для многократно используемых библиотек .NET
    C # 3.0 в двух словах

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...