关于设计原则:面向对象的SOLID设计原则

39次阅读

共计 3707 个字符,预计需要花费 10 分钟才能阅读完成。

在程序设计畛域,罗伯特·C·马丁指出了 面向对象编程 面对对象设计 的五大根本准则:SOLID。开发一个零碎时,如果利用这些准则,将会让零碎变得更加易于保护和扩大。

SOLID 是由五个设计准则的首字母组成的:

  • S – 繁多职责准则 – Single Responsibility Principle
  • O – 凋谢关闭准则 – Open Close Principle
  • L – 里氏替换准则 – Liskov Substitution Principle
  • I – 接口拆散准则 – Interface Segregation Principle
  • D – 依赖倒置准则 – Dependency Inversion Principle

上面就 SOLID 五大设计准则,一起来看看。

S:繁多职责准则(Single Responsibility Principle)

一个类(模块、函数等),只有一个职责;将多职责的类拆分为多个类,放弃各个类的互相独立。

益处:进步代码的可读性和可维护性,升高代码的复杂度,缩小批改代码的影响范畴。

任何代码都有可能批改。如果在一个类中实现多个不相干的职责,那么在批改其中一个职责的相干代码时,很有可能影响到其余职责的相干代码,造成这个类的不稳固影响范畴变大。

因而须要将多职责的类拆分为单职责的类,即便某一个职责相干代码须要批改,其影响范畴仅限于这个类,其余职责的代码不受这个职责代码批改的影响。

例如:

// 一个光猫类,有两个职责:一个是治理连贯(dial 和 hangup);一个是数据传输(send 和 receive)class Model {dial (pno: string) {}
  hangup () {}
  send (msg: string) {}
  receive(data: string) {}}

// 通常这两个职责并没有共同点,在一个类中实现,过于耦合。应该将其离开到两个绝对独立的类中,别离保护。class Connection {dial (pno: string) {}
  hangup () {}
}

class DataChannel {send (msg: string) {}
  receive(data: string) {}}

class Model {constructor () {const connection = new Connection()
    const dataChannel = new DataChannel()}
}

O:凋谢关闭准则(Open Close Principle)

当增加一个新的性能时,应该在已有代码的根底上扩大代码(新增模块、类、函数等),而不是批改已有的代码(批改模块、类、函数等)。

益处:进步代码的稳定性和灵活性。

例如:

// 有两种电脑:苹果电脑和华硕电脑。这两种电脑都继承 Computer 类。class Computer {getPrice() {}}

class MacComputer extends Computer {}
class AsusComputer extends Computer {}

// 当双十一打折促销时,须要对电脑实现提价销售。此时不应该批改类的代码,而是应该扩大一个打折类。class AsusDiscountComputer extends AsusComputer {getDiscountPrice() {}}

// 当双十一购物节过来后,AsusDiscountComputer 类不再须要时,间接删除即可。

L:里氏替换准则(Liskov Substitution Principle)

子类能够 齐全 替换父类;父类能呈现的中央,子类也能够呈现。

例如:

// 创立一个 Bird 类,假如所有的鸟都会飞
class Bird{fly() {}}

const allFly = (birds) => birds.forEach(bird => bird.fly())

allFly([new Bird(), new Bird(), new Bird()])

// 扩大三个子类:鸭子、鹦鹉、天鹅
class Duck extends Bird {quack(){}}

class Parrot extends Bird {repeat(){}}

class Swan extends Bird{beBeautiful(){}}

allFly([new Duck(), new Parrot(), new Swan()])

// 再增加一只企鹅,然而企鹅并不会飞,如果想调用 fly 办法,咱们就抛出一个谬误
class Penguin extends Bird {fly(){throw new Error('Sorry, but I cannot fly')
  }
  swim(){}
}

allFly([new Duck(), new Parrot(), new Swan(), new Penguin()]) // Error: Sorry, but I cannot fly.

// fly 办法并不冀望呈现外部谬误,allFly 办法也只是为会飞的鸟创立的。企鹅不会飞,所以咱们违反了里氏替换准则
// 会飞的鸟与不会飞的鸟不能继承同一个 Bird 类。须要创立一个 FlyingBird 类供会飞的鸟继承
class Bird{
}

class FlyingBird{fly(){}}

class Duck extends FlyingBird {quack(){}}

class Parrot extends FlyingBird {repeat(){}}

class Swan extends FlyingBird{beBeautiful(){}}

class Penguin extends Bird {swim(){}}

I:接口拆散准则(Interface Segregation Principle)

客户端不应该依赖他们不应用的接口(接口应该是精简的,领有尽可能少的行为)。

例如:

// 有一个名为 Troll 的类,它实现了一个名为 Character 的接口,然而 Troll 既不会游泳也不会谈话,所以它仿佛不太适宜实现咱们的接口。interface Character {shoot(): void;
  swim(): void;
  talk(): void;
  dance(): void;}

class Troll implements Character {shoot(): void {// some method}

  swim(): void {// a troll can't swim}

  talk(): void {// a troll can't talk}

  dance(): void {// some method}
}

// 遵循接口隔离准则,删除 Character 接口并将它的性能拆分为四个接口,而后 Troll 类只须要依赖于理论须要的这些接口。interface Talker {talk(): void;
}

interface Shooter {shoot(): void;
}

interface Swimmer {swim(): void;
}

interface Dancer {dance(): void;
}

class Troll implements Shooter, Dancer {shoot(): void {// some method}

  dance(): void {// some method}
}

D:依赖倒置准则(Dependency Inversion Principle)

根本定义是:

  • 高层模块不应该依赖低层模块,应该独特依赖形象。
  • 形象不应该依赖细节,细节应该依赖形象。

这里的形象就是接口和抽象类,而细节就是实现接口或继承抽象类而产生的类。

// 比方宝马 BMW 类、飞驰 Benz 类都有一个 drive 办法,而后 Driver 类通过依赖倒置的形式去实现开不同的车。// 形象
interface ICar {
  brand: string
  drive(): void}

interface IDriver {setCar(car: ICar): void
  drive(car: ICar): void
}

// 细节
class Driver implements IDriver {
  private car: ICar

  constructor (car: ICar) {this.car = car}

  setCar (car: ICar) {this.car = car}

  drive () {this.car.drive()
  }
}

class BMW implements ICar {
  brand: string;

  constructor (brand: string) {this.brand = brand}
  drive () {}
}

class Benz implements ICar {
  brand: string;

  constructor (brand: string) {this.brand = brand}
  drive () {}
}

const li740 = new BMW('BMW')
const s600 = new Benz('Benz')

const driver = new Driver(li740)
driver.setCar(s600) // replace li740 with s600

总结

  • 繁多职责:实现类须要职责繁多。
  • 里氏替换准则:不要毁坏继承体系。
  • 接口隔离准则:设计的接口要精简。
  • 依赖倒置准则:面向接口编程。
  • 【总纲】凋谢关闭准则:对扩大凋谢,对批改敞开。

人无完人,金无足赤。实践结合实际,不要刻意追求完满,而是要在适当的场景遵循适当的设计准则,体现出一种均衡的取舍。帮忙咱们设计出更加优雅的代码构造。

正文完
 0