现在的位置: 首页 > 综合 > 正文

贫血模式和工厂模式,实体类,工具类以及三层架构

2013年01月30日 ⁄ 综合 ⁄ 共 5133字 ⁄ 字号 评论关闭

最近在做一个项目,用到了这些技术,所以稍微整理了一下,希望能对和我一样菜鸟级的任务有所帮助

  1. 三层架构

微软公司推荐的.NET分层式结构一般分为三层架构,如图所示:

表示层(WC)

业务逻辑层(BBL)

数据访问层(DAL)

 

(1)数据访问层:有时候也称持久层,其功能主要是负责数据库的访问。简单地说就

是实现对数据表的 insert(增)、delete(删)、update(改)、select(查)的操作。

(2)业务逻辑层:是整个系统的核心,它与这个系统的业务(领域)有关。以陇原网上商城城为例,业务逻辑层的相关设计均和商城信息管理系统特有的逻辑相关,如果涉及数据库

访问,则调用数据访问层。

(3)表示层:是系统的 UI 部分,负责使用者与整个系统的交互。在这一层中,理想

的状态是不应该包括系统的业务逻辑。表示层中的逻辑代码仅与界面元素有关。

分层结构的优势:

(1):开发人员可以只关注整个结构中的其中某一层。

(2):可以很容易地用新的实现来替换原有层次的实现。

(3):可以降低层与层之间的依赖。

(4):有利于标准化。

(5):有利于各逻辑的复用。

概括来说,分层设计可以达到如下目的:分散关注、松散耦合、逻辑复用、标准定义。

一个好的分层结构可以使得开发人员的分工更加明确。一旦定义好各层次之间的接口,负

责不同逻辑设计的开发人员就可以分散关注、齐头并进。例如 UI 人员只需考虑用户界面的

体验与操作,业务领域的开发人员可以关注业务逻辑的设计,从而数据库设计人员也不必

为繁琐的用户交互而头痛了。每个开发人员的任务得到了确认,开发速度就可以迅速地提

高了。

松散耦合的好处是显而易见的。如果一个系统没有分层,那么各自的逻辑都紧紧纠缠

在一起,彼此间相互依赖,谁都是不可替换的。一旦发生改变,则牵一发而动全身,对项

目的影响极为严重。降低层与层之间的依赖性,既可以良好地保证未来的发展,在复用性

 

上也有明显优势。每个功能模块一旦定义好统一的接口,就可以被各个模块所调用,而不

用为相同的功能进行重复地开发。

分层结构也具有一些缺陷:

(1)降低了系统的性能。如果不采用分层式结构,很多业务可以直接访问数据库,以

此获取相应的数据,如今却必须通过中间层来完成。

(2)有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中

需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据

访问中都增加相应的代码。

  1. 实体类

在实体类中只定义了类的字段和属性,为什么不在里面定义方法?假如定义方法,是

定义业务逻辑层方法还是数据访问层方法?还是两者都需要定义?如果它们的方法都在实

体类中定义,那该如何实现分层?而且也不想在业务逻辑层调用数据访问层的对象。其实

只需要定义接口就可以了。方法定义在接口中,这样就不用担心破坏分层架构。也可以在

访问下一层的时候不用定义该类的对象,只需要一个接口就可以。

这就是实体类,抽象地说它只负责实体的表示和数据的传递,没有其他的功能需要。

  1. 工具类

工具类只是对本系统所用到的工具的一个封装,其实它和分层架构没有多大的关系,

在陇原商城要用到的工具类有三个,一个是缓存,一个是加密,另外一个是常用操作。

缓存可以提高系统的性能。ASP.NET 一共提供了三种可由 Web 应用程序使用的缓存:

输出缓存:它缓存请求所生成的动态响应。

片段缓存:它缓存请求所生成的各部分。

数据缓存:它以编程的方式缓存任意对象。

下面是我的商城所用的缓存类,可能有问题,希望大家指出:

using System;

using System.Collections.Generic;

using System.Text;

using System.Web;

using System.Web.Caching;  //

namespace FlowerShop.Utility

{

    public sealed class CacheAccess

    {

        /// <summary>

        /// 存到缓存中

        /// </summary>

        /// <param name="cacheKey">用于引用该项的缓存键</param>

        /// <param name="cacheObject">要插入缓存中的对象</param>

        /// <param name="dependency">插入的对象的文件依赖项或缓存键依赖项。当任何依赖项更改时,该对象即无效,并从缓存中移除。如果没有依赖项,则此参数包含null。</param>

        public static void SaveToCache(string cacheKey, object cacheObject, CacheDependency dependency)

        {

            Cache cache = HttpRuntime.Cache;

            cache.Insert(cacheKey, cacheObject, dependency);

        }

        /// <summary>

        /// 从缓存中取出

        /// </summary>

        /// <param name="cacheKey">键</param>

        /// <returns>值</returns>

        public static object GetFromCache(string cacheKey)

        {

            Cache cache = HttpRuntime.Cache;

            return cache[cacheKey];

        }

    }

}

将该方法声明为 static 的好处是可以不用声明一个对象,可以直接通过类名调用该方

法。在该方法中,参数 cacheKey 代表用于引用该项的缓存键,cacheObject 表示要插入缓

存中的对象,dependency 表示所插入的对象的文件依赖项或缓存键依赖项。当任何依赖项

更改时,该对象即无效,并从缓存中移除。如果没有依赖项,则此参数为 null。

Cache cache = HttpRuntime.Cache;表示获取当前应用程序的

System.Web.Caching.Cache;

cache.Insert(cacheKey, cacheObject, dependency);表示将对象 cacheObject 插入

到键为 cacheKey 中。

这个类是从缓存中取出对象。首先还是获得一个缓存类的对象,然后通过传入的键值,

返回相应的对象。

 

  1. 工厂模式

工厂是分层架构的核心之处,工厂的理解对于理解三层架构有很大的帮助,如果想更

多的掌握工厂的知识,可以参考有关设计模式的书籍。

设计分层架构是为了实现层与层之间的可替换。如果现在需要换一种方式实现数据访

问层,但是又不想在表示层和业务逻辑层修改任何代码,而是最多只改一下配置文件就可

以。这该如何实现?此外,系统可以进行并行开发,即想让表示层的人与业务逻辑层的人

和数据访问层的人同时开发,这样开发的效率会大大提高。前面实现的接口就是让上层类

不能直接依赖于下层,即上层类不能直接实例化下层中的类。如果实现了下层的类又会怎

样?举个例子:在业务逻辑层定义了一个类 A,在数据访问层中定义了一个类 B,在 A 中定

义了一个 B 的对象,完成一种操作,但是突然想换一种方式实现数据访问层,那么需不需

要修改业务逻辑层的代码呢?答案是当然需要,因为在 A 中定义了 B 的一个对象,这也就

是定义接口的原因。定义了接口,上层类就不能具体依赖于下层类,而只依赖于下层提供

的一个接口,这就是所谓的松散耦合。

在工厂类中需要用到工厂类和依赖注入。工厂的概念属于设计模式的知识。工厂类顾

名思义,是指专门定义的一个类,目的是用这个类来负责创建其他类的实例。该类根据传

入的参数,动态决定应该创建哪个产品类。依赖注入又名 IOC(Inversion of Control),

中文为控制反转。许多应用都是由两个或更多个类通过彼此合作来实现业务逻辑,这使得

每个对象都需要与它合作的对象的引用,如果这个获取过程要靠自身实现,那么将导致代

码高度耦合且难以测试。所以应用依赖注入以后,如果对象 A 需要调用另一个对象 B,在对

象 B 被创建的时候,由依赖注入将对象 B 的引用传给 A 来调用,即将 B 的接口返回给 A,A

可以直接通过 B 的接口实现相应的操作。

在.NET 中怎样实现根据传入参数动态决定应该创建哪个产品类?这如果在以前实现

起来会很复杂,但是.NET 平台的反射机制提供了解决方案。应该怎样利用工厂类和依赖注

入机制来实现分层架构呢?首先考虑分层的要求,分层是在别的层次不改动的情况下来实

现可以更换不同层次的实现,那这是不是说传入工厂类的参数如果是某个层次的某个实现,

那么就可以通过反射来动态地调用该层次的实现,如果实现了动态加载不同的实现,但是

能保证动态加载后,程序仍能稳定运行吗?或者说应该怎样让程序可以稳定地运行下去。

这还是需要接口,前面定义了业务逻辑层和数据访问层的接口,每一层的实现,无论该层

有多少种实现都只是重写方法而已,所以用接口能够实现无缝的结合。依赖注入只是一种

机制,目的是减少耦合度,通俗地来讲就是避免用 new 来实现类,而是通过接口来实现。

 

  1. 贫血模式和充血模式的简单区别

贫血模型:是指领域对象里只有get和set方法,或者包含少量的CRUD方法,所有的业务逻辑都不包含在内而是放在Business Logic层。

      优点是系统的层次结构清楚,各层之间单向依赖,Client->(Business Facade)->Business Logic->Data Access(ADO.NET)。当然Business Logic是依赖Domain Object的。似乎现在流行的架构就是这样,当然层次还可以细分。

      该模型的缺点是不够面向对象,领域对象只是作为保存状态或者传递状态使用,所以就说只有数据没有行为的对象不是真正的对象。在Business Logic里面处理所有的业务逻辑,在POEAA(企业应用架构模式)一书中被称为Transaction Script模式。

  充血模型:层次结构和上面的差不多,不过大多业务逻辑和持久化放在Domain Object里面,Business Logic只是简单封装部分业务逻辑以及控制事务、权限等,这样层次结构就变成Client->(Business Facade)->Business Logic->Domain Object->Data Access。

       它的优点是面向对象,Business Logic符合单一职责,不像在贫血模型里面那样包含所有的业务逻辑太过沉重。

       缺点是如何划分业务逻辑,什么样的逻辑应该放在Domain Object中,什么样的业务逻辑应该放在Business Logic中,这是很含糊的。即使划分好了业务逻辑,由于分散在Business Logic和Domain Object层中,不能更好的分模块开发。熟悉业务逻辑的开发人员需要渗透到Domain Logic中去,而在Domian Logic又包含了持久化,对于开发者来说这十分混乱。  其次,因为Business Logic要控制事务并且为上层提供一个统一的服务调用入口点,它就必须把在Domain Logic里实现的业务逻辑全部重新包装一遍,完全属于重复劳动。

   如果技术能够支持充血模型,那当然是最完美的解决方案。不过现在的.NET框架并没有ORM工具(不算上开源的NHibernate,Castle之类),没有ORM就没有透明的持久化支持,在Domain Object层会对Data Access层构成依赖,如果脱离了Data Access层,Domain Object的业务逻辑就无法进行单元测试,这也是很致命的。如果有像Spring的动态注入和Hibernate的透明持久化支持,那么充血模型还是能够实现的。

   选择了.NET平台开发,就是选择了开发高效、功能强大应用简单,最适合的模型就是贫血模型,或表数据入口,既有编译器和语言平台很好的支持,也符合微软提倡的开发模式。如果跟潮流在项目中使用充血模型,现有的ORM工具都很复杂难用,光是维护大量的映射文件都成问题。说贫血不够OO,它的Domain Object不够丰富,能把项目做好了,有一定的扩展能力和伸缩行就行了,也不一定要讲究优雅的面向对象。正如SOA,讲究松耦合、高内聚,服务端给客户端提供的服务,也就是一系列的方法调用加上DTO而已,不管服务的内部是不是面向对象的实现,至少它暴露出来的是过程式的服务。

抱歉!评论已关闭.