Before you learn about dependency injection and Unity, you need to understand why you should use them. And in order to understand why you should use them, you should understand what types of problems dependency injection and Unity are designed to help you address. This introductory chapter will not say much about Unity, or indeed say much about dependency injection, but it will provide some necessary background information that will help you to appreciate the benefits of dependency injection as a technique and why Unity does things the way it does.ios
在你學習關於依賴注入和Untity以前,你必須明白爲何你應該使用它。和爲了明白你爲何應該使用它,你應該明白依賴注入和Unity能夠幫你解決些什麼樣的問題。這個引導章節講不會講太多關於Unity,或者說是不會講太多關於依賴注入,可是引導章節會提供一些必要的背景知識,這將有助於你欣賞依賴注入的技術,和爲何Unity的作事方法。web
The next chapter, Chapter 2, 「Dependency Injection,」 will show you how dependency injection can help you meet the requirements outlined in this chapter, and the following chapter, Chapter 3, 「Dependency Injection with Unity,」 shows how Unity helps you to implement the dependency injection approach in your applications.數據庫
下個章節,章節2「依賴注入」將展現你怎樣依賴注入能夠幫助你知足本章概述的要求,和下面章節 章節3「依賴注入和Unity」,展現Unity如何幫你實現依賴注入方法在你的程序中。編程
Motivations
When you design and develop software systems, there are many requirements to take into account. Some will be specific to the system in question and some will be more general in purpose. You can categorize some requirements as functional requirements, and some as non-functional requirements (or quality attributes). The full set of requirements will vary for every different system. The set of requirements outlined below are common requirements, especially for line-of-business (LOB) software systems with relatively long anticipated lifetimes. They are not all necessarily going to be important for every system you develop, but you can be sure that some of them will be on the list of requirements for many of the projects you work on.swift
動機安全
當你設計和開發軟件系統,考慮到有許多需求。一些特殊的系統問題和更多的普通的目標。你能夠分類一些功能要求,和一些非功能要求(或質量屬性)。這全套的要求將違反每個不一樣的系統。一組要求描述下是普通需求,特別是業務(LOB)軟件系統相對較長的預期壽命。他他們不都必定會是重要的對於每個系統開發,可是你能夠肯定的是,他們一些將會在你的項目工做上的需求列表中。架構
This chapter introduces a lot of requirements and principles. Don’t assume that they are all relevant all of the time. However, most enterprise systems have some of the requirements, and the principles all point towards good design and coding practicesapp
這個章節初次接觸一些需求和原則。不要假設全部時候他們都是徹底關聯的。不管如何,大部分企業制度有一些這樣的需求和那樣的原則都指向好的設計和代碼規範。less
Maintainability
As systems become larger, and as the expected lifetimes of systems get longer, maintaining those systems becomes more and more of a challenge. Very often, the original team members who developed the system are no longer available, or no longer remember the details of the system. Documentation may be out of date or even lost. At the same time, the business may be demanding swift action to meet some pressing new business need. Maintainability is the quality of a software system that determines how easily and how efficiently you can update it. You may need to update a system if a defect is discovered that must be fixed (in other words, performing corrective maintenance), if some change in the operating environment requires you to make a change in the system, or if you need to add new features to the system to meet a business requirement (perfective maintenance). Maintainable systems enhance the agility of the organization and reduce costs.Therefore, you should include maintainability as one of your design goals, along with others such as reliability, security, and scalability.ide
可維護性
隨着系統變大,和系統的語氣壽命邊長,維護那些系統就變成了愈來愈多的挑戰。甚至時常,原團隊成員誰自主開發的系統再也不可用,或者再也不記得系統的一些細節。文檔也許過時,或者甚至丟失。在同一時間,業務可能要求快速心動去知足一些緊急的新業務需求。可維維護性是西戎軟件質量,那決定你能夠如何簡單的和如何有效的更新他。你也許須要去更新系統,若是缺陷發現必須修理(換句話說,執行維修保養),若是某些操做環境的變化須要你再系統中作出更改,或者若是您須要再系統中添加新功能,以知足業務需求(完成的維護)。可維護的系統加強組織的靈活性和下降成本。你應該包括可維護性做爲你一個設計目標,以及其餘諸如可靠性、安全性和可伸縮性。
It is very hard to make existing systems more maintainable. It is much better to design for maintainability from the very start.
Unity很難使現有系統更易於維護。Unity是從一開始爲可維護性大量更好的設計。
Testability
可測試性
A testable system is one that enables you to effectively test individual parts of the system. Designing and writing effective tests can be just as challenging as designing and writing testable application code, especially as systems become larger and more complex.
一個可測試系統是一個可使你有效測試系統中的各個部分。設計和編寫有效的測試能夠正確的設計和編寫測試應用代碼,特別是系統變大和更加複雜。
Methodologies such as test-driven development (TDD) require you to write a unit test before writing any code to implement a new feature and the goal of such a design technique is to improve the quality of your application.
方法測試驅動的開發(TDD)要求您編寫一個單元測試前編寫任何代碼來實現新功能,這樣的設計技術的目標是提升應用程序的質量。
Such design techniques also help to extend the coverage of your unit tests, reduce the likelihood of regressions, and make refactoring easier.
這樣的設計技巧也能夠幫助你擴展你的單元測試覆蓋率,減小回歸的可能性,和使重構更簡單。
However, as part of your testing processes you should also incorporate other types of tests such as acceptance tests, integration tests, performance tests, and stress tests.
然而,如同部分你的測試過程你應該也包含其餘類型的測試好比驗收測試、綜合測試、性能測試、和壓力測試。
Running tests can also cost money and be time consuming because of the requirement to test in a realistic environment.
運行測試也會消耗財富和時間,由於須要在顯示環節中測試。
For example, for some types of testing on a cloud-based application you need to deploy the application to the cloud environment and run the tests in the cloud.
好比,一些類型是測試雲端應用,你必須配置應用到雲端環境,而且在雲端運行測試。
If you use TDD, it may be impractical to run all the tests in the cloud all of the time because of the time it takes to deploy your application, even to a local emulator.
若是你有用測試驅動開發,它可能不能再任什麼時候間在雲端運行全部的測試,由於在哪一個時候它在準備配置你的應用,甚至是本地本地模擬器。
In this type of scenario, you may decide to use test doubles (simple stubs or verifiable mocks) that replace the real components in the cloud environment with test implementations in order to enable you to run your suite of unit tests in isolation during the standard TDD development cycle.
在這個方案類型裏面,你可能使用雙測試(簡單的存根或可覈查的模擬)取代真正的組件和測試的實如今雲環境中爲了使您可以運行單元測試套件在隔離標準的TDD開發週期。
Testability should be another of the design goals for your system along with maintainability and agility: a testable system is typically more maintainable, and vice versa.
可測試性應該是另外一個系統的設計目標以及可維護性和敏捷性:一個可測試的系統一般更易於維護,反之亦然。
Using test doubles is a great way to ensure that you can continuously run your unit tests during the development process. However, you must still fully test your application in a real environment.
使用雙測試是很好的方式去保證你能夠在開發過程當中不斷的運行你的單元測試。不管如何你必須仍然充分的在現實環境中測試你的應用。
Flexibility and Extensibility
靈活性和可擴展性
Flexibility and extensibility are also often on the list of desirable attributes of enterprise applications.
靈活性和可擴展性也經常是在企業應用理想屬性列表中。
Given that business requirements often change, both during the development of an application and after it is running in production, you should try to design the application to make it flexible so that it can be adapted to work in different ways and extensible so that you can add new features.
考慮到業務需求時常改變,雙方在開發應用的時候和在運行產品以前,你應該嘗試去設計應用,把他作得靈活,因此他能夠適應工做在不一樣的途經和可擴展,因此你能夠添加新的特性。
For example, you may need to convert your application from running on-premises to running in the cloud.
好比,你可能須要把你的應用從本地運行變爲雲端運行。
For a great discussion on the use of test doubles, see the point/counterpoint debate by Steve Freeman, Nat Pryce and Joshua Kerievsky in IEEE Software (Volume: 24, Issue: 3), May/June 2007, pp.80-83.
使用雙測試上的偉大探討,親看Steve Freeman的point/counterpoint debate(點/對位辯論),Nat Pryce和Joshua Kerievsky IEEE軟件(問題:數量:24日3),2007年5月/ 6月pp.80 - 83。
Late Binding
延時綁定
In some application scenarios, you may have a requirement to support late binding.
在一些應用清潔中,你能夠有延時綁定的需求。
Late binding is useful if you require the ability to replace part of your system without recompiling.
延時綁定是有用的,若是你須要在你係統無需從新編譯而更換零件。
For example, your application might support multiple relational databases with a separate module for each supported database type.
好比,你的應用支持多種關係數據庫,爲每個支持數據庫類型用一個獨立的模塊。
You can use declarative configuration to tell the application to use a specific module at runtime.
你能夠用申明配置去告訴應用在運行去使用特定的模塊。
Another scenario where late binding can be useful is to enable users of the system to provide their own customization through a plug-in.
後期綁定的另外一個場景中,多是有用的是讓用戶經過一個插件系統提供本身的定製。
Again, you can instruct the system to use a specific customization by using a configuration setting or a convention where the system scans a particular location on the file system for modules to use。
此外,你能夠經過使用配置或約定系統掃描文件系統上的特定位置的模塊通知系統去使用特定開發。
Not all systems have a requirement for late binding. It is typically required to support a specific feature of the application such as customization using a plug-in architecture.
沒有全部系統須要延時綁定需求,它一般須要支持一個特定的應用程序的功能,如定製使用插件架構。
Parallel Development
並行開發
When you are developing large scale (or even small and medium scale) systems, it is not practical to have the entire development team working simultaneously on the same feature or component.
當你在開發大規模(甚至中小規模)系統,整個團隊同時的工做在相同的功能和組件上是不實際的。
In reality, you will assign different features and components to smaller groups to work on in parallel.
在現實中,你會分配不一樣的功能和組件到小組進行並行工做。
Although this approach enables you to reduce the overall duration of the project, it does introduce additional complexities: you need to manage multiple groups and to ensure that you can integrate the parts of the application developed by different groups to work correctly together.
雖然這種方式是你可以減小項目所有時間,它採用了額外的複雜性:你必須管理多個小組而且確保你能夠整合不一樣小組開發的應用組件在一塊兒並能正常工做。
It can be a significant challenge to ensure that classes and components developed independently do work together.
確保獨立開發的類和組件在一塊兒工做多是一個巨大的挑戰。
Crosscutting Concerns
橫切關注
Enterprise applications typically need to address a range of crosscutting concerns such as validation, exception handling, and logging.
企業應用程序一般須要解決一系列的橫切關注點,如驗證、異常處理和日誌記錄。
You may need these features in many different areas of the application and you will want to implement them in a standard, consistent way to improve the maintainability of the system.
你可能在應用程序的許多不一樣領域須要這些功能而且你會但願在一個標準中實現,一致的方法來提升系統的可維護性。
Ideally, you want a mechanism that will enable you to efficiently and transparently add behaviors to your objects at either design time or run time without requiring you make changes to your existing classes.
理想狀況下,你須要一個技巧,那將使你可以去有效的和透明地將行爲添加到對象,在設計時或者運行時都不須要你更改現有類。
Often, you need the ability to configure these features at runtime and in some cases, add features to address a new crosscutting concern to an existing application.
一般,你需有在運行時配置配置這些特性的能力,在某些狀況下,添加特性去處理一個新的橫切關注點到現有應用程序中。
For a large enterprise system, it’s important to be able to manage crosscutting concerns such as logging and validation in a consistent manner. I often need to change the logging level on a specific component at run time to troubleshoot an issue without restarting the system.
爲了大企業系統,重要的是可以處理橫切關注點,如日誌記錄和驗證以一致的方式。我時常須要更改記錄等級在一個特定的組件在運行時解決問題而不須要從新啓動系統。
A Simple Example
一個簡單的例子
The following example illustrates tight coupling where the Management-Controller class depends directly on the TenantStore class. These classes might be in different Visual Studio projects.
下面的例子說明了緊密耦合的管理控制器類直接取決於TenantStore類。這些類可能在不一樣的Visual Studio 項目中。
///<summary>
///房客存儲
///</summary>
public class TenantStore { ... public Tenant GetTenant(string tenant) { ... } public IEnumerable<string> GetTenantNames() { ... } }
///<summary>
///管理控制器
///</summary>
public class ManagementController { private readonly TenantStore tenantStore; public ManagementController() { tenantStore = new TenantStore(...); } public ActionResult Index() { var model = new TenantPageViewData<IEnumerable<string>>(this.tenantStore.GetTenantNames()) { Title = "Subscribers" }; return this.View(model); } public ActionResult Detail(string tenant) { var contentModel = this.tenantStore.GetTenant(tenant); var model = new TenantPageViewData<Tenant>(contentModel) { Title = string.Format("{0} details", contentModel.Name) }; return this.View(model); } ... }
The ManagementController and TenantStore classes are used in various forms throughout this guide. Although the ManagementController class is an ASP.NET MVC controller, you don’t need to know about MVC to follow along. However, these examples are intended to look like the kinds of classes you would encounter in a real-world system, especially the examples in Chapter 3.
在本指南中ManagementController和TenantStore 類被用於各類形式。儘管ManagementController類是一個ASP.NET MVC 控制器,你不須要跟着瞭解MVC。不管如何,這些例子是刻意看起來像你將在系統系統中遇到的類,特別是第3章的實例。
Loose coupling should be a general design goal for your enterprise applications.
鬆散耦合應該是你的企業應用程序的總設計目標。
In this example, the TenantStore class implements a repository that handles access to an underlying data store such as a relational database, and the ManagementController is an MVC controller class that requests data from the repository.
在這裏例子中,TenantStore類實現了一個庫來處理訪問一個底層數據存儲庫,如關係數據庫,ManagementController是MVC控制器類,他從存儲庫中請求數據。
Note that the ManagementController class must either instantiate a TenantStore object or obtain a reference to a TenantStore object from somewhere else before it can invoke the GetTenant and GetTenantNames methods.
注意,ManagementController類必須實例化一個TenantStore對象或從其餘地方獲取到TenantStore對象的引用才能調用GetTenant和GetTenantNames方法。
The ManagementController class depends on the specific, concrete TenantStore class.
ManagementController 類依賴細節,依賴具體的TenanStore類。
If you refer back to the list of common desirable requirements for enterprise applications at the start of this chapter, you can evaluate how well the approach outlined in the previous code sample helps you to meet them.
若是你回顧這一章開頭企業應用共同的需求列表,你能夠評估方法概述在前面的代碼示例幫助你去知足他們。
• Although this simple example shows only a single client class of the TenantStore class, in practice there may be many client classes in your application that use the TenantStore class. If you assume that each client class is responsible for instantiating or locating a TenantStore object at runtime, then all of those classes are tied to a particular constructor or initialization method in that TenantStore class, and may all need to be changed if the implementation of the TenantStore class changes. This potentially makes maintenance of the TenantStore class more complex, more error prone, and more time consuming.
雖然這個簡單的示例僅僅顯示一個客戶端類屬於TenantStore類,在實踐中可能有不少客戶端類在你的應有程序中使用TenantStore類。若是你認爲每一個客戶端類負責在運行時實例化或定位TenantStore對象,而後全部這些類都要綁定到TenantStore類的構造函數貨實例化方法,若是TenantStore類更改的話你可能所有都須要修改。這可能使得TenantStore類的爲維護性更加複雜、更容易出錯,而且更加費時。
• In order to run unit tests on the Index and Detail methods in the ManagementController class, you need to instantiate a TenantStore object and make sure that the underlying data store contains the appropriate test data for the test. This complicates the testing process, and depending on the data store you are using, may make running the test more time consuming because you must create and populate the data store with the correct data. It also makes the tests much more brittle.
爲了運行ManagementController類中的Index和Detail方法的單元測試,爲了這個測試你須要實例化TenantStore對象和構造包含可靠測試數據的底層的數據存儲。這是一個複雜的測試過程,而且根據你所使用的數據存儲,可能運行測試更費事,由於你必須爲數據存儲建立並填充正確的數據。這也使得測試更加脆弱。
• It is possible to change the implementation of the TenantStore class to use a different data store, for example Windows Azure table storage instead of SQL Server. However, it might require some changes to the client classes that use TenantStore instances if it was necessary for them to provide some initialization data such as connection strings.
爲了應用不一樣的數據存儲可能須要更改TenantStore類的實現,好比Windows Azure表存儲,而不是SQLServer。然而,若是它是必要的讓他們提供一些初始化數據,好比鏈接字符串,它可能須要修改一些使用TenantStore實例的類客戶端。
• You cannot use late binding with this approach because the client classes are compiled to use the TenantStore class directly.
這種方法你沒法使用延時綁定,應爲客戶端類編譯直接使用TenantStore類。
• If you need to add support for a crosscutting concern such as logging to multiple store classes, including the TenantStore class, you would need to modify and configure each of your store classes independently.
若是你須要添加支持橫切關注,如登陸到多個存儲類,包括TenantStore類,你將須要修改和配置你每一個獨立的存儲類。
The following code sample shows a small change, the constructor in the client ManagementController class now receives an object that implements the ITenantStore interface and the TenantStore class provides an implementation of the same interface.
下面代碼示例顯示了小的變化,如今在客戶端ManagementController類構造函數接收一個對象實現ITenantStore接口,而且TenantStore類提供了ITenantStore接口的一個實現。
public interface ITenantStore { void Initialize(); Tenant GetTenant(string tenant); IEnumerable<string> GetTenantNames(); void SaveTenant(Tenant tenant); void UploadLogo(string tenant, byte[] logo); } public class TenantStore : ITenantStore { ... public TenantStore() { ... } ... } public class ManagementController : Controller { private readonly ITenantStore tenantStore; public ManagementController(ITenantStore tenantStore) { this.tenantStore = tenantStore; } public ActionResult Index() { ... } public ActionResult Detail(string tenant) { ... } ... }
This change has a direct impact on how easily you can meet the list of requirements.
這個更改影響到你如何容易的知足那些需求列表。
• It is now clear that the ManagementController class, and any other clients of the TenantStore class are no longer responsible for instantiating TenantStore objects, although the example code shown doesn’t show which class or component is responsible for instantiating them. From the perspective of maintenance, this responsibility could now belong to a single class rather than many.
如今能夠清楚的是ManagementController類和任何其餘TenantStore類客戶端再也不實負責例化TenantStore對象,儘管所顯示的示例代碼不能顯示哪一個類或組件是負責實例化他們。從維護的角度,這個責任顯示屬於一個類,而不是多個類。
• It’s now also clear what dependencies the controller has from its constructor arguments instead of being buried inside of the controller method implementations.
如今也清楚控制器的依賴性是它的構造函數參數,而不是在控制器的實現方法內部。
• To test some behaviors of a client class such as the ManagementController class, you can now provide a lightweight implementation of the ITenantStore interface that returns some sample data. This is instead of creating a TenantStore object that queries the underlying data store for sample data.
測試客戶類的一些行爲,好比ManagementController 類,你如今能夠提供一個輕量級的ITenantStore接口實現返回一些樣本數據。這是查詢而不是建立TenantStore對象底層數據存儲的樣本數據。
• Introducing the ITenantStore interface makes it easier to replace the store implementation without requiring changes in the client classes because all they expect is an object that implements the interface.
介紹ITenantStore接口使它更簡單的替換存儲實現,而不須要更改客戶類,由於他們須要的是一個實現了接口的對象。
If the interface is in a separate project to the implementation, then the projects that contain the client classes only need to hold a reference to the project that contains the interface definition.
若是接口在獨立的項目中實現,含有客戶類的項目只須要引用包含接口定義的項目。
• It is now also possible that the class responsible for instantiating the store classes could provide additional services to the application. It could control the lifetime of the ITenantStore instances that it creates, for example creating a new object every time the client ManagementController class needs an instance, or maintaining a single instance that it passes as a reference whenever a client class needs it.
如今也可能負責實例化的類存儲類能夠提供額外的服務應用程序。它可以管理ITenantStore實例的生命週期,它建立了,好比建立新對象每次客戶ManagementController類都須要實例化,或者維護個單一實例,它傳入引用,每當客戶類須要它時。
• It is now possible to use late binding because the client classes only reference the ITenantStore interface type. The application can create an object that implements the interface at runtime, perhaps based on a configuration setting, and pass that object to the client classes. For example, the application might create either a SQLTenantStore instance or a BlobTenantStore instance depending on a setting in the web.config file, and pass that to the constructor in the ManagementController class.
如今可使用延時綁定,由於客戶類只引用ITenantStore接口類型。應用程序能夠在運行時建立接口的實現對象,也許基於配置設置將對象傳遞給客戶類。好比,應用程序能夠根據Web.config文件中的配置建立SQLTenantStore實例或BlobTenantStore 實例,並傳入到ManagementController類的構造函數中。
• If the interface definition is agreed, two teams could work in parallel on the store class and the controller class.
若是接口定義一致,兩個團隊能夠並行工做存儲類和控制器類。
• The class that is responsible for creating the store class instances could now add support for the crosscutting concerns before passing the store instance on to the clients, such as by using the decorator pattern to pass in an object that implements the crosscutting concerns. You don’t need to change either the client classes or the store class to add support for crosscutting concerns such as logging or exception handling.
那個負責建立存儲實例的類如今能夠在傳入存儲實例到客戶以前添加橫切關注,如經過使用裝飾者模式傳遞一個對象實現橫切關注。你不須要修改客戶類或存儲類去添加支持橫切關注,例如日誌記錄或異常處理。
The approach shown in the second code sample is an example of a loosely coupled design that uses interfaces. If we can remove a direct dependency between classes, it reduces the level of coupling and helps to increase the maintainability, testability, flexibility, and extensibility of the solution.
第二個代碼示例中所示的方法是一個使用接口的鬆散耦合設計的例子。若是咱們能夠刪除一個類之間的直接依賴關係,它下降耦合等級,而且有助於提升可維護性、可測試性、靈活性和解決方案的可擴展性。
What the second code sample doesn’t show is how dependency injection and the Unity container fit into the picture, although you can probably guess that they will be responsible for creating instances and passing them to client classes. Chapter 2 describes the role of dependency injection as a technique to support loosely coupled designs, and Chapter 3 describes how Unity helps you to implement dependency injection in your applications.
第二個代碼示例沒有顯示是如何依賴注入和Unity容器符合,雖然你大概猜中他們負責建立實例並傳遞他們到客戶類。第二章描述了角色關於依賴注入技術去支持鬆散耦合設計,和第三張描述Unity如何幫助你在應用程序中實現依賴注入。
Loose coupling doesn’t necessarily imply dependency injection, although the two often do go together.
鬆散耦合並不必定意味着依賴注入,索然二者常常在一塊兒使用。
When Should You Use a Loosely Coupled Design?
你何時應該使用鬆散耦合的設計?
Before we move on to dependency injection and Unity, you should start to understand where in your application you should consider introducing loose coupling, programming to interfaces, and reducing dependencies between classes. The first requirement we described in the previous section was maintainability, and this often gives a good indication of when and where to consider reducing the coupling in the application. Typically, the larger and more complex the application, the more difficult it becomes to maintain, and so the more likely these techniques will be helpful. This is true regardless of the type of application: it could be a desktop application, a web application, or a cloud application.
在咱們繼續反轉註入和Unity以前,你應該開始明白在你的應用中哪裏應該考慮引入鬆散耦合,設計接口,和下降類之間的依賴。咱們在前一節中描述的首要條件是可維護性,,這每每指示什麼時候和某個地方考慮減小應用程序的耦合性。一般,大型複製應用程序,就變得更復雜,因此更有可能這些技術是有益的。這是真的,不管應用程序的類型:它多是一個桌面應用程序,一個web應用程序,或一個雲應用程序。
At first sight, this perhaps seems counterintuitive. The second example shown above introduced an interface that wasn’t in the first example, it also requires the bits we haven’t shown yet that are responsible for instantiating and managing objects on behalf of the client classes. With a small example, these techniques appear to add to the complexity of the solution, but as the application becomes larger and more complex, this overhead becomes less and less significant.
第一映像,這或許像是違反直覺的:上面所述的第二例子引入了接口而不是第一個例子,它還須要一些咱們還沒顯示的負責實例化和管理客戶類的對象。用一個小例子,這些技術的使用添加了解決的方案的複雜性,但隨着應用程序變得更大、更復雜,這種(複雜性)開銷變得愈來愈不顯着。
The previous example also illustrates another general point about where it is appropriate to use these techniques. Most likely, the ManagementController class exists in the user interface layer in the application, and the TenantStore class is part of the data access layer. It is a common approach to design an application so that in the future it is possible to replace one tier without disturbing the others. For example, replacing or adding a new UI to the application (such as creating an app for a mobile platform in addition to a traditional web UI) without changing the data tier or replacing the underlying storage mechanism and without changing the UI tier. Building the application using tiers helps to decouple parts of the application from each other. You should try to identify the parts of an application that are likely to change in the future and then decouple them from the rest of the application in order to minimize and localize the impact of those changes.
前面的例子也闡述了另外一個廣泛觀點關於在你的應用程序的哪一個地方使用這些技術。最可能,ManagementController類存在於應用程序的用戶界面層,TenantStore類是數據存儲層的一部分。它是設計應用程序的普通方法,以便未來可能替換一個層,而不影響到其餘層。好比,更換或者添加新的UI到應用程序中(例如爲傳統的web UI建立移動平臺的APP)不用修改數據層,或更換下面的存儲機制,不用更改UI層。使用層構建應用程序有助於將應用程序的各個部分彼此分離。你應該嘗試確認應用程序在未來可能會不改變的部分,而後將它們與應用程序的其他部分分離,以便最小化和本地化這些更改的影響。
The list of requirements in the previous section also includes crosscutting concerns that you might need to apply across a range of classes in your application in a consistent manner. Examples include the concerns addressed by the application blocks in Enterprise Library (http://msdn.microsoft.com/entlib) such as logging, exception handling, validation, and transient fault handling. Here you need to identify those classes where you might need to address these crosscutting concerns, so that responsibility for adding these features to these classes resides outside of the classes themselves. This helps you to manage these features consistently in the application and introduces a clear separation of concerns.
前面部分中的需求列表還包括橫切關注,您可能須要以一致的方式在應用程序的一系列類中應用。示例包括企業庫(http://msdn.microsoft.com/entlib)中的應用程序塊所解決的問題,例如日誌記錄,異常處理,驗證和瞬態故障處理。在這裏,你須要確認那些你可能須要解決這些橫切關注的類,因此負責在類自己以外添加特性到這些類。這有助於您在應用程序中一致地管理這些功能,並引入明確的關注點分離。
Small examples of loosely coupled design, programming to interfaces, and dependency injection often appear to complicate the solution. You should remember that these techniques are intended to help you simplify and manage large and complex applications with many classes and dependencies. Of course small applications can often grow into large and complex applications.
鬆散耦合設計的小例子,接口編程,和依賴注入常常是的解決方案更加複雜。你應該牢記這些技術的意義是在與幫助你簡化和管理有許多類和依賴的大型複雜應用程序,固然小的應用程序能夠發在成大的複雜應用程序。
Principles of Object-Oriented Design
面向對象設計原則
Finally, before moving on to dependency injection and Unity, we want to relate the five SOLID principles of object-oriented programming and design to the discussion so far. SOLID is an acronym that refers to the following principles:
• Single responsibility principle
• Open/close principle
• Liskov substitution principle
• Interface segregation principle
• Dependency inversion principle
The following sections describe each of these principles and their relationship to loose coupling and the requirements listed at the start of this chapter.
最後,在繼續依賴注入和Unity以前,咱們但願將面向對象編程和設計的五個SOLID原則與到目前爲止的討論相關聯。SOLID是指如下原則的首字母縮寫:
S:單一職責原則,一個類應該只有一個發生變化的緣由
O:開放原則,對擴展開放,對修改關閉
L:里斯替換原則,任何基類能夠出現的地方,子類必定能夠出現
I:接口隔離原則,客戶端不該該依賴它不須要的接口;一個類對另外一個類的依賴應該創建在最小的接口上
D:依賴倒置原則,高層次的模塊不該該依賴於低層次的模塊,他們都應該依賴於抽象。抽象不該該依賴於具體實現,具體實現應該依賴於抽象。
如下部分描述了這些原理及其與鬆耦合的關係以及本章開頭列出的要求。
Single Responsibility Principle
單一職責原則
The single responsibility principle states that a class should have one, and only one, reason to change. For more information, see the article Principles of Object Oriented Design by Robert C. Martin1.
In the first simple example shown in this chapter, the ManagementController class had two responsibilities: to act as a controller in the UI and to instantiate and manage the lifetime of TenantStore objects. In the second example, the responsibility for instantiating and managing TenantStore objects lies with another class or component in the system.
單一職責原則聲明類應該有一個且只有一個理由去更改。更多信息請看Robert C. Martin1些的面向對象設計的文章。
這章中第一個簡單的示例顯示ManagementController 類有兩個職責:UI中的行爲控制和TenantStore對象的實例化和周的期管理 。在第二個示例中,實例化和管理TenantStore對象的責任在於系統中的另外一個類或組件。
The Open/Closed Principle
開閉原則
The open/closed principle states that 「software entities (classes, modules, functions, and so on) should be open for extension, but closed for modification」 (Meyer, Bertrand (1988). Object-Oriented Software Construction.)
Although you might modify the code in a class to fix a defect, you should extend a class if you want to add any new behavior to it. This helps to keep the code maintainable and testable because existing behavior should not change, and any new behavior exists in new classes. The requirement to be able to add support for crosscutting concerns to your application can best be met by following the open/closed principle. For example, when you add logging to a set of classes in your application, you shouldn’t make changes to the implementation of your existing classes.
開閉原則申明「軟件實體(類、模塊、函數等等)應該對擴展開放,對修改關閉」(Meyer, Bertrand (1988). 面向對象軟件構造。)
雖然你可能在類中修改代碼以修復缺陷,若是你想添加任何新行爲到這個類中,你應該擴展這個類。能夠經過遵循開放/封閉原則來知足對您的應用程序增長對橫切關注的支持的要求。例如。當你添加一組日誌記錄類到你的應用程序中時,你不該該去更改你現有類的實現。
The Liskov Substitution Principle
里斯替代原則
The Liskov substitution principle in object-oriented programming states that in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties, such as correctness, of that program.
里斯替代原則在面向對象編程中寫明,在計算機程序,若是S是T的子類,那麼T類型的對象能夠被替換成S類型的對象而不會改變任何指望值,好比程序的正確性。
In the second code sample shown in this chapter, the ManagementController class should continue to work as expected if you pass any implementation of the ITenantStore interface to it. This example uses an interface type as the type to pass to the constructor of the ManagementController class, but you could equally well use an abstract type.
本章第二段代碼代表,若是您將ITenantStore接口的任何實現傳遞給它,ManagementController類應該繼續按預期工做。這個實例使用接口類型做爲傳遞給ManagementController 類構造函數的類型,一樣你可使用抽象類型。
Interface Segregation Principle
接口隔離原則
The interface segregation principle is a software development principle intended to make software more maintainable. The interface segregation principle encourages loose coupling and therefore makes a system easier to refactor, change, and redeploy. The principle states that interfaces that are very large should be split into smaller and more specific ones so that client classes only need to know about the methods that they use: no client class should be forced to depend on methods it does not use.
接口隔離原則是一種意在使軟件更易維護的軟件開發原則。接口隔離原則鼓勵鬆散耦合,並使系統更易重構、更改和從新部署。原則申明很是大的接口應該拆分紅小的明確的接口,這樣客戶類僅僅須要知道關於他們使用的方法:沒有客戶類應該被迫依賴他們不適用的方法。
In the definition of the ITenantStore interface shown earlier in this chapter, if you determined that not all client classes use the UploadLogo method you should consider splitting this into a separate interface as shown in the following code sample:
在本章前面所示的ITenantStore接口的定義中,若是肯定不是全部的客戶端類都使用UploadLogo方法,您應該考慮將其拆分爲一個單獨的接口,以下面的代碼示例所示:
public interface ITenantStore { void Initialize(); Tenant GetTenant(string tenant); IEnumerable<string> GetTenantNames(); void SaveTenant(Tenant tenant); } public interface ITenantStoreLogo { void UploadLogo(string tenant, byte[] logo); } public class TenantStore : ITenantStore, ITenantStoreLogo { ... public TenantStore() { ... } ... }
Dependency Inversion Principle
依賴倒置原則
The dependency inversion principle states that:
• High-level modules should not depend on low-level modules. Both should depend on abstractions.
• Abstractions should not depend upon details. Details should depend upon abstractions.
The two code samples in this chapter illustrate how to apply this principle. In the first sample, the high-level ManagementController class depends on the low-level TenantStore class. This typically limits the options for re-using the high-level class in another context.
In the second code sample, the ManagementController class now has a dependency on the ITenantStore abstraction, as does the TenantStore class.
依賴倒置原則聲明:
本章兩段代碼示例說明如何應用這一原則。在第一個示例中,高層的ManagementController 類底層的TenantStore 類。這一般限制了在另外一個上下文中重用高層類的選擇。
在第二個示例代碼中,ManagementController 類如今依賴ITenantStore 抽象,TenantStore 類也是如此。
Summary
總結
In this chapter, you have seen how you can address some of the common requirements in enterprise applications such as maintainability and testability by adopting a loosely coupled design for your application. You saw a very simple illustration of this in the code samples that show two different ways that you can implement the dependency between the ManagementController and TenantStore classes. You also saw how the SOLID principles of object-oriented programming relate to the same concerns.
However, the discussion in this chapter left open the question of how to instantiate and manage TenantStore objects if the ManagementController is no longer responsible for this task. The next chapter will show how dependency injection relates to this specific question and how adopting a dependency injection approach can help you meet the requirements and adhere to the principles outlined in this chapter.
在本章中,您已經瞭解瞭如何經過爲您的應用程序採用鬆耦合設計來解決企業應用程序中的一些常見要求,如可維護性和可測試性。您在代碼示例中看到了一個很是簡單的例子,它展現了兩種不一樣的方法,您能夠實現ManagementController和TenantStore類之間的依賴關係。另外你也看到面向對象SOLID原則如何設計涉及這些關注。
可是,本章討論遺留一個問題,若是ManagementController再也不負責這個任務,那麼如何實例化和管理TenantStore 對象。在下一章將會顯示使用依賴注入如何與具體問題相關和採用依賴注入方法如何幫助您知足要求並遵照本章中概述的原則。
More Information
更多信息
All links in this book are accessible from the book’s online bibliography available at: http://aka.ms/unitybiblio
本書中的全部連接都可從本書的在線參考書目得到,網址爲:http://aka.ms/unitybiblio