Java: слабое предварительное условие и сильное постусловие, как это сделать?

Мне очень трудно понять, как должны работать предварительные и постусловия, не нарушая Принцип замещения. Итак, давайте предположим, что у нас есть класс Rectangle и Square — как их связать? Какой из них должен быть подклассом?

Итак, я понимаю, что предварительные условия Subtype могут быть слабее, это означает, что мы можем взять основной «набор» вещей в нашем подклассе, с другой стороны, постусловие может быть сильнее, чтобы мы могли вернуть второстепенный ' набор вещей. Как я могу применить эти правила в моем примере?

Я читал, что базовый класс должен «делать» меньше, чем подкласс, поэтому я думаю, что Square должен быть нашим базовым классом, а Rectangle подклассом. Таким образом, предварительное условие в Square должно утверждать, что height == width, но как насчет пост-условий и предварительных условий в Rectangle?


person Francesco    schedule 27.10.2015    source источник
comment
Я бы сказал, что и Square, и Rectangle являются subclasses из Shape.   -  person nafas    schedule 27.10.2015
comment
предположим, что я хочу установить один из первых двух как подкласс другого   -  person Francesco    schedule 27.10.2015
comment
хм, так что каждый Square является Rectangle --› Rectangle является суперклассом Square   -  person nafas    schedule 27.10.2015
comment
поэтому в этом случае квадрат должен иметь более сильное предварительное условие, чтобы проверить высоту == ширину, поэтому он нарушает принципы SP   -  person Francesco    schedule 27.10.2015
comment
да, условие для прямоугольника (у него есть высота и ширина) и условие для квадрата (у него есть высота и ширина AND height==width)   -  person nafas    schedule 27.10.2015
comment
хорошо, так что Rectangle является суперклассом, что означает, что Square имеет более сильные предварительные условия, потому что высота и ширина в Rectangle могут быть любым целым числом, большим, чем ноль, но в Square они могут быть равны только друг другу   -  person Francesco    schedule 27.10.2015
comment
Давайте продолжим обсуждение в чате.   -  person Francesco    schedule 27.10.2015
comment
на самом деле это типичный пример, в котором наследование терпит неудачу: вы не можете связать их в коде, как в реальном мире. Это где-то из серии видео "Чистый код" от дяди Боба (не помню точно какой)   -  person ThanksForAllTheFish    schedule 27.10.2015
comment
У меня есть аналогичный вопрос здесь: softwareengineering.stackexchange.com/questions/358757/ Я даже использовал тот же пример с квадратом и прямоугольником где-то еще. Интересно, насколько сложно LSP на практике.   -  person inf3rno    schedule 09.10.2017
comment
Я думаю, смысл LSP в том, что если вы наследуете Square от класса Rectangle, то вы будете получать непредвиденные ошибки, используя экземпляр Square в коде, написанном для экземпляра Rectangle. Квадрат имеет ограничения по ширине и высоте, поэтому, установив их в своем коде, вы можете получить неожиданные ошибки, если они не равны. Поскольку квадраты проходят проверку типа для Rectangle, использование LSP — единственный способ гарантировать, что код не сломается неожиданно. Таким образом, Rectangle не может быть базовым классом для класса Square, потому что это нарушило бы LSP. Вместо этого вам нужна другая модель.   -  person inf3rno    schedule 09.10.2017


Ответы (5)


Общий способ узнать супер и подклассы, вам в основном нужно ответить на этот вопрос:

Является ли каждый X Y?

В вашем случае вам нужно сказать эти две вещи:

  • Является ли каждый Rectangle Square? Нет
  • Является ли каждый Square Rectangle? Да

Таким образом, Square является Rectangle. Тогда условие для того, чтобы стать Square:

  • Если это прямоугольник; и
  • высота равна ширине.
person nafas    schedule 27.10.2015

На самом деле все наоборот. Квадрат является подклассом прямоугольника. Почему? Каждый Квадрат является Прямоугольником, но не каждый Прямоугольник является Квадратом.

Вы можете расширить это отношение наследования дальше: каждый объект GeometricShape является объектом, каждый многоугольник является объектом GeometricShape, каждый четырехугольник является многоугольником, каждый прямоугольник является четырехугольником и т. д. Чем дальше вы спускаетесь по иерархии наследования, тем больше ограничений добавляется, что делает подклассы более специальные, чем родительские классы.

Что такое ограничение прямоугольника? Каждый угол имеет прямоугольный угол (90 °), каково ограничение квадрата? Это прямоугольник (90°), все ребра которого имеют одинаковую длину.

person Gerald Mücke    schedule 27.10.2015
comment
поэтому в этом случае квадрат должен иметь более сильное предварительное условие, чтобы проверить высоту == ширину, поэтому он нарушает принципы SP - person Francesco; 27.10.2015
comment
нет, SP не нарушается, всякий раз, когда нужен Rectangle, можно использовать Square, но не наоборот. Вы можете заменить любой прямоугольник (базовый класс) квадратом (подкласс). Но когда требуется Квадрат (более сильное предварительное условие), вы не можете заменить его Прямоугольником (если только это не Квадрат). - person Gerald Mücke; 27.10.2015
comment
Итак, у Square есть более сильное предварительное условие, которое нарушает принципы SP. Если S является подклассом B, то S должен иметь такое же или более слабое предусловие B. - person Francesco; 27.10.2015

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

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

В коде они должны быть 2 разными классами. Я не определил класс многоугольника, но вы поняли его концепцию.

Eg :

public class Quadrilateral extends Polygon {
    ...
}

Следующий :

public class Rectangle extends Quadrilateral {
    private double length;
    private double breadth;

   public Rectangle (double len, double brd ) {...}

}

Затем квадрат:

public class Square extends Rectangle {
    private double side;
    public Square (double sideLength) {
         super(sideLength, sideLength);
    }
}
person StackFlowed    schedule 27.10.2015
comment
Да, хорошо, я знаю геометрию. Но вы не отвечаете на вопрос о том, как связать два класса - person Francesco; 27.10.2015

Подтип

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

Java использует ключевое слово extends, потому что подкласс фактически расширяет суперкласс, часто добавляя функциональность.

Квадрат — это особый, то есть более конкретный тип прямоугольника. Это прямоугольник, но с дополнительным свойством, что высота равна ширине.


Принцип замены Лисков

Однако принцип подстановки — я предполагаю, что вы имеете в виду принцип подстановки Лискова — гласит, что везде, где вы ожидаете определенный класс (в нашем случае Rectangle), вы должны иметь возможность использовать подтип этого класса (в нашем случае Square), не нарушая функциональности или логики. Если нет, дизайн нарушает принцип подстановки Лисков.

Я могу привести пример, но он уже написан в этом ответе. Обратите внимание, что в этом ответе используются упомянутые вами классы.

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

Так что да, объявление Square подклассом Rectangle не соответствует принципу подстановки Лискова.

Обратите внимание, что они также написали об этом на страницу в Википедии. Это очень распространенная проблема.

person MC Emperor    schedule 28.10.2015

  • Прямоугольник является подклассом Квадрата. Причина в том, что прямоугольник является более слабой формой квадрата: у него более слабые предпосылки.
  • Условия квадрата: четыре стороны, все примыкающие под углом 90 градусов, все стороны равны.
  • Прямоугольник имеет только первые два предварительных условия. Будут ли эти два вести себя одинаково для клиентского кода? Да.
  • Квадрат: getArea() {обратная сторона*сторона}
  • Rectangle:
    • getArea() {return l * w} << same interface Square constructor:
  • Квадрат (внутренняя сторона) Конструктор прямоугольника 1:
  • Прямоугольник (внутренняя сторона) {l = сторона; w = сторона;} ‹‹ тот же интерфейс, что и у квадрата
  • Rectangle constructor 2:
    • Rectangle(int l, int w){this.l = s; this.w = w} << new addition
  • Почему прямоугольнику разрешено иметь дополнительный конструктор? Потому что он расширяет квадрат и, следовательно, может добавлять функциональность, но не может ограничивать функциональность суперкласса.
person Apurva Singh    schedule 03.03.2021