华夏人下单不降价

清除依赖便是松耦合,松耦合就必定好呢?当然不是先后的耦合度与内聚度要放在一起说,当然,对于世界层来说,适当的缩减类与类之间的依靠是很有要求的,看下面作者的那段代码

  从前一向不知情重视注入有怎么着利益,甚至觉得它是鸡肋,未来想想,当时当成可笑。

一个事例,二个订单系统,它遵照国家有两样的下单方法,对于低层(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等等。那几个时候,就供给一个东西,去将这么多复杂的家伙分离开来,很喜欢的一句话:高内聚,低耦合。

  举个栗子,未来1个很简单的作用,恐怕只要求通过有个别艺术互相调用就足以兑现,OK,那么随着须求的加码,今后加上了多少个职能,那么,我们把相同的东西划分为三个类吧,类经过而生,同样,类似的,一层一层如下所示:

办法–>类–>接口–>模块–>甚至把种种模块之间的涉嫌抽象出来(比如IOC/DI)

 

 

  同理可得,三个中坚的沉思便是,对于八个大种类来说,把具有的模块尽大概的多分几个,三个里面包车型客车事物太多的话,多分开多少个写,一句话表明:高内聚,低耦合

 

浅谈重视注入

 

眼后天在看一本名为Dependency Injection in
.NET
 的书,首要讲了什么样是注重注入,使用依赖注入的亮点,以及.NET平台上依赖注入的各个框架和用法。在那本书的早先,讲述了软件工程中的一个器重的看法便是关切分离(Separation
of
concern, SoC)。正视注入不是指标,它是一一日千里工具和手腕,最后的目标是协理大家付出出松散耦合(loose
coupled
)、可保险、可测试的代码和程序。那条规则的做法是豪门掌握的面向接口,可能说是面向抽象编制程序。

至于怎么着是借助注入,在Stack
Overflow上边有四个题材,何以向二个伍岁的女孩儿解释注重注入,当中得分最高的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 糟糕的达成

编写松耦合代码的首先步,恐怕大家都耳熟能详,那正是对系统一分配层。比如上面包车型地铁经文的三层架构。

图片 1

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

1.1 紧耦合的代码

有很各个办法来规划三个心灵手巧的,可体贴的纷繁应用,不过n层架构是一种大家相比较熟练的措施,那些中的挑衅在于怎么着正确的落到实处n层架构。

即便要促成三个很简短的电子商务网站,要列出商品列表,如下:

图片 2

上边就具体来演示日常的做法,是何等一步一步把代码写出紧耦合的。

1.1.1 数据访问层

要促成货物列表这一成效,首先要编写制定数据访问层,必要统一筹划数据库及表,在SQLServer中筹划的数据库表Product结构如下:

图片 3

表设计好之后,就足以起首写代码了。在Visual Studio
中,新建叁个名为DataAccessLayer的工程,添加几个ADO.NET Entity Data
Model,此时Visual Studio的向导会自动帮咱们转变Product实体和ObjectContext
DB操作上下文。这样大家的 Data Access Layer就写好了。

图片 4

1.1.2 业务逻辑层

突显层实际上能够一贯访问数据访问层,通过ObjectContext 获取Product
列表。可是多数情状下,大家不是一向把DB里面包车型客车数目表现出来,而是须要对数码实行拍卖,比如对会员,须求对一些商品的价位减价。那样大家就须求工作逻辑层,来处理这几个与现实事情逻辑相关的事体。

新建3个类库,命名为DomainLogic,然后添加2个名为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.2.1 注重关系图

明日我们来分析一下,那三层之间的依赖关系,很明显,上面包车型地铁贯彻中,DomianLogic须要借助SqlDataAccess,因为DomainLogic中用到了Product这一实体,而这一个实体是概念在DataAccess这一层的。WebUI这一层要求借助DomainLogic,因为ProductService在这一层,同时,还须求重视DataAccess,因为在UI中也接纳了Product实体,今后一切系统的依赖关系是这么的:

图片 6

1.2.2 耦合性分析

行使三层构造的首要性指标是分开关心点,当然还有二个缘由是可测试性。我们相应将世界模型从数量访问层和显示层中分离出来,那样那五个层的变迁才不会污染领域模型。在大的系统中,这一点很要紧,那样才能将系统中的差别部分隔绝开来。

明日来看此前的达成中,有没有模块性,有没有至极模块能够凝集出来呢。未来加上多少个新的case来看,系统是或不是能够响应这几个要求:

添加新的用户界面

除去WebForm用户之外,只怕还索要1个WinForm的界面,以往我们是否复用领域层和数据访问层呢?从注重图中能够看来,没有别的多个模块会凭借表现层,由此很不难达成那点浮动。大家只必要创制多少个WPF的富客户端就能够。现在漫天系统的借助图如下:

图片 7

转移新的数据源

兴许过了一段时间,须求把整个体系布局到云上,要利用其余的数额存款和储蓄技术,比如Azure
Table Storage 瑟维斯。以后,整个访问数据的合计产生了变更,访问Azure
Table Storage Service的法门是Http协议,而在此之前的半数以上.NET
访问数据的措施都是基于ADO.NET
的措施。并且数据源的保存方法也发出了改变,此前是关系型数据库,未来改成了key-value型数据库。

图片 8 

由地方的借助关系图能够观察,全体的层都注重了数据访问层,要是改动数据访问层,则领域逻辑层,和表现层都亟待进行对应的改动。

1.2.3 问题

而外上边的各层之间耦合下过强之外,代码中还有任何标题。

  • 世界模型就像都写到了数据访问层中。所以世界模型看起来注重了数码访问层。在数码访问层中定义了名为Product的类,那体系应该是属于世界模型层的。
  • 突显层中掺入了决定某个用户是还是不是是会员的逻辑。这种业务逻辑应该是
    业务逻辑层中应有处理的,所以也相应放置世界模型层
  • ProductService因为依靠了数额访问层,所以也会凭借在web.config
    中配备的数据库连接字符串等音讯。那使得,整个工作逻辑层也供给依靠这几个安顿才能健康运转。
  • 在View中,包罗了太多了函数性作用。他实施了挟持类型转换,字符串格式化等操作,这几个功用应该是在界面显示得模型中形成。

地点也许是大家半数以上写代码时候的完成,
UI界面层去信赖了数据访问层,有时候偷懒就直接引用了这一层,因为实体定义在当中了。业务逻辑层也是凭借数据访问层,直接在工作逻辑之中使用了数额访问层里面包车型客车实业。这样使得整个体系紧耦合,并且可测试性差。那今后大家看看,怎样修改那样2个种类,使之达到松散耦合,从而增强可测试性呢?

2 较好的落到实处

依赖注入能够较好的解决地点出现的题材,今后可以动用这一合计来再度完结后边的连串。之所以重新落成是因为,前边的实现在一方始的就如就不曾考虑到扩张性和松耦合,使用重构的情势很难达到能够的作用。对于小的连串来说大概还足以,可是对于二个大型的系统,应该是比较辛劳的。

在写代码的时候,要保管好凭借,在前头的兑现那种,代码间接控制了借助:当ProductService必要四个ObjectContext类的如同,直接new了二个,当HomeController须要二个ProductService的时候,直接new了3个,那样看起来很酷很方便,实际上使得整个类别具有十分大的局限性,变得紧耦合。new
操作实际就引入了依靠,
控制反转那种考虑便是要使的大家相比好的管制重视。

2.1 松耦合的代码

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有3个含有类型为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)来时形成这一意义,并且在要求的时候,能够替换具体达成。

2.1.3 数据访问层

将来大家能够利用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();
    }
}

在此处要求注意的是,在天地模型层中,大家定义了3个名为Product的领域模型,然后再数据访问层中Entity
Framework帮我们也生成了三个名为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.2.1 注重关系图

现行反革命,整个系统的借助关系图如下:

图片 12

呈现层和多少访问层都依靠领域模型层,那样,在头里的case中,假若大家新添加贰个UI界面;更换一种数据源的蕴藏和取得格局,只供给修改对应层的代码即可,领域模型层保持了安澜。

2.2.2 时序图

全副类别的时序图如下:

图片 13

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

然后采纳调用HomeController的实例方法Index来创制新的ProductService类,并由此构造函数字传送入SqlProductRepository。ProductService的GetFeaturedProducts
方法代理给SqlProductRepository实例去落到实处。

最终,重临填充好了FeaturedProductViewModel的ViewResult对象给页面,然后MVC举办安妥的显现。

2.2.3 新的布局

在1.1的兑现中,采取了三层架构,在立异后的贯彻中,在UI层和天地模型层中投入了肆个人作品彰显模型(presentation
model)层。如下图:

图片 14

 

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

3. 结语

一十分的大心大家就编写出了紧耦合的代码,有时候觉得分层了就能够化解这一标题,可是一大半的时候,都未曾科学的完毕分层。之所以不难写出紧耦合的代码有多少个原因是因为编制程序语言依旧支付环境允许大家假使要求3个新的实例对象,就足以选用new关键字来实例化四个。借使大家必要丰裕正视,Visual
Studio有个别时候能够活动帮大家添加引用。那使得大家很不难就犯错,使用new关键字,就恐怕会引入以来;添加引用就会爆发依赖。

调整和裁减new引入的正视及紧耦合最棒的不二法门是使用构造函数注入注重那种设计形式:即只要大家要求三个依靠的实例,通过构造函数注入。在第3个部分的兑现演示了什么针对抽象而不是有血有肉编制程序。

构造函数注入是反转控制的多个事例,因为大家反转了对借助的支配。不是选用new关键字创立二个实例,而是将那种行为委托给了第③方完结。

瞩望本文能够给我们领悟什么真正兑现三层架构,编写松散耦合,可珍贵,可测试性的代码提供一些增派。

 

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

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

相关文章