教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang抽取接口,依赖注入(依赖倒置)解决包引用关系

golang抽取接口,依赖注入(依赖倒置)解决包引用关系

发布时间:2022-01-05   编辑:jiaochengji.com
教程集为您提供golang抽取接口,依赖注入(依赖倒置)解决包引用关系等资源,欢迎您收藏本站,我们将为您提供最新的golang抽取接口,依赖注入(依赖倒置)解决包引用关系资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg>

本文首发于我的个人博客
本文记录了作者在golang开发中,通过抽取接口,依赖注入的方式,解决包与包之间的不合理引用关系。

总结来说:

<blockquote>

面向接口编程,并且golang中接口函数的参数最好是标准库的类型

</blockquote> <h2>场景</h2>

目前项目中有一个业务逻辑包<code>business_logic</code>,两个工具库包<code>pkg1</code>和<code>pkg2</code>,其中

<ul><li><code>pkg1</code>是旧库,API不宜改动,<code>pkg2</code>是新库,尚未正式使用</li><li><code>business_logic</code>会使用<code>pkg1</code>和<code>pkg2</code></li><li><code>pkg1</code>内部要添加使用<code>pkg2</code>的逻辑</li></ul><pre><code class="lang-go hljs"><span class="token comment">// pkg1/main.go</span> <span class="token keyword">package</span> pkg1 <span class="token keyword">import</span> <span class="token string">"pkg2"</span> <span class="token keyword">func</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pkg2<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span>pkg2<span class="token punctuation">.</span>S<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <pre><code class="lang-go hljs"><span class="token comment">// pkg2/main.go</span> <span class="token keyword">package</span> pkg2 <span class="token keyword">type</span> S <span class="token keyword">struct</span> <span class="token punctuation">{</span> param1 <span class="token builtin">int</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s S<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> </code></pre> <pre><code class="lang-go hljs"><span class="token comment">// business_logic/main.go</span> <span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"pkg1"</span> <span class="token string">"pkg2"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pkg1<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span> pkg2<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span>pkg2<span class="token punctuation">.</span>S<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

这样就引起了一个问题:

<blockquote>

<code>business_logic</code>其实引用了两次<code>pkg2</code>,一次是直接引用,一次是通过<code>pkg1</code>间接引用,将来在版本更迭中,很有可能会出现直接引用的版本和间接引用的版本不一致的情况,从而引起未知bug

</blockquote> <h2>
解决尝试</h2>

如果不希望两次引用,那么最好的方式是消除<code>pkg1</code>对<code>pkg2</code>的引用,消除引用的方式是

<ul><li><code>pkg1</code>抽象出一个接口,</li><li>让<code>pkg2</code>提供结构体,实现<code>pkg1</code>抽象出的接口</li></ul>

这样,<code>pkg2</code>实际上就变成了<code>pkg1</code>的一个插件,只要在<code>business_logic</code>初始化的时候,将<code>pkg2</code>的插件注入到<code>pkg1</code>里去就行

但是这样的尝试失败了,我们先来看一下代码

<pre><code class="lang-go hljs"><span class="token comment">// pkg1/main.go</span> <span class="token keyword">package</span> pkg1 <span class="token keyword">import</span> <span class="token string">"pkg2"</span> <span class="token keyword">type</span> Plugin <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s pkg2<span class="token punctuation">.</span>S<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> plugin Plugin <span class="token keyword">func</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> plugin <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> plugin<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span>pkg2<span class="token punctuation">.</span>S<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">SetPlugin</span><span class="token punctuation">(</span>p Plugin<span class="token punctuation">)</span> <span class="token punctuation">{</span> plugin <span class="token operator">=</span> p <span class="token punctuation">}</span> </code></pre> <pre><code class="lang-go hljs"><span class="token comment">// pkg2/main.go</span> <span class="token keyword">package</span> pkg2 <span class="token keyword">type</span> S <span class="token keyword">struct</span> <span class="token punctuation">{</span> param1 <span class="token builtin">int</span> <span class="token punctuation">}</span> <span class="token keyword">type</span> Plugin <span class="token keyword">struct</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>Plugin<span class="token punctuation">)</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s S<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s S<span class="token punctuation">)</span> <span class="token punctuation">{</span> p <span class="token operator">:=</span> Plugin<span class="token punctuation">{</span><span class="token punctuation">}</span> p<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <pre><code class="lang-go hljs"><span class="token comment">// business_logic/main.go</span> <span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"pkg1"</span> <span class="token string">"pkg2"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pkg1<span class="token punctuation">.</span><span class="token function">SetPlugin</span><span class="token punctuation">(</span><span class="token operator">&</span>pkg2<span class="token punctuation">.</span>Plugin<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> pkg1<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span> pkg2<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span>pkg2<span class="token punctuation">.</span>S<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

我们发现,<code>pkg1</code>对<code>pkg2</code>的引用仍旧存在,其原因在于抽取出来的接口函数中的参数是属于<code>pkg2</code>的

<pre><code class="lang-go hljs"><span class="token keyword">type</span> Plugin <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s pkg2<span class="token punctuation">.</span>S<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h2>
最终解决方案</h2>

由于<code>pkg2</code>是新库,所以我们决定更改它的接口,最终的代码如下

<pre><code class="lang-go hljs"><span class="token comment">// pkg1/main.go</span> <span class="token keyword">package</span> pkg1 <span class="token keyword">type</span> Plugin <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>param <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> plugin Plugin <span class="token keyword">func</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> plugin <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> plugin<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">SetPlugin</span><span class="token punctuation">(</span>p Plugin<span class="token punctuation">)</span> <span class="token punctuation">{</span> plugin <span class="token operator">=</span> p <span class="token punctuation">}</span> </code></pre> <pre><code class="lang-go hljs"><span class="token comment">// pkg2/main.go</span> <span class="token keyword">package</span> pkg2 <span class="token keyword">type</span> Plugin <span class="token keyword">struct</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>Plugin<span class="token punctuation">)</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> p <span class="token operator">:=</span> Plugin<span class="token punctuation">{</span><span class="token punctuation">}</span> p<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <pre><code class="lang-go hljs"><span class="token comment">// business_logic/main.go</span> <span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"pkg1"</span> <span class="token string">"pkg2"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pkg1<span class="token punctuation">.</span><span class="token function">SetPlugin</span><span class="token punctuation">(</span><span class="token operator">&</span>pkg2<span class="token punctuation">.</span>Plugin<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> pkg1<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span> pkg2<span class="token punctuation">.</span><span class="token function">ExternalAPI</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

可以看到,这回彻底解决了<code>pkg1</code>引用<code>pkg2</code>的问题,代价就是将<code>pkg2.S</code>这个结构体参数展开了

视具体业务情况而定,我们可以通过:

<ol><li>展开结构体</li><li>将结构体换做<code>map[string]interface{}</code>(当然需要手动做字段的提取和塞入)</li><li>将结构体换做<code>string</code>,用JSON传参(手动Marshal和Unmarshal)</li><li>将参数类型放到新的第三方库<code>pkg3</code>中(这样就又要维护引用的<code>pkg3</code>版本一致)</li></ol>

软件开发中没有silver-bullet,只有trade-off,这次的方案,也还算满意

到此这篇关于“golang抽取接口,依赖注入(依赖倒置)解决包引用关系”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
Go 语言包管理机制深入分析
Golang笔记:包管理机制
什么是 go vendor
go godep包管理工具详解
Golang包管理工具govendor的使用
工作好多年有可能还未真正了解接口和抽象类
golang编译之vendor机制
php为什么要用依赖注入?
搞定Go单元测试(一)——基础原理
Golang从入门到进阶系列:Go Modules机制

[关闭]
~ ~