【Go实现】实践GoF的23种设计模式:SOLID原则( 九 )


type Registry struct {dbdb.DiskDb // 改成依赖DiskDbserver*http.ServerlocalIpstringsvcManagement *svcManagementsvcDiscovery*svcDiscovery}
更好的设计应该是把和的依赖关系倒置过来,首先我们需要从细节抽象出一个稳定的接口Db:
// Db 数据库抽象接口type Db interface {Query(tableName string, primaryKey interface{}, result interface{}) errorInsert(tableName string, primaryKey interface{}, record interface{}) errorUpdate(tableName string, primaryKey interface{}, record interface{}) errorDelete(tableName string, primaryKey interface{}) error...}
接着,我们让依赖Db接口,而实现Db接口,以此来完成依赖倒置:
type Registry struct {dbdb.Db // 依赖Db抽象接口server*http.ServerlocalIpstringsvcManagement *svcManagementsvcDiscovery*svcDiscovery}// MemoryDb 实现Db接口type MemoryDb struct {tables sync.Map // key为tableName,value为table}func (m *memoryDb) Query(tableName string, primaryKey interface{}, result interface{}) error {...}func (m *memoryDb) Insert(tableName string, primaryKey interface{}, record interface{}) error {...}func (m *memoryDb) Update(tableName string, primaryKey interface{}, record interface{}) error {...}func (m *memoryDb) Delete(tableName string, primaryKey interface{}) error {...}
当高层模块依赖抽象接口时,总得在某个时候,某个地方把实现细节(低层模块)注入到高层模块上 。在上述例子中,我们选择在main函数上,在创建对象时,把注入进去 。
一般地,我们都会在main/启动函数上完成依赖注入,常见的注入的方式有以下几种:
另外,DIP不仅仅适用于模块/类/接口设计,在架构层面也同样适用,比如DDD的分层架构和Uncle Bob的整洁架构,都是运用了DIP:
当然,DIP并不是说高层模块是只能依赖抽象接口,它的本意应该是依赖稳定的接口/抽象类/具象类 。如果一个具象类是稳定的,比如Java中的,那么高层模块依赖它也没有问题;相反,如果一个抽象接口是不稳定的,经常变化,那么高层模块依赖该接口也是违反DIP的,这时候应该思考下接口是否抽象合理 。
最后
本文花了很长的篇幅讨论了23种设计模式背后的核心思想 —— SOLID原则,它能指导我们设计出高内聚、低耦合的软件系统 。但是它毕竟只是原则,如何落地到实际的工程项目上,还是需要参考成功的实践经验 。而这些实践经验正是接下来我们要探讨的设计模式 。
学习设计模式最好的方法就是实践,在《实践GoF的23种设计模式》后续的文章,我们将以本文介绍的分布式应用系统demo作为实践示范,介绍23种设计模式的程序结构、适用场景、实现方法、优缺点等,让大家对设计模式有个更深入的理解,能够用对、不滥用设计模式 。
参考
Clean ,C.(“Uncle Bob”)敏捷软件开发:原则、模式与实践,C.(“Uncle Bob”)使用Go实现GoF的23种设计模式, 元闰子【Java实现】实践GoF的23种设计模式:SOLID原则 , 元闰子SOLID原则精解之里氏替换原则LSP, 人民副首席码仔