Объектно-ориентированное программирование - путаница в дизайне классов - PullRequest
11 голосов
/ 16 ноября 2009

Я пытаюсь обернуть голову вокруг объектно-ориентированного программирования.

Насколько я понимаю, у нас есть объекты, поэтому мы можем разрабатывать наши программы для отражения реальных объектов.

Давайте возьмем иерархию классов:

class Fruit {
    void Eat() {

    }
}

class Apple extends Fruit {

}

Очевидно, что вы можете использовать Fruit полиморфно, если Eat() является виртуальным. Но имеет ли это смысл? Фрукты не могут есть сами!

Должен ли фруктовый объект передаваться человеческому объекту с функцией Eat()?

Я пытаюсь найти правильный способ думать об этом. Насколько близко, вообще говоря, объекты программирования должны отражать реальные объекты?

Ответы [ 13 ]

12 голосов
/ 16 ноября 2009

У вас проблема с дизайном - как вы правильно заметили, Eat () не имеет очевидного смысла как участник Fruit. С другой стороны, «съедобный» атрибут будет иметь больше смысла. Как и событие «onEaten» и т. Д. То, что показывают ваши классы фруктов / яблок (и какие другие объекты имеют смысл в вашей модели), зависит от множества других факторов, включая то, что вы пытаетесь достичь с этими конструкциями в вашем приложении.

Как правило, вы хотите, чтобы ваши классы представляли объекты логического уровня домена. Иногда они соответствуют физической сущности в реальном мире, но во многих случаях они не соответствуют.

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

8 голосов
/ 16 ноября 2009

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

Вам нужно найти основную абстракцию для вашей реальной проблемы, а не просто скопировать существительные в иерархии объектов.

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

Я считаю, что ООП больше относится к пробелам - то, что вы оставляете, важнее.

5 голосов
/ 16 ноября 2009

Я думаю, что вы должны прочитать принципы SOLID, это вам очень поможет. http://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablo-s-topic-of-the-month-march-solid-principles.aspx

3 голосов
/ 16 ноября 2009

Что-то из класса Herbivore будет иметь функцию Eat, как и что-то из класса Carnivore, но Eat каждого из них будет иметь некоторые отличающиеся ограничения на то, какой аргумент может быть передан в функцию Eat. Fruit - это то, что съедено , поэтому оно будет передано в качестве аргумента Herbivore.Eat (), тогда как вы захотите передать объект типа Hamburger в Carnivore.Eat () и вызвать исключение. если Гамбургер был передан Травоядному. Ешьте ().

Но на самом деле я не думаю, что ООП, поэтому мы можем моделировать программные объекты, чтобы они были похожи на реальные объекты. Я обнаружил, что большая часть моего дизайна ООП работает с довольно абстрактными объектами, и только в отношении системы, частью которой они являются. Если бы я написал систему регистрации / проверки библиотеки, я бы смоделировал Книгу с точки зрения ее административных свойств и функций - я бы не стал моделировать ее как коллекцию объектов Page, и я сомневаюсь, что я бы даже определил что-то вроде метода Read () хотя такова основная цель иметь книгу в первую очередь. Роль объекта Book в системе определяет мой дизайн гораздо больше, чем то, что можно сделать с книгами в реальном мире.

1 голос
/ 16 ноября 2009

Если, скажем, вы писали симулятор голодных людей, то я думаю, что было бы гораздо более разумным, как вы говорите, иметь функцию Human::Eat(Fruit f). Ваш Fruit может не иметь методов, так как Fruit сам по себе мало что делает, но может иметь свойство calories и т. Д.

0 голосов
/ 19 июня 2013

Я не думаю, что мы должны пытаться "отражать объекты реальной жизни". Я думаю, что это скорее поиск реальных объектов, которые очень похожи на моделируемое поведение в контексте системы (домена). Класс Fruit в игре, где вы нарезаете фрукты на очки, может иметь совершенно другое поведение и атрибуты, чем класс Fruit в игре, где персонаж бегает, собирая фрукты на очки; или симуляция людей, которые едят фрукты. Присвоение поведения классам, названным в честь реальных объектов, облегчает принятие поведения модулей кода и размышления об их взаимодействиях.

0 голосов
/ 16 ноября 2009

Насколько я понимаю, у нас есть объекты, поэтому мы можем разрабатывать наши программы для отражения реальных объектов.

Может быть, больше похоже на «связать» их с объектами реальной жизни, где это применимо.

Должен ли фруктовый объект передаваться человеческому объекту с функцией Eat ()?

Да, или что-то более общее, чем человек.

Я пытаюсь найти правильный способ думать об этом. В общем, насколько близко объекты программирования должны отражать объекты реальной жизни.

Определите только то, что вам нужно определить. Тогда реализация (как правило) становится очень очевидной. Другими словами, вы, вероятно, слишком много думаете.

0 голосов
/ 16 ноября 2009

Я всегда нахожу примеры с использованием «животных» или «фруктов» нелогичными. Люди являются сложными объектами для моделирования, и не похоже, что вы столкнетесь с приложениями с такими же требованиями.

Использование концепции антропоморфизации действительно может помочь назначить ответственных лиц. По сути, представьте, что ваш объект - это человек (я обычно рисую их с помощью лица и конечностей во время сеансов проектирования).

Теперь вы можете задать следующие вопросы о вашем объекте:

  • "Какие отклики есть у моего объекта в системе ?"
  • "Является ли этот объект ответственным за выполнение xxx , или xxx должен нести ответственность другого объекта?"
  • "Этот объект делает больше, чем должен быть?" (например, если он предназначен для расчета чего-либо, он не должен отвечать за загрузку значений)

«Думая об объектах» Дэвида Уэста - хорошая книга для чтения по теме.

0 голосов
/ 16 ноября 2009

Как правило, насколько близко объекты программирования должны отражать реальные объекты.

Не очень, только достаточно.

Одной из основных характеристик ООП, является абстракция. Вам не нужно иметь все атрибуты / методы объекта, чтобы иметь возможность его использовать.

Вам просто нужно базовое, чтобы использовать его.

Все, что связано с объектами, состоит в том, чтобы данные и функции выполняли что-то с этими данными в одном месте.

Так что в вашем фруктовом классе мне лучше иметь что-то вроде Color или указание, если это будет съедено. Например:

 Fruit
     + color : Color
     - isMature : Boolean

     + canBeEaten() : Boolean
          return isMature

Таким образом, вы можете создавать разные фрукты

 apple = Fruit()
 appe.color = Color.red
 out.print( "Can this fruit be eaten? %s ", apple.canBeEaten() )

 orange = Fruit()
 orage.color = Color.orange
 out.print( "Can this fruit be eaten? %s ", orange.canBeEaten() )

1022 * Etc. *

Если вы видите, что атрибуты (color и isMature) хранятся внутри объекта. Таким образом, вам не нужно следить за их состоянием извне.

Что касается наследования, то оно имеет смысл только тогда, когда вам нужно добавить новое поведение в какой-либо метод, и да, методы относятся к атрибутам или характеристикам объекта. Как вы отмечаете, fruit.eat() не имеет особого смысла.

Но рассмотрим способ получения сока из фрукта.

Fruit
    + getJuice(): Juice

Apple -> Fruit
     + getJuice(): Juice
         // do what ever is needed internally to create the juice

Orange -> Fruit
    + getJuice(): Juice
        // here, certainly the way to get the juice will be different
0 голосов
/ 16 ноября 2009

Я склонен думать о:

Имеет

Итак, яблоко - это фрукт, поэтому наследование имеет смысл.

Но плод, который можно есть (есть), может иметь смысл, но это показывает, что это свойство плода, а не действие (метод).

Например, у вас может быть незрелое яблоко, которое не будет съедобным (съедобным), поэтому вы можете установить это свойство.

Теперь, что бы это ни съело, вы можете установить, является ли яблоко частью его диеты.

Теперь Has a будет для композиции. Таким образом, у яблока есть семя, что означает, что семя не расширяет яблоко, но у яблока будет коллекция семян.

Итак, у вас есть проблема с дизайном, и я надеюсь, что эти две концепции могут помочь прояснить ситуацию.

...