面向对象的设计模式学习笔记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 Main(string[] agrs)

{

  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#面向对象设计模式纵横谈》和程杰的《大话设计模式》[*]

posted @ 2010-05-15 22:35  渔江晚晴の收拾箱  阅读(255)  评论(0)    收藏  举报