SOLID五大设计原则
| 首字母 | 指代 | 概念 |
|---|---|---|
| S(single responsibility principle) | 单一职责原则 | 单一功能原则认为对象应该仅具有一种单一功能的概念 |
| O(open closed principle) | 开放封闭原则 | 开闭原则认为软件体应该是对于扩展开放的,但是对于修改封闭的的概念 |
| L Liskov Substitution Principle | 里氏替换原则 | 里氏替换原则认为程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的的概念 |
| I (Interface segregation princple) | 接口隔离原则 | 接口隔离原则认为多个特定客户端接口要好于一个宽泛用途的接口的概念 |
| D | 依赖反转原则 | 依赖反转原则认为一个方法应该遵从依赖于抽象而不是一个实例的概念,依赖注入是该原则的一种实现方式。 |
S - 单一职责原则
- Single responsibility principle
- 一个类或者模块只负责完成一个职责,如果功能特别复杂就进行拆分,单一职责可以降低类的复杂性,提高代码可读性、可维护性
- 当类代码行数过多、方法过多、功能太多、职责太杂的时候就要对类进行拆分了
- 拆分不能过度,如果拆分过度会损失内聚性和维护性
- lodashjs, jquery 中的代码组织方式就是 单一职责原则。
O 开放封闭原则
Open Closed Principle- 对扩展开放,对修改关闭,增加需求时,扩展新代码,而非修改已有代码,开闭原则是设计模式中的总原则
写法1:vip 和 普通顾客享受着不同的折扣优惠,如果不采用开放封闭原则,每次增加一个折扣力度,就要在商品的判断语句当中修改代码,判断是 vip 还是顾客 还是其他,然后对应计算折扣价格。
class Customer {
constructor(public rank: string) { }
}
class Product {
constructor(public name: string, public price: number) {
}
cost(customer: Customer) {
switch (customer.rank) {
case 'member':
return this.price * .8;
case 'vip':
return this.price * .6;
default:
return this.price;
}
}
}
let p1 = new Product('笔记本电脑', 1000);
let member = new Customer('member');
let vip = new Customer('vip');
let guest = new Customer('guest');
console.log(p1.cost(member));
console.log(p1.cost(vip));
console.log(p1.cost(guest));
写法2:vip 和 普通顾 客享受着不同的折扣优惠,采用开放封闭原则,每次扩展增加一个折扣力度,不需要修改代码,通过 开放封闭原则 将折扣作为 顾客 和 vip 上的属性。商品对象只需要将价格乘以对应的折扣就可以。
class Customer {
+ constructor(public rank: string, public discount: number = 1) { }
+ getDiscount() {
+ return this.discount;
+ }
}
class Product {
constructor(public name: string, public price: number) {
}
cost(customer: Customer) {
+ return this.price * customer.getDiscount();
}
}
+let p1 = new Product('笔记本电脑', 1000);
+let member = new Customer('member', .8);
+let vip = new Customer('vip', .6);
let guest = new Customer('guest');
console.log(p1.cost(member));
console.log(p1.cost(vip));
console.log(p1.cost(guest));
2.2.3 L 里氏替换原则
- Liskov Substitution Principle
- 所有引用基类的地方必须能透明地使用其子类的对象
- 子类能替换掉父类,使用者可能根本就不需要知道是父类还是子类,反之则不行
- 里氏替换原则是开闭原则的实现基础,程序设计的时候尽量使用基类定义及引用,运行时再决定使用哪个子类
- 里氏替换原则可以提高代码的复用性,提高代码的可扩展性,也增加了耦合性
- 相对于多态,这个原则是讲的是类如何设计,子类如果违反了父类的功能则表示违反了里氏替换原则
abstract class AbstractDrink {
abstract getName(): string;
}
class CocaCola extends AbstractDrink {
getName(): string {
return '可乐';
}
}
class Sprite extends AbstractDrink {
getName(): string {
return '雪碧';
}
}
class Fanta extends AbstractDrink {
getName(): string {
return '芬达';
}
}
class Customer {
drink(drink: AbstractDrink) {
console.log('喝' + drink.getName());
}
}
let customer = new Customer();
let cocaCola = new CocaCola();
let sprite = new Sprite();
let fanta = new Fanta();
customer.drink(cocaCola);
customer.drink(sprite);
customer.drink(fanta);
接口隔离原则
- Interface Segregation Principle
- 保持接口的单一独立,避免出现胖接口
- 客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上
- 接口尽量细化,而且接口中的方法尽量的少
- 类似于单一职责原则,更关注接口
interface Running {
run():void;
}
interface Flying {
fly():void;
}
interface Swimming {
swim():void;
}
classAutomobileimplementsRunning,Flying,Swimming {
run() { }
fly() { }
swim() { }
}
D 依赖倒置原则
- Dependence Inversion Principle
- 面向接口编程,依赖于抽象而不依赖于具体实现
- 要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类
- 使用方只关注接口而不关注具体类的实现
abstract class GirlFriend {
public age: number;
public height: number;
public abstract cook(): void;
}
class LinZhiLing extends GirlFriend {
public cook(): void {
}
}
class HanMeiMei extends GirlFriend {
public cook(): void {
}
}
class SingleDog {
constructor(public girlFriend: GirlFriend) {
}
}
let s1 = new SingleDog(new LinZhiLing());
let s2 = new SingleDog(new HanMeiMei());
总结
- 开闭原则是核心,对修改关闭对扩展开放是软件设计的基石
- 单一职责要求我们设计接口和模块功能的时候尽量保证单一性和原子性,修改一条不影响全局和其它模块
- 里氏替换原则和依赖倒置原则要求面向接口和抽象编程,不要依赖具体实现,否则实现一改,上层调用者就要对应修改