松耦合就必定好呢

扫除依赖正是松耦合,松耦合就自然好吧?当然不是程序的耦合度与内聚度要放在一块儿说,当然,对于世界层来讲,适当的收缩类与类之间的依赖是很有必不可缺的,看上边小编的这段代码

  从前平昔不明了正视注入有何好处,以致认为它是鸡肋,今后怀想,当时就是可笑。

2个例子,3个订单系统,它依据国家有例外的下单方法,对于低层(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)

 

 

  同理可得,一个焦点的沉思正是,对于二个大类型以来,把具有的模块尽也许的多分多少个,一个里面包车型大巴东西太多以来,多分开多少个写,一句话表明:高内聚,低耦合

 

浅谈注重注入

 

近来几天在看一本名叫Dependency Injection in
.NET
 的书,首要讲了何等是借助注入,使用正视注入的长处,以及.NET平台上注重注入的各个框架和用法。在那本书的发端,讲述了软件工程中的八个重大的见地便是关爱分离(Separation
of
concern, SoC)。重视注入不是目标,它是一八种工具和手腕,最后的目标是帮扶大家付出出松散耦合(loose
coupled
)、可保障、可测试的代码和程序。那条规则的做法是大家纯熟的面向接口,可能说是面向抽象编制程序。

至于怎样是依附注入,在Stack
Overflow下面有多个主题材料,什么向多少个六周岁的小兄弟解释正视注入,当中得分最高的一个答案是:

“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 倒霉的贯彻

编写松耦合代码的首先步,可能大家都如数家珍,那正是对系统一分配层。譬喻上边包车型大巴出色的三层架构。

图片 1

分完层和落实好是两件事情,并不是说分好层之后就可见松耦合了。

1.1 紧耦合的代码

有诸多样主意来安插三个灵活的,可爱慕的错综相连应用,然而n层架构是1种我们比较熟识的法子,那么些中的挑衅在于怎样科学的兑现n层架构。

例如要落到实处三个非常粗大略的电子商务网址,要列出商品列表,如下:

图片 2

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

壹.一.一 数据访问层

要落到实处商品列表那1效果,首先要编写数据访问层,供给统一图谋数据库及表,在SQLServer中安排的数据库表Product结构如下:

图片 3

表设计好以往,就可以起来写代码了。在Visual Studio
中,新建三个名称为DataAccessLayer的工程,增加三个ADO.NET Entity Data
Model,此时Visual Studio的向导会自动帮大家转移Product实体和ObjectContext
DB操作上下文。这样我们的 Data Access Layer就写好了。

图片 4

一.一.二 业务逻辑层

表现层实际上能够直接待上访问数据访问层,通过ObjectContext 获取Product
列表。然而多数状态下,大家不是直接把DB里面包车型地铁数据表现出来,而是要求对数码进行拍卖,比方对会员,要求对少数商品的价位促销。那样大家就须要职业逻辑层,来拍卖那个与现实事情逻辑相关的事务。

新建1个类库,命名叫DomainLogic,然后增多三个名字为Product瑟维斯的类:

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中,三层之间的依赖能够通过项目引用表现出来。

壹.2.1 正视关系图

今天大家来剖析一下,那三层之间的凭借关系,很显然,下面的贯彻中,DomianLogic供给依靠SqlDataAccess,因为DomainLogic中用到了Product那壹实体,而以此实体是概念在DataAccess这一层的。WebUI那一层需求依赖DomainLogic,因为ProductService在这一层,同有时间,还索要重视DataAccess,因为在UI中也接纳了Product实体,现在整种类统的注重关系是那样的:

图片 6

一.二.二 耦合性剖判

运用三层结构的重中之重指标是分离关怀点,当然还应该有四个缘故是可测试性。我们相应将世界模型从数据访问层和显示层中分离出来,那样那八个层的转移才不会污染领域模型。在大的系统中,那点很要紧,那样技艺将系统中的差异部分隔开开来。

今昔来看从前的实现中,有未有模块性,有未有相当模块能够凝集出来吧。未来加上多少个新的case来看,系统是或不是能够响应那一个要求:

加多新的用户分界面

除了这些之外WebForm用户之外,恐怕还索要三个WinForm的分界面,以往我们可以还是不可以复用领域层和数据访问层呢?从正视图中得以看出,未有别的二个模块会借助表现层,由此很轻巧达成这或多或少变型。大家只供给成立叁个WPF的富客户端就能够。今后漫天系统的注重性图如下:

图片 7

转移新的数据源

或是过了壹段时间,需求把任何类别布局到云上,要动用别的的数目存款和储蓄技艺,比方Azure
Table Storage 瑟维斯。未来,整个访问数据的商业事务产生了变通,访问Azure
Table Storage Service的法子是Http协议,而在此以前的大大多.NET
访问数据的办法都以基于ADO.NET
的点子。并且数据源的保留方法也时有爆发了改换,从前是关系型数据库,今后改为了key-value型数据库。

图片 8 

由地点的依赖性关系图能够看看,全数的层都看重了数量访问层,借使改造数据访问层,则领域逻辑层,和呈现层都亟待举办相应的修改。

1.2.3 问题

除却上边的各层之间耦合下过强之外,代码中还会有其余标题。

  • 天地模型就像都写到了数量访问层中。所以世界模型看起来重视了数码访问层。在数额访问层中定义了名称为Product的类,那连串应该是属于世界模型层的。
  • 显示层中掺入了调节有个别用户是还是不是是会员的逻辑。这种业务逻辑应该是
    业务逻辑层中应有管理的,所以也应有放权世界模型层
  • Product瑟维斯因为依附了多少访问层,所以也会借助在web.config
    中安顿的数据库连接字符串等音信。那使得,整个事情逻辑层也供给依赖那么些安顿技巧不奇怪运转。
  • 在View中,包涵了太多了函数性功用。他实践了挟持类型转变,字符串格式化等操作,那几个作用应该是在分界面呈现得模型中达成。

上边大概是大家超越55%写代码时候的贯彻,
UI分界面层去正视了数量访问层,不经常候偷懒就直接引用了那一层,因为实体定义在内部了。业务逻辑层也是信赖数据访问层,直接在作业逻辑之中使用了数据访问层里面包车型大巴实体。那样使得整个连串紧耦合,并且可测试性差。那现在大家看看,怎么着修改那样一个类别,使之达到松散耦合,从而抓好可测试性呢?

二 较好的兑现

依附注入能够较好的消除地点出现的标题,以后得以使用那1观念来再一次实现后面包车型大巴连串。之所以重新完结是因为,前边的贯彻在壹开头的仿佛就一贯不考虑到扩大性和松耦合,使用重构的章程很难达到能够的效益。对于小的体系来讲也许还足以,不过对于二个重型的系统,应该是比较不方便的。

在写代码的时候,要管制好依据,在后面包车型地铁落到实处这种,代码直接调节了借助:当ProductService必要2个ObjectContext类的如同,直接new了3个,当HomeController供给二个ProductService的时候,直接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字段中。

此间就引进了3个视图模型(View-Specific
Models),他封装了视图的一颦一笑,那几个模型只是轻松的POCOs对象(Plain Old CLR
Objects)。FeatureProductsViewModel中隐含了2个List列表,每一个元素是一个ProductViewModel类,当中定义了有的简短的用于数据显示的字段。

图片 9

未来在Controller中,我们只必要给View重回FeatureProductsViewModel对象就能够。比方:

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

近期赶回的是空驶列车表,具体的填充情势在世界模型中,我们跟着看领域模型层。

二.一.2 领域逻辑层

新建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有三个暗含类型为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抽象类的三个实例,然后将该实例保存在概念的民用的只读的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)来时做到这一意义,并且在供给的时候,能够轮换具体实现。

二.壹.三 数据访问层

现行反革命大家得以接纳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类型的靶子,他单纯包蕴领域模型中必要使用的一些中坚字段,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中创制了3个自定义了Controller工厂类,应用程序将其保存在本地便几种,当页面请求进入的时候,程序出发该工厂类的CreateController方法,并查找web.config中的数据库连接字符串,将其传递给新的SqlProductRepository实例,然后将SqlProductRepository实例注入到HomeControll中,并赶回。

然后选拔调用HomeController的实例方法Index来创立新的ProductService类,并经过构造函数字传送入SqlProductRepository。ProductService的GetFeaturedProducts
方法代理给SqlProductRepository实例去贯彻。

末段,重临填充好了FeaturedProductViewModel的ViewResult对象给页面,然后MVC进行适宜的表现。

贰.贰.三 新的构造

在1.一的达成中,选取了三层架构,在创新后的兑现中,在UI层和天地模型层中进入了2人作品呈现模型(presentation
model)层。如下图:

图片 14

 

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

3. 结语

一相当大心我们就编写出了紧耦合的代码,有的时候候认为分层了就可以解决那1主题素材,但是大部分的时候,都未有科学的达成分层。之所以轻便写出紧耦合的代码有一个原因是因为编制程序语言依旧支付碰着允许大家假设须要二个新的实例对象,就足以接纳new关键字来实例化3个。假如大家须要充分依赖,Visual
Studio有个别时候能够活动帮大家增加引用。那使得大家很轻松就犯错,使用new关键字,就恐怕会引进以来;增多引用就能时有产生正视。

缩减new引进的依附及紧耦合最棒的不贰诀要是采纳构造函数注入重视这种设计方式:即假诺大家供给四个依据的实例,通过构造函数注入。在其次个部分的贯彻演示了什么样针对抽象而不是实际编制程序。

构造函数注入是反转调控的2个例证,因为大家反转了对借助的决定。不是选取new关键字成立二个实例,而是将这种作为委托给了第三方完毕。

企望本文能够给大家探听哪些真正落实三层架构,编写松散耦合,可爱慕,可测试性的代码提供部分支持。

 

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

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

相关文章