我怎样才能设计得更好?(在面向对象的设计中避免切换语句)

本文关键字:面向对象的 语句 更好 | 更新日期: 2023-09-27 17:52:42

我对面向对象设计有一点了解,但我不确定如何在我的代码中使用这些原则。下面是我正在做的:

    public void Query(Agency agency, Citation queryCitation) {
        queryCitation.AgencyCode = agency.AgencyCode;
        switch (agency.ClientDb.Type) {
            case "SQL":
                QueryOracle(agency, queryCitation);
                break;
            case "PIC":
                QueryPick(agency, queryCitation);
                break;
        }
    }

(大多数是来自NHibernate的对象。我正在使用一个遗留数据库系统,并将其部分重构为代码库。)显然,我可以在这里做一些不同的事情,这样我就不需要为具有相同输入的不同数据库查询使用重复的函数。它应该根据代理对象知道是使用Oracle数据库还是Pick数据库连接。(如果你从未听说过Pick数据库,那么我在这里工作之前也没有听说过。我们通过HTTP请求对它进行查询,所以它不是SQL。)

我是否应该创建一个接口,例如称为"ClientDbConnection",然后创建两个实现该接口的类,将查询数据库的代码移动到这些类,然后使用"agency.clientDb.Query(queryCitation)"之类的东西替换整个函数?

我怎样才能设计得更好?(在面向对象的设计中避免切换语句)

代理是你控制的类吗?如果是,执行如下操作:

public abstract class GenericDb
{
    public abstract void Query(parms);
}
在你的代理类中,你可以有
public GenericDb ClientDb {get; set;}

然后创建一个SqlDb类,如:

public class SqlDb : GenericDb
{
    public void Query(parms);
}
public class PicDb : GenericDb
{
    public void Query(parms);
}

那么下面的代码:

public void Query(Agency agency, Citation queryCitation) {
        queryCitation.AgencyCode = agency.AgencyCode;
        switch (agency.ClientDb.Type) {
            case "SQL":
                QueryOracle(agency, queryCitation);
                break;
            case "PIC":
                QueryPick(agency, queryCitation);
                break;
        }
    }

public void Query(Agency agency, Citation queryCitation) {
        queryCitation.AgencyCode = agency.AgencyCode;
        agency.ClientDb.Query(queryCitation);
    }

由于继承,它将知道ClientDb有GenericDb的基类。它将通过ClientDb参数的类型知道它应该运行SqlDb还是PicDb或Oracle等。

您可能希望在这里实现策略模式。基本上,switch语句中的每个可能的"类型"都将成为实现相同接口的自己的类。

应用策略模式

你可以使用一个工厂方法,它的参数是type值。该方法将返回正确的类(其返回类型是上面提到的接口)。

我会重构以利用接口。我可能会这样写:

public interface IQuery
{
    void Execute(Agency agency, Citation query);
}
public class OracleQuery : IQuery
{
    // Implementation
}
public class PickQuery : IQuery
{
    // Implementation
}

你可以改变代理类来存储一个IQuery对象的实例,而不是(或者除了)ClientDb对象:

public class Agency
{
    public IQuery Query { get; set; }
}

然后在你的初始化代码中(你通常会设置ClientDb属性),你可以设置实例到适当的IQuery实现:

agency.Query = new PickQuery();

要编写更少的代码,但要提高声明性代码级别的可读性,您可以为每个数据库切换到带有委托的字典。它可以很容易地扩展,并且可读性很强。考虑到这种更实用的方法你会得到像

这样的东西
void Query(Agency agency, Citation queryCitation)
{
    Dictionary<string, Action<Agency, Citation>> QueryMap = new Dictionary<string, Action<Agency, Citation>>
    {
        { "SQL", QueryOracle},
        { "PIC", QueryPic}
    };

    queryCitation.AgencyCode = agency.AgencyCode;
    QueryMap[agency.ClientDb.Type](agency, queryCitation);
}

ADO。. NET有一组通用类:DbCommand, DbConnection等。还实现了另一组通用接口:IDbCommand, IDbConnection等…

所以你可以使用它们,但最后可能会变得相当复杂。你的解决方案的优点是可读性很强。另外,也许pick数据库没有任何ADO。净提供者……

PS:我将替换Type属性类型,并使用enum代替。

有两种OOP解决方案:多态性和访问者模式。