Принцип подстановки Лискова VS Принцип разделения интерфейса
У меня проблемы с пониманием этих двух принципов. Это немного давно читаемый вопрос, так что наберитесь терпения.
Предположим, что у нас есть класс
abstract class Shape {
abstract void onDraw();
}
и интерфейс
interface SideCountable {
int getSidesCount();
}
Затем мы создаем два дочерних класса
class Quad extends Shape {
@Override
public void onDraw() {
//some draw logic here
}
}
class Triangle extends Shape {
@Override
public void onDraw() {
//some draw logic here
}
}
А теперь сделаем Shape
орудия SideCountable
abstract class Shape implements SideCountable{
abstract void onDraw();
}
и вносить изменения в детские классы
class Quad extends Shape {
@Override
public int getSidesCount() {
return 4;
}
@Override
public void onDraw() {
//some draw logic here
}
}
class Triangle extends Shape {
public int getSidesCount() {
return 3;
}
public void onDraw() {
//some draw logic here
}
}
И создадим тестовую функцию для этой структуры (следуя LSP):
public void printSidesCount(List<Shape> shapes) {
for(int i=0; i < shapes.size(); i++) {
System.out.println(shapes.get(i).getSidesCount());
}
}
И здесь я хочу остановиться, потому что на самом деле на следующем этапе я застрял. Что, если мы создадим третий классCircle
?
class Circle extends Shape {
public int getSidesCount() {
return ???;
}
@Override
public void onDraw() {
//some draw logic here
}
}
Круг не имеет сторон, поэтому реализация SideCountable
для детей это звучит смешно. Хорошо, мы можем перенести реализацию только на Quad и Triangle, но в этом случае LSP больше не будет работать. Может ли кто-нибудь описать, что мне лучше всего делать?
- Покидать
SideCountable
вShape
class и вернуть 0 дляCircle
и нарушить принцип разделения интерфейса? - Переехать
SideCountable
кQuad
а такжеTriangle
и нарушить принцип LSP?
1 ответ
Прежде всего, ваш метод printSidesCount
нужно только знать, что список содержит SideCountable
объекты. Таким образом, задав его параметру типList<Shape>
является более конкретным, чем необходимо. Дайте емуList<SideCountable>
вместо:
public void printSidesCount(List<SideCountable> sideCountables) {
for(int i=0; i < (); i++) {
System.out.println(sideCountables.get(i).getSidesCount());
}
}
Или даже List<? extends SideCountable>
что означает "список произвольного неизвестного типа, который реализует SideCountable
":
public void printSidesCount(List<? extends SideCountable> sideCountables) {
for(int i=0; i < sideCountables.size(); i++) {
System.out.println(sideCountables.get(i).getSidesCount());
}
}
Если не все формы имеют счетное количество сторон, тогда класс Shape
не должен реализовывать интерфейс SideCountable
. Вместо этого сделайте классыQuad
а также Triangle
реализовать интерфейс SideCountable
помимо расширения класса Shape
:
class Quad extends Shape implements SideCountable {
// ...
}
class Triangle extends Shape implements SideCountable {
// ...
}
И сделать класс Circle
расширять Shape
но не реализовать SideCountable
:
class Circle extends Shape { // Not SideCountable
// ...
}
Когда вы сделаете это так, система типов поможет вам:
- вы можете пройти
List<Quad>
илиList<Triangle>
кprintSidesCount
поскольку эти типы реализуютSideCountable
- ты не можешь пройти
List<Circle>
кprintSidesCount
, что хорошо, потому что пытаться сделать это не имеет смысла для списка кругов