设计模式 之 工厂方法

工厂方法

当要创建某 一个 系列的产品时,我们可以优先考虑用工厂方法来创建这系列的产品。要特别理解好“系列产品”是什么意思,注意工厂方法是针对“一个”系列,或者说单系列产品的。

        : 假设我们需要为SQL ServerOracle提供一个数据库连接Connection(只是假设而已^_^),并为其实现打开连接、关闭连接这两个操作。
注意:这里的SQL Server数据库连接、Oracle数据库连接可以说是一个系列产品,因为它们归属于“数据库连接”这一类型(或说这一系列)。
如果我们这么写:
public class Connection

{

    protected void OpenSQLServerConnection()

    {

        //...打开SQL Server数据库连接

    }

    protected void OpenOracleConnection()

    {

        //...打开Oracle数据库连接

    }

    public void Open(string databaseType)

    {

        if (databaseType == "SQLServer")

        {

            this.OpenSQLServerConnection();

        }

        else if (databaseType == "Oracle")

        {

            this.OpenOracleConnection();

        }

    }

 

    protected void CloseSQLServerConnection()

    {

        //...关闭SQL Server数据库连接

    }

    protected void CloseOracleConnection()

    {

        //...关闭Oracle数据库连接

    }

    public void Close(string databaseType)

    {

        if (databaseType == "SQLServer")

        {

            this.CloseSQLServerConnection();

        }

        else if (databaseType == "Oracle")

        {

            this.CloseOracleConnection();

        }

    }

}
写过操作数据库相关代码的朋友可能会觉得这种写法有点怪异,因为你已经习惯了正规的写法。这里,我想说的是在不再增加数据库类型的前提下,这种写法完全是可以的,而且,层次也比较清晰。但如果我们哪天真要为MySql数据库也添加一个数据库连接,也要有打开连接、关闭连接的操作,那我们肯定要修改上面
Connection,相信你也能很快改好这个类(因为它写得还算清晰^_^)

   
有一天,幸运之星看好了你,你写的这个Connection变成了一套国际标准(我这里所说的标准是每个数据库连接都要有打开连接、关闭连接的操作),别人都要遵循你这套标准(真是太爽了o(_)o…哈哈)。这时,有人向你申请添加支持MySql数据库的连接,这是对你标准的认可,相信你不会拒绝的J
   
当你很乐意的答应别人,你也不辞辛苦地在Open(string databaseType)Close(string databaseType)方法中加上对MySql的判断(哪怕是还有其他的AccessDB2Sqlite等数据库也要加上),并在你权威的Connection中加上OpenMySqlConnection()CloseMySqlConnection()。这时,你会发现问题来了:你必须让第三方把OpenMySqlConnection()方法和CloseMySqlConnection()方法的实现放到你的Connection中,这可是牵涉到知识产权的问题,大哥,俺只能提醒提醒你了,知识产权的问题俺也不大清楚。当然,你可能会想到很多解决这个问题的方案,你可以用代理的方式,给客户程序提供一个函数指针,函数内部具体实现由客户代码去实现即可。其实,这种做法是比较麻烦的,每添加一个种新的类型的数据库,就得修改Connection。这时,你会开始思索有没有不改动Connection 就能达到我们要求的方案。

  既然是一套标准那就应该public出来,而且其他具体的数据库连接必须实现好这套标准。所以我们可以定义一个接口:
public interface IConnection

{

    void Open();//接口中的方法是默认public,因为接口就是一套标准。

    void Close();

}
这样,我们再让SQLServerOracle来实现这套标准
:
public class SqlServerConnection : IConnection

{

    public void Open()

    {

        //...打开SQL Server数据库连接

    }

    public void Close()

    {

        //...关闭SQL Server数据库连接

    }

}

 

public class OracleConnection : IConnection

{

    public void Open()

    {

        //...打开Oracle数据库连接

    }

    public void Close()

    {

        //...关闭Oracle数据库连接

    }

}
当我们要增加一类新数据库连接时,如MySql数据库连接
:
public class MySqlConnection : IConnection

{

    public void Open()

    {

        //...打开MySql数据库连接

    }

    public void Close()

    {

        //...关闭MySql数据库连接

    }

}
我们并没有改我们的标准(
IConnection接口),而是以扩展的方式添加了一个MySqlConnection类。MySqlConnectionOpen()Close() 我想理所当然的应该由MySqlConnection自己来做,我们不能把标准的东西(所谓的抽象)和具体实现糅杂在一起。

   
当然,如果我们自始至终只有SqlServer或只有Oracle数据库,那么就没有标准可言了,因为标准就意味着要大家去遵守,既然是要“大家”去遵守,也就意味着有多个成员,而且成员还可以再增加,就像我们的数据库连接有SqlServer数据库连接、Oracle数据库连接、MySql数据库连接等成员(或称产品),以后可能还有其他新类型的数据库的数据库连接。但不管怎样,这些都属于一类产品(或者说同属于一系列的产品),那就是它们都是数据库连接^_^当要创建某 一个 系列的产品时,我们可以优先考虑用工厂方法来创建这系列的产品。要特别理解好“系列产品”是什么意思,注意工厂方法是针对“一个”系列,或者说单系列产品的。
  上面我们写的继承自IConnection接口的SqlServerConnection OracleConnection MySqlConnection都是数据库连接这个系列产品中的具体产品,还剩下生产这些产品的工厂:
public class ConnectionFactory

{

    private IConnection _connection;

    public IConnection Create()

    {

        string databaseType = ReadAppConfig();//通过读取配置文件获取数据库类型

 

        //通过数据库类型创建该类型下的数据库连接。

        switch (databaseType)

        {

            case "SqlServer":

                _connection = new SqlServerConnection();

                break;

            case "Oracle":

                _connection = new OracleConnection();

                break;

        }

        return _connection;

    }

}
这样,当我们新增其他数据库的数据库连接时,要修改的是
ConnectFactory类中swith-case语句,当然我们可以用反射机制做到连ConnectFactory
类也不修改。

整个代码如下:

public interface IConnection

{

    void Open();//接口中的方法是默认public,因为接口就是一套标准。

    void Close();

}


public class SqlServerConnection : IConnection

{

    public void Open()

    {

        //...打开SQL Server数据库连接

    }

    public void Close()

    {

        //...关闭SQL Server数据库连接

    }

}

 

public class OracleConnection : IConnection

{

    public void Open()

    {

        //...打开Oracle数据库连接

    }

    public void Close()

    {

        //...关闭Oracle数据库连接

    }

}

public class ConnectionFactory

{

    private IConnection _connection;

    public IConnection Create()

    {

        string databaseType = ReadAppConfig();//通过读取配置文件获取数据库类型

 

        //通过数据库类型创建该类型下的数据库连接。

        switch (databaseType)

        {

            case "SqlServer":

                _connection = new SqlServerConnection();

                break;

            case "Oracle":

                _connection = new OracleConnection();

                break;

        }

        return _connection;

    }

 

SqlServerConnection
void Open()
void Close()

ConnectionFactory
IConnection Create()

IConnection
void Open()
void Close()

}

类图:








工厂模式局限性:IConnection接口有变动时,如新增一个接口方法,所有子类都得修改。
大众观点^_^
(1)“改就改呗”观点,可能你也认为改就改呗。但在面向对象中,我们一贯的原则是以扩展的方式来写代码,如果我们已有的代码继承了微软类库中的某个接口,哪天微软将这个接口添加了一个方法,而且必须实现该方法才能达到某种我们需要的效果,而我们的代码已经打包发布出去了。怎么办? 只能找到我们的源码,实现相应的接口方法,然后再重新编译,部署发布。然后,我们还会担心修改后的代码会不会影响已有的代码,可能我们还得重新测试。真麻烦!这时,我们会想,有没有不用修改已有代码,不用重新编译部署发布的办法。我们可能希望通过添加一个新的dll到已发布的程序中或只是修改一下配置文件就能解决问题。
(2)“接口不行用抽象类”^_^,有朋友可能会想到这个方法。
不错,抽象类中的方法子类可以不用重载,那么这样可以避免不修改子类吗?试想一下,你在基类中添加的新方法是做什么用的?肯定是用来实现新功能或达到什么效果,如果你只是在基类中定义了,而没在其他地方调用(或者说没在子类的调用J),那岂不白写了。修改抽象层的东西(或者说是修改基类、接口)不可避免的要修改其他代码。所以,我们平常Coding时,讲究的是具体实现依赖于抽象,我们只希望修改具体的实现,而不想动我们的框架。因为动框架牵动太多了。

关联设计模式:

(1)如果在IConnection中,我们一定、必须、非得要加一个方法用于实现某中功能,而且这个方法过几个月可能就要改动改动^_^,客户太挑了,没办法,苦命的程序员。这时,我们可能会想到预留一个接口,这就要求我们对客户需求非常清楚,这样才能做到有备无患o(_)o…哈哈,访问者模式就是应对这种情形的。访问者嘛,过几个月,我们的代码用一个“访问者”,再过几个月又改换另外一个“访问者”
^_^
(2)抽象工厂,前面我们强调工厂方法是针对
单系列产品的,那么对应就有个多系列

在数据库中,除了数据库连接IConnection外,还有执行Sql语句的Command,还有数据适配器等,而这些Command、数据适配器 数据库连接一样也分SqlServerOracleMySql。所以Command对应有一个系列,数据适配器对应有一个系列。而我们操作数据库往往是综合使用这些对象,当我们要创建多个系列的对象时,就应该想到使用抽象工厂模式。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值