.Net Framework框架模式(第三篇 简单工厂模式)
2008-08-02 13:16
337 查看
小菜与简单工厂模式,工厂模式,包括抽象工厂模式的结缘都源至于以前支持多数据库的学习过程.
现在就来回顾下该过程,回顾下工厂系列模式.希望能带给你不一样的感觉,不一样的体会.
因为回顾写太长了,所以就没有写.Net Framework框架源码中的模式了.下篇会都补上.
小菜前段时间写了一些梦游Discuz!NT2.0的文章,现在就以它为例来说明吧.
我们的工作是支持SqlServer数据库,Access数据库,MySql数据库.
这样的话,每一种对数据库的操作都会对应三种不同的实现.
以dnt_forums(论坛版块信息表)为例. 已有数据 (这个表被小菜简化了,因为这样更能说明问题)
现在要求提供操作.
1.按fid访问数据库取出name ==> string GetForumName(int fid)
等等数据库相关操作......
先来看看代码的组织吧.
using System;
namespace Discuz.Data
SqlServer数据库实现
using System;
using Discuz.Data;
namespace Discuz.Data.SqlServer
大家不要骂小菜说骗人噢,假的实现, :) 因为这里是讲设计模式,不应该把太多的实现带进来.
如果想复习下数据库的操作,可参看 <小菜梦游Discuz!NT数据层设计部份>
Access数据库实现
using System;
using Discuz.Data;
namespace Discuz.Data.Access
MySql数据库实现
using System;
using Discuz.Data;
namespace Discuz.Data.MySql
这样的话,那么我们的客户程序便可以方便的使用了.
Default.aspx页面调用
using System;
using Discuz.Data;
public partial class _Default : System.Web.UI.Page
如果小菜想在页面Forum.aspx也使用ForumManage类,怎么办呢?这还不简单.
using System;
using Discuz.Data;
public partial class Forum : System.Web.UI.Page
苍天啊,大地啊,怎么会这样啊,一模一样的代码竟然出现.(代码中有坏味道,看来我们得重构一下它才行)
怎么办呢? 把创建具体ForumManage的逻辑独立出来,放入某个类中,就设为ForumFactory吧.
这样我们就引入ForumFactory类
using System;
using Discuz.Data;
public class ForumFactory
那么,我们在Default.aspx和Forum.aspx等地方使用ForumManage将方便许多,不信就接着往下看.
Default.aspx页面调用
using System;
using Discuz.Data;
public partial class _Default : System.Web.UI.Page
Forum.aspx页面调用
using System;
using Discuz.Data;
public partial class Forum : System.Web.UI.Page
有一天,老板觉的应该把SqlServer数据库换成MySql数据库怎么办呢?
偶早就想到有这一天了,打开VS2005点击->编辑->查找与替换 将所有ForumFactory.Create("SqlServer")替换为ForumFactory.Create("MySql").然后重新编译.
噢,瞧瞧,糟糕透了.相信你也不想这样干吧.
看来情况不妙,有什么解决低招呢? 把当前使用的数据库类型放入配置文件Web.config中.(也称作依赖注入)
<appSettings>
<add key="DbType" value="SqlServer"/>
</appSettings>
那么修改我们的ForumFactory代码吧.
using System;
using System.Configuration;
using Discuz.Data;
public class ForumFactory
还能不能在简洁些呢? 如果有一天老板觉得要使用Oracle数据库才算跟的上潮流.那我们该怎么办呢?
第一步:定义一个Discuz.Data.Oracle.ForumManage类,实现Discuz.Data.IForumManage接口. (对扩展开放)
第二步:修改ForumFactory中的switch代码,添加代码. (对修改开放)
switch (dbType)
第三步:修改Web.config中的<add key="DbType" value="Oracle" />
看来我们做的不够好,我们的设计应该对扩展开放对修改关闭,而不是对修改开放
不知道聪明的你想到什么解决方法呢?
.Net中那么好的反射功能不用岂不太可惜了.
using System;
using System.Reflection;
using System.Configuration;
namespace Discuz.Data
现在不仅代码简洁了,我们也成功实现了对扩展开放对修改关闭.
因为就算有一天老板要求我们把数据库换成DB2
第一步:定义一个Discuz.Data.DB2.ForumManage类,实现Discuz.Data.IForumManage接口.
第二步:修改Web.config中的<add key="DbType" value="DB2" />
我们不再需要往ForumFactory中的switch中添加代码,实现了对修改关闭
switch (dbType)
也由于使用了反射,我们可以把ForumFactory移入Discuz.Data类库中.
之前不行吗?不行,因为Discuz.Data无法引用Discuz.Data.SqlServer或者Discuz.Data.Access等空间.
会出现循环引用.
接下来就来看看简单工厂模式的类图吧.
using System;
using System.Reflection;
using System.Configuration;
using Discuz.Data;
public class ForumFactory
看起来好象不错,使用了static,下一次调用ForumFactory.Create()时,就不用实例化forumManage了,直接返回
但你发现问题了没?
1.Web.config中的配置项是<add key="DbType" value="Access" />
2.Default.aspx使用ForumManage调用GetForumName方法
IForumManage forumManage = ForumFactory.Create() ==> 实例化的将是 new Discuz.Data.Access.ForumManage();
输出: Access版块
3.Web.config中的配置修改为<add key="DbType" value="SqlServer" />也就是换数据库
刷新Default.aspx页面,分析一下将输出什么呢?
执行IForumManage forumManage = ForumFactory.Create()
调用ForumFactory.Create(),因为其中的forumManage在第2步中已经被实例化过,不等于null,直接返回forumManage
所以返回的还是new Discuz.Data.Access.ForumManage()
那么应该输出:Access版块,也就是换数据库不成功
恩,分析的很好,不过输出却是:SqlServer版块,说明换数据库成功.
为什么呢?分析错了吗? 不是,是 因为修改Web.config会导致应用程序重启.(一切重新来过)
这时你会说了,那不是很好,关我什么事! 但我们经常把会改变的配置项独立出来,放在某个配置文件中,这样也避免了修改配置文件导致应用程序重启导致的性能损耗.
如Discuz!NT2.0中,就将数据库类型放在DNT.config配置文件中.
<?xml version="1.0" encoding="utf-8"?>
<BaseConfigInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DbType>Access</DbType>
</BaseConfigInfo>
这样的话,我们要取配置项,就需要反序化它.
详细的反序化过程可以参看 <小菜梦游Discuz!NT配置文件部份> 包括如何监视配置文件更改获取最新值.
在这里,我们只需要知道,我们可以通过BaseConfigFileManager.GetDbType就能得到最新的配置文件中的DbType结点的值
string dbType = ConfigurationManager.AppSettings["DbType"];
我们修改为
string dbType = BaseConfigFileManager.GetDbType;
1.DNT.config中的配置是<add key="DbType" value="Access" />
2.Default.aspx使用ForumManage调用GetForumName方法
IForumManage forumManage = ForumFactory.Create() ==> 实例化的将是 new Discuz.Data.Access.ForumManage();
输出: Access版块
3.DNT.config中的配置修改为<add key="DbType" value="SqlServer" />也就是换数据库
刷新Default.aspx页面,输出Access版块.
我们应该如何解决这个问题呢?
1.我们可以为ForumFactory提供一个Reset()方法,将forumManage重置为null
using System.Xml.Serialization;
using System.Reflection;
using System.Configuration;
namespace Discuz.Data
那ForumFactory.Reset()将在什么时候调用呢?
2.避免通过打开DNT.config来修改DbType配置项
因为不方便,不直观,而且可能出现输入错误的情况
我们可以通过定制相应的页面,比如下拉菜单来更改数据库类型,间接修改DNT.config配置文件,如果数据库类型更改,
则调用ForumFactory.Reset()
顺便来复习下单件模式吧.通常我们可以把简单工厂改造成单件模式.Discuz!NT2.0也这么做了.
不过小菜认为这里有点不太合适,因为反而会把代码设计的更复杂,简洁就是美,而且达到功能的要求.
不信你就看看下面的代码与上面的代码对比一下.
using System;
using System.Reflection;
using Discuz.Config
namespace Discuz.Data
如果你看了小菜的前一篇 <.Net Framework框架源码学习单件模式> 的话 你就会发现这个单件模式很特别.
它把小菜单件模式的第一种和第四种都用上了,杂合体.
为什么呢?
想想看把Instance属性修改成如下,也就是仅使用第一种.
public static IForumManage Instance
有什么问题呢?初看没问题,但关键出在ResetProvider()方法上,如果它被调用,_instance为空.那Instance的返回也将是null
那下次调用ForumManage.Create()将返回什么呢?当然还是null了.因为静态构造函数只会被调用一次,所以你可别指望它.
这也就是为什么在Instance属性中使用了double-check双检查,重新加载_instance的原因.即第四种的原因.
建议有点乱的朋友看一下小菜的前一篇 <.Net Framework框架源码学习单件模式>
所以大家也应该清楚,并不是用模式就是好的.
也许看到该篇简单工厂模式,很多人会想到工厂模式.
现在就来对比一下,针对这个应用使用工厂模式合不合适.
啥,今天不是讲简单工厂吗?怎么又跑到工厂模式.是不是跑题了?
模式之间的碰撞才能出现火花不是吗.
using System;
using Discuz.Data;
namespace Discuz.DALFactory
那我们如何使用呢?
还是在Default.aspx页面调用
using System;
using Discuz.Data;
using Discuz.DALFactory;
public partial class _Default : System.Web.UI.Page
噢,天呐,我既然看到了new SqlServerForumFactory();这和之前的ForumFactory.Create("SqlServer");看来是不相上下,只能说我们走入了歧途,但是有办法走出来吗?
那我们就在Web.config中动动手脚
<appSettings>
<add key="DbType" value="Access"/>
<add key="Access" value="Discuz.DALFactory.AccessForumFactory"/>
<add key="SqlServer" value="Discuz.DALFactory.SqlServerForumFactory"/>
<add key="MySql" value="Discuz.DALFactory.MySqlForumFactory"/>
</appSettings>
那我们在Default.aspx页面调用来看看
using System;
using System.Reflection;
using System.Configuration;
using Discuz.Data;
using Discuz.DALFactory;
public partial class _Default : System.Web.UI.Page
那我们在Forum.aspx页面调用看看
using System;
using System.Reflection;
using System.Configuration;
using Discuz.Data;
using Discuz.DALFactory;
public partial class Forum : System.Web.UI.Page
噢..代码基本上一模一样,重复的代码太多,这样不是又要把相同的代码独立出来,那不是又和简单工厂类似.
看来工厂模式用在这里是此地无银三百两的作法.
所以不管是从本篇的单件模式的运用,还是工厂模式与简单工厂的碰撞值的大家思考.
现在就来回顾下该过程,回顾下工厂系列模式.希望能带给你不一样的感觉,不一样的体会.
因为回顾写太长了,所以就没有写.Net Framework框架源码中的模式了.下篇会都补上.
小菜前段时间写了一些梦游Discuz!NT2.0的文章,现在就以它为例来说明吧.
我们的工作是支持SqlServer数据库,Access数据库,MySql数据库.
这样的话,每一种对数据库的操作都会对应三种不同的实现.
以dnt_forums(论坛版块信息表)为例. 已有数据 (这个表被小菜简化了,因为这样更能说明问题)
fid : 版块编号 | name:版块名称 |
1 | 版块1 |
2 | 版块2 |
1.按fid访问数据库取出name ==> string GetForumName(int fid)
等等数据库相关操作......
先来看看代码的组织吧.
using System;
namespace Discuz.Data
SqlServer数据库实现
using System;
using Discuz.Data;
namespace Discuz.Data.SqlServer
大家不要骂小菜说骗人噢,假的实现, :) 因为这里是讲设计模式,不应该把太多的实现带进来.
如果想复习下数据库的操作,可参看 <小菜梦游Discuz!NT数据层设计部份>
Access数据库实现
using System;
using Discuz.Data;
namespace Discuz.Data.Access
MySql数据库实现
using System;
using Discuz.Data;
namespace Discuz.Data.MySql
这样的话,那么我们的客户程序便可以方便的使用了.
Default.aspx页面调用
using System;
using Discuz.Data;
public partial class _Default : System.Web.UI.Page
如果小菜想在页面Forum.aspx也使用ForumManage类,怎么办呢?这还不简单.
using System;
using Discuz.Data;
public partial class Forum : System.Web.UI.Page
苍天啊,大地啊,怎么会这样啊,一模一样的代码竟然出现.(代码中有坏味道,看来我们得重构一下它才行)
怎么办呢? 把创建具体ForumManage的逻辑独立出来,放入某个类中,就设为ForumFactory吧.
这样我们就引入ForumFactory类
using System;
using Discuz.Data;
public class ForumFactory
那么,我们在Default.aspx和Forum.aspx等地方使用ForumManage将方便许多,不信就接着往下看.
Default.aspx页面调用
using System;
using Discuz.Data;
public partial class _Default : System.Web.UI.Page
Forum.aspx页面调用
using System;
using Discuz.Data;
public partial class Forum : System.Web.UI.Page
有一天,老板觉的应该把SqlServer数据库换成MySql数据库怎么办呢?
偶早就想到有这一天了,打开VS2005点击->编辑->查找与替换 将所有ForumFactory.Create("SqlServer")替换为ForumFactory.Create("MySql").然后重新编译.
噢,瞧瞧,糟糕透了.相信你也不想这样干吧.
看来情况不妙,有什么解决低招呢? 把当前使用的数据库类型放入配置文件Web.config中.(也称作依赖注入)
<appSettings>
<add key="DbType" value="SqlServer"/>
</appSettings>
那么修改我们的ForumFactory代码吧.
using System;
using System.Configuration;
using Discuz.Data;
public class ForumFactory
还能不能在简洁些呢? 如果有一天老板觉得要使用Oracle数据库才算跟的上潮流.那我们该怎么办呢?
第一步:定义一个Discuz.Data.Oracle.ForumManage类,实现Discuz.Data.IForumManage接口. (对扩展开放)
第二步:修改ForumFactory中的switch代码,添加代码. (对修改开放)
switch (dbType)
第三步:修改Web.config中的<add key="DbType" value="Oracle" />
看来我们做的不够好,我们的设计应该对扩展开放对修改关闭,而不是对修改开放
不知道聪明的你想到什么解决方法呢?
.Net中那么好的反射功能不用岂不太可惜了.
using System;
using System.Reflection;
using System.Configuration;
namespace Discuz.Data
现在不仅代码简洁了,我们也成功实现了对扩展开放对修改关闭.
因为就算有一天老板要求我们把数据库换成DB2
第一步:定义一个Discuz.Data.DB2.ForumManage类,实现Discuz.Data.IForumManage接口.
第二步:修改Web.config中的<add key="DbType" value="DB2" />
我们不再需要往ForumFactory中的switch中添加代码,实现了对修改关闭
switch (dbType)
也由于使用了反射,我们可以把ForumFactory移入Discuz.Data类库中.
之前不行吗?不行,因为Discuz.Data无法引用Discuz.Data.SqlServer或者Discuz.Data.Access等空间.
会出现循环引用.
接下来就来看看简单工厂模式的类图吧.
using System;
using System.Reflection;
using System.Configuration;
using Discuz.Data;
public class ForumFactory
看起来好象不错,使用了static,下一次调用ForumFactory.Create()时,就不用实例化forumManage了,直接返回
但你发现问题了没?
1.Web.config中的配置项是<add key="DbType" value="Access" />
2.Default.aspx使用ForumManage调用GetForumName方法
IForumManage forumManage = ForumFactory.Create() ==> 实例化的将是 new Discuz.Data.Access.ForumManage();
输出: Access版块
3.Web.config中的配置修改为<add key="DbType" value="SqlServer" />也就是换数据库
刷新Default.aspx页面,分析一下将输出什么呢?
执行IForumManage forumManage = ForumFactory.Create()
调用ForumFactory.Create(),因为其中的forumManage在第2步中已经被实例化过,不等于null,直接返回forumManage
所以返回的还是new Discuz.Data.Access.ForumManage()
那么应该输出:Access版块,也就是换数据库不成功
恩,分析的很好,不过输出却是:SqlServer版块,说明换数据库成功.
为什么呢?分析错了吗? 不是,是 因为修改Web.config会导致应用程序重启.(一切重新来过)
这时你会说了,那不是很好,关我什么事! 但我们经常把会改变的配置项独立出来,放在某个配置文件中,这样也避免了修改配置文件导致应用程序重启导致的性能损耗.
如Discuz!NT2.0中,就将数据库类型放在DNT.config配置文件中.
<?xml version="1.0" encoding="utf-8"?>
<BaseConfigInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DbType>Access</DbType>
</BaseConfigInfo>
这样的话,我们要取配置项,就需要反序化它.
详细的反序化过程可以参看 <小菜梦游Discuz!NT配置文件部份> 包括如何监视配置文件更改获取最新值.
在这里,我们只需要知道,我们可以通过BaseConfigFileManager.GetDbType就能得到最新的配置文件中的DbType结点的值
string dbType = ConfigurationManager.AppSettings["DbType"];
我们修改为
string dbType = BaseConfigFileManager.GetDbType;
1.DNT.config中的配置是<add key="DbType" value="Access" />
2.Default.aspx使用ForumManage调用GetForumName方法
IForumManage forumManage = ForumFactory.Create() ==> 实例化的将是 new Discuz.Data.Access.ForumManage();
输出: Access版块
3.DNT.config中的配置修改为<add key="DbType" value="SqlServer" />也就是换数据库
刷新Default.aspx页面,输出Access版块.
我们应该如何解决这个问题呢?
1.我们可以为ForumFactory提供一个Reset()方法,将forumManage重置为null
using System.Xml.Serialization;
using System.Reflection;
using System.Configuration;
namespace Discuz.Data
那ForumFactory.Reset()将在什么时候调用呢?
2.避免通过打开DNT.config来修改DbType配置项
因为不方便,不直观,而且可能出现输入错误的情况
我们可以通过定制相应的页面,比如下拉菜单来更改数据库类型,间接修改DNT.config配置文件,如果数据库类型更改,
则调用ForumFactory.Reset()
顺便来复习下单件模式吧.通常我们可以把简单工厂改造成单件模式.Discuz!NT2.0也这么做了.
不过小菜认为这里有点不太合适,因为反而会把代码设计的更复杂,简洁就是美,而且达到功能的要求.
不信你就看看下面的代码与上面的代码对比一下.
using System;
using System.Reflection;
using Discuz.Config
namespace Discuz.Data
如果你看了小菜的前一篇 <.Net Framework框架源码学习单件模式> 的话 你就会发现这个单件模式很特别.
它把小菜单件模式的第一种和第四种都用上了,杂合体.
为什么呢?
想想看把Instance属性修改成如下,也就是仅使用第一种.
public static IForumManage Instance
有什么问题呢?初看没问题,但关键出在ResetProvider()方法上,如果它被调用,_instance为空.那Instance的返回也将是null
那下次调用ForumManage.Create()将返回什么呢?当然还是null了.因为静态构造函数只会被调用一次,所以你可别指望它.
这也就是为什么在Instance属性中使用了double-check双检查,重新加载_instance的原因.即第四种的原因.
建议有点乱的朋友看一下小菜的前一篇 <.Net Framework框架源码学习单件模式>
所以大家也应该清楚,并不是用模式就是好的.
也许看到该篇简单工厂模式,很多人会想到工厂模式.
现在就来对比一下,针对这个应用使用工厂模式合不合适.
啥,今天不是讲简单工厂吗?怎么又跑到工厂模式.是不是跑题了?
模式之间的碰撞才能出现火花不是吗.
using System;
using Discuz.Data;
namespace Discuz.DALFactory
那我们如何使用呢?
还是在Default.aspx页面调用
using System;
using Discuz.Data;
using Discuz.DALFactory;
public partial class _Default : System.Web.UI.Page
噢,天呐,我既然看到了new SqlServerForumFactory();这和之前的ForumFactory.Create("SqlServer");看来是不相上下,只能说我们走入了歧途,但是有办法走出来吗?
那我们就在Web.config中动动手脚
<appSettings>
<add key="DbType" value="Access"/>
<add key="Access" value="Discuz.DALFactory.AccessForumFactory"/>
<add key="SqlServer" value="Discuz.DALFactory.SqlServerForumFactory"/>
<add key="MySql" value="Discuz.DALFactory.MySqlForumFactory"/>
</appSettings>
那我们在Default.aspx页面调用来看看
using System;
using System.Reflection;
using System.Configuration;
using Discuz.Data;
using Discuz.DALFactory;
public partial class _Default : System.Web.UI.Page
那我们在Forum.aspx页面调用看看
using System;
using System.Reflection;
using System.Configuration;
using Discuz.Data;
using Discuz.DALFactory;
public partial class Forum : System.Web.UI.Page
噢..代码基本上一模一样,重复的代码太多,这样不是又要把相同的代码独立出来,那不是又和简单工厂类似.
看来工厂模式用在这里是此地无银三百两的作法.
所以不管是从本篇的单件模式的运用,还是工厂模式与简单工厂的碰撞值的大家思考.
相关文章推荐
- Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)
- [ASP.NET MVC]Entity Framework框架之CodeFirst模式学习笔记
- 不安装.net framework框架运行.Net 程序的方法
- Winform开发框架之通用Windows摄像头调用拍照--SNF快速开发平台3.3-Spring.Net.Framework
- [转]让.Net 程序脱离.net framework框架运行
- RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2->新增记录SQL执行过程
- RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件介绍
- 在.NET Framework 类库中用到的设计模式
- visual studio如何修改c++项目的.net framework框架版本
- RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件介绍
- RDIFramework.NET-.NET快速信息化系统开发整合框架 【开发实例 EasyUI】之产品管理(MVC版)
- Net设计模式实例之简单工厂模式(Simple Factory Pattern)
- RDIFramework.NET -.NET快速信息化系统开发整合框架 【开发实例 EasyUI】之产品管理(MVC版)
- .net core Entity Framework Core Code First 框架 分层开发
- RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件WinForm业务平台
- RDIFramework.NET ━ .NET快速信息化系统开发框架-4.7 组织机构管理模块
- RDIFramework.NET ━ .NET快速信息化系统开发框架-第2章 产品概述
- RDIFramework.NET ━ .NET快速信息化系统开发框架-4.4 员工(职员)管理
- RDIFramework.NET — 基于.NET的快速信息化系统开发框架 - 5.3 数据库连接管理模块
- MagicAjax.NET Framework Features 框架特征