这个文档是结合了孙亚民的《实战揭秘:开发.Net平台应用系统框架》这篇文章形成的。
一、系统的基本结构
如今,N层应用程序已经成为构建企业级软件的标准。对于大多数人来说,N层应用程序就是被分成多个独立的逻辑部分的应用程序。
所谓层次的概念就是一层一层分割、一目了然的处理方式。层次体系就是利用分层的方式来处理复杂的功能,层次系统要求上层子系统可以使用下层子系统的功能,而下层子系统不能使用上层子系统的功能。一般下层每个每个程序接口执行当前的一个简单功能,而上层通过调用不同的的下层程序,并按不同的顺序来执行这些下层程序,层次体系就是以这种方式完成多个复杂的业务功能的。
软件分层有以下优点:
l 良好的透明和封装;
l 高内聚、低耦合;
l 易于扩展、维护和重用;
l 开发人员易于分工,提供工作效率。
缺点:
l 项目前期效率降低;
l 开发难度增大。
通常说的三层体系架构,是在客户端与数据库之间加入一个中间层。对于典型的三层应用系统来说,通常可以把系统分成以下三个层次:
l 数据库层
l 用户界面层
l 应用服务层
对于应用系统来说,在这三个层次中,系统的主要功能和业务逻辑在应用服务层进行处理,对于系统框架来说,主要处理的也是这个层次的架构。
对于应用服务层来说,需要处理以下几个方面的问题:
l 数据的表示方式,也就是实体类的表示方式,以及同数据库的对应关系,即所谓的O-R Map的问题。在框架中,这个部分位于数据实体层
l 数据的存取方式,也就是实体类的持久化问题,通常采用数据库来永久存储数据实体,这就需要解决同数据库的交互问题。在框架中,这个部分位于实体控制层
l 业务逻辑的组织方式。在面向对象的系统中,业务逻辑是通过对象间的消息传递来实现的。在这个部分,为了保证逻辑处理的正确性和可靠性,还必须支持事务处理的能力。在框架中,这个部分位于业务规则层
l 业务服务的提供方式。为了保证系统的灵活性和封装性,系统必须有一个层来封装这些业务逻辑,向客户端提供服务,同时作为系统各个模块间功能调用的接口,保证系统的高内聚和低耦合性。这里的客户指的不是操作的用户,而是调用的界面、其他程序等。Web层(ASP.Net页面)通常只同这个部分交互,而不是直接调用业务逻辑层或者数据实体的功能。在框架中,这个部分位于业务外观层。
将系统划分成这么多层次,其好处是能够使得系统的架构更加清晰,并且,系统的层次划分的清晰,就意味着每个层次完成的功能就比较单一,也就意味着这些功能的代码是有规律可循的,也就意味着我们可以开发一些工具来生成这些代码,从而减少代码编写的工作量,使得开发人员可以将更多的精力放到业务逻辑的处理上。正是基于这个想法,我们开发了针对这个框架的开发工具,在实际的使用,能够减少很多代码的编写量,效果非常好。同时,为了应用服务层更好的工作,可以设计一个支持这个框架的应用系统中间件。
N层体系架构的应用程序将中间层分解为业务外观、业务规则、数据访问、实体控制层等层进行处理,这种程序成为“应用服务器”。整个系统的结构图如下:
(图中的箭头表示使用关系)
开发人员可以将应用程序中的业务逻辑放在中间层应用服务器上,这样业务逻辑与用户界面分开。在保证客户端功能的前提下,为用户提供一个简洁的界面。这就意味着如果需要修改应用程序代码,只需要对应用服务器进行修改,而不用修改成千上万的的客户端应用程序。从而使开发人员可以专注于应用系统核心业务逻辑的分析、设计和开发,简化了应用系统的开发、更新和升级工作。
.Net技术为N层体系架构的实现提供了良好的技术基础。.Net Framework 是微软推出的一套目前主流的开发平台。基于开发人员的角度来说,它是一个公共平台的类库,包含了近100个命名空间(namespace)的近5000个类,想想看这提供的功能是多么的强大,此外还包括一个公共语言运行库(CLR)。只要符合.Net公共运行规范(CLS)的语言都可以使用它提供的强大的类,并编译为微软的中间语言(MSIL),在其它的应用中就可以当作一个组件来调用。这些组件同时享受公共运行库所带来的一切好处,例如自动垃圾回收(GC)、实时编译(JIT)、跨语言跨平台。另外如ASP.Net编译执行、CodeBedind方式,在加上.Net库提供的支持事件的各种Web控件,以及.Net公共平台的类库,这和以前编写网页方式相比可谓是一场巨大的革命。以上这些技术将推动N层体系架构成为开发技术的主流。
二、各个层次的详细说明
对应系统的结构,在每个层次,都有相应的一套策略,详细说明如下:
1、数据库服务层
数据库用于对象的持久化储存,也即数据的存放。数据库层采用关系型数据库。系统可以支持各种主流的关系型数据库系统,包括SQL Server7.0以上版本和Oracle8以上版本的数据库。
框架提供一套类库,作为客户端同数据库之间交互的中介,很大一部分功能就是要对两者之间的交互进行控制,并提供一系列提高性能和安全性的服务。
2、用户界面层
用户界面层用于同用户的交互,可以是Web浏览器用户或传统客户端。
3、数据实体层
这个层用于封装实体类的数据结构,用于映射数据库的数据,表现实体类的类结构和数据。在开发过程中,我们充分感觉到了.Net中DataSet数据对象的强大功能,因此,在O-R Map上,我们实际上是通过DataSet来实现的。
数据实体层包含三个方面的内容:
1) 核心类库定义了EntityData类,这个类继承了DataSet作为所有实体类的框架类,定义了各个实体类的一般结构,至于每个实体类具体的结构,在运行时刻由下述办法确定:
2) 实体类的定义通过XML文件来确定,该XML文件符合JIXML对象实体描述语言的规范(注:JIXML是迪讯开发的 对象—实体 映射语言),用于确定实体类的结构。例如,一个关于订单的实体类的定义可能类似于下面的结构:
3) 实体类的结构由一系列的类构造器在运行时刻,根据上述规范制定的XML来生成。这些类构造器实现IClassBuilder接口。系统核心类库预定义了一些标准的Builder,一般情况下,直接使用这些标准的Builder就可以了。
类构造器采用的类构造工厂的设计模式,如果使用者觉得标准的Builder不能满足要求,也可以扩展IClassBuilder接口,编写自己的类构造器,然后在系统配置文件中指明某各类的类构造器的名称即可。
系统同时提供了实体对象缓存服务。通过上述方式产生的实体对象可以被缓存,这样,在第二次调用该对象时,可以从缓存中读取,而不用从头重新生成,从而大大提高了系统的性能。
数据实体层采用这种设计模式具有以下优点:
u 实体类定义XML文件可以通过工具来自动生成,减轻开发工作量。
u 在修改实体类的定义时,如果修改的部分不涉及到业务逻辑的处理,只需要修改XML文件就可以了,不用修改其它程序和重新编译。
u 系统提供的实体对象缓存服务可以大大提高了系统的性能。
u 类构造工厂的设计模式大大提高了系统的灵活性。
4、数据访问层
这个层次提供对数据库操作的服务,通常执行以下一些操作:
1) 连接数据库
2) 执行数据库操作
3) 查询数据库,返回结果
4) 维护数据库连接缓存
5) 数据库事务调用
数据访问层提供了代表调用者在数据库执行下列操作的方法:
u 创建(Create)记录;
u 读取(Read)数据库中的记录,并将业务实体数据返回调用者;
u 通过调用者提供的修改过的实体数据更新(Update)数据库中的记录;
u 删除(Delete)数据库中的记录。
执行上述任务的方法常被称为CRUD方法,其中CRUD是每个任务的首字母缩写。
在数据访问层设计中,还要需考虑以下细节:
u 管理锁定和并发性;
u 安全性和授权的问题;
u 处理事务的问题;
u 数据分页;
u 为非事务数据的查询、显示实现高速缓存的策略;
u 数据流化和数据串行化的问题。
以上问题都是设计数据访问层时需要注意的问题,深入的考虑这些问题将提高应用程序的健壮性、稳定性、安全性和高性能。
框架的类库中包含了数据访问服务,封装了常用的对各种数据库的操作,可以访问不同类型的数据库,使得应用系统在更换数据库时,不用修改原有的代码,大大简化了开发和部署工作。
数据访问服务还维护数据库连接缓存,提高系统性能,以及对数据库事务调用的服务。
数据访问服务在核心类库中主要通过DBCommon类来提供对数据访问功能调用的服务。
5、实体控制层
实体控制层用于控制数据的基本操作,如增加、修改、删除、查询等,同时为业务规则层提供数据服务。
实体控制层的类实现IEntityDAO接口。这个接口定义了实现数据操纵的主要必要方法,包括增加、修改、删除和查找。
IEntityDAO的定义如下:
public interface IEntityDAO : IDisposable
{
void InsertEntity(EntityData entity);
void UpdateEntity(EntityData entity);
void DeleteEntity(EntityData entity);
EntityData FindByPrimaryKey(object strKeyValue);
}
同数据实体层相结合,这两部分实现了应用服务层同数据库的交互。
采用数据实体和实体控制分开的设计方法,具有以下优点:
u 避免了J2EE体系中操纵EntityBean系统资源消耗大,效率低下的缺陷。
u 解决了J2EE体系中使用EntityBean传输数据时开销大,过程复杂、效率低的缺陷。
u 可以单独修改实体结构和对实体数据的操纵,使得系统更加灵活
u 实体控制层的类可以通过工具自动生成,减轻开发工作量。
6、业务规则层
业务规则层包含各种业务规则和逻辑的实现。业务规则完成如客户帐户和书籍订单的验证这样的任务。这是整个应用系统中最为复杂的部分。
业务规则层的设计通常需要进行很好的建模工作。业务规则的建模,一般采用UML来进行。可以使用UML的序列图、状态图、活动图等来为业务规则建模。这个部分的工作,通常通过一系列的类之间的交互来完成。例如,在一个库存系统的入库单入库操作中,除了需要保存入库单外,在这个之前,还必须对入库单涉及的产品的数量进行修改,其代码通常如下:
public void StoreIntoWarehouse(EntityData IndepotForm)
{
DataTable tbl=IndepotForm.Tables["InDepotFormDetail"];
try
{
ProductEntityDAO ped=new ProductEntityDAO();
for(int i=0;i<tbl.Rows.Count;i++)
{
DataRow formdetail=tbl.Rows[i];
string productID=formdetail["ProductID"].ToString();
decimal inCount=(decimal)formdetail["InCount"];
EntityData product=ped.FindByPrimaryKey(productID);
DataRow productRow=product.GetRecord("Product");
productRow["CurrentCount"]=(decimal)productRow["CurrentCount"]+inCount;
ped.UpdateEntity(product);
}
ped.Dispose();
InDepotFormEntityDAO inDepotForm=new InDepotFormEntityDAO();
inDepotForm.InsertEntity(IndepotForm);
IndepotForm.Dispose();
ContextUtil.SetComplete();
}
catch(Exception ee)
{
ContextUtil.SetAbort();
throw ee;
}
}
7、业务外观层
业务外观层为 Web 层提供处理、浏览和操作的界面。业务外观层用作隔离层,它将用户界面与各种业务功能的实现隔离开来。
业务外观层只是将已经完成的系统功能,根据各个模块的需要,对业务规则进行高层次的封装。
框架没有规定采用在业务外观层采用何种实现方式,但是建议使用Web Service来提供服务。采用IIS为Web服务器,可以很方便的部署Web Service。
8、Web层
Web 层为客户端提供对应用程序的访问。Web 层由 ASP.NET Web 窗体和代码隐藏文件组成。Web 窗体只是用 HTML 提供用户操作,而代码隐藏文件实现各种控件的事件处理。
除了上述6个逻辑层(3-8层)以外,系统通常还包括一个系统配置项目,提供应用程序配置和跟踪类。
三、框架中间件提供的主要服务
框架中间件提供一系列的服务,以实现对构筑其上的应用软件的支持。
1、O-R Map:对象—关系数据库映射服务
这部分完成应用程序中的实体对象同关系型数据库的映射,主要为数据实体层提供服务。
在这个部分中,定义了JIXML实体—对象映射语言。这是我们开发的一种使用XML来描述对象—实体间的映射关系的规范语言,开发者可以使用它来描述对象—实体间的映射关系。开发者也可以直接扩展IClassBuilder接口,手工完成对象—实体间映射关系的代码。系统在运行时刻,会根据配置文件的设置,调用实体类的构造器,动态构造出实体对象的结构。
2、Database Access:数据库访问服务
这个部分提供对数据库访问的服务。在这个框架上构建的应用软件系统,不直接操纵数据库,而是通过类库提供的数据访问服务来进行。数据库访问服务作为应用程序同数据库之间的中介者,能够有效防止对数据库的不安全操作。
数据库访问服务同时提供了对数据库库事务处理的调用方法,开发者可以很方便的通过数据库访问服务调用数据库的事务处理功能。
3、DML Search:数据操纵语句查询服务
在系统架构中,对数据库进行操作的SQL语句不在程序中硬编码,而是同数据实体层的实体类结构一样在XML文件中描述,其结构符合JIXML规范。这些操纵语句中的基本部分,如数据的插入、删除、修改、查询等语句,可以通过我们自己开发的工具生成。这样,在系统的便于修改性和灵活性上能够得到很大的提高。这样一来,系统必须提供这些数据操纵语句的查询服务。核心类库提供了在XML文件中查找这些数据操纵语句和相关参数的服务。
4、Entity Buffer&Search:实体对象缓存&查找服务
系统中的实体对象在第一次创建后,就被系统缓存起来,当系统第二次需要访问该对象时,不需要再从头创建这个对象,而只需要从缓存中取出即可。这就是迪通提供的实体对象缓存服务。同这个服务相关联的是实体对象的查找服务,即从这些缓存的实体对象中寻找相应的实体对象的服务。
5、Transaction:事务处理服务
我们充分利用Windows COM+事务处理机制的强大功能,使在应用程序能够充分使用事务处理的功能,保证应用系统的稳定性和可靠性。
当某个类需要使用事务处理功能时,首先使该类继承System.EnterpriseServices名称空间下的ServicedComponent类,然后使用如下方式申明该类使用的事务类型:[Transaction(TransactionOption.Required)]。系统在该类第一次被调用时,自动在COM+服务中注册中该类,使得应用程序可以使用COM+的事务处理功能。
系统支持如下几种事务处理类型:
成员名称 | 说明 |
Disabled | 忽略当前上下文中的任何事务。 |
NotSupported | 使用非受控事务在上下文中创建组件。 |
Required | 如果事务存在则共享事务,并且如有必要则创建新事务。 |
RequiresNew | 使用新事务创建组件,而与当前上下文的状态无关。 |
Supported | 如果事务存在,则共享该事务。 |
四、核心类库概要说明
在框架的核心类库中,我们提供了以下核心类和接口:
u EntityData:定义实体类的通用结构
u IClassBuilder:定义实体类结构构造的结构。迪通中预定义了根据这个接口实现的几个标准类:AbstractClassBuilder、SingletableClassBuilder、ThickClassBuilder、StandardClassBuilder。这些Builder通过ClassBuilderFactory进行管理。
u IEntityDAO:定义实体控制类的接口
u EntityDataManager:提供对所有实体类的缓存管理和查找服务
u DBCommon:封装数据库操作
u ApplicationConfiguration:记录系统配置
u SqlManager:管理系统的SQL语句及其参数。
开发人员开发时,只需要引入提供的核心类库就可以充分利用以上编程接口的功能了。
五、附录
1、什么是持久化和ORM及为什么要做持久化和ORM设计
何谓“持久化”
持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
何谓“持久层”
持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。
何谓“对象数据映射(ORM)”
ORM-Object/Relational Mapper,即“对象-关系型数据映射组件”。对于O/R,即 Object(对象)和 Relational(关系型数据),表示必须同时使用面向对象和关系型数据进行开发。
[备注]:建模领域中的 ORM 为 Object/Role Modeling(对象角色建模)。另外这里是“O/R Mapper”而非“O/R Mapping”。相对来讲,O/R Mapping 描述的是一种设计思想或者实现机制,而 O/R Mapper指以O/R原理设计的持久化框架(Framework),包括 O/R机制还有 SQL自生成,事务处理,Cache管理等。
在目前的企业应用系统设计中,MVC,即 Model(模型)- View(视图)- Control(控制)为主要的系统架构模式。MVC 中的 Model 包含了复杂的业务逻辑和数据逻辑,以及数据存取机制(如 JDBC的连接、SQL生成和Statement创建、还有ResultSet结果集的读取等)等。将这些复杂的业务逻辑和数据逻辑分离,以将系统的紧耦合关系转化为松耦合关系(即解耦合),是降低系统耦合度迫切要做的,也是持久化要做的工作。
MVC 模式实现了架构上将表现层(即View)和数据处理层(即Model)分离的解耦合,而持久化的设计则实现了数据处理层内部的业务逻辑和数据逻辑分离的解耦合。而 ORM 作为持久化设计中的最重要也最复杂的技术,也是目前业界热点技术。
简单来说,按通常的系统设计,使用 JDBC 操作数据库,业务处理逻辑和数据存取逻辑是混杂在一起的。
一般基本都是如下几个步骤:
1、建立数据库连接,获得 Connection 对象。
2、根据用户的输入组装查询 SQL 语句。
3、根据 SQL 语句建立 Statement 对象或者 PreparedStatement 对象。
4、用 Connection 对象执行 SQL语句,获得结果集 ResultSet 对象。
5、然后一条一条读取结果集 ResultSet 对象中的数据。
6、根据读取到的数据,按特定的业务逻辑进行计算。
7、根据计算得到的结果再组装更新 SQL 语句。
8、再使用 Connection 对象执行更新 SQL 语句,以更新数据库中的数据。
7、最后依次关闭各个 Statement 对象和 Connection 对象。
由上可看出代码逻辑非常复杂,这还不包括某条语句执行失败的处理逻辑。其中的业务处理逻辑和数据存取逻辑完全混杂在一块。而一个完整的系统要包含成千上万个这样重复的而又混杂的处理过程,假如要对其中某些业务逻辑或者一些相关联的业务流程做修改,要改动的代码量将不可想象。另一方面,假如要换数据库产品或者运行环境也可能是个不可能完成的任务。而用户的运行环境和要求却千差万别,我们不可能为每一个用户每一种运行环境设计一套一样的系统。
所以就要将一样的处理代码即业务逻辑和可能不一样的处理即数据存取逻辑分离开来,另一方面,关系型数据库中的数据基本都是以一行行的数据进行存取的,而程序运行却是一个个对象进行处理,而目前大部分数据库驱动技术(如ADO.NET、JDBC、ODBC等等)均是以行集的结果集一条条进行处理的。所以为解决这一困难,就出现 ORM 这一个对象和数据之间映射技术。
举例来说,比如要完成一个购物打折促销的程序,用 ORM 思想将如下实现(引自《深入浅出Hibernate》):
业务逻辑如下:
public Double calcAmount(String customerid, double amount)
{
// 根据客户ID获得客户记录
Customer customer = CustomerManager.getCustomer(custmerid);
// 根据客户等级获得打折规则
Promotion promotion = PromotionManager.getPromotion(customer.getLevel());
// 累积客户总消费额,并保存累计结果
customer.setSumAmount(customer.getSumAmount().add(amount);
CustomerManager.save(customer);
// 返回打折后的金额
return amount.multiply(protomtion.getRatio());
}
这样代码就非常清晰了,而且与数据存取逻辑完全分离。设计业务逻辑代码的时候完全不需要考虑数据库JDBC的那些千篇一律的操作,而将它交给 CustomerManager 和 PromotionManager 两个类去完成。这就是一个简单的 ORM 设计,实际的 ORM 实现框架比这个要复杂的多。
2、设计模式概述
设计模式从本质上说是一种规则,从形式上说,分为创建型、结构型、行为型。
设计模式的应用是为了实现软件设计中的几个原则,其中一个重要原则是:减少模块之间的耦合程度。为了确保这个目的,在设计一个类时,要针对接口,而非实现。(Programming to an Interface, not an Implementation)设计的时候只关心类的接口,编程的时候可以先实现一个简单的接口,供别的模块调用。使用一个类的时候只对接口工作,不关心具体的实现,也不关心具体的类型。这样也符合人类认识世界的规律,一般说来人们总是先了解一个事情的大概情况,比如,我们先了解一台电视机的大概功能,然后才能了解每个功能具体是怎样实现的。
开始的时候不提供实现,正是为了以后能够最大限度的实现。
设计模式不受语言的限制,使用.net或者java更容易实现。
厂模式(Factory)
工厂模式属于一种创建型模式(Creational)。同样属于创建型模式的还有单件模式(Singleton)。
工厂模式的要点:
1:存在一个创建对象的工厂;
2:调用者从工厂中取得某些对象;
3:由工厂决定如何创建对象;
4:客户不知道对象是如何生成的。
举一个例子:
Namer对象是FirstFirst和LastFirst的基类,用户调用Namer类的时候,不直接new出Namer类或者他的子类,而是使用NameFactory的getName方法得到具体的对象。这样在用户就不用关心自己正在使用的是哪一个Namer,正在调用哪一个方法。用户只针对 Namer进行工作,而不用关心具体的类型。在实际工程中可以将Namer类的子类的构造函数只对NameFactory开放,进一步限制程序员。
C#代码如下:
1:Namer的实现
using System;
namespace NameFactory
{
// Summary description for Name
//Base class for getting split names
public class Namer
{
//parts stored here
protected string frName, lName;
//return first name
public string getFrname()
{
return frName;
}
//return last name
public string getLname()
{
return lName;
}
}
}
2、FirstFirst类的实现:
using System;
namespace NameFactory
{
///
/// Summary description for FirstFirst.
///
public class FirstFirst : Namer
{
public FirstFirst(string name)
{
int i = name.IndexOf (" ");
if(i > 0) {
frName = name.Substring (0, i).Trim ();
lName = name.Substring (i + ).Trim ();
}
else {
lName = name;
frName = "";
}
}
}
}
3、LastFirst类的实现:
using System;
namespace NameFactory
{
///
/// Summary description for LastFirst.
///
public class LastFirst : Namer
{
public LastFirst(string name) {
int i = name.IndexOf (",");
if(i > 0) {
lName = name.Substring (0, i);
frName = name.Substring (i + ).Trim ();
}
else {
lName = name;
frName = "";
}
}
}
}
4、NameFactory,工厂的实现
using System;
namespace NameFactory
{
///
/// Summary description for NameFactory.
///
public class NameFactory {
public NameFactory() {}
public Namer getName(string name) {
int i = name.IndexOf (",");
if(i > 0)
return new LastFirst (name);
else
return new FirstFirst (name);
}
}
}
5:调用者,一个窗体:
按钮的响应事件代码如下:
private void btCompute_Click(object sender, System.EventArgs e)
{
Namer nm = nameFact.getName (txName.Text );
txFirst.Text = nm.getFrname ();
txLast.Text = nm.getLname ();
}
程序并不复杂,运行不运行无所谓,关键是要搞清楚Factory所起的作用;他隐藏了Namer类的创建细节,调用者始终不知道他创建的是哪一个类,也不需要关心他调用的方法是哪一个子类的。如果以后的程序的需求发生改动,比如:某国的人不仅有First Name和Last Name,还有Mid Name,就可以很方便的进行扩充,添加一个Namer的子类,修改一下Factory。调用者甚至不知道有新加了一种Namer的类型。
实际的应用
举一个简单的例子:工程中需要使用多种数据库,Oracle、SQL Server、Sybase,为这些数据库建立了各自的连接和查询操作的类,这些类有一个共同的基类BaseConn。可以建立一个ConnFactory类,用于根据不同情况产生具体的类。调用者不必关心自己调用的是谁。大大简化了业务代码。
实际的使用例子还有很多。
单件模式(Singleton)
是一个非常简单的模式,这是我第一个理解并且能运用的模式。有时候模式的复杂程度并不在于本身,而是由于他的应用目的。最初的时候面对一个模式经常充满了困惑,一个简单的调用为什么要搞的如此复杂?为什么要建立这么多类,只是为了打开一个文件。
通常说来学习一个模式是一个接受、认可、领会的过程。接受:了解模式的结构,了解实例的意义;认可:认可该模式在实际工程中的作用和可行性;领会:将模式应用到开发过程中。
而模式的应用目的说到底无非是为了降低模块之间在时间和空间上的耦合程度,以此提高模块的复用性,降低出错的概率。
单件模式
Singleton模式是一个较为简单的模式,下面的代码就可以建立一个Singlton模式的例子,这是一个写系统日志的类。
public class LogWriter { //申明一个静态的变量,类型为类本身 private static LogWriter _instance = null;
//将类的构造函数私有化,使得这个类不可以被外界创建 private LogWriter() { }
//提供静态的方法,创建类的实例 public static LogWriter GetInstance() { if (_instance == null) { _instance = new LogWriter(); } return _instance; }
//下面实现类的其他功能 //.... } |
调用者不可以通过new的形式得到Singleton的实例,调用的例子如下:
LogWriter log = LogWriter.GetInstance();
log.Debug("hello world");
实际应用
很容易想象,单件模式适用于下面的情况:整个系统中某个对象是唯一的,或者是有固定数量的。比如数据库连接,配置文件,等等……
模式是一种程序员间的约定术语,语言可以成为思维的基础。有了这样的语言,一些复杂的概念就变得容易交流了。比如设计人员只要说到,某个类是一个单件模式,是一个Singleton,程序员听见以后就至少应该明白这个类的调用方式了,而不用再详细的说明。
3、 NHibernate(Hibernate.Net) 介绍
NHibernate是一个面向.NET环境的针对关系型数据库的对象持久化类库。
NHibernate来源于非常优秀的基于Java的Hibernate关系型持久化工具。 NHibernate从数据库底层来持久化你的.Net对象到关系型数据库。NHibernate为你处理这些,你不用自己写SQL去数据库存取对象。你的代码仅仅和对象关联,NHibernat自动产生SQL语句,并确保对象提交到正确的表和字段中去.大量减少开发时人工使用SQL和ADO.NET处理数据的时间. NHibernate可以帮助你消除或者包装那些针对特定数据库的SQL代码,并且帮你把结果集从表格式的表示形式转换到一系列的对象去。因此对于那些在基于.NET的中间层的应用中,它们实现面向对象的业务模型和商业逻辑的应用,NHibernate是最有用的。
NHibernate编程模式 |
实体类 |
Hbm.xml |
表 |
configuration |
session |
sessionFactory |
Transaction |
Save Update Remove |
行为类 |
开发者要做的 |
开发者要做的 |
Schema Export |
4、 NSpring(Spring.Net) 介绍
Spring.NET 是一个关注于.NET企业应用开发的应用程序框架。它能够提供宽广范围的功能,例如依赖注入、面向方面编程(AOP)、数据访问抽象, 以及ASP.NET集成等。基于Java的spring框架的核心概念和价值已被应用到.NET。Spring.NET 1.0 包含一个完全功能的依赖注入容器和AOP库。后续的发布将包含对ASP.NET、Remoting和数据访问的支持。下图展现出了 Spring .NET的各个模块。具有黑色阴影的模块包含在1.0版本中,其他模块计划在将来的发布中推出。在很多情况下,你可以在我们的下载网站中发现可以工作的计划模块的实现。
Spring .NET框架概览
Spring.Core 库是框架的基础, 提供依赖注入功能。Spring.NET中大多数类库依赖或扩展了Spring.Core的功能。IObjectFactory接口提供了一个简单而优雅的工厂模式,移除了对单例和一些服务定位stub写程序的必要。允许你将真正的程序逻辑的配置和依赖的详细情况解耦。作为对IObjectFactory的扩展,IApplicationContext接口也在Spring.Core库中,并且添加了许多企业应用为中心的功能,例如利用资源文件进行文本本地化、事件传播、资源加载等等。
Spring.Aop 库提供对业务对象的面向方面编程(AOP) 的支持。Spring.Aop 库是对Spring.Core库的补充,可为声明性地建立企业应用和为业务对象提供服务提供坚实的基础。
Spring.Web 库扩展了ASP.NET,添加了一些功能,如对ASP.NET页面的依赖注入,双向数据绑定,针对 ASP.NET 1.1的Master pages以及改进的本地化支持。
Spring.Services库可让你将任何“一般”对象(即没有从其他特殊的服务基类继承的对象)暴露为企业服务或远程对象,使得.NET Web services 获得依赖注入的支持,并覆盖属性元数据。此外还提供了对Windows Service的集成。
Spring.Data 库提供了数据访问层的抽象,可以被多个数据访问提供者(从ADO.NET 到多个ORM 提供者)应用。它还包含一个对ADO.NET的抽象层,移除了为ADO.NET编写可怕的编码和声明性的事务管理的必要。
Spring.ORM库提供了对常见对象关系映射库的的集成,提供了一些功能,比如对声明性事务管理的支持。
Container
|
Spring编程模式 |
切片类 |
AppContext.xml |
业务类 |
Proxy |
消费类 |
类工厂 |
派生类 |
业务类 |
切片类 |
1 |
2 |
3 |
动态继承业务类 |
方法调用时调用切片过程 |
4 |
AppContext.xml |
注:第2步通过Emit依据业务类动态构造一个派生类,实例化后返回给消费对象类。 |
开发者要做的 |
【注】
1、Spring的切片机制概念
面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。 面向对象将应用程序分解成 各个层次的对象,而AOP将程序分解成各个方面 或者说 关注点 。 这使得可以模块化诸如事务管理等这些横切多个对象的关注点。(这些关注点术语称作 横切关注点。)
Spring的一个关键组件就是AOP框架。 Spring IoC容器(BeanFactory 和ApplicationContext)并不依赖于AOP, 这意味着如果你不需要使用,AOP你可以不用,AOP完善了Spring IoC,使之成为一个有效的中间件解决方案,。
AOP在Spring中的使用:
· 提供声明式企业服务,特别是作为EJB声明式服务的替代品。这些服务中最重要的是 声明式事务管理,这个服务建立在Spring的事务管理抽象之上。
· 允许用户实现自定义的方面,用AOP完善他们的OOP的使用。
让我们从定义一些重要的AOP概念开始。这些术语不是Spring特有的。不幸的是,Spring的术语 不是特别地直观。而且,如果Spring使用自己的术语,这将使人更加迷惑。
· 方面(Aspect): 一个关注点的模块化,这个关注点实现可能 另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。
· 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
· 通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类 型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架 包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器 链。
· 切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。
· 引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。
· 目标对象(Target Object): 包含连接点的对象。也被称作 被通知或被代理对象。
· AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
· 织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。
各种通知类型包括:
· Around通知: 包围一个连接点的通知,如方法调用。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为。它们负责选择继续执行连接点或通过 返回它们自己的返回值或抛出异常来短路执行。
· Before通知: 在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
· Throws通知: 在方法抛出异常时执行的通知。Spring提供 强类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable 或Exception强制类型转换。
· After returning通知: 在连接点正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。
5、NStruts、NSpring与NHibernate
NStruts、NSpring、NHibernate都是其.net平台对应java平台的产物。NStruts包含了MVC,即 Model(模型)- View(视图)- Control(控制)系统架构模式。NHibernate作为持久层的框架,和.net中的DataSet绝对是两种思想,ado.net更适合表入口和行入口数据模式,NHibernate很好得把对象转换成关系数据,值的学习。NSpring是很好的框架,.net中目前没有J2EE应用服务器的对等物,windows本身就是应用服务器,所以很多如事务处理都需要依赖企业服务器(COM+),而Spring就是很好地作应用服务这方面的框架,但目前NSpring似乎还没有这么强大,只有核心部分得到了很好的实现(IOC,AOP),不知道将来如何,值得关注。
6、在C#中使用COM+实现事务控制
.NET技术是微软大力推广的下一代平台技术,现在已同JAVA成为两大主流的技术平台。而一个新的技术平台得以快速发展的最重要的前提是:他不会彻底的摒弃以前的技术,这一点对于.NET技术来说指的就是COM/COM+技术了。
一般来说,在IT技术界以及硬件产业,技术的更新换代速度非常得惊人,而惯例是所有的新技术都会遵循向下兼容的原则,但是.NET技术不仅仅做到了这一点,.NET甚至实现了相互之间的各自调用,这一点是非常难能可贵的。也就是说,不但我们可以在.NET组件中调用COM组件,同时也可以在COM组件中正常的调用.NET组件。这点带来的好处是显而易见的,一方面我们可以保持现有的技术资源,另一方面,在现有资源中可以利用.NET所带来的各种新技术。
一般的数据库事务控制要求事务里所做的操作必须在同一个数据库内,这样在出现错误的时候才能回滚(RllBack)到初始状态。这就存在一个问题,在分布式应用程序中,我们往往需要同时操作多个数据库,使用数据库本身的事务处理,很难满足程序对事务控制的要求。在COM+中,提供了完整的事务服务,我们可以利用它来完成在分布式应用程序中的事务控制。
7、.Net 反射机制
反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。