化解依赖正是松耦合

扫除注重正是松耦合,松耦合就自然好吧?当然不是先后的耦合度与内聚度要放在1块儿说,当然,对于世界层来讲,适当的回落类与类之间的依赖是很有供给的,看上边小编的这段代码

  从前向来不领悟依赖注入有啥便宜,以至以为它是鸡肋,未来合计,当时当成可笑。

两个例证,1个订单系统,它依据国家有两样的下单方法,对于低层(DATA层)是平等的,但对于世界层,也叫职业逻辑层,它是差异等的,只怕小日本下单打5拍,中夏族民共和国人下单不减价,作者也是足以清楚的,因为中华夏族民共和国天然气在对于中中原人民共和国人上是很抠门的,而对于小日本则慷慨解囊。不多说了,看代码:

  这些主见正就如说接口是向来不用处同样。

    /// <summary>

    /// 订单实体

    /// </summary>

    class Order

    {

 

    }

 

    #region 传统作法,无有考虑约束

    /// <summary>

    /// 针对美国人的订单

    /// </summary>

    class OrderUSA

    {

        public void Insert(Order order) { }

    }

    /// <summary>

    /// 什对日本人的订单

    /// </summary>

    class OrderJPN

    {

        public void Insert(Order order) { }

    }

    class OrderService

    {

        OrderUSA orderAction1 = new OrderUSA();

        public void Insert(Order order)

        {

            orderAction1.Insert(order);

        }

    }

    #endregion

 

    #region 解除具体依赖后的程序

    /// <summary>

    /// 和订单操作相关的接口

    /// </summary>

    public interface IOrderAction

    {

        void Insert(Order order);

        void Delete(Order order);

    }

    /// <summary>

    /// 针对中国人的订单

    /// </summary>

    public class OrderChina : IOrderAction

    {

        public void Insert(Order order)

        {

            throw new NotImplementedException();

        }

        public void Delete(Order order)

        {

            throw new NotImplementedException();

        }

    }

    public class OrderService2

    {

        private IOrderAction iOrderInsert;

        /// <summary>

        /// 根据所传递的接口类型的参数来确定使用哪种对象

        /// </summary>

        /// <param name="_iOrderInsert"></param>

        public OrderService2(IOrderAction _iOrderInsert)

        {

            this.iOrderInsert = _iOrderInsert;

        }

        public void InsertOrder(Order order)

        {

            this.iOrderInsert.Insert(order);

        }

        public void DeleteOrder(Order order)

        {

            this.iOrderInsert.Delete(order);

        }

    }

    #endregion

  当整个项目非常庞大,各类艺术之间的调用特别复杂,那么,能够设想一下,借使说未有任何的分离模块的主张,各样关系非常的错综复杂,不便于维护以及查找bug等等。那个时候,就须要3个事物,去将这么多复杂的玩意分离开来,很喜爱的一句话:高内聚,低耦合。

  举个栗子,以往二个很轻松的效劳,可能只需要经过一些办法相互调用就能够实现,OK,那么随着需要的充实,未来增多了多少个效益,那么,大家把一样的事物划分为贰个类吧,类经过而生,同样,类似的,1层壹层如下所示:

艺术–>类–>接口–>模块–>乃至把各种模块之间的涉嫌抽象出来(比方IOC/DI)

 

 

  同理可得,3个主导的酌量正是,对于1个大门类以来,把具有的模块尽可能的多分多少个,四当中间的事物太多以来,多分开几个写,一句话表明:高内聚,低耦合

 

浅谈依赖注入

 

近期几天在看一本名称为Dependency Injection in
.NET
 的书,首要讲了何等是借助注入,使用依赖注入的长处,以及.NET平台上重视注入的种种框架和用法。在那本书的开首,讲述了软件工程中的3个首要的见识正是关心分离(Separation
of
concern, SoC)。依赖注入不是目标,它是壹多元工具和手段,最后的指标是扶持大家付出出松散耦合(loose
coupled
)、可珍惜、可测试的代码和顺序。那条标准的做法是豪门熟悉的面向接口,大概说是面向抽象编制程序。

有关什么是依赖注入,在Stack
Overflow上边有四个难题,怎样向1个五岁的毛孩子解释注重注入,当中得分最高的2个答案是:

“When you go and get things out of the refrigerator for yourself, you
can cause problems. You might leave the door open, you might get
something Mommy or Daddy doesn’t want you to have. You might even be
looking for something we don’t even have or which has expired.

What you should be doing is stating a need, “I need something to drink
with lunch,” and then we will make sure you have something when you sit
down to eat.”

炫丽到面向对象程序支付中便是:高层类(四虚岁幼童)应该依据底层基础设备(家长)来提供必需的服务。

编写松耦合的代码说到来很简短,可是实际写着写着就产生了紧耦合。

运用例子来注解只怕更加精简,首先来看望哪些的代码是紧耦合。

一 倒霉的落实

编写松耦合代码的第壹步,只怕我们都听得多了自然能详细说出来,那就是对系统一分配层。譬如上边包车型客车经文的三层架构。

图片 1

分完层和促成好是两件事情,并不是说分好层之后就能够松耦合了。

一.壹 紧耦合的代码

有很二种办法来规划二个灵活的,可爱抚的纷纷应用,但是n层架构是1种我们比较熟习的章程,那当中的挑衅在于如何科学的落到实处n层架构。

若是要促成一个异常粗略的电子商务网址,要列出商品列表,如下:

图片 2

上边就实际来演示平时的做法,是何等一步一步把代码写出紧耦合的。

一.1.一 数据访问层

要达成商品列表这一成效,首先要编写数据访问层,需求规划数据库及表,在SQLServer中规划的数据库表Product结构如下:

图片 3

表设计好今后,就足以起来写代码了。在Visual Studio
中,新建一个名字为DataAccessLayer的工程,增多3个ADO.NET Entity Data
Model,此时Visual Studio的向导会自动帮大家调换Product实体和ObjectContext
DB操作上下文。那样大家的 Data Access Layer就写好了。

图片 4

壹.一.二 业务逻辑层

展现层实际上能够从来访问数据访问层,通过ObjectContext 获取Product
列表。可是许多地方下,大家不是一贯把DB里面包车型客车多寡显现出来,而是须要对数据开展拍卖,举个例子对会员,须求对一些商品的价钱降价。那样我们就供给职业逻辑层,来管理这一个与具象专门的学业逻辑相关的事情。

新建叁个类库,命名叫DomainLogic,然后增加多少个名称叫ProductService的类:

public class ProductService {
    private readonly CommerceObjectContext objectContext;

    public ProductService()
    {
        this.objectContext = new CommerceObjectContext();
    }

    public IEnumerable<Product> GetFeaturedProducts(
        bool isCustomerPreferred)
    {
        var discount = isCustomerPreferred ? .95m : 1;
        var products = (from p in this.objectContext
                            .Products
                        where p.IsFeatured
                        select p).AsEnumerable();
        return from p in products
                select new Product
                {
                    ProductId = p.ProductId,
                    Name = p.Name,
                    Description = p.Description,
                    IsFeatured = p.IsFeatured,
                    UnitPrice = p.UnitPrice * discount
                };
    }
}

当今咱们的事情逻辑层已经落实了。

1.1.3 表现层

前天兑现表现层逻辑,这里运用ASP.NET MVC,在Index
页面的Controller中,获取商品列表然后将数据重回给View。

public ViewResult Index()
{
    bool isPreferredCustomer = 
        this.User.IsInRole("PreferredCustomer");

    var service = new ProductService();
    var products = 
        service.GetFeaturedProducts(isPreferredCustomer);
    this.ViewData["Products"] = products;

    return this.View();
}

接下来在View大校Controller中回到的数据显现出来:

<h2>Featured Products</h2>
<div>
<% var products =
        (IEnumerable<Product>)this.ViewData["Products"];
    foreach (var product in products)
    { %>
    <div>
    <%= this.Html.Encode(product.Name) %>
    (<%= this.Html.Encode(product.UnitPrice.ToString("C")) %>)
    </div>
<% } %>
</div>

1.2 分析

前几日,依据三层“架构”大家的代码写好了,并且也到达了供给。整个项目标构造如下图:

 图片 5

那应该是大家一般通常写的所谓的三层架构。在Visual
Studio中,三层之间的重视可以因此品种引用表现出来。

1.贰.一 信赖关系图

于今我们来深入分析一下,那三层之间的正视关系,很显明,下面的兑现中,DomianLogic须求借助SqlDataAccess,因为DomainLogic中用到了Product那一实体,而以此实体是概念在DataAccess那1层的。WebUI那一层要求借助DomainLogic,因为ProductService在那1层,同时,还需求依赖DataAccess,因为在UI中也运用了Product实体,今后漫天系统的注重性关系是那般的:

图片 6

壹.贰.二 耦合性深入分析

采纳三层组织的严重性目标是分别关切点,当然还有一个原因是可测试性。大家应该将世界模型从数额访问层和展现层中分离出来,那样那四个层的转移才不会传染领域模型。在大的种类中,那一点很注重,那样手艺将系统中的分裂部分隔绝开来。

近来来看后面包车型客车落到实处中,有未有模块性,有未有极度模块能够凝集出来呢。现在丰裕多少个新的case来看,系统是不是能够响应这几个需要:

增添新的用户分界面

除了WebForm用户之外,或者还须求两个WinForm的分界面,现在大家可不可以复用领域层和数目访问层呢?从正视图中可以看到,未有其他一个模块会依据表现层,因而很轻松达成那一点生成。我们只供给创制三个WPF的富客户端就能够。以往总体系统的重视图如下:

图片 7

转移新的数据源

恐怕过了一段时间,要求把全体种类铺排到云上,要动用此外的数额存款和储蓄本领,举个例子Azure
Table Storage Service。未来,整个访问数据的协议产生了更换,访问Azure
Table Storage Service的法子是Http协议,而以前的大许多.NET
访问数据的办法都以凭借ADO.NET
的办法。并且数据源的保存方法也发出了变动,此前是关系型数据库,以往造成了key-value型数据库。

图片 8 

由地点的依赖关系图能够观望,全数的层都依赖了数额访问层,假如退换数据访问层,则领域逻辑层,和突显层都亟待展开对应的退换。

1.2.3 问题

而外上面的各层之间耦合下过强之外,代码中还有任何主题材料。

  • 天地模型就像是都写到了数额访问层中。所以世界模型看起来正视了多少访问层。在数据访问层中定义了名称为Product的类,那连串应该是属于世界模型层的。
  • 呈现层中掺入了决定有个别用户是不是是会员的逻辑。这种专门的学业逻辑应该是
    业务逻辑层中应当管理的,所以也应当放权世界模型层
  • ProductService因为依据了数码访问层,所以也会依靠在web.config
    中陈设的数据库连接字符串等新闻。那使得,整个事情逻辑层也急需借助那么些计划技艺不奇怪运行。
  • 在View中,包罗了太多了函数性成效。他试行了挟持类型调换,字符串格式化等操作,这一个意义应该是在界面展现得模型中完成。

上面只怕是大家大多数写代码时候的兑现,
UI分界面层去正视了数额访问层,有的时候候偷懒就一向引用了那一层,因为实体定义在当中了。业务逻辑层也是依据数据访问层,直接在工作逻辑之中使用了数码访问层里面包车型大巴实业。那样使得全体系统紧耦合,并且可测试性差。那以后我们看看,如何修改这样二个种类,使之达到松散耦合,从而进步可测试性呢?

二 较好的兑现

借助于注入能够较好的消除地点出现的主题素材,未来得以选拔那一思维来重新完成前边的系统。之所以再一次达成是因为,前面包车型客车兑未来一同来的就像是就从未有过设想到扩充性和松耦合,使用重构的章程很难达到规定的标准可观的功效。对于小的系统的话大概还是能,可是对于三个巨型的体系,应该是相比劳苦的。

在写代码的时候,要治本好依据,在前头的兑现这种,代码直接决定了信赖:当ProductService供给2个ObjectContext类的仿佛,直接new了贰个,当HomeController需求一个Product瑟维斯的时候,直接new了八个,那样看起来很酷很方便,实际上使得整个系列拥有相当的大的局限性,变得紧耦合。new
操作实际就引进了依赖,
调整反转这种考虑便是要使的大家相比好的管住注重。

2.一 松耦合的代码

2.1.1 表现层

先是从显示层来深入分析,表现层主若是用来对数码开始展览显示,不该包蕴过多的逻辑。在Index的View页面中,代码希望能够写成这么

<h2>
    Featured Products</h2>
<div>
    <% foreach (var product in this.Model.Products)
        { %>
    <div>
        <%= this.Html.Encode(product.SummaryText) %></div>
    <% } %>
</div>

能够看来,跟以前的表现层代码比较,要清洁多数。很显眼是不要求开始展览类型调换,要促成那样的指标,只供给让Index.aspx那个视图承袭自
System.Web.Mvc.ViewPage<FeaturedProductsViewModel>
就可以,当大家在从Controller成立View的时候,可以开展选用,然后会自动生成。整个用于展现的新闻放在了SummaryText字段中。

此处就引进了二个视图模型(View-Specific
Models),他封装了视图的表现,这么些模型只是轻便的POCOs对象(Plain Old CLR
Objects)。FeatureProductsViewModel中蕴藏了一个List列表,种种成分是3个ProductViewModel类,其中定义了一部分简练的用来数据呈现的字段。

图片 9

这段日子在Controller中,我们只必要给View重返FeatureProductsViewModel对象就能够。举个例子:

public ViewResult Index()
{
    var vm = new FeaturedProductsViewModel();
    return View(vm);
}

现在回去的是空驶列车表,具体的填写格局在圈子模型中,大家跟着看领域模型层。

2.1.2 领域逻辑层

新建二个类库,那之中富含POCOs和部分浮泛类型。POCOs用来对领域建立模型,抽象类型提供抽象作为达到世界模型的进口。信赖注入的规则是面向接口而不是现实性的类编制程序,使得大家得以替换具体贯彻。

今昔大家须求为呈现层提供数据。因而用户分界面层必要引用领域模型层。对数码访问层的简要抽象能够应用Patterns
of Enterprise Application
Architecture一书中讲到的Repository格局。因而定义三个ProductRepository抽象类,注意是抽象类,在圈子模型库中。它定义了1个赢得具有特价商品的肤浅方法:

public abstract class ProductRepository
{
    public abstract IEnumerable<Product> GetFeaturedProducts();
}

本条方法的Product类中只定义了货品的中央消息比方名称和单价。整个涉及图如下:

图片 10

现行反革命来看表现层,HomeController中的Index方法应该要采纳ProductService实例类来得到商品列表,实施价格减价,并且把Product类似转化为ProductViewModel实例,并将该实例加入到FeaturesProductsViewModel中。因为ProductService有2个含有类型为ProductReposity抽象类的构造函数,所以这里能够通过构造函数注入完毕了ProductReposity抽象类的实例。这里和此前的最大分化是,大家未有动用new关键字来马上new多少个指标,而是经过构造函数的艺术传入具体的完成。

当今来看表现层代码:

public partial class HomeController : Controller
{
    private readonly ProductRepository repository;

    public HomeController(ProductRepository repository)
    {
        if (repository == null)
        {
            throw new ArgumentNullException("repository");
        }

        this.repository = repository;
    }

    public ViewResult Index()
    {
        var productService = new ProductService(this.repository);

        var vm = new FeaturedProductsViewModel();

        var products = productService.GetFeaturedProducts(this.User);
        foreach (var product in products)
        {
            var productVM = new ProductViewModel(product);
            vm.Products.Add(productVM);
        }

        return View(vm);
    }

}

在HomeController的构造函数中,传入了贯彻了ProductRepository抽象类的1个实例,然后将该实例保存在概念的民用的只读的ProductRepository类型的repository对象中,那便是压倒一切的经过构造函数注入。在Index方法中,获取数据的ProductService类中的首要功效,实际上是经过传播的repository类来代劳完毕的。

ProductService类是七个彻彻底底的圈子对象,达成如下:

public class ProductService
{
    private readonly ProductRepository repository;

    public ProductService(ProductRepository repository)
    {
        if (repository == null)
        {
            throw new ArgumentNullException("repository");
        }

        this.repository = repository;
    }

    public IEnumerable<DiscountedProduct> GetFeaturedProducts(IPrincipal user)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }

        return from p in
                        this.repository.GetFeaturedProducts()
                select p.ApplyDiscountFor(user);
    }
}

能够看看ProductService也是通过构造函数注入的方法,保存了落实了ProductReposity抽象类的实例,然后借助该实例中的GetFeatureProducts方法,获取原始列表数据,然后开展巨惠管理,进而实现了温馨的GetFeaturedProducts方法。在该GetFeaturedProducts方法中,跟此前不相同的地点在于,未来的参数是IPrincipal,而不是事先的bool型,因为推断用户的现象,那是四个政工逻辑,不应有在展现层管理。IPrincipal是BCL中的类型,所以不设有额外的借助。咱们相应依附接口编制程序IPrincipal是应用程序用户的壹种规范方法。

这里将IPrincipal作为参数字传送递给有个别方法,然后再里面调用完毕的秘诀是借助注入中的方法注入的一手。和构造函数注入同样,同样是将内部贯彻代理给了传播的正视性对象。

今后大家只剩下两块地点尚未管理了:

  • 并未有ProductRepository的切切实实落到实处,那几个很轻松达成,前面停放数据访问层里面去管理,我们只必要创设三个具体的达成了ProductRepository的数据访问类就可以。
  • 私下认可上,ASP.NET MVC
    希望Controller对象有友好的暗许构造函数,因为大家在HomeController中增多了新的构造函数来注入依赖,所以MVC框架不精通什么化解创立实例,因为有依据。那些标题得以经过开辟二个IControllerFactory来缓慢解决,该对象可以创造一个现实的ProductRepositry实例,然后传给HomeController这里不多讲。

现行反革命大家的园地逻辑层已经写好了。在该层,大家只操作领域模型对象,以及.NET
BCL
中的基本对象。模型使用POCOs来代表,命名称为Product。领域模型层必须能够和外边进行交流(database),所以供给多少个抽象类(Repository)来时形成那百分之十效,并且在须求的时候,能够轮换具体完成。

2.一.三 数据访问层

于今大家得以选择LINQ to
Entity来兑现具体的数量访问层逻辑了。因为要兑现世界模型的ProductRepository抽象类,所以供给引进世界模型层。注意,这里的借助形成了多少访问层正视领域模型层。跟以前的恰好相反,代码实现如下:

public class SqlProductRepository : Domain.ProductRepository
{
    private readonly CommerceObjectContext context;

    public SqlProductRepository(string connString)
    {
        this.context =
            new CommerceObjectContext(connString);
    }

    public override IEnumerable<Domain.Product> GetFeaturedProducts()
    {
        var products = (from p in this.context.Products
                        where p.IsFeatured
                        select p).AsEnumerable();
        return from p in products
                select p.ToDomainProduct();
    }
}

在这里供给留意的是,在圈子模型层中,大家定义了一个名称为Product的圈子模型,然后再数据访问层中Entity
Framework帮大家也生成了2个名称为Product的多寡访问层实体,他是和db中的Product表各种对应的。所以大家在点子重临的时候,须要把品种从db中的Product调换为世界模型中的POCOs
Product对象。

图片 11 

Domain
Model中的Product是叁个POCOs类型的指标,他只是包蕴领域模型中供给采取的1对中央字段,DataAccess中的Product对象是炫丽到DB中的实体,它包涵数据库中Product表定义的享有字段,在数码表现层中大家定义了三个ProductViewModel数据显现的Model。

那四个目的之间的调换很简短:

public class Product
{
    public Domain.Product ToDomainProduct()
    {
        Domain.Product p = new Domain.Product();
        p.Name = this.Name;
        p.UnitPrice = this.UnitPrice;
        return p;
    }
}

2.2 分析

二.2.壹 信赖关系图

当今,整个系统的依据关系图如下:

图片 12

展现层和数据访问层都重视领域模型层,那样,在头里的case中,假诺大家新扩充加二个UI分界面;改换1种数据源的蕴藏和获得方式,只需求修改对应层的代码就能够,领域模型层保持了安静。

2.2.2 时序图

万事系统的时序图如下:

图片 13

系统运维的时候,在Global.asax中成立了二个自定义了Controller工厂类,应用程序将其保存在该地便二种,当页面请求进入的时候,程序出发该工厂类的CreateController方法,并招来web.config中的数据库连接字符串,将其传递给新的SqlProductRepository实例,然后将SqlProductRepository实例注入到HomeControll中,并回到。

接下来利用调用HomeController的实例方法Index来创立新的ProductService类,并经过构造函数字传送入SqlProductRepository。ProductService的GetFeaturedProducts
方法代理给SqlProductRepository实例去达成。

最后,再次回到填充好了FeaturedProductViewModel的ViewResult对象给页面,然后MVC实行适度的呈现。

二.二.3 新的构造

在1.壹的完结中,选拔了三层架构,在校正后的兑现中,在UI层和领域模型层中进入了3个表现模型(presentation
model)层。如下图:

图片 14

 

将Controllers和ViewModel从表现层移到了表现模型层,仅仅将视图(.aspx和.ascx文件)和聚合根对象(Composition
Root)保留在了显示层中。之所以那样管理,是能够使得尽恐怕的驱动表现层能够可配置而别的一些尽只怕的能够保持不改变。

3. 结语

一相当的大心我们就编写出了紧耦合的代码,一时候感觉分层了就可以缓慢解决那1主题素材,但是半数以上的时候,都未曾精确的实现分层。之所以轻易写出紧耦合的代码有一个缘由是因为编制程序语言依旧支付意况允许大家只要供给1个新的实例对象,就足以运用new关键字来实例化一个。如果大家须要充足正视,Visual
Studio有些时候能够自动帮大家抬高引用。那使得大家很轻便就犯错,使用new关键字,就大概会引进以来;增添引用就能够发生重视。

调减new引进的重视及紧耦合最棒的措施是使用构造函数注入正视这种设计方式:即只要大家须求二个借助的实例,通过构造函数注入。在其次个部分的落实演示了什么针对抽象而不是有血有肉编制程序。

构造函数注入是反转调节的叁个事例,因为我们反转了对注重的支配。不是采纳new关键字成立2个实例,而是将这种表现委托给了第壹方完毕。

仰望本文能够给我们了解怎么确实完成三层架构,编写松散耦合,可爱抚,可测试性的代码提供部分赞助。

 

http://stackoverflow.com/questions/1362962/when-not-to-use-ioc-and-di

参考http://www.cnblogs.com/yangecnu/p/Introduce-Dependency-Injection.html