LINUX.ORG.RU

Полиморфизм? - Можно кормить Зайку мясом?

 


0

3

Какие принципы ООП вы знаете?

Их четыре:
▫️наследование;
▫️инкапсуляция;
▫️полиморфизм;
▫️абстракция.

🔹Наследование
Наследование позволяет новому классу наследовать атрибуты и методы уже существующего класса. Новый класс называется производным (дочерним). Существующий — базовым (родительским).

🔹Инкапсуляция
Этот принцип заключается в ограничении доступа к внутренним методам и переменным класса извне. В Python принцип реализован лишь на уровне соглашений: приватные атрибуты выделяются подчёркиванием — одинарным _ или двойным __. Эти подчёркивания сигнализируют другим программистам о приватности. Однако доступ к ним всё равно можно получить. 

🔹Полиморфизм
Полиморфизм позволяет использовать одну функцию для разных форм (типов данных). В Python это проявляется, например, когда дочерний класс переопределяет методы родительского класса или когда разные классы имеют методы с одинаковыми именами, но собственной реализацией.

🔹Абстракция
Абстракция позволяет определить общее поведение для группы объектов. Это достигается путём создания классов, которые имеют некоторые общие свойства и методы, но не включают все детали реализации.

#вопросы_с_собеседований

Наследование - всё ясно. Из одного Зайки - можно сделать второго. Гораздо большего размера с разным цветом глаз - Свойства

Инкапсуляция - Зайку резать нельзя. Можно кормить - вход. Убирать дерьмо - выход. Зайка может прыгать - Методы.

Полиморфизм? - Можно кормить Зайку мясом? Или можно «прикрутить» к нему крылья?

Абстракция? - Зайка и Крокодил могут прыгать вместе? - Используя одно и тоже Свойство?

Перемещено leave из general



Последнее исправление: sokolov (всего исправлений: 1)
Ответ на: комментарий от FishHook

Чистый HTML — это ещё не графический интерфейс, а просто структурированный текст. Внутри браузера оно в виде DOM, Document Object Model. Да и снаружи с этим самым DOM придётся работать, если нужно что-то большее, чем гипертекст из веб 1.0. И там уже от ООП довольно много, хоть оно и не в чистом виде.

CrX ★★★★★
()
Ответ на: комментарий от monk

Если писать, что селектор должен иметь строго определённый тип, то от него невозможно отнаследоваться. И если в команде сделать-пиктограммы нужен селектор-файлов-с-изображениями, то потом команду с типом селектор-файлов-с-изображениями нельзя будет использовать в команде обработать-файлы (типы не совпадают). Придётся писать преобразователь.

type file_selector = unit -> file list

var селектор_файлов                 : file_selector
var селектор_файлов_с_изображениями : file_selector

type file_processor : file -> unit

var обработать_файлы : file_selector -> file_processor -> unit

Ну или

var обработать_файлы : (unit -> 'a list) -> ('a -> unit) -> unit

Пока не понятно в чём проблема.

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 1)
Ответ на: комментарий от monk

Если

var обработать_изображения : file_selector -> unit

то будет можно пытаться выполнить

обработать_изображения селектор_файлов 

И может остановится только когда уже запортит файлы с документами.

monk ★★★★★
()
Ответ на: комментарий от monk

Ну как пищит? Он там помимо плюсов, ещё и серьёзные минусы приводит: жручесть ресурсов, проблемы с layout-ами. Ещё он не рассмотрел вопрос, что, если понадобится кастомный драг-н-дроп. А вывод делает такой:

на ImGui можно написать прототип серьёзной программы или какой-то фичи, опробовать её на практике, найти недостающие фичи, и только потом писать серьёзно, на миллионы пользователей, в retained mode (т.е. с ООП - hobbit).

hobbit ★★★★★
()
Ответ на: комментарий от anonymous

«Зато дёшево и практично».

Ниже типичный код main процедуры для WTL Windows.

#include "stdafx.h"
#include "resource.h"
#include "StructStorage_i.h"
#include "dllmain.h"

CStructStorageModule _AtlModule;

extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
        hInstance;
        return _AtlModule.DllMain(dwReason, lpReserved); 
}

WTL весьма удобна.
Достаточно один раз суметь написать нормальный код хотя бы для
одной библиотеки, а остальные все с вершины WTL - «вариации на тему».
Удобно, дёшево и практично.

Это был пост про OLE и ActiveX для Windows.

Фича OLE в том, что API разработанное с помощью его легко использовать в любом проекте.
Много фич.

Так эшо и диалоговые формы пишутся без проблем.
В WTL можно диалоговые формы и не использовать, а использовать в качестве некой библиотеки (что весьма удобно).

На счёт

Полиморфизм? - Можно кормить Зайку мясом?

скажу так - «Бабе мороженное, детям цветы».

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 9)
Ответ на: комментарий от Forum0888

Ещё немного про COM и OLE в Windows.

Может быть core и менялось, но в любой версии Windows можно
использовать ActiveX или приложения, использующие OLE даже 1997 года.
Windows вся пронизана COM и OLE.
Ещё DCOM и COM+ интересны и бывают полезны, а так в осномном OLE и реже COM (разве что для серверных приложений).

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 1)
Ответ на: комментарий от Nervous

ООП предоставляет полиморфизм. Нынче в Си++, конечно, перешли на утиную типизацию вместо ООП. Но полиморфизм ООП позволяет указать тип явно, а не template<typename T> int f(T s) ..., где нужный T угадай по документации.

Ещё можно как пример взять Haskell с его классами типов. Там полиморфизм удобный для описания, но не очень удобный для реализации, потому что нет наследования, а значит нельзя переопределять методы.

Идеальный вариант тут CLOS из лиспа. Там можно переопределять методы любых конкретных комбинаций типов. Например, translate-to-foreign можно доопределять для любых комбинаций из типа лиспа и типа си.

monk ★★★★★
()
Ответ на: комментарий от monk

ООП предоставляет полиморфизм

Ну так и топором теоретически можно бриться, если умеючи. Но нужно ли?

взять Haskell с его классами типов. Там полиморфизм удобный для описания, но не очень удобный для реализации, потому что нет наследования, а значит нельзя переопределять методы

Автомобиль неудобное средство передвижения, потому что непонятно, куда лошадь запрягать, ага.

Идеальный вариант тут CLOS из лиспа

Мне больше нравятся мультиметоды и протоколы из кложи.

Nervous ★★★★★
()
Ответ на: комментарий от Nervous

Вот наглядный пример силы ООП: https://fare.livejournal.com/155094.html

Балансированное дерево просто наследуется от обычного и меняется один метод. Как такое сделаешь без ООП?

monk ★★★★★
()
Ответ на: комментарий от Nervous

Автомобиль неудобное средство передвижения, потому что непонятно, куда лошадь запрягать, ага.

Из за этого AVL дерево нельзя стандартным способом превратить в список списков. Потому что AVL в хаскеле не дерево.

monk ★★★★★
()
Ответ на: комментарий от monk

Балансированное дерево просто наследуется от обычного и меняется один метод. Как такое сделаешь без ООП?

Портянку не читал. У дерева есть некоторый интерфейс (набор из N функций, которые его принимают/возвращают — конструируют, преобразуют и т.д.), так ведь? Это протокол, определяем его (defprotocol).

Делаем два пользовательских типа данных, реализуем для них протокол — пишем N функций для простого дерева, потом ещё M < N для балансированного, реализация которых отличается; указываем, что вот этот набор из N функций реализует этот протокол для простого дерева, а вот этот — для балансированного (например, extend-protocol), причём большая часть функций в этих наборах одна и та же.

Примерно то же самое можно провернуть с мультиметодами, там даже пользовательские типы данных не нужны, достаточно обычных словарей (хотя в кложе и пользовательские типы данных — deftype/defrecord — по сути, те же самые словари — ассоциативные структуры данных); и диспетчеризация будет множественная — по произвольной функции от всех аргументов, а не только по типу и только первого аргумента. Правда, за гибкость придётся заплатить скоростью.

Зачем ООП? Зачем изменяемое состояние, доступ к которому инкапсулирован методами, намертво приколоченными к описанию структуры данных, размазанному по причудливой иерархии наследования? Это всё лишнее. Есть структуры данных, есть функции, которые ими оперируют — этого достаточно.

Nervous ★★★★★
()
Ответ на: комментарий от Nervous

Делаем два пользовательских типа данных, реализуем для них протокол — пишем N функций для простого дерева, потом ещё M < N для балансированного, реализация которых отличается.

А как компилятор узнает, что N функций для простого дерева работают на балансированном, если балансированное не наследник простого?

monk ★★★★★
()
Ответ на: комментарий от monk

«Тип объекта» часто используется в разном API.
Не утверждаю, что ООП плох.
Для обработки big data ООП однозначно не пригоден.
В ОПП с иерархией больше три, код выглядит как «дремучий лес».
Десятки гетов, сетов, ... ИМХО сильно портят readable текст кода.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 3)
Ответ на: комментарий от monk

как компилятор узнает, что N функций для простого дерева работают на балансированном, если балансированное не наследник простого?

Ты ему скажешь, с помощью extend (или карамелизованных вариантов extend-type/extend-protocol).

Nervous ★★★★★
()
Ответ на: комментарий от Nervous

Ты ему скажешь, с помощью extend (или карамелизованных вариантов extend-type/extend-protocol).

Так это практически тот же ООП, только в с кучей бойлерплейта. И потенциальной проблемой, что все функции для дерева надо обязательно объявлять в протоколе, иначе они не будут работать для балансированного.

monk ★★★★★
()
Ответ на: комментарий от monk

это практически тот же ООП

Не «тот же ООП», а «позволяет делать то же, что ООП» (только лучше — проще, понятнее и надёжнее, без лишнего говна и страданий). Согласись, есть разница.

только в с кучей бойлерплейта

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

Nervous ★★★★★
()
Ответ на: комментарий от Nervous

Зачем ООП? Зачем изменяемое состояние, доступ к которому инкапсулирован методами, намертво приколоченными к описанию структуры данных, размазанному по причудливой иерархии наследования? Это всё лишнее. Есть структуры данных, есть функции, которые ими оперируют — этого достаточно.

Почему намертво приколоченными? В CLOS методы можно описывать отдельно. И даже описывать методы для чисел, конкретного значения или произвольного класса.

А иерархия нужна, чтобы наследника можно было передать в любое место, где ожидают предка. Вот сделал ты балансированное дерево и есть функция для работы с деревьями. Без ООП тебе придётся или переписывать функцию или писать функцию-преобразователь (причём с копированием данных).

monk ★★★★★
()
Ответ на: комментарий от monk

все функции для дерева надо обязательно объявлять в протоколе, иначе они не будут работать для балансированного

Что мешает определить одну функцию, а потом сослаться на неё в двух (трёх, восьми) разных реализациях протокола? Ничего не мешает.

Nervous ★★★★★
()
Ответ на: комментарий от monk

Почему намертво приколоченными? В CLOS методы можно описывать отдельно.

CLOS немного лучше, чем его более популярные собратья, да. Хотя от попыток разобраться в каше из :before, :after и :around и как они в каждом конкретном случае сочетаются, наверное, недолго и кукухой поехать %) Сделали из простых полиморфных функций какого-то франкенштейна, my ass.

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

Зачем? Любое значение, для типа которого реализован протокол (интерфейс), можно передать в любое место, которое ожидает этот интерфейс/протокол (использует только его функции/методы для работы с этим значением). На кой хрен тут ещё какое-то наследование?

По крайней мере, в общем случае. Для случаев, когда это реально удобно и полезно, можно и небольшую ad-hoc иерархию замутить, да — но зачем навязывать её всем подряд?

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 2)
Ответ на: комментарий от Nervous

Определение структур данных, оперирующих ими функций и связей первых со вторыми — всё это тебе придётся делать в любом случае.

Только в ООП я пишу

(defclass tree () (left right))

(defmethod map-tree ((tree tree) f) ...)

(defmethod fold-tree ((tree tree) f init) ...)

(defmethod insert ((tree tree) node) ...)

(defmethod delete ((tree tree) node) ...)

(defclass balanced-tree (tree) ())

(defmethod insert ((balanced-tree balanced-tree) node) ...)

(defmethod delete ((balanced-tree balanced-tree) node) ...)

А с протоколами получается что-то вроде

(defprotocol Tree
  (map-tree [x f])
  (fold-tree [x f i])
  (insert [x n])
  (delete [x n]))

(def DefaultTree
  {:map-tree (fn [x f] ...)
   :fold-tree (fn [x f i] ...)})

(defrecord tree [left right])

(extend tree
  Tree
  (assoc DefaultTree
    :insert (fn [x n] ...)
    :delete (fn [x n] ...)))

(defrecord balanced-tree [left right])

(extend balanced-tree
  Tree
  (assoc DefaultTree
    :insert (fn [x n] ...)
    :delete (fn [x n] ...)))

Плюс, если в ООП я могу писать

(defmethod insert ((balanced-tree balanced-tree) node)
  (call-next-method)
  (rebalance balanced-tree))

то в кложе придётся копипастить.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от Nervous

Зачем? Любое значение, для типа которого реализован протокол (интерфейс), можно передать в любое место, которое ожидает этот интерфейс/протокол. На кой хрен тут ещё какое-то наследование?

То есть для каждого типа надо делать дубль-протокол? Кложе повезло, что она динамическая и можно тип аргумента не писать.

monk ★★★★★
()
Ответ на: комментарий от monk

То есть для каждого типа надо делать дубль-протокол?

Всмыслий? Вот у тебя есть функция foo, которая ожидает значение, реализующее протокол Frob с одним методом frob (у себя внутри она вызывает только этот метод для работы со своим аргументом). Для чего ты этот протокол реализуешь, то и сможешь передать в foo — хоть строку, хоть число, хоть словарь, хоть record, хоть nil.

можно тип аргумента не писать

Чем тебе Frob не тип, интересно.

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 1)
Ответ на: комментарий от LongLiveUbuntu

Инкапсуляция (англ. encapsulation, от лат. in capsula) — в информатике, процесс разделения элементов абстракций, определяющих её структуру (данные) и поведение (методы); инкапсуляция предназначена для изоляции контрактных обязательств абстракции (протокол/интерфейс) от их реализации. На практике это означает, что класс должен состоять из двух частей: интерфейса и реализации. В реализации большинства языков программирования (C++, C#, Java и другие) обеспечивается механизм сокрытия, позволяющий разграничивать доступ к различным частям компонента.

  • педивикия

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

  • мацилла

Many people, the designers of C++ and Java among them, confuse encapsulation with InformationHiding.


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

/r набег на педивикию с целью уничтожения ереси

rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 2)