TypeScript 中实用的 SOLID 原则(四):接口隔离原则

Image

继续我们的 SOLID 原则之旅,讨论对代码设计有深远影响的接口隔离原则。

当初学者开始他们的编程旅程时,最初的重点通常是算法和适应一种新的思维方式。一段时间后,他们深入研究面向对象编程(OOP)。

如果这个转变被延迟,那么从函数式编程思维模式的转变可能会有挑战性,然而,最终,他们会接受对象的使用,并在必要时将它们合并到代码中,有时甚至在不需要它们的地方。

当他们学习抽象并努力使代码更可重用时,他们可能会过度概括,导致抽象应用到所有地方,这可能会阻碍未来的开发。

在某种程度上,他们开始意识到为过度泛化设置边界的重要性。幸运的是,接口隔离原则已经为此提供了指导方针,代表了SOLID中的“I”。

怎么算违反接口隔离原则
 

Image

接口隔离原则(ISP)指出,我们应该保持接口小,这样用户就不会依赖于他们不需要的东西。

Uncle Bob提出了这个原则,你可以在他的博客上找到更多细节,这个原则的简单表述就是:尽可能地缩小接口,我们不应该只把接口理解为一个方法接口,而是应该更多地关注接口所包含的特性的内聚性。

重大声明:JavaScript本身并不支持接口,但它们作为TypeScript的概念存在,以便应用接口隔离原则。

为了理解这个原则是如何被违背的,让我们检查一下下面处理 User 接口的代码块:

User接口

 
interface User {  addToShoppingCart(product: Product);  isLoggedIn(): boolean;  pay(money: Money);  hasPremium(): boolean;}
 


Guest类

class Guest implements User {  constructor(    private cart: ShoppingCart  ) {}    public addToShoppingCart(product: Product) {    this.cart.Add(product);  }
  public isLoggedIn(): boolean {    // should this be obvious?    return false;  }
  public pay(money: Money) {    // should unathenticated user be able to pay?    throws new Error("user is not logged in");  }
  public hasPremium(): boolean {    // should this be obvious?    return false;  }}


Customer类

 
class Customer implements User {  constructor(    private cart: ShoppingCart,    private wallet: Wallet,    private isPremium: boolean  ) {}    public addToShoppingCart(product: Product) { this.cart.Add(product);  }    public isLoggedIn(): boolean {    // should this be obvious?    return true;  }    public pay(money Money) {    this.wallet.Deduct(money);  }    public hasPremium(): boolean {    this.isPremium;  }}
 

假设我们想要交付一个购物应用程序,其中一种方法是定义一个接口  User ,就像我们在代码示例中所做的那样。这个接口包含用户可以拥有的许多特性。

我们平台上的 User 可以为 ShoppingCart 添加 Product ,他们可以购买,如果他们有高级账户,他们可以获得折扣。

这个接口的实际实现有两个类。第一个是  Guest 。它应该是一个没有登录到我们的系统的  User ,但至少他们可以向  ShoppingCart  添加一个  Product 。第二个实现是  Customer ,它可以使用我们系统的所有功能。

方法  HasPremium  和  Pay  对于  Guest  没有任何意义。如果那个类代表那些没有登录的  Users ,为什么我们要考虑实现折扣的方法呢?

方法  IsLoggedIn  对于这两种实现都没有任何意义,如果它应该支持为授权用户保留的特性,那么它就是实现的实际决策者。

为了更好地泛化,我们在代码中引入了一些不必要的实现,我们得到了:

  1. 类中没有使用的方法。

  2. 我们需要标记的方法,以便其他人不要使用它们。

  3. 单元测试的大量额外代码。

  4. 非自然的多态性。


让我们重构一下。

如何遵守接口隔离原则
 

Image

为了让代码遵守接口隔离原则,必须围绕最小内聚特性组构建接口。

user接口

 
interface User {  addToShoppingCart(product: Product);}
interface LoggedInUser implements User {  pay(money: Money);  hasPremium(): boolean;}
 

现在,我们有两个接口,而不是一个接口,LoggedInUser 代表会话中完全授权的用户,User 代表网站上的所有用户。

注意:我们确实增加了一个接口,但我们删除了一个方法:IsLoggedIn .那些方法不是我们接口签名的一部分。

Guest类
 
class Guest implements User {  constructor(    private cart: ShoppingCart) {}    public addToShoppingCart(product: Product) {    this.cart.Add(product);  }}
 


Customer类

 
class Customer implements LoggedInUser {  constructor(    private cart: ShoppingCart,    private wallet: Wallet,    private isPremium: boolean  ) {}    public addToShoppingCart(product: Product) {    this.cart.Add(product);  }    public pay(money Money) {    this.wallet.Deduct(money);  }    public hasPremium(): boolean {    this.isPremium;  }}
 

以前所有的类现在都更轻量了,而不是每个类都有五个方法,其中许多方法根本就没有用到,现在它们只有真正需要的方法。

结论
 

接口隔离原则是SOLID原则中的第四条,由SOLID中的字母“I”代表。它教导我们尽可能地保持接口的最小化。

 欢迎关注公众号:文本魔术,了解更多

  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值