Я читаю некоторые слайды класса на объектно-ориентированных языках программирования и ступил в определение подтипа типа:
Barbara Liskov, “Абстракция данных и иерархия”, SIGPLAN замечает, 23,5 (май 1988):
Что требуется, вот что-то как следующее свойство замены: Если для каждого объекта o_s типа S существует объект o_T типа T, таким образом это для всех программ P
определенный с точки зрения T, поведение P неизменно, когда o_S заменяют o_T затем S, подтип T
Затем это идет с примером:
Точка = {x:Integer, y:Integer}
PositivePoint = {x:Positive, y:Positive}
где Положительный = {k:Integer | k> 0}Мы можем сказать ту Точку PositivePoint ≤?
Да, потому что элемент типа PositivePoint может всегда заменять элемент Точки типа в программе, определенной в терминах Точки!
Теперь... для меня кажется, что это должно быть как раз наоборот: ≤ PositivePoint Точки, потому что я не мог использовать PositivePoint в программе, которая использует Точку с отрицательными координатами, в то время как я мог к противоположному.
Я сомневался, был ли синтаксис Type ≤ Sub-type
или Sub-Type ≤ Type
, но оператор кажется более ясным, что случилось затем?
Только для создания вещей легче вопрос: можно ли сказать это PositivePoint
подтип Point
? Почему?
Я сообщаю здесь, что я записал в комментарии, надеясь, что он сделает мою проблему более ясной:
Предположим, что программа должна нарисовать квадратную карту из
Point
(-100,-100) кPoint
(100, 100). Что произошло бы, если Вы используете типPositivePoint
? Поведение программы было бы неизменно? Это не было бы. Это "неизменное поведение" является единственной вещью, которую я не получаю. Если определение подтипа было простоinheriting and overriding
от другого типа это было бы в порядке, но это, кажется, не имеет место.
Лисков правильный, PositivePoint ≤ Point, потому что PositivePoint является уточнением Point. Любой код, использующий Point, также должен иметь возможность использовать PositivePoint, потому что всегда была вероятность того, что координаты Point в любом случае были положительными . Обратное неверно, потому что код, использующий PositivePoint, может действовать в предположении, что координаты всегда положительны, и замена PositivePoint на Point нарушит это предположение.
Обратите внимание, что она не говорит, что PositivePoint может заменить Point, просто PositivePoint может использоваться там, где требуется Point.
Вы можете моделировать отношения типов с помощью подмножеств .
PositivePoint ⊂ Point
выполняется по той же причине, что и PositiveInt ⊂ Int
: положительные числа - это подмножество всех возможных чисел!
Каждая PositivePoint
принадлежит Point
s, но не наоборот.
Идея заключается в том, что любая функция, принимающая PositivePoint, полагается на тот факт, что значения точки положительны. Если вы передадите точку Point, значения которой не являются положительными, предположение будет ложным, и функция потерпит неудачу.
Функция, принимающая точку Point, однако, не будет делать никаких предположений о положительности точки, так что если вы передадите PositivePoint, все будет в порядке.
Обратите внимание, что это справедливо только для неизменяемого класса Point. Если бы вы могли изменять значение точки, то PositivePoint и Point могли бы вообще не иметь отношений подкласса, потому что операция p.x = -1
для PositivePoints была бы неудачной.
Edit: To elaborate:
Допустим, у нас есть 2-мерный массив, который автоматически увеличивается, когда это необходимо (т.е. вы никогда не получите ошибку index-out-of-bounds при передаче двух положительных индексов). Теперь у нас есть функция, которая принимает PositiveInteger p, а затем обращается к 2-мерному массиву по индексам x,y. Это не может привести к ошибке, потому что x и y гарантированно положительны, а 2d-массив может быть проиндексирован любой парой положительных индексов. Однако если бы Point был подтипом PositivePoint, p мог бы иметь отрицательные значения, несмотря на то, что он объявлен положительным. Это означало бы, что использовать его для индексации массива уже небезопасно.
Однако функция, принимающая точку Point, не знает, являются ли значения точки отрицательными или положительными - она уже должна учитывать возможность того, что они положительны. Поэтому передача PositiveInteger не может ничего нарушить.
Я не видел символа ≤, используемого для обозначили это раньше, но то, что я думаю, означает PositivePoint ≤ Point
, означает, что Point
имеет больший диапазон потенциальных значений, чем PositivePoint
(то есть: PositivePoint
является подмножеством Point
, все экземпляры PositivePoint
могут быть заменены действительным экземпляром Point
, но не наоборот.)