37
Динамический полиморфизм Денис С. Мигинский

Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

  • Upload
    others

  • View
    19

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Динамический полиморфизм

Денис С. Мигинский

Page 2: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Основные принципы ООП

Абстракция

+

Инкапсуляция

+

Модульность

= SoC + KISS

Наследование

+

(динамический) Полиморфизм

= специфичные для ООП механизмы SoC

Page 3: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Вариант общего определения полиморфизма

Полиморфизм - способность объектов различных типов обладать одним и тем же публичным контрактом

Page 4: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Разновидности полиморфизма

Специальный (ad-hoc) – перегрузка функций. Имеет смысл только в системах со строгой типизацией, иначе вырождается в динамический полиморфизм

Параметрический (parametric) – параметризация типов другими типами. Имеет смысл только в системах со строгой типизацией. Примечание: в ФП и теории типов называется просто полиморфизмом

Динамический (subtype) – единый контракт для типов, связанных отношением генерализации. Примечание: в ООП называется просто полиморфизмом

Page 5: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Определение наследования

Следующие утверждения эквивалентны:

1. Если некоторое утверждение истинно для всех объектов типа T, то это же утверждение истинно для всех объектов типа S.

2. S является подтипом T.

(принцип подстановки Барбары Лисков, LSP)

1=>2: естественное отношения наследования.

Пример: утиная типизация. Если нечто выглядит как утка, крякает как утка и плавает как утка, значит это утка.

Page 6: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

LSP: пояснение

2=>1: декларируя отношение наследования S <: T накладываем ограничения на объекты класса S не меньшие, чем на объекты класса T.

В частности:

Для объектов подтипа должны быть доступны все операции супертипа

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

Возвращаемые значения методов подтипа не должны быть выше по иерархии возвращаемых значений в супертипе

Нельзя усиливать предусловия

Нельзя ослаблять постусловия

Должны сохраняться инварианты

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

Page 7: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Разделение ответственностей через полиморфизм

Вызов обычной функции:

зависимость только от контракта функции, но не от ее реализации;

реализация в системе ровно одна.

Вызов полиморфной функции (обобщенная функция, посылка сообщения):

зависимость только от контракта функции, но не от ее реализации;

для параметрического полиморфизма: одна реализация, параметризованная типом (-ами)

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

только для динамического полиморфизма: реализация функции может комбинироваться из частей, которые мало знают (или вообще ничего не знают) друг про друга. Простейшая форма комбинации – через super.

Page 8: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

«Многомерный» полиморфизм

Базовый полиморфизм:

одиночное наследование, диспетчеризация по одному параметру (посылка сообщений)

Расширения:

множественное наследование (multiple inheritance)

диспетчеризация по нескольким параметрам(multiple dispatch)

вспомогательные методы (auxiliary methods)

Page 9: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

CLOS: Common Lisp Object

SystemОсобенности: Поддерживает все перечисленные формы

динамического полиморфизма

Поведение определяется в форме обобщенных функций

Нет сокрытия состояния (из-за предыдущего пункта –больше ответственности на программисте)

Можно изменить иерархию классов в динамике, изменить класс объекта «на лету» и т.д.

В большинстве реализаций в основе имеет Meta-Object Protocol, что позволяет менять и саму объектную модель (изменить понятие класса, метода, наследования и т.д.)

Page 10: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Множественное наследование

Некоторые формы множественного наследования:

Множественная имплементация интерфейсов

Аннотирование с помощью интерфейсов (строго говоря, к ООП отношения не имеет)

Включение примесей

Наследование от нескольких классов, не связанных логически (не имеют общих предков, сообщений/функций) – представление композиции через наследование

Наследование от связанных классов (ромбовидная иерархия)

Page 11: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Иерархия классов

+capabilities()

Vehicle

FlyingVehicle FloatingVehicle LandVehicle SimpleVehicle

Armour

AmphibianVehicle

Plane Bicycle

Вызов capabilities должен печатать возможности данного транспорта. Летающее средство – летает, плавающее –плавает, простое может управляться даже обезьяной.

Page 12: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

CLOS: определение иерархии;;;базовый класс, потомок standard-object(defclass vehicle ()

((name :initarg :name:accessor vehicle-name)))

;;;наследник vehicle(defclass land-vehicle (vehicle) ())

;;;наследник land-vehicle и floating-vehicle(defclass amphibian-vehicle

(land-vehicle floating-vehicle) ());;;остальные классы аналогично;;;...;;;обобщенная функция, не привязанная к классу(defgeneric capabilities (vh))

Page 13: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Экземпляры классов

(defparameter t-90 (make-instance 'armour :name "T-90"))

(defparameter my-bicycle (make-instance 'bicycle :name "Bicycle"))

(defparameter il-86 (make-instance 'plane :name "Il-86"))

Page 14: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Методы функции capabilities(defmethod capabilities ((vh vehicle))(format t "~a allows to:~%" (vehicle-name vh)))

(defmethod capabilities ((vh flying-vehicle))(call-next-method)(format t "fly~%"))

(defmethod capabilities ((vh land-vehicle))(call-next-method)(format t "move on land~%"))

(defmethod capabilities ((vh floating-vehicle))(call-next-method)(format t "sail~%"))

(defmethod capabilities ((vh simple-vehicle))(call-next-method)(format t "be driven by anyone~%"))

;;что напечатается?(capabilities t-90)

Page 15: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Поведение call-next-method

(capabilities t-90)>>T-90 allows to: (3)sail (2)move on land (1)

(capabilities my-bicycle)>>Bicycle allows to:be driven by anyonemove on land

Алгоритм диспетчеризации при вызове:

выбираются все подходящие методы

выбранные методы упорядочиваютсяв порядке обхода в ширину

вызывается наиболее специфичный метод

call-next-method вызывает следующий метод по порядку (не обязательно метод суперкласса)

+capabilities()

Vehicle

FloatingVehicle LandVehicle

Armour

AmphibianVehicle

1 2

3

Page 16: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Особенности методов в иерархии с множественным наследованием

При вызове call-next-method метод может:

опираться на общие правила, определяемые контрактом обобщенной функции;

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

считать, что всегда можно вызвать call-next-method; на корень иерархии правило не распространяется.

При проектировании иерархии следует:

при использовании комбинирования методов (call-next-method) определять методы для базового класса иерархии (даже если они пустые);

делать все методы коммутативными (по крайней мере для несвязанных наследованием классов);

без необходимости избегать наследования в ромбовидной форме (хотя бояться его тоже не надо).

Page 17: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Классическая задача

Есть иерархия фигур (бизнес-логика).

Есть несколько системных представлений фигур для разный графических фреймворков:

SWT, Swing, MySuperFramework.

Задача:

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

Проблема:

Решение «в лоб» - иерархия с множественным наследованием из классов типа

SWTRectangle <: SWTShape, Rectangle

SwingCircle <: SwingShape, Circle

приводит к комбинаторному взрыву.

+draw()

Shape

Polygon Circle

Triangle Rectangle

Page 18: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Классическое решение: мост

+draw()

Shape

Polygon Circle

Triangle Rectangle

+drawPoint()

+drawLine()

SystemShape

SWTShape SwingShape

+drawCircle()

MySuperShape

1 1

Проблема:

Мы вынуждены довольствоваться минимальным базисом для всех фигур, хотя знаем, что MySuperFramework умеет рисовать еще и круги.(про SWT и Swing мы этого не знаем, потому как настоящие ковбои документацию не читают)

Хотим иметь специальный метод draw для Circle + MySuperShape, не ломая при этом всей иерархии.

Page 19: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Другая задача: идем кататься!

+capabilities()

Vehicle

FlyingVehicle FloatingVehicle LandVehicle SimpleVehicle

Armour

AmphibianVehicle

Plane Bicycle

Driver

AnimalDriver HumanDriver

Pilot

Page 20: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

CLOS: определение иерархии

(defclass driver ()((name :initarg :name

:accessor driver-name)))

(defclass human-driver (driver) ())

(defclass pilot-driver (human-driver) ())

(defclass animal-driver (driver) ())

;;;vehicle об этом может ничего не знать(defgeneric ride (dr vh))

Page 21: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Экземпляры классов

(defparameter monkey (make-instance 'animal-driver :name "Monkey"))

(defparameter anonymous (make-instance 'human-driver

:name "Anonymous"))

(defparameter pirx(make-instance 'pilot-driver :name "Pirx"))

Page 22: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Поехали!;;;метод по умолчанию(defmethod ride ((dr driver) (vh vehicle))(format t "~a rides ~a~%"

(driver-name dr) (vehicle-name vh)))

;;;научили обезьяну кататься на велосипеде(defmethod ride ((dr animal-driver)

(vh simple-vehicle))(format t "~a is smart and rides ~a~%"

(driver-name dr) (vehicle-name vh)))

;;;но не на всем остальном;;;(менее специфичный метод, чем предыдущий)(defmethod ride ((dr animal-driver) (vh vehicle))(format t "~a is not smart enough to ride ~a~%"

(driver-name dr) (vehicle-name vh)))

Page 23: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

А теперь полетели!

;;;кого попало за штурвал не садим(defmethod ride ((dr driver) (vh flying-vehicle))(format t "~a requires special training to fly ~a~%"

(driver-name dr) (vehicle-name vh)))

;;;а вот пилота - садим(defmethod ride ((dr pilot-driver) (vh flying-vehicle))(format t "~a flies on ~a~%"

(driver-name dr) (vehicle-name vh)))

Page 24: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Диспетчеризация по двум параметрам в действии

(ride monkey my-bicycle)>>Monkey is smart and rides Bicycle

(ride monkey t-90)>>Monkey is not smart enough to ride T-90

(ride anonymous il-86)>>Anonymous requires special training to fly Il-86

(ride pirx il-86)>>Pirx flies on Il-86

Page 25: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Проблемы с приоретизацией(defclass base1 () ())(defclass derived1 (base1) ())

(defclass base2 () ())(defclass derived2 (base2) ())

(defparameter p1 (make-instance 'derived1))(defparameter p2 (make-instance 'derived2))

(defgeneric mix (p1 p2))

(defmethod mix ((p1 derived1) (p2 base2))(format t "1st param specific"))

(defmethod mix ((p1 base1) (p2 derived2))(format t "2nd param specific"))

;;;что будет напечатано?(mix p1 p2)

Page 26: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Решение проблемы с приоретизацией

Если используется комбинирование (call-next-method работает точно также!) и мы соблюдаем все правила (в т.ч. коммутативность) – проблем нет.

Если не используется комбинирование, то либо не допускаем неоднозначностей, либо считаем, что нам не важно, какой метод будет вызван

CLOS в этом случае вызовет какой-то из методов с наибольшей специфичностью. В Clojure требуется явно установить приоритет.

Примечание: для множественного наследования проблема также актуальна

Page 27: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Вспомогательные методы: готовим и обслуживаем транспорт;;методы :before и :after комбинируются сами;;call-next-method не нужен(defmethod ride :before ((dr driver) (vh land-vehicle))(format t "Fuel the tank~%"))

(defmethod ride :after ((dr driver) (vh land-vehicle))(format t "Turn on alarm~%"))

(defmethod ride :before ((dr driver) (vh floating-vehicle))(format t "Set sails~%"))

(defmethod ride :after ((dr driver) (vh floating-vehicle))(format t "Take in sails~%"))

(defmethod ride :before ((dr driver) (vh flying-vehicle))(format t "Check parachute~%"))

(defmethod ride :after ((dr driver) (vh flying-vehicle))(format t "Be happy with successful landing~%"))

Page 28: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Грузим в танк боеприпасы и наблюдаем за процессом

(defmethod ride :before ((dr driver) (vh armour))(format t "Load ammunition~%"))

(defmethod ride :after ((dr driver) (vh armour))(format t "Leave vehicle without hurts~%"))

(defmethod ride :around ((dr driver) (vh vehicle))(format t "Start observing the show~%");;стандартный паттерн :around-метода:;;запоминаем результат, и потом его возвращаем;;call-next-method вызывает то что «завернули»(let ((result (call-next-method)))(format t "Finish observing the show~%")result))

;;;что будет напечатано?(ride pirx t-90)

Page 29: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Результат поездки в танке

(ride pirx t-90)>>Start observing the showLoad ammunitionFuel the tankSet sailsPirx rides T-90Take in sailsTurn on alarmLeave vehicle without hurtsFinish observing the show

Page 30: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Алгоритм стандартного комбинатора методов

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

Порядок вызова:

1. :around методы начиная с наиболее специфичного (явно, через call-next-method)

2. :before методы начиная с наиболее специфичного (неявно)

3. основные методы, начиная с наиболее специфичного (явно, через call-next-method)

4. :after методы начиная с наименее специфичного (неявно)

Замечания по производительности: может быть слишком накладно вычислять комбинацию при

каждом вызове комбинация методов зависит только от классов параметров –

можно использовать кеширование (сбрасывая кэш при изменении иерархии или декларации новых методов)

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

Page 31: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Ошибка в проектировании: обезьяна с гранатой

(ride monkey t-90)>>Start observing the showLoad ammunitionFuel the tankSet sailsMonkey is not smart enough to ride T-90Take in sailsTurn on alarmLeave vehicle without hurtsFinish observing the show

;;может, надо было заранее проверить насчет обезьяны?

Page 32: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Использование вспомогательных методов по назначению

:around – единственный, кто может отменить или подменить выполнение всей комбинации методов. Самая естественная его функция – проверка контракта (пред-, пост-условия), авторизация

:before, :after – операции, не связанные с бизнес-логикой(блокировки, открытие/закрытие соединений, отложенная инициализация, отладка и профилирование). Все то же самое можно сделать через :around, но сложнее и опаснее.

Основные методы – основная логика

Примечание: все вышеперечисленно догмой не является, имеет право на существование любое использование, соблюдающее SoCи KISS

Page 33: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Что все это дает?Плюсы:

За счет обобщенных функций не привязанных к классам и разных форм динамического полиморфизма –широкие возможности по разделению функциональности.

Атомарная единица модульности – меньше класса и даже меньше функции (в обычном понимании). Это основа для АОП.

Механизмы не привязаны к специфике Lisp и вообще динамических языков.

Минусы:

Дилемма: как всю эту «мелочь» правильно упаковать в разумное число модулей. Принципы проектирования пакетов (Common Closure, Common Reuse, Release-Reuse Equivalency) – в помощь

Код из-за раздробленности сложнее читать, требуются удобные IDE, визуализирующие переплетения аспектов

Требуется высокая культура программирования, иначе получаем богатые возможности «выстрелить себе в ногу» (и не только себе)

Page 34: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Полиморфизм в Clojure: определение иерархии

;;;вместо деклараций классов - метки типов

(derive ::floating-vehicle ::vehicle)(derive ::land-vehicle ::vehicle)(derive ::amphibian-vehicle ::floating-vehicle)(derive ::amphibian-vehicle ::land-vehicle)(derive ::armour ::amphibian-vehicle)(derive ::simple-vehicle ::vehicle)(derive ::bicycle ::land-vehicle)(derive ::bicycle ::simple-vehicle)

(derive ::animal-driver ::driver)(derive ::human-driver ::driver)

Page 35: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Определение обобщенных функций и мультиметодов в Clojure;;;аналог defgeneric в CLOS(defmulti ride ;;;функция вычисления типа(fn [driver vehicle] [(:type driver) (:type vehicle)]))

(defmethod ride [::driver ::vehicle] [driver vehicle](println (:name driver) "rides" (:name vehicle)))

(defmethod ride [::animal-driver ::simple-vehicle] [driver vehicle](println (:name driver) "is smart and rides" (:name vehicle)))

(defmethod ride [::animal-driver ::vehicle] [driver vehicle](println (:name driver) "is not smart enough to ride"(:name vehicle)))

Page 36: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

И снова поехали!

(let [monkey {:type ::animal-driver,:name "Monkey"}

anonymous {:type ::human-driver,:name "Anonymous"}

t-90 {:type ::armour,:name "T-90"}

bicycle {:type ::bicycle,:name "Bicycle"}]

(ride monkey bicycle)(ride monkey t-90)(ride anonymous t-90))

>>Monkey is smart and rides BicycleMonkey is not smart enough to ride T-90Anonymous rides T-90

Page 37: Динамический полиморфизмccfit.nsu.ru/~shadow/DT6/pdf/lecture_3_5_subtype...в динамический полиморфизм Параметрический

Отличия от CLOSПлюсы:

Полиморфизм не привязан к определенной объектной модели: понятие типа определяет пользователь

Работает в т.ч. для объектной модели Java

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

Минусы:

Нет возможности комбинирования методов

Автоматически не разрешаются конфликты при множественном наследовании или диспетчеризации по нескольким параметрам