面向对象的设计模式学习笔记NO.2(抽象工厂模式)
面向对象的设计模式学习NO.2(抽象工厂模式Abstract Factory )
一.一般原理
场景A:
如果你做了一个ERP系统。这个系统是用SqlServer数据库的。这时你在逻辑层使用System.Data.SqlClient的命名空间,然后使用如下对象实现
sqlConnection sqlcon = new sqlConnection();
sqlCommand sqlcom = new sqlCommand();
…
一天老板说,算了,客户公司小,也没啥钱,给他装Access数据库够用了。
你说容易呀,把命名空间改为System.Data.OleDb,然改掉sqlConnection,sqlCommand为对应的Access对象就行了。于是乎,你花了几天时间没头没脑地加班,哪知道,替换后,错误百出,SqlServer和Access的sql语法也有很多不同。你焦头烂额地debug,debug(调试),经历折磨之后,终于可以可以去跟老板交差了。这时老板又很高兴地说,我们有一个新客户很有钱,他们要求使用Oracle数据库,你当场晕倒!(妈妈呀!又要改。。。。今天是母亲节,想妈了。)
场景B:
玩过赛车,CS(半条命)等游戏的都知道,玩之前得选一张地图。不同地图有不同的主题,如古典形地图(古老的泥路,古老的房屋,古老的天空,古老的人)和现代的地图(现代的道路,现代的房屋,现代的天空,现代的人)。发现规律了吧,每一幅地图都不同,但一系列的基本的元素都是相同的。而且是相互关联的,古老的泥路对应现代的房屋就不太协调了。如果你是赛车游戏或CS游戏的程序开发员,你应该如何设计呢?
分析场景A和场景B,我们可以把每种数据库看作一个系列,把每一副地图看作一个系列。我们可不可以在不改动原来的代码的情况下,费最小的劲,去添加一个新的系列呢?答案是肯定的,使用一个抽象工厂模式,就能满足你的要求。
动机(Motivation):在软件系统中,经常面临着“一系统相互依赖的对象”的创建工作;同时,由于需求的变化,往往有更多系列需要创建。如何应对这种变化?如何绕过常规的创建方法(new)提供一种“封装机制”来避免客户程序员和这种“多系列具体对象创建工作”的紧耦合?
意图(intent):提供一个接口,让该接口负责创建一系列“相关或相互依赖的对象”,无需指定它们具体的类-----《设计模式》GOF。“四人帮”的这句话不是一般的抽象,我只觉得它只描述了抽象工厂的结构,没有描述到其意图。意图是什么呀,就是解决场景A,场景B类同的问题。(我也不懂怎么概括意图)
二.代码实现
1.抽象工厂模式结构
OK,如果我再想添加一个Oracle数据库将如何做呢?只添加一套与Sql Server或Access结构相同的节点就可以拉。你会发现,代码虽然添加了,但是丝毫没有影响到原来的代码呢。
代码如下:
/*****************系统用户抽象产品*****************************************/
Interface IUser // 抽象系统用户接口(对应图中的系统用户抽象产品)
{
Viod Insert(User user);
User GetUser(int id);
}
Class SqlserverUser : IUser //具体实体(对应图中的Sql Server系统用户实体产品)
{
Public void Insert(User user)
{
Console.WriteLine(“在Sql server中添加了一个User”);
}
Public User GetUser(int id)
{
Console.WriteLine(“通过userID 获得一个User”);
}
}
Class AccessUser : IUser //具体实体(对应图中的Access系统用户实体产品)
{
Public void Insert(User user)
{
Console.WriteLine(“在Access中添加了一个User”);
}
Public User GetUser(int id)
{
Console.WriteLine(“通过userID 获得一个User”);
}
}
/*****************账单统计抽象产品*****************************************/
Interface IBill // 抽象账单(对应图中的账单抽象产品)
{
Viod Insert(Bill bill);
User GetBill(int id);
}
Class SqlserverIBill //具体账单实体(对应图中的Sql Server账单实体产品)
{
Public void Insert(Bill bill)
{
Console.WriteLine(“在Sql server中添加了一个Bill”);
}
Public User GetBill(int id)
{
Console.WriteLine(“通过BiilID 获得一个Bill”);
}
}
Class AccessBill : IBill //具体实体(对应图中的Access账单实体产品)
{
Public void Insert(Bill bill)
{
Console.WriteLine(“在Access中添加了一个Bill”);
}
Public User GetBill(int id)
{
Console.WriteLine(“通过BillID 获得一个Bill”);
}
}
/*****************抽象工厂*****************************************/
Interface IFactory
{
IUser CreateUser();
IBill CreateBill();
}
Class SqlServerFactory : IFactory
{
Public IUser CreateUser()
{
Return new SqlserverUser();
}
Public CreateBill()
{
Return new SqlserverIBill();
}
}
Class AccessFactory : IFactory
{
Public IUser CreatUser()
{
Ruturn new AccessUser();
}
Public IBill CreatBill()
{
Return new AccessBill();
}
}
/*****************客户端代码******************************/
Static void
{
User user = new User();
Bill bill = new Bill();
IFactory facory = new AccessFactory();
//IFactory factory = new SqlserverFactory(); 用这句替换则是访问Sqlserver数//据库.只改这里,引用的数据库就改变了。
Iuser iu = facory.CreateUser();
iu.Insert(user);
iu.GetUser(123);
IBill ib = factory.CreateBill();
ib.Inser(bill);
ib.GetBill(123);
}
三.模式扩展
以上抽象模式可以很方便地切换两个数据库间的访问代码。但是如果需求增加了呢,比如现在要添加工资表(Salary),你需要改动哪些地方呀?
至少要添加三个类:ISalary,SqlserverSalary,AccessSalary,
还需要更改三个类:IFactory,SqlServerFactroy,AccessFactory.
用反射+抽象工厂+配置文件实现数据访问程序
去除IFactory,SqlServerFactroy,AccessFactory,取而代之的是DataAccess类
Using System.Reflection; //引入反射
Class DataAccess
{
Private static readonly string AssemblyName =”抽象工厂模式”//程序集名字
Prvaite static readonly stirng db =ConfigurationManager.AppSetings[“DB”];// 读取配置文件
Public static Iuser CreateUser()
{
String cassName = AssemblyName + “.” + db + “User”;
Return (IUser)Assemble.Load(AssemblyName).CreateInstance(cassName);
}
Public static Iuser CreateBill()
{
String cassName = AssemblyName + “.” + db + “Bill”;
Return (IBill)Assemble.Load(AssemblyName).CreateInstance(cassName);
}
}
App.Config(Web.Config配置如下)
<?xml version =”1.0” encoding=”utf-8” ?>
<configuration>
</appSetting>
<add key =”DB” value=”Sqlserver/> ß配置文件,可以换成Access|Oracle|MySQLà
</assSetting>
</configuration>
[*]学习内容来自李建忠老师的《C#面向对象设计模式纵横谈》和程杰的《大话设计模式》[*]

浙公网安备 33010602011771号