情景
我正在创建一个动态查询构建器以发送到另一个组件(报表生成器)。
查询的某些部分有占位符。例如:
SELECT DISTINCT ID, NAME AS VALUE FROM EVENTS
WHERE {{ESTABLISHMENTFILTER.ID}} IS NULL OR ESTABLISHMENT_ID = {{ESTABLISHMENTFILTER.ID}}
where子句中要替换的数据可以是Integer,String,Date,每个都有不同的行为(例如:在字符串的情况下包含单引号)。
我的第一个方法是创建一个枚举:
public enum FilterType
{
Integer,
String
}
并像这样使用它(例如在业务层中)
switch (filter.Type)
{
case FilterType.Integer:
//Do replace logic for an integer
break;
case FilterType.String:
//Do replace logic for a string
break;
default:
break;
}
我也将SOLID原则应用于我的代码,我发现这可能会破坏OCP。所以我重构了使用基类
public abstract class FilterType
{
public abstract string Replace(string baseString, string oldValue, string newValue);
}
并且每个Type都有自己的实现:
public class FilterTypeInteger : FilterType
{
public override string Replace(string baseString,string oldValue, string newValue)
{
//Do logic to replace for an Integer type
}
}
问题
SOLID解决方案适用于我的测试,但在生产代码中,数据库中有一个int列来确定类型。所以我基本上转移了' switch-case'数据层的逻辑,必须检查此列以实例化正确的FilterType(下面的代码是伪代码,因为我还没有实现它):
if (dataReader["FILTERTYPE"] == 1)
filter.Type = new FilterTypeInteger();
else if (dataReader["FILTERTYPE"] == 2)
filter.Type = new FilterTypeString();
问题
1)实施' if-else'的方法上面的逻辑打破了OCP?因为如果创建了新的Type,则必须实现新的else子句 2)是否有另一种方法可以将SOLID OCP原则保留在数据库和业务代码中,而无需使用switch if if-else子句?
答案 0 :(得分:1)
Replacing conditional with polymorphysim将确保决策只需要发生一次,所以这可能是一个好主意。如果您在某些时候对每种类型都有其他专门操作,那么它们应该易于实现。
现在,为了创建具体类型,您可以将此逻辑封装在工厂中。在它最简单的形式中,工厂将是具有大型switch语句的静态工厂。它并不尊重OCP,但在大多数情况下它仍然是可接受的设计。
但是,如果您希望通过设计进行扩展,并且在运行时无法进行扩展,则需要引入一种允许在运行时发现/注册新类型的方法。
这可以通过多种方式完成,但一个例子就是在工厂设置一个允许您注册新类型的方法。
E.g。
filterTypeFactory.RegisterFilter(1, typeof(FilterTypeInteger));
无论如何,在您拥有并构建自己的SQL语句构建器之前,您应该查看现有的库。您可能有一个中间DSL(您的模板)被解析为AST,然后处理此AST以生成SqlCommand或类似的东西。