[笔记] SOLID Go Design

2 minute read

原文链接视频

Code review 的重要性不言而喻。但是怎样区分好代码和坏代码呢?这需要一些客观的原则或者标准,而不是主观性的评价。

坏代码的特点

  • Rigid. Is the code rigid? Does it have a straight jacket of overbearing types and parameters, that making modification difficult?
  • Fragile. Is the code fragile? Does the slightest change ripple through the code base causing untold havoc?
  • Immobile. Is the code hard to refactor? Is it one keystroke away from an import loop?
  • Complex. Is there code for the sake of having code, are things over-engineered?
  • Verbose. Is it just exhausting to use the code? When you look at it, can you even tell what this code is trying to do?

好的设计——SOLID

Robert Martin 提出了好的软件应该遵循的五个原则:SOLID

  • Single Responsibility Principle
  • Open / Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

Single Responsibility Principle

A class should have one, and only one, reason to change. –Robert C Martin

尽管 Go 中没有类的概念,这句话同样有它的意义。经常用来描述代码的改造难度的词是 coupling 和 cohesion。我们应当追求高内聚,低耦合。 以 Go 的包名为例,好的包名如 net/httpencoding/json 等体现了 SRP 原则,它清晰地表明了这个包的用途。而差的包名,如 servercommon 等往往不知所云,职责不清晰或是承担了太多职责,因此会频繁的改动。

Go 的包设计应当遵循 UNIX 哲学:small, sharp tools which combine to solve larger tasks

Open / Closed Principle

Software entities should be open for extension, but closed for modification. –Bertrand Meyer, Object-Oriented Software Construction

对扩展开放,对修改关闭

作者举了一个类型内嵌(embedding)的例子,来说明 Go 的类型是符合开闭原则的。

Liskov Substitution Principle

Two types are substitutable if they exhibit behaviour such that the caller is unable to tell the difference. - Barbara Liskov

在基于类的语言中,该原则常用来描述抽象基类和具体子类之间的关系。那么对于 Go 这种没有类和继承的语言,该怎么应用呢?

这就要提到 interface 了。Go 中,接口是被隐式实现的。一般地,建议设计小的接口,即方法数量少的接口,这样接口的实现也会变简单。一个很好的例子就是 io.Reader 接口,它只有一个方法,却有着各种各样的实现:基于字符串,字节数组,stdin,网络流,tar 文件,等等。由于这些实现形式都遵从同样的“合约”,它们之间是可以互相替换的。

Interface Segregation Principle

Clients should not be forced to depend on methods they do not use. –Robert C. Martin

在 Go 中,这个原则体现为:将一个函数要完成它的工作所需要的行为隔离。(原文是:isolating the behaviour required for a function to do its job)

比如,编写一个函数,它的任务是将一份文档存盘。可能你会把函数签名设计为:

// Save writes the contents of doc to the file f.
func Save(f *os.File, doc *Document) error

这个设计有很明显的缺陷:如果将来需要把文档存在网络存储中,这个函数就失效了。

思考一下,要完成 Save 操作,函数对入参f所需要的最小行为是什么呢? 答案就是 io.Writer。 我们只需要往某个地方写入,并不关心写入的对象是文件还是网络,或是别的什么东西。我们不需要 *os.File 具备的其他方法或能力。 另一方面,这也增强了该函数的可测试性。

个人感觉这也印证了所谓的“面向接口编程”。

A great rule of thumb for Go is accept interfaces, return structs. –Jack Lindamood

Dependency Inversion Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions. –Robert C. Martin

在 Go 中,这体现为不要出现循环引用。这不仅会导致编译错误,更表明了设计有严重缺陷。

SOLID Go Design

  • S: encourages you to structure the functions, types, and methods into packages that exhibit natural cohesion; the types belong together, the functions serve a single purpose.

  • O: encourages you to compose simple types into more complex ones using embedding.

  • L: encourages you to express the dependencies between your packages in terms of interfaces, not concrete types. By defining small interfaces, we can be more confident that implementations will faithfully satisfy their contract.

  • I: encourages you to define functions and methods that depend only on the behaviour that they need. If your function only requires a parameter of an interface type with a single method, then it is more likely that this function has only one responsibility.

  • D: encourages you move the knowledge of the things your package depends on from compile time–in Go we see this with a reduction in the number of import statements used by a particular package–to run time.

一句话总结:

Interfaces let you apply the SOLID principles to Go programs.

布道

作者最后感慨了下,随着 Go 开发者的数量增多,如果不想让 Go 演变成 C++ 那般过度复杂的语言,他希望:

Go programmers need to start talking less about frameworks, and start talking more about design. We need to stop focusing on performance at all cost, and focus instead on reuse at all cost.

不愧是一个热爱 Go 语言,有社区责任感的人啊!

Tags: ,

Categories:

Updated:

Comments