From 467d44a01c23fa0933af516e9bed15228c608b54 Mon Sep 17 00:00:00 2001 From: Xargin Date: Mon, 26 Mar 2018 17:13:46 +0800 Subject: [PATCH] update interface section --- ch6-web/ch6-09-interface-and-web.md | 77 ++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/ch6-web/ch6-09-interface-and-web.md b/ch6-web/ch6-09-interface-and-web.md index 892d607..826877b 100644 --- a/ch6-web/ch6-09-interface-and-web.md +++ b/ch6-web/ch6-09-interface-and-web.md @@ -16,11 +16,11 @@ TODOTODO 这里有图,标明控制流的方向 从架构的角度讲,这个控制流会给我们带来很多问题: -``` 1. dao 的变动必然会引起 logic 的变动 2. 核心的业务逻辑 logic 代码变动会给我们带来较大的风险 -3. -``` +3. 项目的依赖于大量的外部服务、组件,难以测试 + +本节主要解决 1 和 2 两个问题。 interface 这时候就成为了我们的救星,如果我们在 a->b 这个控制方向上不满意,不想让 b 的变化引起 a 的不适,那么我们就在 a 与 b 之间插入一层 interface。 @@ -36,14 +36,77 @@ TODOTODO,这里是控制流和依赖流的示意图。 ![插件化架构](../images/ch6-08-plugin-arch.jpg) -dao 成为了 logic 的 plugin(插件)。如果我们要把 dao 里的 kv 数据库从 rocksdb(假如) 替换为自研的 thrift 协议 kv 存储,那么新的 dao 实现也只要遵从之前定义好的 interface 就可以,logic 不需要任何变化。这就是所谓的插件化架构。 +dao(DB) 成为了 logic(business rules) 的 plugin(插件)。如果我们要把 dao 里的 kv 数据库从 rocksdb(假如) 替换为自研的 thrift 协议 kv 存储,那么新的 dao 实现也只要遵从之前定义好的 interface 就可以,logic 不需要任何变化。这就是所谓的插件化架构。 + +## 实例 稍微具体一点: ```go -type RecordSaver interface { - func Save(r MyRecord) error +// src/dto +type Order struct { + OrderID int64 + UserID int64 + ProductID int64 + CreateTime time.Time } + +// src/logic +type OrderRep interface { + func Save(r dto.Order) error + func Get(orderID int64) (dto.Order, error) +} + +// one of many dependencies +var orderService OrderRep + +// init order dependency +func InitOrderService(os OrderRep) error { + orderService = os +} + +// api provided by logic +func CreateOrder(orderID int64, userID int64, productID int64, createTime time.Time) error { + o := Order{ + OrderID : orderID, + UserID : userID, + ProductID : productID, + CreateTime : createTime, + } + + err := orderService.Save(o) + + if err != nil { + // do someth + return err + } + return nil +} + +// src/dao/order +type OrderService struct {} + +func (od *OrderService) Save(r dto.Order) error { + // save this order + return nil +} + +func (od *OrderService) Get(orderID int64) (dto.Order, error) { + // save this order + return nil +} + +// src/main +func initAllDependencies() { + // order service init + orderService := orderdao.OrderService{} + orderlogic.InitOrderService(orderService) +} + ``` -## \ No newline at end of file +上面是一个简单将 order 的 dao 层和 logic 进行控制反转的例子。有了这种手段,我们可以将控制流中,logic 所有下层逻辑全部变成 logic 的插件。 + +用这种控制反转手段把我们的核心业务逻辑隔离出来的手段实际上也存在着一些争议,Uncle Bob 认为这是最佳实践,哪怕我们的项目模块没有那么复杂,也应该允许把这些内部的 interface 先设计好,并保留在项目中,以方便日后的扩展。但 interface 的引入多多少少会给项目带来一定程度的复杂性,而且有一部分 IDE 对 interface 的支持并不是非常好(比如非常流行的 vscode,在查找 interface 的 implementation 时就极其缓慢)。 + +总的来说,interface 依然是一种非常理想的手段,就算短时间内用不上。在未来某一天你被反复变动的下游逻辑恶心到的时候,就会对这种控制反转手段理解更为深刻了吧。