我读设计模式之工厂方法模式
看了好几篇blog学习工厂方法模式,都晕晕的,最后跟着TerryLee的文章范例,敲了一遍,又在纸上画了UML图,才大体搞清楚是怎么回事。感谢TerryLee,感谢吕老师(吕老师的解析说的非常透彻)
之前读的简单工厂模式,可以看到:如果设计者很清楚要创建几个对象(具体类),并且将来不怎么要求扩展的情况下,用它基本上可以工作的很好。但是,还拿我的举例来说,如果哪天真的有了需求变化,新增了一个数据库,那么我们除了要新增一个具体类外(不可避免),还要修改工厂类,添加判断条件。可以设想一下,对于一个复杂的系统,如果这样的需求经常发生,那么我们就要整天修改工厂类,使之逐渐庞大...
很显然,这个时候再利用简单工厂模式,就不太恰当。这个时候的变更实际上违背了设计原则中的开发封闭原则(对修改关闭,对扩展开放),单一职责原则(工厂类的作用)。
为了避免上述缺点,工厂方法模式对其做了扩展,对工厂类进行了进一步的抽象,使得我们的设计更加灵活。工厂方法模式主要包括四个部分:
1.抽象工厂角色:对具体工厂类的抽象,任何在模式中创建的具体对象的工厂类必须实现这个接口。
2.具体工厂角色:实现抽象工厂接口的具体工厂类。
3.抽象产品角色:对具体实现类的抽象,是每个具体实现类的父类或共同的接口。
4.具体产品角色:是抽象产品的具体实现。
这么说,理论的太多,读起来有点晕,还是结合具体的实例来的真切。这里对在简单工厂模式中的举例进行重新设计。

首先,我们通常会用到两种数据库(Sql server, Oracle),先对这两种具体产品进行抽象:
using System;
using System.Data;
/// <summary>
/// IDAL 的摘要描述
/// </summary>
///
namespace Demo3
{
public interface IDAL
{
DataSet getDs();
}
}然后针对此抽象接口,实现具体对象:
SQLDAL:
using System;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;

/// <summary>
/// SQLDAL 的摘要描述
/// </summary>
///
namespace Demo3
{
public class SQLDAL : IDAL
{
string connstr = ConfigurationManager.AppSettings["SQLServer"].ToString();
public SQLDAL()
{
}
public DataSet getDs()
{
using (SqlConnection conn = new SqlConnection(connstr))
{
conn.Open();
string sql = "select * from mis";
SqlDataAdapter ada = new SqlDataAdapter(sql, conn);
DataSet ds = new DataSet();
ada.Fill(ds);
return ds;
}
}
}
}OraDAL:
using System;
using System.Data;
using System.Configuration;
/// <summary>
/// OraDAL 的摘要描述
/// </summary>
///
namespace Demo3
{
public class OraDAL : IDAL
{
public OraDAL()
{
}
public DataSet getDs()
{
string sql = "select * from ivan_test ";
ISDApp01.ISDApp01 isd01 = new ISDApp01.ISDApp01();
DataSet ds = isd01.ExecuteQuery(sql);
return ds;
}
}
}
因为这里涉及到两类对象(sql,oracle),那么对应的要有工厂类为其服务,现在先对此工厂类进行抽象:
IDALFactory:
using System;
using System.Data;
/// <summary>
/// IDALFactory 的摘要描述
/// </summary>
///
namespace Demo3
{
public interface IDALFactory
{
IDAL GetDAL();
}
}两个具体工厂实现:
using System;
using System.Data;
using System.Configuration;
/// <summary>
/// SQLDALFactory 的摘要描述
/// </summary>
///
namespace Demo3
{
public class SQLDALFactory : IDALFactory
{
public SQLDALFactory()
{
}
public IDAL GetDAL()
{
return new SQLDAL();
}
}
}
using System;
using System.Data;
using System.Configuration;
/// <summary>
/// OraFactory 的摘要描述
/// </summary>
///
namespace Demo3
{
public class OraFactory : IDALFactory
{
public OraFactory()
{
}
public IDAL GetDAL()
{
return new OraDAL();
}
}
}
客户端:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Data;

namespace Demo3
{
class Program
{
static void Main(string[] args)
{
string factoryName = ConfigurationManager.AppSettings["factoryName"].ToString();

IDALFactory factory = (IDALFactory)Assembly.Load("Demo3").CreateInstance("Demo3." + factoryName);

IDAL cDal = factory.GetDAL();

DataSet ds = cDal.getDs();

int count = ds.Tables[0].Rows.Count;

Console.WriteLine(count);
}
}
}需要说明的一点:客户端并不关心目前用的那种数据库,它现在只是要数据。我们可以根据要求,获得对应的具体工厂类,因为,每个工厂类,只为一种具体产品服务,所以得到这个工厂类后,具体的产品类也就确定了。
通常我们会把要用到的工厂名称作为配置信息写入配置文件,这样在需求变化的时候,就不用更改源代码。
<appSettings>
<add key="factoryName" value="OraFactory"/>
<add key="SQLServer" value="server=xx.xx.xxx.xx;database=MIS;uid=sa;pwd=mis"/>
<add key="Oracle" value="Data source=oratest;uid=sa;password=sa"/>

</appSettings>
Code
参考学习文章:
http://www.cnblogs.com/zhenyulu/articles/36590.aspx
http://www.cnblogs.com/Terrylee/archive/2006/01/04/310716.html
很显然,这个时候再利用简单工厂模式,就不太恰当。这个时候的变更实际上违背了设计原则中的开发封闭原则(对修改关闭,对扩展开放),单一职责原则(工厂类的作用)。
为了避免上述缺点,工厂方法模式对其做了扩展,对工厂类进行了进一步的抽象,使得我们的设计更加灵活。工厂方法模式主要包括四个部分:
1.抽象工厂角色:对具体工厂类的抽象,任何在模式中创建的具体对象的工厂类必须实现这个接口。
2.具体工厂角色:实现抽象工厂接口的具体工厂类。
3.抽象产品角色:对具体实现类的抽象,是每个具体实现类的父类或共同的接口。
4.具体产品角色:是抽象产品的具体实现。
这么说,理论的太多,读起来有点晕,还是结合具体的实例来的真切。这里对在简单工厂模式中的举例进行重新设计。

首先,我们通常会用到两种数据库(Sql server, Oracle),先对这两种具体产品进行抽象:
using System;
using System.Data;
/// <summary>
/// IDAL 的摘要描述
/// </summary>
///
namespace Demo3
{
public interface IDAL
{
DataSet getDs();
}
}SQLDAL:
using System;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
/// <summary>
/// SQLDAL 的摘要描述
/// </summary>
///
namespace Demo3
{
public class SQLDAL : IDAL
{
string connstr = ConfigurationManager.AppSettings["SQLServer"].ToString();
public SQLDAL()
{
}
public DataSet getDs()
{
using (SqlConnection conn = new SqlConnection(connstr))
{
conn.Open();
string sql = "select * from mis";
SqlDataAdapter ada = new SqlDataAdapter(sql, conn);
DataSet ds = new DataSet();
ada.Fill(ds);
return ds;
}
}
}
}
using System;
using System.Data;
using System.Configuration;
/// <summary>
/// OraDAL 的摘要描述
/// </summary>
///
namespace Demo3
{
public class OraDAL : IDAL
{
public OraDAL()
{
}
public DataSet getDs()
{
string sql = "select * from ivan_test ";
ISDApp01.ISDApp01 isd01 = new ISDApp01.ISDApp01();
DataSet ds = isd01.ExecuteQuery(sql);
return ds;
}
}
}
IDALFactory:
using System;
using System.Data;
/// <summary>
/// IDALFactory 的摘要描述
/// </summary>
///
namespace Demo3
{
public interface IDALFactory
{
IDAL GetDAL();
}
}
using System;
using System.Data;
using System.Configuration;
/// <summary>
/// SQLDALFactory 的摘要描述
/// </summary>
///
namespace Demo3
{
public class SQLDALFactory : IDALFactory
{
public SQLDALFactory()
{
}
public IDAL GetDAL()
{
return new SQLDAL();
}
}
}
using System;
using System.Data;
using System.Configuration;
/// <summary>
/// OraFactory 的摘要描述
/// </summary>
///
namespace Demo3
{
public class OraFactory : IDALFactory
{
public OraFactory()
{
}
public IDAL GetDAL()
{
return new OraDAL();
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Data;
namespace Demo3
{
class Program
{
static void Main(string[] args)
{
string factoryName = ConfigurationManager.AppSettings["factoryName"].ToString();
IDALFactory factory = (IDALFactory)Assembly.Load("Demo3").CreateInstance("Demo3." + factoryName);
IDAL cDal = factory.GetDAL();
DataSet ds = cDal.getDs();
int count = ds.Tables[0].Rows.Count;
Console.WriteLine(count);
}
}
}通常我们会把要用到的工厂名称作为配置信息写入配置文件,这样在需求变化的时候,就不用更改源代码。
<appSettings>
<add key="factoryName" value="OraFactory"/>
<add key="SQLServer" value="server=xx.xx.xxx.xx;database=MIS;uid=sa;pwd=mis"/>
<add key="Oracle" value="Data source=oratest;uid=sa;password=sa"/>
</appSettings>
这样,如果有新的需求,新增实现类(access),那么我们要做的就是新增实现了IDAL接口的具体类AccessDAL,和对应的此具体类的工厂类(AccessFactory).对于其他的任何对象,我们都不用改动。这就很好的实现了开发-封闭原则。
【实践】
用工厂方法模式对WMS入库作业进行休整。
说明:在利用反射的时候,通常是读取配置档案的信息来生成具体的类型。对于我们的不同的mvt的入库作业,可以在选择不同的mvt的同时就改变配置档案的信息,这样在反射的时候直接读取档案就可以获取有效信息。
SourceCode:/Files/Ivan-Yan/FactoryMathodPattern.rar
http://www.cnblogs.com/zhenyulu/articles/36590.aspx
http://www.cnblogs.com/Terrylee/archive/2006/01/04/310716.html

浙公网安备 33010602011771号