TypeScript 的 SOLID 设计原则

阅读时长 13 分钟读完

SOLID 是面向对象编程中的五个设计原则,它们的首字母组成了 SOLID。这些原则旨在提高代码的可读性、可维护性和可扩展性。在 TypeScript 中,遵循 SOLID 原则可以帮助开发者编写高质量的代码。

单一职责原则(Single Responsibility Principle)

单一职责原则(SRP)要求一个类或者模块只负责一项任务。这个原则的目的是让类或者模块更加可读、可维护和可测试。

在 TypeScript 中,我们可以通过将一个类或者模块拆分成多个小的类或者模块来遵循 SRP。

以下是一个违反 SRP 原则的例子:

-- -------------------- ---- -------
----- ----------- -
  ---------------- ----- -
    -- ---
  -

  ---------------- ----- -
    -- ---
  -

  -------------- ------- -
    -- ---
  -

  --------------- ----- -------- ------- -
    -- ---
  -
-

在上面的例子中,UserService 负责了两个不同的任务:管理用户和发送邮件。这样的设计不仅难以维护,而且难以测试。

我们可以将 UserService 拆分成两个类:UserManagerEmailService。这样每个类都只负责一项任务,代码就更加清晰易懂。

-- -------------------- ---- -------
----- ----------- -
  ---------------- ----- -
    -- ---
  -

  ---------------- ----- -
    -- ---
  -

  -------------- ------- -
    -- ---
  -
-

----- ------------ -
  --------------- ----- -------- ------- -
    -- ---
  -
-

开放封闭原则(Open-Closed Principle)

开放封闭原则(OCP)要求一个类或者模块应该对扩展开放,对修改封闭。这个原则的目的是让代码更加灵活和可扩展。

在 TypeScript 中,我们可以通过使用抽象类和接口来遵循 OCP。

以下是一个违反 OCP 原则的例子:

在上面的例子中,DiscountService 中的 applyDiscount 方法是直接计算折扣后的价格。如果我们需要添加一个新的折扣类型,就需要修改 DiscountService 中的代码。

我们可以使用抽象类和接口来遵循 OCP。首先,我们定义一个抽象类 Discount

然后,我们可以创建具体的折扣类,例如 PercentageDiscountFixedDiscount

-- -------------------- ---- -------
----- ------------------ ------- -------- -
  ------------------- --------- ------- -
    --------
  -

  ------------ -------- ------ -
    ------ ----- - -- - ---------------
  -
-

----- ------------- ------- -------- -
  ------------------- --------- ------- -
    --------
  -

  ------------ -------- ------ -
    ------ ----- - --------------
  -
-

现在,我们可以通过创建新的折扣类来扩展代码,而不需要修改 DiscountService 中的代码:

-- -------------------- ---- -------
----- --------------- -
  -------------------- ------- --------- --------- -
    ------ ----------------------
  -
-

----- ------------------ - --- ------------------------
----- ------------- - --- -----------------

----- --------------- - --- ------------------
---------------------------------------------- --------------------- -- -- --
---------------------------------------------- ---------------- -- -- --

里氏替换原则(Liskov Substitution Principle)

里氏替换原则(LSP)要求子类可以替换父类并且不会影响程序的正确性。这个原则的目的是让代码更加灵活和可扩展。

在 TypeScript 中,我们可以通过使用接口和抽象类来遵循 LSP。

以下是一个违反 LSP 原则的例子:

-- -------------------- ---- -------
----- --------- -
  ------------------- ------ ------- ------- ------- ------- --

  --------------- ------- -
    ---------- - ------
  -

  ----------------- ------- -
    ----------- - -------
  -

  ---------- -
    ------ -----------
  -

  ----------- -
    ------ ------------
  -

  --------- -
    ------ ---------- - ------------
  -
-

----- ------ ------- --------- -
  --------------- ------- -
    ---------- - ------
    ----------- - ------
  -

  ----------------- ------- -
    ----------- - -------
    ---------- - -------
  -
-

在上面的例子中,Square 继承自 Rectangle,但是重写了 setWidthsetHeight 方法。这样的设计违反了 LSP 原则,因为 Square 的行为和 Rectangle 不同。

我们可以使用接口和抽象类来遵循 LSP。首先,我们定义一个 Shape 接口:

然后,我们可以创建具体的形状类,例如 RectangleSquare

-- -------------------- ---- -------
----- --------- ---------- ----- -
  ------------------- ------ ------- ------- ------- ------- --

  --------- -
    ------ ---------- - ------------
  -
-

----- ------ ---------- ----- -
  ------------------- ----- ------- --

  --------- -
    ------ --------- - ----------
  -
-

现在,我们可以使用 Shape 接口来定义变量和参数,这样就可以在不影响程序正确性的情况下替换不同的形状类:

-- -------------------- ---- -------
-------- ---------------- ------ -
  -----------------------------
-

----- --------- - --- ------------- ----
----- ------ - --- -----------

--------------------- -- -- ---
------------------ -- -- ---

接口隔离原则(Interface Segregation Principle)

接口隔离原则(ISP)要求一个接口应该只包含其实现类需要的方法。这个原则的目的是减少接口的复杂度,提高代码的可读性和可维护性。

在 TypeScript 中,我们可以通过使用接口和抽象类来遵循 ISP。

以下是一个违反 ISP 原则的例子:

-- -------------------- ---- -------
--------- ---- -
  --- -------
  ----- -------
  ------ -------
  --------- -------
  -------- --------

  ---------------- ------ -----
  ---------------- ------ -----
  -------------- -------- -----
  --------------- ----- -------- -------- -----
-

在上面的例子中,User 接口包含了太多的方法,其中一些方法可能只有管理员才能使用。这样的设计不仅使接口变得复杂,而且使代码难以测试。

我们可以使用接口和抽象类来遵循 ISP。首先,我们定义一个 User 接口,只包含用户信息的方法:

-- -------------------- ---- -------
--------- ---- -
  --- -------
  ----- -------
  ------ -------
  --------- -------
-

--------- ----------- -
  ---------------- ------ -----
-

--------- ----------- -
  ---------------- ------ -----
-

--------- ----------- -
  -------------- -------- -----
-

--------- ----------- -
  --------------- ----- -------- -------- -----
-

然后,我们可以创建具体的类,例如 UserManagerEmailService,来实现这些接口:

-- -------------------- ---- -------
----- ----------- ---------- ------------ ------------ ----------- -
  ---------------- ----- -
    -- ---
  -

  ---------------- ----- -
    -- ---
  -

  -------------- ------- -
    -- ---
  -
-

----- ------------ ---------- ----------- -
  --------------- ----- -------- ------- -
    -- ---
  -
-

现在,我们可以使用这些接口来定义变量和参数,这样就可以只使用需要的方法:

依赖倒置原则(Dependency Inversion Principle)

依赖倒置原则(DIP)要求高层模块不应该依赖底层模块,它们都应该依赖于抽象。这个原则的目的是提高代码的灵活性和可维护性。

在 TypeScript 中,我们可以通过使用依赖注入(Dependency Injection)来遵循 DIP。

以下是一个违反 DIP 原则的例子:

-- -------------------- ---- -------
----- ----------- -
  ------------------- --------------- --------------- --

  ---------------- ----- -
    ---------------------------------
  -

  ---------------- ----- -
    ---------------------------------
  -

  -------------- ------- -
    -------------------------------
  -
-

----- -------------- -
  ------------ ----- -
    -- ---
  -

  ------------ ----- -
    -- ---
  -

  ---------- ------- -
    -- ---
  -
-

在上面的例子中,UserService 依赖于 UserRepository。这样的设计使得 UserServiceUserRepository 之间的耦合度很高,难以维护和测试。

我们可以使用依赖注入来遵循 DIP。首先,我们定义一个 UserRepository 接口:

然后,我们将 UserRepository 作为参数传递给 UserService

-- -------------------- ---- -------
----- ----------- -
  ------------------- --------------- --------------- --

  ---------------- ----- -
    ---------------------------------
  -

  ---------------- ----- -
    ---------------------------------
  -

  -------------- ------- -
    -------------------------------
  -
-

现在,我们可以使用不同的实现来注入 UserRepository

或者使用依赖注入框架,例如 InversifyJS

总结

SOLID 设计原则是编写高质量代码的重要指南。在 TypeScript 中,我们可以使用抽象类、接口、依赖注入等技术来遵循这些原则。遵循 SOLID 原则可以提高代码的可读性、可维护性和可扩展性,从而使我们的代码更加健壮和可靠。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/657ab376d2f5e1655d524834

纠错
反馈