How to Avoid Producing Legacy Code at the Speed of Typing
英語不好翻譯很爛。英語好的去看原文。
About the Author
I am a software architect/developer/programmer.
I have a rather pragmatic approach towards programming, but I have realized that it takes a lot of discipline to be agile. I try to practice good craftsmanship and making it work.
lars.michael.dk, 2 Mar 2015 CPOL
?
原文地址:http://www.codeproject.com/Articles/882165/How-to-Avoid-Producing-Legacy-Code-at-the-Speed-of
This article provides a recipe on how to avoid producing legacy code at the speed of typing by using a proper architecture and unit testing.
Introduction
作為一個企業軟件開發者,你經常和產出遺留代碼(legacy code)做斗爭-那種不值得維護或支持的代碼。你在不斷努力避免重寫重復的東西抱著微弱的希望以期下次你能剛好做的正確。【原文:You are constantly struggling to avoid re-writing stuff repeatedly in a faint hope that next time you will get it just right.】
遺留代碼(legacy code)的特征是,其中,不好的設計和建造模式或者依賴于過時的框架或第三方組件。一下是你可能認識的幾個典型例子:
你或者你的團隊產出了一個漂亮的,功能豐富的Windows應用。之后,你意識到真正的需求是一個瀏覽器(web應用)或者移動應用。你意識到為你的應用替換UI將需要付出更加大量的努力,因為你嵌入了太多領域功能(domain functionality)在它的UI里面。
另一個情景可能是你編寫了一個后端,它是深度滲透在某一個特定的ORM-例如Nhibernate或者Entity Framework-或者高度依賴與某一個RDBMS。在這一個點上,你想要改變策略來讓后端避免使用ORM和使用文件存儲的持久化數據庫,但是很快你就意識到這幾乎是不可能完成的,因為你domain functionality 和data layer 是緊緊耦合。
在上述兩種情況下,你以打字的速度來生產遺留代碼(legacy code)。
?
然而,那還是有希望的。通過采用一些簡單技巧和原則,你可以永遠改變這一已經注定的局面。
?
The Architectural Evolution
下面,我將描述三個階段標準商業軟件開發的三個典型模式。幾乎所有開發者都處于第二階段,但關鍵是要進入第三階段,你將最終成為一個建筑模式的忍者。
?
Phase 1 - Doing it Wrong
大多數開發者聽過分層設計模式,所以很多第一次嘗試設計模式就像下面一樣-把前后端進行功能責任分離的兩層結構:
?
到目前為止還好,但是很快你就意識到那有一個極大的問題,也就是引用程序的業務邏輯和前端以及后端糾纏在一起,并且依賴于它們。
?
Phase 2 – A Step Forward
因此,下一個嘗試是引入一個中間層-一個domain layer-由你應用程序的真正的業務邏輯組成:
?
這種模式看起來具有迷惑性的良好結構和解耦性。然而,事實并非如此。問題是紅色的依賴箭頭表明domain layer對后端具有天生的依賴-典型的,因為你在domain layer使用new(c#或者java)來創建后端類(backend classes)的實例。domain layer 和后端是緊緊耦合的。這有許多缺點:
- domain layer 功能不能再其他的上下文環境中單獨重用。你需要把他的依賴項(the backend)一并引入。
- domain layer 無法單獨的進行單元測試。你需要關聯它的依賴項,后端代碼
-
一個后端的實現(例如一個使用RDMBS數據庫)無法簡單的被另一個后端(使用文件數據庫)實現替換
All of these disadvantages dramatically reduces the potential lifetime of the domain layer. That is why you are producing legacy code at the speed of typing.
所有這些缺點都在減少domain layer的聲明周期。這是為什么你在以打字的速度產生遺留代碼的原因
Phase 3 – Doing it Right
你要做的其實很簡單。你只需調轉代表依賴關系的紅色箭頭。這是一個微小的調整,但是結果大不同:
?
這一設計模式堅持依賴倒置原則【Dependency Inversion Principle 】(DIP)-面向對象設計最重要的原則之一。重點是,一旦這一模式被確立-依賴關系立刻調轉-領域層的潛在生命周期得到大幅度增加。UI需求或者轉變從Windows窗口到瀏覽器或者移動設備,或者你的持久化存儲可能從關系型數據庫(RDBMS)轉換到文件型存儲,但是現在所有改變都可以很容易在不修改領域層的情況下實現。因為這樣的實現前端和后端很好的與領域層解耦。因此,領域層編程一個代碼庫理論上你幾乎永遠不用去替代-至少持續到你的業務改變或者整體框架發生改變的時候。現在,你可以有效地和你的遺留代碼戰斗了
另一方面來說,讓我給你一個簡單的示例來演示如何在實踐中提升DIP:
也許你有一個product service在領域層,它可以對定義在后端的products repository執行CRUD操作。這樣經常導致像下圖一樣的錯誤指向的依賴關系:
?
這樣是因為你不得不在product service的某處使用”new“,這就產生了對product repository的依賴:
var repository = new ProductRepository();?應用DIP原則來倒轉這樣依賴關系,你必須在領域層以接口的方式引入一個product repository的抽象并且讓product repository 實現這個接口(implementation of this interface):?
?
現在,作為使用New產生product repository 實例的替代方案,你可以注入repository 到service 通過一個構造參數(constructor argument):
private readonly IProductRepository _repository; public ProductService(IProductRepository repository){ _repository = repository;}
?
這是依賴注入的知識(Dependency injection DI)。我以前已經在一篇博客中做過詳細介紹見:Think Business First.
?
一旦你正確的應用了全部設計模式,對抗遺留代碼的目標顯而易見:把盡量多個功能引入domain layer(領域層),讓前端和后端不斷收縮同時讓domain layer(領域層)不斷豐滿:
這一設計模式產出的一個實用的副產品,它使它自己很容易對domain functionality(領域功能)進行單元測試。因為domain layer 的耦合特性以及面對所有的依賴都是表現為抽象的(如一個接口或者一個抽象基類)。這樣很容易為他們的抽象偽造出一個對象來實現單元測試。所以它是”在公園散步“來守衛整個domain layer和單元測試(unit tests)【注:原文 So it is “a walk in the park” to guard the entire domain layer with unit tests.? 】.你要做的無外乎就是努力提供超過100%覆蓋率的單元測試來保證你的domain layer足夠健壯并且堅如磐石。這有增加了你domain layer的生命周期。
你可能已經了解到這不僅僅是傳統的前端和后端,但是所有其他的組件-包括單元測試或者一個http-based 的Web API-會擔當一個domain layer的消費者角色。因為,這樣的設計模式描述起來像一個onion layers:
?
最外層的組件消費領域庫代碼(domain library code)-通過提供領域抽象(接口或者基類)具體實現或者作為領域方法(domain functionality)的直接用戶(domain model 和services)。
無論如何,要記住:耦合的方向總是指向中心的-指向domain layer。
在這一點上,它看起來好像太理論化,and,well…,有點抽象。不過,它原則上不需要做很多。在另一篇文章中(CodeProject article of mine ),我描述和提供了一些遵從所有原則的簡單的代碼。那個示例的代碼非常簡單,但是非常接近于正式的產品代碼。
?
Summary
作為一個商業軟件開發者避免產生遺留代碼(legacy code)是一場持久的戰斗。想獲勝的話,執行下列操作:
- 確保所有的依賴箭頭通過應用依賴倒置原則(DIP)和依賴注入(DI)而指向中央和獨立的domain layer
- 不斷地健壯domain layer,通過盡可能多的把functionality移動到domain layer,使domain layer 變得豐滿而是外層(onion layer 中的outer layer)逐漸萎縮。
-
使用單元測試(unit tests)覆蓋領域層(domain layer)的每個的單個功能。
?
遵循這些簡單原則也許最終將匯合到一起。你的code也許將比以前擁有一個超乎想象的長生命周期,因為:
- 領域層的功能(domain layer functionality)可以在許多不同的上下文環境中復用。
-
100%覆蓋率的單元測試(unit test)可以使domain layer 非常健壯和堅如磐石。
-
領域層的抽象(例如持久化機制)實現可以輕松的替換成其他的實現方式
-
領域層是容易維護的。
?
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
轉載于:https://www.cnblogs.com/buyixiaohan/p/4627803.html
總結
以上是生活随笔為你收集整理的How to Avoid Producing Legacy Code at the Speed of Typing的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用软件测试路由器性能报告,小米路由器网
- 下一篇: Tomcat 打开一闪而过