我读设计模式之工厂方法模式

    看了好几篇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>


    这样,如果有新的需求,新增实现类(access),那么我们要做的就是新增实现了IDAL接口的具体类AccessDAL,和对应的此具体类的工厂类(AccessFactory).对于其他的任何对象,我们都不用改动。这就很好的实现了开发-封闭原则。

      【实践】
      用工厂方法模式对WMS入库作业进行休整。
      说明:在利用反射的时候,通常是读取配置档案的信息来生成具体的类型。对于我们的不同的mvt的入库作业,可以在选择不同的mvt的同时就改变配置档案的信息,这样在反射的时候直接读取档案就可以获取有效信息。

Code


      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

posted on 2008-07-18 14:48  easy2Dev  阅读(320)  评论(0)    收藏  举报

导航