Liskov 替换原则 VS 接口隔离原则
Liskov substitution principle VS Interface Segregation principle
我在理解这两个原则时遇到了一些麻烦。这个有点long-read-question,耐心点
让我们假设我们有一个 class
abstract class Shape {
abstract void onDraw();
}
和界面
interface SideCountable {
int getSidesCount();
}
然后我们创建两个children classes
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();
}
并在 children classes
中进行更改
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());
}
}
我想在这里停下来,因为实际上在下一步我被卡住了。
如果我们创建第三个 class Circle
会怎么样?
class Circle extends Shape {
public int getSidesCount() {
return ???;
}
@Override
public void onDraw() {
//some draw logic here
}
}
圆没有边,所以 SideCountable
这个 children 的实现听起来很荒谬。好的,我们可以只将实现转移到 Quad 和 Triangle,但在这种情况下,LSP 将不再起作用。有人可以描述我应该做的最好的方法吗?
- 将
SideCountable
留在Shape
class和return 0 for Circle
并打破接口隔离原则?
- 将
SideCountable
移动到Quad
和Triangle
并破坏LSP原则?
首先,你的方法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>
这意味着 "a list of some arbitrary unknown type that implements SideCountable
":
public void printSidesCount(List<? extends SideCountable> sideCountables) {
for(int i=0; i < sideCountables.size(); i++) {
System.out.println(sideCountables.get(i).getSidesCount());
}
}
如果不是所有的形状都有可数边,那么class Shape
不应该实现接口SideCountable
。相反,除了扩展 class Shape
:
之外,让 classes Quad
和 Triangle
实现接口 SideCountable
class Quad extends Shape implements SideCountable {
// ...
}
class Triangle extends Shape implements SideCountable {
// ...
}
并使 class Circle
扩展 Shape
但不实施 SideCountable
:
class Circle extends Shape { // Not SideCountable
// ...
}
当你这样做时,类型系统会帮助你:
- 您可以将
List<Quad>
或 List<Triangle>
传递给 printSidesCount
,因为这些类型实现了 SideCountable
- 您不能将
List<Circle>
传递给 printSidesCount
,这很好,因为尝试这样做对于圈子列表没有意义
我在理解这两个原则时遇到了一些麻烦。这个有点long-read-question,耐心点
让我们假设我们有一个 class
abstract class Shape {
abstract void onDraw();
}
和界面
interface SideCountable {
int getSidesCount();
}
然后我们创建两个children classes
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();
}
并在 children classes
中进行更改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());
}
}
我想在这里停下来,因为实际上在下一步我被卡住了。
如果我们创建第三个 class Circle
会怎么样?
class Circle extends Shape {
public int getSidesCount() {
return ???;
}
@Override
public void onDraw() {
//some draw logic here
}
}
圆没有边,所以 SideCountable
这个 children 的实现听起来很荒谬。好的,我们可以只将实现转移到 Quad 和 Triangle,但在这种情况下,LSP 将不再起作用。有人可以描述我应该做的最好的方法吗?
- 将
SideCountable
留在Shape
class和return 0 forCircle
并打破接口隔离原则? - 将
SideCountable
移动到Quad
和Triangle
并破坏LSP原则?
首先,你的方法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>
这意味着 "a list of some arbitrary unknown type that implements SideCountable
":
public void printSidesCount(List<? extends SideCountable> sideCountables) {
for(int i=0; i < sideCountables.size(); i++) {
System.out.println(sideCountables.get(i).getSidesCount());
}
}
如果不是所有的形状都有可数边,那么class Shape
不应该实现接口SideCountable
。相反,除了扩展 class Shape
:
Quad
和 Triangle
实现接口 SideCountable
class Quad extends Shape implements SideCountable {
// ...
}
class Triangle extends Shape implements SideCountable {
// ...
}
并使 class Circle
扩展 Shape
但不实施 SideCountable
:
class Circle extends Shape { // Not SideCountable
// ...
}
当你这样做时,类型系统会帮助你:
- 您可以将
List<Quad>
或List<Triangle>
传递给printSidesCount
,因为这些类型实现了SideCountable
- 您不能将
List<Circle>
传递给printSidesCount
,这很好,因为尝试这样做对于圈子列表没有意义