如何在现实世界的示例中使用 Dart 实现 LSP(里氏替换原理)

问题描述 投票:0回答:1

里氏替换原则(LSP)规定超类的对象应该能够被子类的对象替换,而不影响程序的正确性。换句话说,子类应该能够在不破坏代码的情况下替换其超类。

我试图弄清楚它如何在现实世界的示例中成为一个很好的实现,在这个示例中,我们在代码中的任何地方都使用子类,并且仅在实例化时使用超类。 这是我的方法:

第一个镜头会是这样的:

class User {
  const User({
    required this.id,
    required this.avatar,
  });

  final String id;
  final String avatar;
}

class UserWithName extends User {
  const UserWithName(
      {required super.id, required super.avatar, required this.name});

  final String name;

}

void main() {
  User userWithName = new UserWithName(id: '0', avatar: 'avatar', name: 'User Name');
  
  print('hello ${(userWithName as UserWithName).name}');
}

但对我来说它很丑陋(需要使用 as UserWithName),并且如果我们想要一个新功能,而不是参数,因为它在前面的示例中已被丑陋地解决,可以执行下面的示例以保留在 LSP 中,但是在这里我们打破了开闭原则:

class User {
  const User({
    required this.id,
    required this.avatar,
  });

  final String id;
  final String avatar;

  String? getName() => null;
}

class UserWithName extends User {
  const UserWithName(
      {required super.id, required super.avatar, required this.name});

  final String name;

  String getName() => this.name;
}

void main() {
  User user = new User(id: '0', avatar: 'avatar');
  User userWithName = new UserWithName(id: '0', avatar: 'avatar', name: 'User Name');

  print('hello ${user.getName()}');
  print('hello ${userWithName.getName()}');
}

退货:

hello null
hello User Name

非常欢迎任何建议或更正。

dart liskov-substitution-principle
1个回答
0
投票

在现实世界中,在各种情况下都需要这个原则,当超类是一个大默认值时,但子类具有相同类型的能力,但它们的能力的性质与大默认值不同。

将国际象棋视为一种游戏和一个引擎,用于检查游戏中可能的合法走法是否相似。你可能有类似的方法

  • getValidMoves(...)
  • getValidKingMoves(...)
  • getValidQueenMoves(...)
  • getValidRookMoves(...)
  • getValidBishopMoves(...)
  • getValidKnightMoves(...)
  • getValidPawnMoves(...)

所有这些都是为了国际象棋而实现的。现在,您需要为国际象棋的变体实现类似的类,例如 Fischer Random Chess、Chaturanga,其中您还有一张 8x8、白棋和黑棋的桌子,起始位置大致相同(第一行和八行在费舍尔随机国际象棋的情况),但规则不同,一些动作需要被覆盖。因此,您创建子类,每个变体一个子类并重写方法。

现在,如果您的项目计划周全,那么引擎会要求有效的移动,这是一种遍历所有部分的方法,并为每个部分获取其有效的移动。结果会不同,但是请求它们并解析它们的逻辑将是相同的。

© www.soinside.com 2019 - 2024. All rights reserved.