ASP.Net设计模式读书笔记

S.O.L.I.D:

单一责任原则(SRP):它要求每个对象只应该为一个元素而改变而且只有一个职责关注点。遵循这个原则,就可以避免单体类(软件领域的瑞士军刀,能解决很多问题)设计问题。使每个类均保持简介,就可以提升系统的可读性和可维护性。

开放封闭原则(OCP):对拓展开发,对修改封闭,这样就能够在不改变类内部行为的情况下添加新功能并拓展类。这个原则努力避免破坏已有类以及其他依赖它的类,因为这会在应用程序中造成bug和错误的涟漪效应。

里氏替换原则(LSP):子类替换父类。

接口分离原则(ISP):ISP原则关注的是将契约的方法分成若干职责分组,并且为这些分组指派不同的接口,这样客户端就不需要实现一个庞大的接口和一堆它们并不使用的方法。这个原则背后的目的:使用相同接口的类之需要实现特定的一组方法,而不是实现一个庞大的单体方法接口。

依赖倒置原则(DIP):DIP原则的宗旨是将自己编写的类与具体的实现类隔离开来,让这些类依赖与抽象类或接口。它倡导面向接口(而不是实现)编程,这确保代码不会与某种实现紧密耦合,从而提高了系统的灵活性。

 

 

应用程序分层=>关注点分离。

 


          典型系统分层

 

1、业务层=> Domain Model

2、服务层=>充当应用程序入口,有时候又被成为门面(facade),为表示层提供强类型视图模型(表示模型)。

  facade模式为一系列的接口和子系统提供一个简单的接口并控制对其的访问。视图模型:为特定视图优化的强类型的类,并包含用来辅助完成数据表示的逻辑。

  为了让客户端和服务层交互,使用Request/Response消息模式;Request部分由客户端提供,它将携带所有必要的参数。Response对象要定义更多属性,以便客户端能够检查请求是否完成,还需要提供一个message属性,在没有完成的情况下,提供更多的信息给客户端。

3、数据访问层=>Repository

4、表示层=>Model-View-Presenter、 Model-View-Controller。

5、用户体验层。(js)


一、业务逻辑层:Fowler总结了4种模式来组织业务逻辑。

  1、Transcation Script

    ①、遵循面向过程的开发风格。

    ②、易于理解,便于上手。

    ③、适合业务逻辑少或者不包含可能增长的功能集合的小型应用程序,以及不熟悉面向对象编程概念的初级开发者团队。

    ①、当需要拓展应用程序的时候,方法数目也会变多,从而形成一个充斥着功能交叠的细粒度方法的API;即使是使用子方法来避免代码重复,但是在工作流中的复制不可避免,而且当程序规模变大的时候,代码基会变得笨重不可维护。

 

  2、Active Record

    ①、非常适用于当数据库和业务模型之间具有一对一映射关系的简单应用程序。(博客,论坛引擎)。

    ②、因为模型和数据库具有一对一的映射关系,可以使用代码生成器为数据库模型快速生成CRUD方法。

    ①、当数据模型与数据库出现不匹配的时候(阻抗失配),就不适合这种模式了。

  3、Domain Model => Domain-Driven Design

    ①、当业务领域非常丰富,有着大量的复杂规则、逻辑和工作流,那么适合使用Domain Model。

    ②、业务模型不必与数据库建立一对一的映射关系。

    ③、容易进行单元测试

    ①、不适合只包含非常少量业务逻辑的应用程序。

    ②、该模式的学习曲线陡峭。    

    Domain Model Demo 

  4、Anemic Model

  Anemic Model有时候被称为反模式,与Domain Model模式相似,但是model中并不包含行为,只包含数据让Model只是作为简单的数据传输类。领域服务扮演更加过程式的代码,与Transaction Script模式比较类似,这会带来一些与之相关的问题。其中一个问题就是违背了“讲述而不是询问”原则,也就是对象应该告诉客户端它们能够做什么或者不能够做什么,而不是暴露属性让客户端来决定某个对象是否处于执行给定动作所需的状态。

 


 

领域驱动设计 Domain-Driven Design:一种流行的利用Domain Model模式的设计方法学。

领域驱动设计中需要关注的几个方面:

  1、通用语言

  它充当一个公共的词汇,开发者、领域专家以及任何其他参与项目的人都使用它来描述该领域。开发者懂得编程,领域专家懂得业务逻辑。二者在讨论需求的时候可以使用通用语言来进行业务层面的沟通,使开发者能够明白具体的业务流程。有点像UML中的活动图。

  2、实体

  以一种抽象的方式包含了真实实体中的数据和行为,任何实体相关的逻辑都应该包含在它内部。实体属于需要标识符的事物,在其整个生命周期中,该标识符都将保持不变。通常、系统使用某种唯一标识符或自动编号值来为所有无法采用自然方式表示的实体提供标识符。有时候、实体确实有自然键,如:身份号、工号。并不是领域模型中所有的对象都需要唯一标识符。对于某些对象,数据是最重要的,而不是标识符。这些对象被称为值对象。

   3、值对象

  值对象没有标识符,他们之所以重要只是因为他们的特性。值对象通常不会单独存在,他们通常是实体的属性。

  4、聚合和聚合根

  大型系统或复杂的领域可能有上千个实体和值对象,它们之间有着错综复杂的关系。领域模型需要一种方法来管理这些关联,更重要的是,在逻辑上属于同一分组的实体和值对象需要定义一个接口,让其他的实体能够通过接口与他们交互。如果没有这类构造,那么以后不同分组对象之间的交互将会相互干扰并产生问题。

  聚合的概念:“一族处于数据变化目的而被视作一个单元的相关联对象”。  聚合根是一个实体,它是这个聚合中唯一能够允许聚合外的对象持有引用的成员。

  在DDD中,聚合是为了确保领域模型中的数据完整性。聚合根是一个充当进入聚合的逻辑途径的特殊实体。

  5、领域服务

  在Domain Model模式的Demo中,BankAccountService类包含了一个银行转账的方法,在这个方法中仅仅是使用了两个领域实体进行了交互。所以那些没有真正位于单个实体中的方法,或者是不需要访问资源库的方法都会被放到领域服务中。领域服务层还可以包含自己的领域逻辑,而且可以作为领域模型的重要组成部分,像实体和值对象一样。

  6、应用程序服务

  应用程序服务是位于领域模型之上的一个瘦层,负责协调应用程序活动。它并不包含业务逻辑,也没有保存任何实体的状态。但它可以存放业务工作流事务的状态。在Domain Model的Demo中,采用了Request-Reply消息传送模式,使用应用程序服务来提供访问领域模型的API。

  7、资源库

  Repository模式充当业务实体的内存集合或仓库,它完全将底层的数据基础设施抽象出来。该模式可以用来将领域模型与任何基础设施关注点分离,使其成为POCO(Plain Old CLR (Common Language Runtime) Object)和PI(Persistence Ignorance)

  POCO(普通CLR对象)优点:

        ①、简单数据存储机制,简化层间数据传递和序列化。

        ②、可用于依赖注入和仓库(Repository)模式。

        ③、减少对其它逻辑层的依赖和复杂性,实现松耦合(较高的逻辑层需要考虑POCO,POCO不关心其它任何事物)。

        ④、通过简化实现可测试性。

  PI(隐式持久化?):领域模型应当忽略数据持久化的细节。

  8、分层

  在DDD中分层是一种重要的概念,因为它有助于加强关注点的分离。

  

                   DDD分层结构图


 业务逻辑层

在业务逻辑层中,经常会使用到下面的几个设计模式来解决程序开发中碰到的业务问题。

  Factory MethodDecoratorTemplate MethodStateStrategy

  使用Specification(规格)模式和Composite(组合)模式来帮助编写业务逻辑查询条件。(自己在类中写方法,达到LINQ中链式调用方法的效果)

  使用Layered Supertype(层超类型)模式来移除公用函数中的重复代码。

  

在业务逻辑层中,还需要遵守一些设计原则,才能实现高内聚、低耦合的目标。

    1、依赖倒置原则和依赖注入模式

    2、接口分离原则

    3、里氏替换原则

 


 

服务层   

服务层的责任:

    1、对内;协调业务用例事务,并将工作委托给业务对象来完成所有低层的实现细节。

    2、对外;服务层封装业务模型并充当各方访问应用程序的接口。

SOA(Service Oriented Architecture,面向服务体系结构):是指一组松散集成服务的设计原则和实践。通常(并非总是)针对分布式应用程序。

    服务基本上就是提供一个或者多个业务应用程序使用的核心业务功能,可以将这组服务是为业务的API。业务应用程序建模过程需要是动态的且经常变化的,而核心业务规则往往是保持不变的。将这些核心业务规则作为独立的服务可以提供更加灵活的体系结构,并且能够更加容易地围绕基本业务规程来快速建立业务应用程序。下面两张图,前者是使用了SOA的系统架构,后者是未使用SOA的系统架构。

                                  

  ①、每个应用程序都有各自的领域解释,所以存在业务逻辑重复现象,如果由于业务的某个方面发生改变,而需要修改软件逻辑,就需要将其传播到所有子系统中,会造成维护难题。

  ②、存在大量的业务流程重复,这是因为每个应用程序的上下文往往相互重叠。

  ③、应用程序与数据结构紧密耦合,如果需要修改数据库,则需要同步修改所有使用数据表的应用程序,从而使它们同时更新。

  ④、需要大量的开发人员来维护每个子系统中的领域逻辑,新加入团队的开发人员通常需要花很长的时间来研究每个应用程序才能掌握公司的业务逻辑。

  ⑤、需要一种审计记录和标准日志机制来横跨所有应用程序,这样客户服务部门就能轻易地查看到某个订单的历史,但由于隐含成本而将该功能搁置。

  ①、维护不再是大问题,现在只在一个地方修改业务逻辑:服务接口之后。随着公司不断成长,只要服务契约保持不变,就可以在不影响整个系统的情况下替换或重新架构应用程序。应用程序只包含很少的业务逻辑,因此初级开发人员也能够轻易编写使用服务的瘦应用程序。新加入的开发人员不再需要理解公司的内部运行机制,这是因为现在有一个API可以把所有业务功能暴露给他们。

  ②、由于存在一个中心位置,因此可以将日志和审计记录应用到所有的业务实务。

  ③、现在已经为该公司定义了API,该逻辑将在所有应用程序中共享,使人们能够快速、容易地为个部门部署小的目标应用程序。

  ④、现在,数据存储从服务层背后抽象出来。这意味着可以在一个中心位置缓存数据,而且对数据模式所做的任何修改之影响服务层后面的业务逻辑,而不是所有的应用程序。

  ⑤、应用程序与服务层之间的交互通过粗粒度接口完成,这简化了系统接口,从而确保数据库不再有伸缩性方面的问题。

SOA的4项信条:

  1、边界明确

  服务接口要尽可能干净、简单,而且有一直的数据交换方法。

  2、服务自治   

  服务方法应该松散耦合而且不依赖于其他方法来执行业务事务,不应该要求客户端按照特定的顺序来调用方法以执行业务事务。客户端应该能够调用单项服务并在一个原子动作内接收到有关该事物成功或失败的响应。服务方法还应该是无状态的。而且在调用另一项服务来完成某个请求之前不让系统处分部分完成状态。

  3、服务共享数据模式和契约而不是类

  SOA的目标之一是互操作行,因此服务应该只暴露契约而不是服务的实现。通信是通过XML完成的,又被称为消息,这些事平台中立的,而且有助于实现互操作性。

  4、基于策略确定服务兼容性

  服务应该暴露其能够用于实现什么功能的策略。然后客户端可以在充分了解如何使用服务以及期待什么响应的情况下使用该服务。可以使用WS-Policy规范来暴露有关服务功能的信息,WS-Policy表示一组描述服务的功能和约束的规范。

 Facade模式是SOA客户端使用的一种常见的模式。

使用Messaging模式:以一种统一的形式解决在众多彼此分离的系统中共享数据的问题。

  1、Document Message模式

  这种模式使得能够采用一种统一、灵活的方法与服务通信。该模式并不适用典型的RPC风格的参数化方法来暴露服务API,而是采用消息对象。

        /// <summary>
        /// 通过国家来获取客户记录
        /// </summary>
        /// <param name="country"></param>
        /// <returns></returns>
        Customer[] RetrieveCustomers(string country);

        /// <summary>
        /// 通过国家、邮政编码来获取客户记录
        /// </summary>
        /// <param name="country"></param>
        /// <param name="postalCode"></param>
        /// <returns></returns>
        Customer[] RetrieveCustomers(string country, string postalCode);

        /// <summary>
        /// 通过国家、邮政编码、街道地址来获取客户记录
        /// </summary>
        /// <param name="country"></param>
        /// <param name="postalCode"></param>
        /// <param name="street"></param>
        /// <returns></returns>
        Customer[] RetrieveCustomers(string country, string postalCode, string street); 
RPC风格

  当参数逐渐变多的之后,代码就变得难以维护,不利于API客户端代码使用它们。

Customer[] FindBy(CustomerSearchRequest request);

    public class CustomerSearchRequest {
        public string Country { get; set; }
        public string PostalCode { get; set; }
        public string MyProperty { get; set; }
    }
Document Message模式

  在通信中使用Document Message模式,可以很容易地改进服务方法并包含额外的参数,而不需要修改方法的签名。

  2、Request-Response模式确保响应和请求一样均使用Document Message模式:CustomerSearchResponse RetrieveCustomers(CustomerSearchRequest request);

    与Request对象一样,Response也可以继承自某个基类,该基类可以提供对通用消息、成功标记及Correlation ID等常见属性的访问。

  3、Reservation模式

  有时,在一次复杂的事务期间有必要维持长流程状态。对于这种情况,可以为第一个响应指派一个预订号码。客户端代码可以在后续的请求中使用该预订号码,让服务层来获取事务。通常使用一个超时时间预订状态在给定时间之后超时,这样就不会无限制地持有资源了。

  4、Idempotent模式

  Idempotent模式规定任何修改状态的请求都应该用一个唯一标识符标记。这个唯一标识符应该接受某种响应存储器的检查,以确保之前尚未处理过该请求。如果发现响应,则返回结果而不影响最初调用的流程的状态。

    

  除了每个请求中包含了一个唯一的标识符,还可以让服务在返回客户端的响应结果中包含同样的ID,这样可以带来另一个好处:允许调用该服务的客户代码验证匹配请求的相应,在这里这个唯一标识符称为关联ID。


数据访问层 

数据访问层DAL仅负责数据存储交互并执行业务对象检索和持久化

  1、DAL中有一般分为两种数据访问策略:1、Repository模式。2、Data Access Objects模式

  2、数据访问模式:

  Unit of Work 模式:用来维护一个由已经被业务事务修改(增、删、改)的业务对象组成的列表。Unit of Work模式负责协调这些修改的持久化工作以及所有标记的并发问题。在DAL中采用Unit of Work模式带来的好处是能够确保数据的完整性。如果在持久化一系列业务对象的过程中出现问题,那么应该将所有的修改回滚,以确保数据始终处于有效状态。

  Unit of Work Demo

  3、数据并发控制:

  数据并发控制(Data Concurrency Control)是用来处理在同一时刻对被持久化的业务对象进行多次修改的系统。当多个用户修改业务对象的状态并发地将其持久化到数据库时,需要一种机制来确保一个用户不会对另一个并发用户的事务状态造成负面影响。

  并发控制分为两种:乐观和悲观。

  乐观并发控制:假设当多个用户对也读对象的状态同时进行修改不会造成任何问题,也称最晚修改生效(last change wins)。对于某些系统,这是相当合理的行为。但入股业务对象的状态需要与从数据库中取出的状态保持一致的时候,就需要悲观并发控制了。

  悲观并发控制:悲观并发控制有多种风格,既可以在检索出记录后锁定数据表,也可以保存业务原始内容的副本,然后在进行更新之前将该副本与数据存储中的版本进行比对,确保在这次事务期间没有对该记录进行修改。在Demo中使用版本号来检查在业务实体从数据库中检索之后是否被修改。更新时,把业务实体的版本号与数据库中的版本号进行比对之后再提交修改,这就确保业务实体在被检索出后没有被修改。

  悲观并发控制Demo

  4、Lazy Loading和Proxy模式

  Lazy Loading是一种企业设计模式,它把资源的加载工作推迟到真正需要的时候。

  Proxy模式充当另一个对象的代理人,使得代理能够控制对象的访问并且可以添加与该操作相关的额外逻辑。

  5、Identity Map 模式:为事务中使用的所有业务对象均保存一个版本,如果同一个Employee实体被请求两次,则返回同一个实体。通常每个业务事务使用一个Identity Map,这可以确保如果在同一个事务中两次检索同一个实体,则该实体将是唯一的,且包含了该事务所做的任何修改。

  6、Query Object模式:表示数据库查询到对象。Query Object模式与LinQ之间存在一定的相似性。System.Linq.Expressions命名空间是Query Object模式的一种实现。而且LINQ to SQL的底层工作机制与上面创建的框架非常相似。

  7、使用对象关系映射器。(EF,书中给的是NHibernate)。

 


 

表示层

 

  1、Factory Method

  2、控制反转 AutoFac

  3、MVC模式

 

    

 

  

  

posted @ 2020-06-05 10:25  水墨晨诗  阅读(236)  评论(0编辑  收藏  举报