TypeScript 中的 SOLID 原则
下面的文章解释了正确使用 TypeScrip的 SOLID原则。
原文地址:https://samueleresca.net/2016/08/solid-principles-using-typescript/
作者:Samuele Resca
翻译:杨晓东(Savorboard)
前言
SOLID 是由 Robert C. Martin 在面向对象设计的(OOD)中提出的五个原则,你可以在这里更一步了解关于@UncleBob,这五个原则(SOLID)就是:
- 单一职责原则(Single Responsibility Principle):当需要修改某个类的时候原因有且只有一个
- 开放封闭原则(Open Closed Principle):软件实体应该是可扩展,而不能可修改的
- 里氏替换原则(Liskov Substitution Principle):子类的实例应该能够替换任何其超类的实例
- 接口分离原则(Interface Segregation Principle):使用多个专门的接口比使用单一的总接口总要好
- 依赖倒置原则(Dependency Inversion Principle):依赖于抽象不应该依赖于细节
这些原则使得程序员可以轻松地开发易于维护和扩展的软件。它们还使开发人员的代码能够容易地避免坏气味,轻松重构代码,并且也是敏捷或自适应软件开发的一部分。
单一责任原则(SRP)
SRP要求类只能有一个更改的原因。遵循这个原则来执行一些特定的相关任务。在考虑SRP时,你不需要将你的思维限制到类。你可以将这个原则应用到方法或者模块,确保他们仅仅只是做一件事情并且只有一个理由可以修改它们。
例子 - 错误的方式
这个 Task
类定义了一些于模型相关的属性,但是它也在一个基本的数据操作上定义了一些保存实体的数据访问的方法。
UML
代码
// 这个类没有遵循 SRP 原则
class Task {private db: Database;constructor(private title: string, private deadline: Date) {this.db = Database.connect("admin:password@fakedb", ["tasks"]);}getTitle() {return this.title + "(" + this.deadline + ")";}save() {this.db.tasks.save({ title: this.title, date: this.deadline });}
}
例子 - 正确的方式
UML
代码
class Task {constructor(private title: string, private deadline: Date) {}getTitle() {return this.title + "(" + this.deadline + ")";}}class TaskRepository {private db: Database;constructor() {this.db = Database.connect("admin:password@fakedb", ["tasks"]);}save(task: Task) {this.db.tasks.save(JSON.stringify(task));}
}
开放封闭原则(OCP)
软件实体应该对扩展开放,对修改关闭。
改变现有类的风险是,你会引入一个无意的行为变化。解决方案是创建另一个类,覆盖原始类的行为。通过OCP原则,一个组件应尽可能包含可维护并且可重复使用的代码。
例子 - 正确的方式
CreditCard
类描述了一个计算 monthlyDiscount()
的方法。这个 monthlyDiscount()
依赖了具体的Card类型,也就是:Silver 或者 Gold。如果要改变月度折扣计算(monthlyDiscount)那么应该建立另外一个类,重写monthlyDiscount()
方法。目前这个的解决方案是新建两个类:每个类型一个类。
UML
代码
class CreditCard {private Code: String;private Expiration: Date;protected MonthlyCost: number;constructor(code: String, Expiration: Date, MonthlyCost: number) {this.Code = code;this.Expiration = Expiration;this.MonthlyCost = MonthlyCost;}getCode(): String {return this.Code;}getExpiration(): Date {return this.Expiration;}monthlyDiscount(): number {return this.MonthlyCost * 0.02;}}class GoldCreditCard extends CreditCard {monthlyDiscount(): number {return this.MonthlyCost * 0.05;}
}class SilverCreditCard extends CreditCard {monthlyDiscount(): number {return this.MonthlyCost * 0.03;}
}
里氏替换原则(LSP)
子类不应该破坏父类的类型定义
这一原则的概念是由 Barbara Liskov 在1987年大会上发表,随后与 Jannette Wing 一起在1994年发表论文。
就这么简单,一个子类应当有一种方式覆写它的父类的方法,但是从客户的角度来看没有破坏它的功能。
例子
在下面的例子中,ItalyPostalAddress
, UKPostalAddress
和 USAPostalAddress
继承了一个公共的基类:PostalAddress
。
AddressWriter
类有一个引用指向了 PostalAddress
这个基类:也就是说 参数可以被三个不同的之类替换。
代码
abstract class PostalAddress {Addressee: string;Country: stringPostalCode: string;City: string;Street: stringHouse: number;/** @returns Formatted full address*/abstract WriteAddress(): string;
}class ItalyPostalAddress extends PostalAddress {WriteAddress(): string {return "Formatted Address Italy" + this.City;}
}
class UKPostalAddress extends PostalAddress {WriteAddress(): string {return "Formatted Address UK" + this.City;}
}
class USAPostalAddress extends PostalAddress {WriteAddress(): string {return "Formatted Address USA" + this.City;}
}class AddressWriter {PrintPostalAddress(writer: PostalAddress): string {return writer.WriteAddress();}
}
接口分离原则(ISP)
有一个很常见的现象就是,在描述一个类的时候,基本上一个接口就把它覆盖完了,就是一个接口就描述了一整个类。ISP 原则指出,我们应该写一系列更加小并且具体的接口,交给该类来实现。而每个接口只提供单一的行为。
示例 - 错误的方式
下面的 Printer
接口,它有一个实现的类 SimplePrinter
,该接口具有 Copy 和 Print 的功能。
interface Printer {copyDocument();printDocument(document: Document);stapleDocument(document: Document, tray: Number);
}class SimplePrinter implements Printer {public copyDocument() {//...}public printDocument(document: Document) {//...}public stapleDocument(document: Document, tray: Number) {//...}}
例子 - 正确的方式
下面的示例显示了将方法分组到更加具体的接口和可以被替代的方法,它描述了一些契约,他们可以被一个单独的 SimplePrinter 类,或 SimpleCopier 类,或 SuperPrinter 类实现。
interface Printer {printDocument(document: Document);
}interface Stapler {stapleDocument(document: Document, tray: number);
}interface Copier {copyDocument();
}class SimplePrinter implements Printer {public printDocument(document: Document) {//...}
}class SuperPrinter implements Printer, Stapler, Copier {public copyDocument() {//...}public printDocument(document: Document) {//...}public stapleDocument(document: Document, tray: number) {//...}
}
依赖倒置原则(DIP)
DIP 简单的说就超类不应该依赖于低级的组件,而应该依赖于抽象。
例子 - 错误的方式
高级的 WindowSwitch
依赖于底层低级的 CarWindow
类。
UML
代码
class CarWindow {open() {//... }close() {//...}
}class WindowSwitch {private isOn = false;constructor(private window: CarWindow) {}onPress() {if (this.isOn) {this.window.close();this.isOn = false;} else {this.window.open();this.isOn = true;}}
}
总结
TypeScript 可以将所有的OOP原则和实践带入到你的软件中,使用 SOLID 原则来指导你的设计模式吧。
GitHub 完整的示例代码。
转载于:https://www.cnblogs.com/savorboard/p/typescript-solid.html
TypeScript 中的 SOLID 原则相关推荐
- 【推荐阅读】JavaScript 中的 SOLID 原则(一):“S”代表什么
你可能已经了解过一些设计原则或者设计模式,本文主要渐进的讲解了SOLID原则: 不使用SOLID是怎么编写代码的,存在什么问题? 应该使用SOLID中的哪个原则? 使用SOLID我们应该如何对代码进行 ...
- 独家 | Python中的SOLID原则(附链接)
作者:Mattia Cinelli翻译:朱启轩校对:欧阳锦本文约3500字,建议阅读15分钟本文通过一些Python示例代码介绍了可以提高代码可靠性的SOLID编码准则. 标签:数据结构,编程,数据科 ...
- 设计模式中的solid原则
好的代码不只为了完成现有功能,也会考虑后续扩展. 在结构设计上松耦合易读易扩展,在领域实现上高内聚不对外暴露实现细节不被外部干扰. 在众多项目的锤炼和对程序设计的不断追求,多年编程历程提炼出来的心得体 ...
- Java中的SOLID原则
我是小黑,一名在互联网"苟且"的程序员 关注同名公众号[小黑说Java],更多干货内容抢先送达,更有不定期抽奖送书活动. 流水不争先,贵在滔滔不绝 前言 Robert C. Mar ...
- 用 SOLID 原则保驾 React 组件开发
概述 本世纪初,美国计算机专家和作者 Robert Cecil Martin 针对 OOP 编程,提出了可以很好配合的五个独立模式:后由重构等领域的专家 Michael Feathers 根据其首字母 ...
- 设计模式之SOLID原则
介绍 设计模式中的SOLID原则,分别是单一原则.开闭原则.里氏替换原则.接口隔离原则.依赖倒置原则.前辈们总结出来的,遵循五大原则可以使程序解决紧耦合,更加健壮. S ...
- 单元测试中的 FIRST 原则
单元测试在软件测试中是最基础的测试,它通常在程序员完成编码以后完成,如果是测试驱动的开发 (TDD) 那么更需要在编码完成之前就写好单元测试.单元测试是白盒测试,它只测试软件中的某块代码,而不像集成测 ...
- html5 中的solid,图解你身边的 SOLID 原则
这篇文章我们来简单介绍一下 SOLID 原则(这五个字母代表了面向对象编程的五个基本原则) 我们用身边的事物来举例,让它们更易于理解和记忆. 好啦,开始吧~ S - 单一职责原则 Single Res ...
- C# 最佳做法--- C# 中 SOLID 原则
违背 C# 中 SOLID 原则的危险 随着编写软件的流程从理论领域发展到实际的工程学科,许多原则也应运而生. 当我提及原则时,我所指的是计算机代码帮助维持代码价值的一项功能. 模式是指常见的代码方案 ...
最新文章
- SharePoint 客户端经常弹出Windows验证登录框问题
- Cloudify — OpenStack Infrastructure Plugin V3
- 手机显示服务器无数据返回,服务器无返回数据处理
- 干粉灭火器的维护保养
- Queue接口中add()与offer(),remove()与poll(),element()与peek()方法区别
- Spark数据倾斜的完美解决
- 多业务融合推荐策略实践与思考
- SAP BSP应用configuration的加载原理
- 腾讯2013实习生笔试题+答案1-5aadaa 6-10adbcc 11-15 acacc16-20 bbddc
- 安卓手机刷软路由_华为路由AX3 Pro上手测评:用过最方便的路由器,没有之一...
- 谷歌测试工程师分享前端性能监控利器Performance
- 使用Homebrew安装Git与Github在idea中的配置
- Oracle 创建表语句
- WinForm中 SplitContainer的使用
- FS4052单节2A充电IC采用三段式充电管理IC
- 51单片机DS1302实时时钟
- JavaFX调用虚拟键盘
- Pytest如何并发执行自动化脚本
- zsh与bash区别
- scribe - 日志收集器 分析