diff options
| author | muqiuhan <[email protected]> | 2025-09-09 06:17:00 +0000 |
|---|---|---|
| committer | muqiuhan <[email protected]> | 2025-09-09 06:17:00 +0000 |
| commit | d5de65fdb1802cdf498d65d93397f290813c377c (patch) | |
| tree | 92fe61a76d20d1203203665d4b48e7f151f088bd /2025/03/31 | |
| parent | 48efa2dfde7c263f84ee5bb0872747034908d607 (diff) | |
| download | blog-d5de65fdb1802cdf498d65d93397f290813c377c.tar.gz | |
deploy: 9665097f0fa0dae9f92123fac54d26c0818758a5
Diffstat (limited to '2025/03/31')
| -rw-r--r-- | 2025/03/31/在F-中处理复杂依赖注入的实践指南/index.html | 45 |
1 files changed, 22 insertions, 23 deletions
diff --git a/2025/03/31/在F-中处理复杂依赖注入的实践指南/index.html b/2025/03/31/在F-中处理复杂依赖注入的实践指南/index.html index 21e0a840..e778cdd4 100644 --- a/2025/03/31/在F-中处理复杂依赖注入的实践指南/index.html +++ b/2025/03/31/在F-中处理复杂依赖注入的实践指南/index.html @@ -192,51 +192,50 @@ </div> </div> <div class="post-content"> - <h2 id="一、传统手艺:Partial-Application"><a href="#一、传统手艺:Partial-Application" class="headerlink" title="一、传统手艺:Partial Application"></a>一、传统手艺:Partial Application</h2><p>在函数式编程中,Partial Application 是传递依赖的常用方式。例如:</p> + <h2 id="一、传统手艺:Partial-Application"><a class="header-anchor" href="#一、传统手艺:Partial-Application">¶</a>一、传统手艺:Partial Application</h2> +<p>在函数式编程中,Partial Application 是传递依赖的常用方式。例如:</p> <figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> foo bar baz request <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> wired <span class="operator">=</span> foo dependency1 dependency2</span><br><span class="line"><span class="keyword">let</span> response <span class="operator">=</span> wired request</span><br></pre></td></tr></table></figure> - -<p>优点: </p> +<p>优点:</p> <ul> -<li>无需框架或反射,直接通过函数参数传递依赖。 </li> +<li>无需框架或反射,直接通过函数参数传递依赖。</li> <li>符合函数式编程的纯函数理念。</li> </ul> -<p>缺点: </p> +<p>缺点:</p> <ul> -<li>参数爆炸:当功能扩展时,参数数量激增(如日志、数据库、加密等)。 </li> -<li>维护困难:新增依赖需修改所有调用点的参数传递。 </li> +<li>参数爆炸:当功能扩展时,参数数量激增(如日志、数据库、加密等)。</li> +<li>维护困难:新增依赖需修改所有调用点的参数传递。</li> <li>隐式依赖:难以从函数签名直接区分核心参数与辅助依赖。</li> </ul> <hr> -<h2 id="二、结构化方法:单一环境参数(env)"><a href="#二、结构化方法:单一环境参数(env)" class="headerlink" title="二、结构化方法:单一环境参数(env)"></a>二、结构化方法:单一环境参数(<code>env</code>)</h2><p>为解决参数爆炸问题,可将依赖封装为单一环境对象<code>env</code>,并通过接口约束访问权限:</p> +<h2 id="二、结构化方法:单一环境参数(env)"><a class="header-anchor" href="#二、结构化方法:单一环境参数(env)">¶</a>二、结构化方法:单一环境参数(<code>env</code>)</h2> +<p>为解决参数爆炸问题,可将依赖封装为单一环境对象<code>env</code>,并通过接口约束访问权限:</p> <figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">[<Interface>]</span> <span class="keyword">type</span> <span class="title class_">ILog</span> <span class="operator">=</span> <span class="keyword">abstract</span> Logger<span class="operator">:</span> ILogger</span><br><span class="line"><span class="meta">[<Interface>]</span> <span class="keyword">type</span> <span class="title class_">IDb</span> <span class="operator">=</span> <span class="keyword">abstract</span> Database<span class="operator">:</span> IDatabase</span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> Log <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> info (env<span class="operator">:</span> #ILog) <span class="operator">=</span> env.Logger.Info(<span class="string">"Message"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> Db <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> fetchUser (env<span class="operator">:</span> #IDb) <span class="operator">=</span> env.Database.Query(<span class="operator">...</span>)</span><br></pre></td></tr></table></figure> - -<p>优点: </p> +<p>优点:</p> <ul> -<li>显式依赖声明:函数签名仅需<code>env</code>参数,编译器验证接口实现。 </li> -<li>模块化隔离:各模块仅声明所需接口(如<code>ILog</code>、<code>IDb</code>),避免全局依赖。 </li> +<li>显式依赖声明:函数签名仅需<code>env</code>参数,编译器验证接口实现。</li> +<li>模块化隔离:各模块仅声明所需接口(如<code>ILog</code>、<code>IDb</code>),避免全局依赖。</li> <li>易于测试:通过模拟<code>env</code>实现单元测试,无需依赖具体实现。</li> </ul> -<p>应用场景: </p> +<p>应用场景:</p> <figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> changePass env req <span class="operator">=</span> <span class="keyword">task</span> {</span><br><span class="line"> <span class="keyword">let!</span> user <span class="operator">=</span> Db.fetchUser env req.UserId</span><br><span class="line"> Log.info env <span class="string">"Processing user: %i"</span> user.Id</span><br><span class="line"> <span class="operator">...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure> - <hr> -<h2 id="三、Reader-Monad"><a href="#三、Reader-Monad" class="headerlink" title="三、Reader Monad"></a>三、Reader Monad</h2><p>为消除显式的<code>env</code>传递,可引入 Reader Monad,将环境隐式注入计算流程:</p> +<h2 id="三、Reader-Monad"><a class="header-anchor" href="#三、Reader-Monad">¶</a>三、Reader Monad</h2> +<p>为消除显式的<code>env</code>传递,可引入 Reader Monad,将环境隐式注入计算流程:</p> <figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">[<Struct>]</span> <span class="keyword">type</span> <span class="title class_">Effect</span><span class="operator"><</span><span class="symbol">'env</span>, <span class="symbol">'out</span><span class="operator">></span> <span class="operator">=</span> Effect <span class="keyword">of</span> (<span class="symbol">'env</span> <span class="operator">-></span> <span class="symbol">'out</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> Effect <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> run env (Effect fn) <span class="operator">=</span> fn env</span><br><span class="line"> <span class="keyword">let</span> bind f effect <span class="operator">=</span> Effect (<span class="keyword">fun</span> env <span class="operator">-></span> run env (f (run env effect)))</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">EffectBuilder</span>() <span class="operator">=</span></span><br><span class="line"> <span class="keyword">member</span> __.Bind(e, f) <span class="operator">=</span> Effect.bind f e</span><br><span class="line"> <span class="keyword">member</span> __.Return(x) <span class="operator">=</span> Effect (<span class="keyword">fun</span> _ <span class="operator">-></span> x)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> effect <span class="operator">=</span> EffectBuilder()</span><br></pre></td></tr></table></figure> - <p>然后:</p> <figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> changePass req <span class="operator">=</span> <span class="keyword">effect</span> {</span><br><span class="line"> <span class="keyword">let!</span> user <span class="operator">=</span> Db.fetchUser req.UserId</span><br><span class="line"> <span class="keyword">let!</span> salt <span class="operator">=</span> Random.bytes <span class="number">32</span></span><br><span class="line"> <span class="keyword">do!</span> Log.info <span class="string">"Password updated for user %i"</span> user.Id</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">Ok</span>()</span><br><span class="line">}</span><br></pre></td></tr></table></figure> - -<p>优点: </p> +<p>优点:</p> <ul> -<li>隐式依赖管理:通过<code>effect</code>计算表达式自动传递<code>env</code>,减少样板代码。 </li> -<li>组合性:支持与其他计算表达式(如<code>async</code>/<code>task</code>)结合,处理异步操作。</li> +<li>隐式依赖管理:通过<code>effect</code>计算表达式自动传递<code>env</code>,减少样板代码。</li> +<li>组合性:支持与其他计算表达式(如<code>async</code>/<code>task</code>)结合,处理异步操作。</li> </ul> -<p>缺点: </p> +<p>缺点:</p> <ul> -<li>性能开销:频繁的闭包创建和间接调用可能导致性能下降。 </li> +<li>性能开销:频繁的闭包创建和间接调用可能导致性能下降。</li> <li>生态兼容性:需自定义计算表达式,与现有异步框架集成复杂。</li> </ul> -<h2 id="Refs"><a href="#Refs" class="headerlink" title="Refs."></a>Refs.</h2><ul> +<h2 id="Refs"><a class="header-anchor" href="#Refs">¶</a>Refs.</h2> +<ul> <li>Spring的构造器注入</li> <li>Blazor的DI实现</li> </ul> |
