本文转载自微信公众号《董泽润的技术笔记》,作者董泽润。转载本文请联系董泽润技术笔记公众号。怪不得码农自嘲是个CRUD小子,果然每天都在不停地堆屎,缝补别人的屎山。下面的案例没有任何责怪任何人的意思,我也是一坨屎^^如有雷同,请勿接案。最近看了一个商业代码。状态机接口定义有40个函数。检查提交日志。一开始只有10个。每当添加新的业务需求时,//OrderManagerhandlesoperationonorderentitytypeOrderManagerinterface{LoadOrdersByIDs(ctxcontext.Context,orderIDs[]string)([]*dbentity.Order,error)......TransitOrdersToState(ctxcontext.Context,orderIDs[]string,toStateorderstate.OrderState)([]*dbentity.Order,error)......Stop()error}业务中很多接口都是通过mockUT来实现依赖倒置而不是业务多态。OrderManager就属于这一类,所以接口的扩展对项目的质量影响不大,只是看起来没有凝聚力……为什么接口要小接口越大,抽象越弱。Go谚语[1]RobPike提到:接口越大,抽象能力越弱,比如系统库中的io.Reader、io.Writer等,接口定义只有一两个函数。为什么界面很小?例如typeFooBeeperinterface{Bar(sstring)(string,error)Beep(sstring)(string,error)}typethingstruct{}func(l*thing)Bar(sstring)(string,error){...}func(l*thing)Beep(sstring)(string,error){...}typedifferentThingstruct{}func(l*differentThing)Bar(sstring)(string,error){...}typeanotherThingstruct{}func(l*anotherThing)Beep(sstring)(string,error){...}FooBeeper接口定义了两个函数:Bar、Beep。由于接口实现是隐式的,我们有以下结论:thing实现了FooBeeper接口,differentThing没有实现,缺少Bar函数。anotherThing也没有实现,并且缺少Beep功能。但是如果我们把FooBeeper打散,它就是多个接口的组合做的更小,让differentThinganotherThing可以重用接口组合改造。关于如何改造OrderManger,可以借鉴etcdclientv3[2]定义的思路,将相关功能聚合成小接口,通过接口的组合实现typeClientstruct{ClusterKVLeaseWatcherAuthMaintenanceconn*grpc.ClientConncfgConfig……}。以上就是clientV3结构体的定义。虽然不是接口,但是思路可以参考//OrderManagerhandlesoperationonorderentitytypeOrderManagerinterface{OrderOperatorTransitOrdersStop()error}实际上可能只需要提取三个接口。OrderOperator负责订单的CRUD操作,TransitOrders负责转场机流。原来的40个功能都放到小接口里做冗余改造,只抽象出来做小接口是不行的。LoadOrderByXXXX里面有一堆定义可以根据不同的条件获取订单,但实际上这些都是可转换的funcLoadOrders(ctxcontext.Context,FiltersParamsoptions...)这种情况下可以传入options,或者使用结构体作为参数container,比如一个状态机的流程包括TransitOrdersToState,TransitOrdersToStateByEntity,TransitOrdersStateByEntityForRegularDelivery,这些都是多余的定义。还有一个根本没有用到,或者说不应该暴露在这一层的冗余接口。拆分本质上是一个ISP接口遵循隔离原则[3],不应强制任何代码依赖于它不使用的方法。IT界有个笑话,当你的MR只有几行的时候,同行就会有几十条评论。但是当你的MR有几百行的时候,他们只会回复LGTMPeerreview。你还是要负责,如果成本不高,建议重构旧代码。重构代码有几个原则。可以参考重构2CIlint的最佳实践。不知道是否支持查看接口线数,但如果线数成为一个指标,可能就是本末倒置了。参考[1]Go谚语:https://go-proverbs.github.io/,[2]etcdclientv3:https://github.com/etcd-io/etcd/blob/main/client/v3/client.go#L44,[3]接口隔离原则:https://en.wikipedia.org/wiki/Interface_segregation_principle,
