Это распространённая тема для холиваров. Например:
Не используйте ООП. Никогда. Это ошибка.Цитата из холивара
На эту тему есть много материалов, к примеру: www.youtube.com/watch?v=QM1iUe6IofM
Если ООП все еще кажется вам хорошей идеей, то решите простую задачку.
Есть три объекта: кошка, кормушка и человек. Вам необходимо написать метод, который бы позволял человеку покормить кошку, воспользовавшись кормушкой.
Вопрос: методом какого класса будет являться метод.покормить()?
Просьба привести аргументированный ответ, в соответствии с иерархией классов, и другими лучшими практиками ООП.
Теперь сравните это с функциональной реализацией: у вас есть функция покормитьКошку() принимающая в качестве аргумента ссылку на кошку и кормушку.
Как ответить на данный вопрос?
Вначале давайте рассмотрим пример из физики.
Кейс 1 (из физики): закон Ома
Закон Ома: I=U/R, где I — сила тока, U — напряжение, R — сопротивление.
Несложно заметить, что закон Ома, как и любую другую формулу из трех переменных, можно записать тремя способами: I=U/R, R=U/I, U=IR. Как выработать правило, позволяющее однозначно определить единственную форму записи? Очень просто: надо записать с левой стороны производную величину, т.е. ту, которая становится имеющей определённое значение, в зависимости от остальных величин.
I=U/R «Сила тока СТАНОВИТСЯ равной отношению напряжения на концах проводника к сопротивлению проводника» — верно.
U=IR «Напряжение на концах проводника СТАНОВИТСЯ равным его сопротивлению, умноженному на силу тока через проводник» — не верно.
R=U/I «Сопротивление проводника СТАНОВИТСЯ равным отношению напряжения на концах к силе тока» — не верно.
Видите — как только мы условились, что производное значение находится слева, остался только один вариант.
Вот так же, мы поступим и в ООП. Условимся, что метод принадлежит тому, кто воздействует:
Кто_действует.Метод(Объект_воздействия);
Кейс 2: Кто взял Измаил?
Следовательно, при ответе на вопрос «Кто взял Измаил?» с точки зрения объектно ориентированного программирования, и с позиции «кто воздействует?», правильный ответ будет Суворов.ВзятьКрепость(Измаил, Турки):boolean. Все другие варианты, такие как: Турки.Про***тьКрепость(Измаил, Суворову), Измаил.СменаСобственника(Турки, Суворов), АбстрактнаяКрепость.БытьЗахваченной(Исмаил, Суворов, Турки) и т.д. — все эти варианты не верны.
Кейс 3: Человек, кормушка и кошка
«Покорми кошку!» с сайта corchaosis.ru
Человек насыпал еды в кормушку. Это метод:
Человек.НасыпатьЕдыКошке(ПакетЕды,Кормушка)
. При выполнении метода, в глобальной переменной ПакетЕды количество еды уменьшается, а в глобальной переменной Кормушка появляется.А как же кошка? А кошка существует ДО вызова метода НасыпатьЕдыКошке, как условие его вызова. Если кошка на даче, то и метод НасыпатьЕдыКошке вызываться не будет.
Кейс 4: Игрок в DOOM, шотган и монстр
Допустим, что вопрос попадания в монстра не имеет градаций: либо попал, либо не попал.
Правильная реализация. Всё начинается с метода Игрока:
Игрок (либо, Игрок_1 в многопользовательской игре).Выстрел(Монстр_1)
Внутри реализации метода Выстрел, мы видим, что текущее оружие игрока — шотган.
Следовательно, вызываем метод вложенного объекта:
Игрок_1.Оружие[Игрок_1.Номер_выбранного_оружия].Выстрел(Монстр_1)
Игрок_1.Оружие — это класс TWeapon.
В данном случае, вызывается метод класса TShotgun, который является дочерним к TWeapon.
Итак, имеем:
Шотган.Выстрел(Монстр_1)
Шотган, при выполнении данного действия, изменяет внутреннее состояние: для шотгана это количество патронов (а для другого вида оружия могла бы быть, например, и температура). Также, мы определяем силу урона — так, шотган стреляет по 2 патрона, но может выстрелить и один, если в заряде остался только один патрон.
Если бы мы выстрелили из ракетницы, то появился бы новый объект — ракета. Со своим методом Tick, обрабатывающим действия за один тик игрового времени (игровое время изменяется, обычно, в тиках). Но мы выстрелили из оружия, поражающего без задержки, поэтому — знаем количество выстреленных патронов (1 или 2), знаем расстояние (сравнивая Игрок.Position и Монстр_1.Position), и внутри метода класса Шотган, рассчитываем ущерб.
И, наконец,
Монстр_1.НанесёноПовреждение(сила_повреждения:Float)
. Теперь, как и перед этим шотган менял внутреннее состояние (кол-во патронов), теперь Монстр_1 меняет внутреннее состояние Монстр_1.Здоровье
, и сценарий поведения (особенно, если Здоровье стало меньше нуля).Итак, мы видим, что благодаря ООП, мы можем легко добавить новое оружие: достаточно описать его как дочерний класс от TWeapon, определить Выстрел и разместить на карте. Класс Игрок уже умеет подбирать и добавлять в свой игровой набор объекты TWeapon. Всё. Хотя нет, не всё. Если оружие будет дарить монстрам цветочки, заставляя их влюбляться в игрока, то и монстрам следует прописать метод
ОтветитьНаПризнаниеВЛюбви:boolean
, а также набор других методов — в зависимости от степени проработки, в этом случае вам может потребоваться и ряд новых ООП объектов и их методов.Кейс 5: Потрогать руками — это функция или метод
Не только ответ на этот вопрос, но и сам интерфейс взаимодействия, очевидно, находится в зависимости от трогаемого объекта.
Потрогать_руками::дом
— это функция.Потрогать_руками:: ноутбук — это метод объекта «ноутбук». Вы должны использовать интерфейс, чтобы вызвать методы ноутбука «KeyDown, KeyUp, KeyPressed» передав в эти методы правильные данные: какая кнопка была нажата, в какой момент времени и т.д.
Потрогать_руками::огонь
— это метод объекта «вы». Вам необходим объект «self», изменение состояние которого правильно опишет ожог руки. Огонь состояния не изменит.Думаю, вы и сами можете продолжить список интерфейсов объектов, которые можно потрогать руками, с целью получения практики использования объектно-ориентированного программирования.
VIkrom
Холивар из предисловия рождается как правило из ложного тезиса, что ООП это моделирование объектов реального мира. Но ООП совсем не про то, ООП это моделирование абстракций исходя из контекста конкретной задачи. Никакого реального мира, это маркетинговый буллщит из времен, когда надо было продвигать ООП-языки на рынок.
JustDont
Тут нет никакого противоречия, потому что в холиварных примерах обычно посылка именно такая, «замоделировать мир». Да и в целом задача моделирования какой-то области реальности в коде — весьма нередка.
netricks
Вот только она обычно плохо решается путём перенесения объекта в код. Обычно получается что-то околоподобное но со странными смысловыми извращениями. Например, огнестрельное оружие вместо того, чтобы мочь стрелять, на самом деле лишь добавляет экипированному им персонажу способность выстрелить.
В общем, любой фанатизм хорош в меру.
michael_vostrikov
Все нормально моделируется в объектах реального мира. Просто методы должны быть в обоих объектах — отдающий и принимающий.
lamerok
А что должен делать метод кормушки?
В реальном мире это же не актор, у нее не может быть методов, это просто контейнер для корма.
VolCh
Сходить за кормом :)
michael_vostrikov
В реальном мире это вообще набор молекул, это мы в воображении объединяем их в один объект. Метод кормушки содержит технические детали реализации, а не бизнес-логику. Например, так:
Вообще можно и снаружи писать
кормушка->количествоКорма += корм.количество
, с точки зрения моделирования нет большой разницы.'+='
можно считать методом типаint
, то есть тоже есть какая-то цепочка вызовов.lamerok
Ну тогда надо просто держать ссылку на корм в кормушке, а прибавлять убавлять в самом корме.
В кормушку же можно разный корм положить, и прибавление и убавление разное будет… Там молоко налить или сухой корм положить.
michael_vostrikov
Это и есть детали реализации. Можно сделать перегрузку функции
добавитьКорм()
по типу аргумента. Смысл в том, что мы моделируем передачу какого-то объема корма в кормушку, посылаем кормушке сообщение путем вызова метода. А уж как она его обработает, к вызывающему объекту не относится.BeMySlaveDarlin
Собственно вы пришли к тому уровню сложности моделирования (что в итоге правильно в рамках адекватности поведения абстракций), от которого у противников ООП контекст разъезжается, голова болит и куча переполняется)
dvserg
Кормушка должна уметь принять/отдать корм и выдать свой статус. Все, на
этом ее функционал закончен.
anonymous
А где класс Кошки, у которой есть метод Кушать, который в качестве аргумента может принимать Кормушку?
michael_vostrikov
Там примерно то же самое, моделируем передачу какого-то объема корма из кормушки кошке. Суть в том, что информационные объекты моделируют реальные.
anonymous
Кошка должна проверить голодна ли она, и должна проверить наличие корма в кормушке.
nekt
Что-то я не улавливаю семантику свойства `кормушка` у объекта `человек`.
michael_vostrikov
Это не сама кормушка, это представление кормушки. Она может быть вообще за интерфейсом скрыта. С технической точки зрения важный момент то, что это ссылка на объект, а не сами данные объекта. Ссылка представляет объект, так же как у реального человека в воображении есть образ кормушки, который представляет реальную кормушку.