Unity文书档案阅读 第一章 入门

• 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.

In reality, you will assign different features and components to smaller
groups to work on in parallel.


In some application scenarios, you may have a requirement to support
late binding.

Bertrand (一九八七). 面向对象软件构造。)
The Liskov Substitution Principle

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.

• 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.

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.


利用双测量检验上的赫赫钻探,亲看Steve Freeman的point/counterpoint
debate(点/对位议论),Nat Pryce和Joshua Kerievsky
IEEE软件(难点:数量:14日3),二〇〇七年11月/ 二月pp.80 – 83。

The Open/Closed Principle


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.


Loose coupling should be a general design goal for your enterprise

油滑和可扩张性 Flexibility and extensibility are also often on the list of desirable
attributes of enterprise applications.

Another scenario where late binding can be useful is to enable users of
the system to provide their own customization through a plug-in.


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,




总结 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
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


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.




public class TenantStore
    public Tenant GetTenant(string tenant)
    public IEnumerable<string> GetTenantNames()




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);



When Should You Use a Loosely Coupled Design?


• 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.

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.






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.


横切关怀 Enterprise applications typically need to address a range of
crosscutting concerns such as validation, exception handling, and


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.

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()


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.

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:

It can be a significant challenge to ensure that classes and components
developed independently do work together.


Studio 项目中。


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.


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
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.

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.

在第一个示范代码中,ManagementController 类现在依据ITenantStore
抽象,TenantStore 类也是那样。

Flexibility and Extensibility

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.

• 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.

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.




• It’s now also clear what dependencies the controller has from its
constructor arguments instead of being buried inside of the controller
method implementations.



This change has a direct impact on how easily you can meet the list of

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。

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.

• If the interface definition is agreed, two teams could work in
parallel on the store class and the controller class.

The ManagementController class depends on the specific, concrete
TenantStore class.

Dependency Inversion Principle

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.

Loose coupling doesn’t necessarily imply dependency injection, although
the two often do go together.


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)

• 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

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.

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

All links in this book are accessible from the book’s online
bibliography available at: http://aka.ms/unitybiblio

It is very hard to make existing systems more maintainable. It is much
better to design for maintainability from the very start.


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 practices

More Information


You can use declarative configuration to tell the application to use a
specific module at runtime.

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.



Late binding is useful if you require the ability to replace part of
your system without recompiling.


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.

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.

Late Binding


• 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.



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.

类被用于各个方式。就算ManagementController类是二个ASP.NET MVC



• 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.

Principles of Object-Oriented Design

Interface Segregation Principle

A Simple Example

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

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
In the second code sample, the ManagementController class now has a
dependency on the ITenantStore abstraction, as does the
TenantStore class.

Parallel Development



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.

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


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.

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.


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.

• 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.


For example, you may need to convert your application from running
on-premises to running in the cloud.


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.



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.


相互之间开辟 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 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.

For example, your application might support multiple relational
databases with a separate module for each supported database type.


• You cannot use late binding with this approach because the client
classes are compiled to use the TenantStore class directly.

• 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.

• 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.



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.






纯净职务标准表明类应该有叁个且唯有七个说辞去改变。更加多消息请看罗Bert C.


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.

  • 高层模块不该借助底层模块。都应有依赖抽象。
  • 虚幻不该依附细节。细节应该借助抽象。


Single Responsibility Principle

Such design techniques also help to extend the coverage of your unit
tests, reduce the likelihood of regressions, and make refactoring

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.


Crosscutting Concerns

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.



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.

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.

Running tests can also cost money and be time consuming because of the
requirement to test in a realistic environment.


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


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.


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