我的.NET生活

活着便精彩!
随笔 - 41, 评论 - 680, 引用 - 66

导航

关于

标签

每月存档

最新留言

广告

用Factory Method模式扩展MyMSDNTVLibrary

MyMSDNTVLibrary是我以前写的一个小型WinForms项目,当时的想法是为初学者们演示如何创建一个简单但却完整的小项目。今天我又把这个小项目翻出来,通过应用Factory Method模式,使该项目可以支持多种不同的数据源(Access/ SQL Server ...)。大家如果感兴趣可以看我写的这篇文章

 

目前我增加了对Access和SQL Server的支持,对其它数据源的支持也可以很容易增加。

 

BTW:不知大家对MCT领域是否关注,我昨天看到正在进行“2004年微软金牌认证讲师”评选。在其中列出的MCT名单中就有我们博客堂的马骐,一位有着四年授课经验的资深MCT(同时也是我的好老师、引路人);此外还有我们博客堂长期以来的好朋友李争(上次聚会时站在我旁边的那个高个子:)。如果大家也和我同样欣赏和佩服这二位MCT的话,那么请支持他们!

posted on 2004-03-19 12:05:00 by musicland  评论(14) 阅读(5365)

用Factory Method模式扩展MyMSDNTVLibrary

MyMSDNTVLibrary (http://blog.joycode.com/musicland/posts/13776.aspx) 是我以前写的一个小型WinForms项目,当时的想法是为初学者们演示如何创建一个简单但却完整的小项目。很多朋友都对这个简单的小东西很感兴趣,西安的一位朋友在看着源码重新做了一遍之后,甚至还自己增加了添加TV的新功能。这让我感到非常欣慰。

?

正好最近又复习了一遍设计模式,我开始重新审视原有的应用程序结构,发现了一些应该改进的地方。比如说,我在写MyMSDNTVLibrary的第一个版本时就非常想让它能够很方便地支持不同种类的数据源,例如AccessSQL Server,甚至是单纯的XML。我知道实现起来并不难,但怎样做才能最有效最有利于代码复用?想来想去,我决定在数据访问这一部分应用Factory Method模式。

?

Factory MethodGOFDesign Pattersn一书中给出的一种模式,GOF为它做出的定义是:

?

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

?

简单来说,Factory Method的目的是想创建几个相似的(实现同一接口或继续同一父类)类中的某一个,为了达到这一目的,需要创建几个相似的creator类,通过creator类来决定创建哪一个所需的对象类。它的UML图示如下:

?

?

具体到这个项目来说,我需要针对不同的数据源来创建几个不同的DBHelper(我的个人习惯是通过DBHelper来封装针对特定的数据源的访问动作),如OleDBHelperSqlDBHelper等,这些Helper有非常相近的结构,因此可以让它们继续于同一接口——IDBHelperIDBHelper的定义如下:

?

using System;

using System.Data;

?

namespace musicland.MSDNTVLibrary.Component

{

?? public interface IDBHelper

?? {

????? DataSet GetAll();

?? }

}

?

注意其中给出了一个有待实现的方法GetAll,通过实现类对该方法的调用,可以获得应用程序所需的全部数据。

?

接下来就是从IDBHelper继续而来的两个具体数据访问辅助类。

?

OleDBHelper

?

using System;

using System.Configuration;

using System.Data;

using System.Data.OleDb;

?

namespace musicland.MSDNTVLibrary.Component

{

?? // This is the helper class that will interact with OleDB for info.

?? internal class OleDBHelper: IDBHelper

?? {

????? private OleDbConnection conn;

?

????? public OleDBHelper()

????? {

???????? conn=new OleDbConnection(ConfigurationSettings.AppSettings["OleConnectionString"]);

????? }

?

????? public DataSet GetAll()

????? {

???????? OleDbDataAdapter da=new OleDbDataAdapter("select * from Episode order by Date desc", conn);

???????? DataSet ds=new DataSet();

???????? try

???????? {

??????????? da.Fill(ds, "Episode");

???????? }

???????? catch (OleDbException ex)

???????? {

??????????? throw ex;

???????? }

???????? finally

???????? {

??????????? if (conn.State!=ConnectionState.Closed)

??????????????? conn.Close();

???????? }

???????? return ds;

????? }

?

?? }

}

?

SqlDBHelper

?

using System;

using System.Configuration;

using System.Data;

using System.Data.SqlClient;

?

namespace musicland.MSDNTVLibrary.Component

{

?? // This is the helper class that will interact with SqlServer for info.

?? internal class SqlDBHelper: IDBHelper

?? {

????? SqlConnection conn;

?

????? public SqlDBHelper()

????? {

???????? conn=new SqlConnection(ConfigurationSettings.AppSettings["SqlConncectionString"]);

????? }

?

????? public DataSet GetAll()

????? {

???????? SqlCommand cmd=new SqlCommand("GetAll", conn);

???????? cmd.CommandType=CommandType.StoredProcedure;

?

???????? SqlDataAdapter da=new SqlDataAdapter(cmd);

???????? DataSet ds=new DataSet();

???????? try

???????? {

??????????? conn.Open();

??????????? da.Fill(ds, "Episode");

???????? }

???????? catch (SqlException ex)

???????? {

??????????? throw ex;

???????? }

???????? finally

???????? {

??????????? if (conn.State!=ConnectionState.Closed)

??????????????? conn.Close();

???????? }

???????? return ds;

????? }

?? }

}

?

代码很简单,分别是针对不同的数据源来获取Episode表的全部内容(因为这个应用程序所需要的数访内容非常少,结构也很简单,因此一张Episode表就足够了)。其中,针对AccessOleDBHelper我直接使用SQL语句进行查询,而针对SQL ServerSqlDBHelper里这一部分则改用了存储过程,这也就非常清晰地体现出了不同数据源的访问方式的差别。

?

上面已经把数据访问辅助类构建完毕,接下来就该创建CreatorConcreteCreator来动态调用这些辅助类了。OK,创建一个IDBCreatro接口:

?

using System;

?

namespace musicland.MSDNTVLibrary.Component

{

?? public interface IDBCreator

?? {

????? IDBHelper CreateDBHelper();

?? }

}

?

通过实现该接口,我们可以获得用于创建不同的数据访问辅助类的具体类:

?

OleDBCreator

?

using System;

?

namespace musicland.MSDNTVLibrary.Component

{

?? // This is the ConcreteCreator class that helps to

?? // create and return the OleDBHelper class.

?? public class OleDBCreator: IDBCreator

?? {

????? public OleDBCreator() {}

?

????? public IDBHelper CreateDBHelper()

????? {

???????? return new OleDBHelper();

????? }

?? }

}

?

SqlDBCreator

?

using System;

?

namespace musicland.MSDNTVLibrary.Component

{

?? // This is the ConcreteCreator class helps to

?? // create and return the SqlDBHelper class.

?? public class SqlDBCreator: IDBCreator

?? {

????? public SqlDBCreator() {}

?????

????? public IDBHelper CreateDBHelper()

????? {

???????? return new SqlDBHelper();

????? }

?? }

}

?

以上两个类均实现了IDBCreator接口。通过实现CreateDBHelper方法,就可以创建一个数据访问辅助类(OleDBHelperSqlDBHelper)的实例并传回调用方,这正是我们所需要的。

?

OK,主要框架已经搭建完毕,接下来我们只要在应用程序中通过适当的逻辑来进行调用了。我的初步设想是在.config文件中增加一个自定义的DBType键,通过为DBType设值来决定应用程序需要哪一种数据访问方式。配置文件部分内容如下:

?

?? <appSettings>

????? --

????? Note: DBType indicates which DB need to interact with.

????? currently:

???????? 0: SqlServer

???????? 1: OleDB

????? -->

????? <add key="DBType" value="1" />

????? <add key="OleConnectionString" value="Provider=Microsoft.Jet.OLEDB.4.0; Data Source=MSDNTV.mdb;" />

????? <add key="SqlConncectionString" value="Server=(local); Database=MyMSDNTVLibrary; Integrated Security=SSPI" />

????? ...

?? appSettings>

?

设置好DBType以后,我们要做的只是在应用程序中读出这一配置值,然后去创建相应的DBCreator即可,代码如下:

?

????? // Get the corresponding IDBCreator class

????? public static IDBCreator GetDBCreator()

????? {

???????? int dbType=Convert.ToInt32(ConfigurationSettings.AppSettings["DBType"]);

???????? switch (dbType)

???????? {

??????????? case 0:

??????????????? return new SqlDBCreator();

??????????? case 1:

??????????????? return new OleDBCreator();

??????????? default:

??????????????? return new OleDBCreator();

???????? }

????? }

?

以上代码可返回一个实现了IDBCreator接口的具体DBCreator,即OleDBCreatorSqlDBCreator,通过调用该类所实现的CreateDBHelper方法即可获得应用程序所需的DBHelper

?

????? // Get the corresponding IDBHelper class

????? public static IDBHelper GetDBHelper(IDBCreator dbCreator)

????? {

???????? return dbCreator.CreateDBHelper();

????? }

?

这样,应用程序就可以针对特定的数据源来动态采用特定的数据访问方式了。

?

OK,以上我通过一个简单的例子来扩展了MyMSDNTVLibrary,同时也说明了Factory Method在具体应用程序中的应用。当然,设计模式不止这一种,而Factory Method的应用也不仅限于此。我所给出的环境只是个非常小的特例,在更多的情况下,不同的设计模式需要结合起来应用(Patterns are supposed to be sewn together to solve a problem)。下面我推荐一些有关设计模式的书籍和资料,希望对大家有帮助:

?

Design Patterns – Elements of Reusable Object-Oriented Software (设计模式——可复用面向对象软件的基础)(太经典了)

Design Patterns Explained – A new Perspective on Object-Oriented Design (设计模式解析) (结合上面一书学习)

C# Design Patterns: A Tutorial C#设计模式)(完全用C#实现,有一定参考价值)

Data & Object Factory

?

Update:

JGTM’2004[MVP]指点,我对上文中所述的环境再次进行了重构,通过增加插件的形式(继承于IProvider接口的一个assembly)使Library从真正意义上动态支持多数据源而无须重新编译,从真正意义上做到了unplugged。具体的实现思路请见JGTM’2004[MVP]在文后给出的精彩回复。大家如果感兴趣也可以在这里下载全部源码(感谢JohnnyHu提供空间!)。

?

posted on 2004-03-19 11:26:00 by musicland  评论(27) 阅读(8872)

临时数据存储可以考虑的方法——IsolatedStorage

前段日子读代码时发现了一个不曾用过的命名空间——System.IO.IsolatedStorage,通过该命名空间下面的一些类(如IsolatedStorageIsolatedStorageFileIsolatedStorageFileStream等)可以把应用程序中一些敏感的信息保存在文件系统之外,这让我感到很兴奋。

 

举个简单的例子,下面这行代码可以在IsolatedStorageMSDN中译为“独立存储区”)寻找是否有一个名为PersonalFeedList.xml的文件:

 

if (IsolatedStorageFile.GetUserStoreForDomain().GetFileNames("PersonalFeedList.xml").Length!=0)

    {

        IsolatedStorageFileStream listLoader=new IsolatedStorageFileStream("PersonalFeedList.xml",

                                               FileMode.Open, IsolatedStorageFile.GetUserStoreForDomain());

        // TODO: process the FileStream here

        }

 

关于IsolatedStorage命名空间的具体应用大家如有兴趣可以查阅MSDN,我个人认为IsolatedStorage非常适合存储那些应用程序运行过程中产生的临时文件,比如下载到本机需要进一步处理的xml文件。这些文件不便于呈现给最终用户,有些还不能让用户看到(比如一些临时性的密码存储)。由于IsolatedStorage独立于用户的文件系统,因此这种存储方式在某种程度上保证了数据的保密性。

 

不过此项应用也有诸多不足,最主要的一点是:你不应该用它来存储永久性文件,因为用户可以使用.NET SDK中的storeadm.exe工具删除独立存储区的所有数据,方法是:

 

storeadm /remove

 

posted on 2004-03-10 21:13:00 by musicland  评论(17) 阅读(5919)

有感于“小弟e文很差”

最近有很多初学.NET开发的朋友给我发来邮件,在询问一些具体问题之余还希望能得到一些关于学习方法的提示,末了很多人还会要一些电子资料,并特别强调要中文的,而且千篇一律地会说一句话:“小弟e文很差”。

 

其实我真的很想和这些“e文很差”的小弟们面对面地好好谈一谈,告诉他们怎样才能度过学习初期的难关,怎样才能满腔热情地去做自己喜欢的事。说真的,我很不喜欢别人说类似“小弟e文很差”这样的话,英语不好可以努力去学,天质不高可以靠勤奋去补,不能因为自己英语不好就“自觉地”把自己关在英文资料大门之外,也不能因为这一项缺点的存在而就放弃努力。我就真的见过这么一位初学者,在和我联系过的几个月后又一次向我表示“小弟e文很差”,天哪,士别三日,当刮目相看,都几个月过去了,你怎么就还没进步呢?

 

下面我给小弟们讲个故事吧,故事的主人公叫Yngwie Malmsteen,是一位来自瑞典的超级吉他大师(我没说跑题),他小时候立志刻苦练琴的经历绝对值得我们每个开发人员学习。下面一段来自一篇对他的访谈:

 

有人经常问我是如何提高我的演奏技巧的,在这个问题上没有什么秘密。在我刚开始学琴的好几年中,我除了练吉他之外,几乎不做任何事情。我从早上一醒来就会抓起吉他,一直练习到晚上睡着了,当我再次醒来时会发现手里还攥着吉他。

 

也许你会觉得他太疯狂了,没错,就是这样的疯狂精神使他一度成为世界上弹吉他速度最快的大师,也就是这样刻苦的经历使他开创了吉他音乐史上的新篇,新古典主义从此诞生。

 

希望我那些“e文很差”的小弟们能从中体会到什么,希望小弟们能真的少一些抱怨,多一些刻苦。

posted on 2004-03-01 22:42:00 by musicland  评论(29) 阅读(5361)

Powered by: Joycode.MVC引擎 0.5.2.0