在这种情况下使用多态性优于枚举有什么好处吗?
Is there any benefit about using Polymorphism over Enum in this scenario?
场景
我正在创建动态查询生成器以发送到另一个组件(报表生成器)。
查询的某些部分有占位符。例如:
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。所以我重构为使用基数 class
public abstract class FilterType
{
public abstract string Replace(string baseString, string oldValue, string newValue);
}
并且每个类型都有自己的实现:
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) 是否有另一种方法可以在不使用 switch ou if-else 子句的情况下将 SOLID OCP 原则同时应用于数据库和业务代码?
Replacing conditional with polymorphysim 将确保决策只需要发生一次,所以这可能是个好主意。如果您在某些时候对每种类型有额外的专门操作,那么它们应该很容易实现。
现在,为了创建具体类型,您可以将此逻辑封装在工厂中。在最简单的形式中,工厂将是一个带有大量 switch 语句的静态工厂。它不遵守 OCP,但在大多数情况下它仍然是一个可以接受的设计。
但是,如果您希望通过设计在运行时实现可扩展,那将无法实现,您需要引入一种允许在运行时 discover/register 新类型的方法。
这可以通过多种方式完成,但一个例子是在工厂上有一个允许您注册新类型的方法。
例如
filterTypeFactory.RegisterFilter(1, typeof(FilterTypeInteger));
无论如何,在您拥有并构建自己的 SQL 语句生成器之前,您应该查看现有的库。您可能有一个中间 DSL(您的模板)被解析为 AST,然后处理该 AST 以生成 SqlCommand 或类似的东西。
场景
我正在创建动态查询生成器以发送到另一个组件(报表生成器)。
查询的某些部分有占位符。例如:
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。所以我重构为使用基数 class
public abstract class FilterType
{
public abstract string Replace(string baseString, string oldValue, string newValue);
}
并且每个类型都有自己的实现:
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) 是否有另一种方法可以在不使用 switch ou if-else 子句的情况下将 SOLID OCP 原则同时应用于数据库和业务代码?
Replacing conditional with polymorphysim 将确保决策只需要发生一次,所以这可能是个好主意。如果您在某些时候对每种类型有额外的专门操作,那么它们应该很容易实现。
现在,为了创建具体类型,您可以将此逻辑封装在工厂中。在最简单的形式中,工厂将是一个带有大量 switch 语句的静态工厂。它不遵守 OCP,但在大多数情况下它仍然是一个可以接受的设计。
但是,如果您希望通过设计在运行时实现可扩展,那将无法实现,您需要引入一种允许在运行时 discover/register 新类型的方法。
这可以通过多种方式完成,但一个例子是在工厂上有一个允许您注册新类型的方法。
例如
filterTypeFactory.RegisterFilter(1, typeof(FilterTypeInteger));
无论如何,在您拥有并构建自己的 SQL 语句生成器之前,您应该查看现有的库。您可能有一个中间 DSL(您的模板)被解析为 AST,然后处理该 AST 以生成 SqlCommand 或类似的东西。