Подтип поведения - Behavioral subtyping

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

Например, рассмотрим тип Stack и тип Queue, которые имеют положить метод добавления элемента и получать способ удалить один. Предположим, что в документации, связанной с этими типами, указано, что методы типа Stack должны вести себя так, как ожидалось для стеков (т.е. они должны демонстрировать LIFO поведение), и методы Queue этого типа должны вести себя так, как ожидалось для очередей (т. е. они должны показывать ФИФО поведение). Предположим, теперь этот тип Stack был объявлен как подкласс типа Queue. Большинство компиляторов языков программирования игнорируют документацию и выполняют только те проверки, которые необходимы для сохранения безопасность типа. Поскольку для каждого метода типа Queue тип Stack предоставляет метод с совпадающим именем и подписью, эта проверка будет успешной. Однако клиенты, обращающиеся к объекту Stack через ссылку типа Queue, будут, основываясь на документации Queue, ожидать поведения FIFO, но наблюдать поведение LIFO, делая недействительными доказательства правильности этих клиентов и потенциально приводя к неправильному поведению программы в целом.

В этом примере нарушается поведенческий подтип, поскольку тип Stack не является поведенческим подтипом типа Queue: это не тот случай, когда поведение, описанное в документации типа Stack (то есть поведение LIFO), соответствует документации типа Queue (для которого требуется поведение FIFO) .

Напротив, программа, в которой Stack и Queue являются подклассами типа Bag, спецификация которого для получать просто удаляет немного элемент, удовлетворяет поведенческим подтипам и позволяет клиентам безопасно рассуждать о правильности на основе предполагаемых типов объектов, с которыми они взаимодействуют. Действительно, любой объект, который удовлетворяет спецификации стека или очереди, также удовлетворяет спецификации Bag.

Важно подчеркнуть, что является ли тип S поведенческим подтипом типа T зависит только от Технические характеристики (т.е. документация) типа Т; в выполнение типа T, если он есть, совершенно не имеет отношения к этому вопросу. Действительно, типу T даже не нужно иметь реализацию; это может быть чисто абстрактный класс. Другой пример: тип Stack выше является поведенческим подтипом типа Bag, даже если тип Bag выполнение демонстрирует поведение FIFO: важно то, что тип Bag Технические характеристики не указывает, какой элемент удаляется методом получать. Это также означает, что поведенческое подтипирование может обсуждаться только в отношении конкретной (поведенческой) спецификации для каждого задействованного типа, и что, если задействованные типы не имеют четко определенной поведенческой спецификации, поведенческие подтипы не могут обсуждаться осмысленно.

Проверка поведенческого подтипа

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

Спецификация метода, предоставленная предварительное условие пs и постусловие Qs сильнее, чем предусловие Pт и постусловие Qт (формально: (Ps, Qs) ⇒ (Pт, Qт)) если Ps является слабее чем Pт (т.е. Pт следует Ps) и Qs сильнее Qт (т.е. Qs следует Qт). То есть усиление спецификации метода может быть выполнено путем усиления постусловия и ослабление предварительное условие. Действительно, спецификация метода сильнее, если она налагает более конкретные ограничения на выходы для входов, которые уже поддерживались, или если для поддержки требуется больше входов.

Например, рассмотрим (очень слабую) спецификацию для метода, вычисляющего абсолютное значение аргумента. Икс, который задает предусловие 0 ≤ x и постусловие 0 ≤ result. В этой спецификации говорится, что метод не должен поддерживать отрицательные значения для Икс, и нужно только убедиться, что результат тоже неотрицательный. Двумя возможными способами усиления этой спецификации являются усиление постусловия, чтобы указать результат = | x |, т.е. результат равен абсолютному значению x, или ослабление предусловия до "истина", т.е. все значения для Икс следует поддерживать. Конечно, мы также можем объединить и то, и другое в спецификацию, в которой указано, что результат должен быть равен абсолютному значению Икс, для любого значения Икс.

Обратите внимание, однако, что можно усилить спецификацию ((Ps, Qs) ⇒ (Pт, Qт)) без усиления постусловия (Qs ⇏ Qт).[2][3] Рассмотрим спецификацию для метода абсолютного значения, которая определяет предварительное условие 0 ≤ x и результат постусловия = x. Спецификация, которая определяет предварительное условие «истина» и результат постусловия = | x | усиливает эту спецификацию, даже если результат постусловия = | x | не усиливает (и не ослабляет) результат постусловия = x. Необходимое условие для спецификации с предусловием Ps и постусловие Qs быть сильнее, чем спецификация с предусловием Pт и постусловие Qт это что Ps слабее Pт и "Qs или нет Ps"сильнее" Qт или нет Pт". Действительно," result = | x | или false "усиливает" result = x or x <0 ".

«Заменяемость»

В влиятельном программном выступлении[4] об абстракции данных и иерархиях классов на конференции по исследованию языков программирования OOPSLA 1987, Барбара Лисков сказал следующее: "Здесь требуется что-то вроде следующего свойства подстановки: если для каждого объекта типа S есть объект типа T так, что для всех программ P, определенных в терминах T, поведение P не меняется, когда заменяется на , то S является подтипом T. "Эта характеристика с тех пор широко известна как Принцип замещения Лискова (LSP). К сожалению, здесь есть несколько проблем. Во-первых, в своей первоначальной формулировке он слишком силен: мы редко хотим, чтобы поведение подкласса было идентично поведению его суперкласса; замена объекта подкласса на объект суперкласса часто выполняется с намерением изменить поведение программы, хотя и при соблюдении поведенческих подтипов, таким образом, чтобы сохранить желаемые свойства программы. Во-вторых, здесь не упоминается технические характеристики, поэтому он предлагает неправильное чтение, где выполнение типа S сравнивается с выполнение типа T. Это проблематично по нескольким причинам, одна из которых состоит в том, что он не поддерживает общий случай, когда T является абстрактным и не имеет реализации. В-третьих, что наиболее тонко, в контексте объектно-ориентированного императивного программирования трудно точно определить, что означает универсальная или экзистенциальная количественная оценка объектов данного типа или замена одного объекта другим.[3] В приведенном выше примере мы не заменяем объект Stack объектом Bag, мы просто используем объект Stack в качестве объекта Bag.

В интервью в 2016 году сама Лисков объясняет, что то, что она представила в своем программном выступлении, было «неформальным правилом», которое позже Жаннетт Винг предложила им «попытаться точно выяснить, что это означает», что привело к их совместной публикации[1] о поведенческих подтипах, и действительно, что «технически это называется поведенческим подтипом».[5] Во время интервью она не использует замещающую терминологию для обсуждения концепций.

Примечания

  1. ^ а б Лисков, Варвара; Крыло, Жаннетт (1994-11-01). «Поведенческое понятие подтипов». Транзакции ACM по языкам и системам программирования. 16 (6): 1811–1841. Дои:10.1145/197320.197383.
  2. ^ Паркинсон, Мэтью Дж. (2005). Местное обоснование Java (PDF) (Кандидат наук). Кембриджский университет.
  3. ^ а б Ливенс, Гэри Т .; Науманн, Дэвид А. (август 2015 г.). «Поведенческое определение подтипов, наследование спецификаций и модульное мышление». Транзакции ACM по языкам и системам программирования. 37 (4). Дои:10.1145/2766446.
  4. ^ Лисков, Б. (Май 1988 г.). «Основной доклад - абстракция и иерархия данных». Уведомления ACM SIGPLAN. 23 (5): 17–34. Дои:10.1145/62139.62141.
  5. ^ ван Влек, Том (20 апреля 2016 г.). Интервью с Барбарой Лисковой. ACM.

Рекомендации

  • Паркинсон, Мэтью Дж .; Бирман, Гэвин М. (январь 2008 г.). «Логика разделения, абстракция и наследование». Уведомления ACM SIGPLAN. 43 (1): 75–86. Дои:10.1145/1328897.1328451.