【问题标题】:C# Abstracting methodsC# 抽象方法
【发布时间】:2011-02-24 16:37:04
【问题描述】:

我有一组方法,接受日期和布尔值。然后这些方法使用一些 SQL,然后将其传递给另一个方法并返回一个列表。除了 SQL 和返回的 List 之外,每个方法都几乎相同。现在我知道有更好的方法来执行这些方法,但我不确定如何。

由于方法几乎相同,有人对我如何抽象或使用更好的设计有任何想法吗?

代码如下:

private List<ENT_Message> GetMessageData(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
{
    List<ENT_Message> ret = new List<ENT_Message>();
    string sql = "";

    if (IsSelectAll)
    {
        sql = "select * from tbl_message";
    }
    else
    {
        sql = " Select * from tbl_message where created_Date between @start_Date and @end_date";
    }

    return Converter.SerializeToMessageList(this.GetData(startDate, endDate, IsSelectAll, sql));                   
}

private List<ENT_SensorData> GetSensorData(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
{
    List<ENT_SensorData> ret = new List<ENT_SensorData>();
    string sql = "";

    if (IsSelectAll)
    {
        sql = "select * from tbl_sensor_data";
    }
    else
    {
        sql = "select * from tbl_sensor_data where Timestamp between @start_date and @end_Date";
    }

    return Converter.SerializeToSensorDataList(this.GetData(startDate, endDate, IsSelectAll, sql)); 
}

private List<ENT_SensorDataEvent> GetSensorDataEvents(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
{
    List<ENT_SensorDataEvent> ret = new List<ENT_SensorDataEvent>();
    string sql = "";

    if (IsSelectAll)
    {
        sql = "select * from tbl_sensor_data_event";
    }
    else
    {
        sql = "select * from tbl_sensor_data_event where start_time between @start_date and @end_Date";
    }

    return Converter.SerializeToSensorEventDataList(this.GetData(startDate, endDate, IsSelectAll, sql)); 
}

private List<ENT_SensorDataState> GetSensorDataState(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
{
    List<ENT_SensorDataState> ret = new List<ENT_SensorDataState>();
    string sql = "";

    if (IsSelectAll)
    {
        sql = "select * from tbl_sensor_data_state";
    }
    else
    {
        sql = "select * from tbl_sensor_data_state where start_time between @start_date and @end_Date";
    }

    return Converter.SerializeToSensorDateStateList(this.GetData(startDate, endDate, IsSelectAll, sql)); 
}

private List<ENT_WorkOrder> GetWorkOrders(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
{
    List<ENT_WorkOrder> ret = new List<ENT_WorkOrder>();
    string sql = "";

    if (IsSelectAll)
    {
        sql = "select * from tbl_workorder";
    }
    else
    {
        sql = "select * from tbl_sensor_data_state where completed_date between @start_date and @end_Date";
    }

    return Converter.SerializeToWorkOrderList(this.GetData(startDate, endDate, IsSelectAll, sql));   
}

【问题讨论】:

  • 您为什么不直接添加代码作为您问题的一部分?
  • @RQDQ:代码有点长,会降低问题的可读性。为了清楚起见,我认为它在其他地方没有问题。
  • @Henk 如果您查看代码,您会看到返回的列表
  • @RQDQ:因为代码会占用大量空间
  • 这个问题应该在codereview

标签: c# design-patterns methods abstraction


【解决方案1】:

您可以大大缩短您的功能。例如,而不是

private List<ENT_SensorDataState> GetSensorDataState(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
{
    List<ENT_SensorDataState> ret = new List<ENT_SensorDataState>();
    string sql = "";

    if (IsSelectAll)
    {
        sql = "select * from tbl_sensor_data_state";
    }
    else
    {
        sql = "select * from tbl_sensor_data_state where start_time between @start_date and @end_Date";
    }

    return Converter.SerializeToSensorDateStateList(this.GetData(startDate, endDate, IsSelectAll, sql));
}

你可以简单地写

private List<ENT_SensorDataState> GetSensorDataState(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
{
    string sql = "select * from tbl_sensor_data_state"
                 + (IsSelectAll ? "" : "where start_time between @start_date and @end_Date");

    return Converter.SerializeToSensorDateStateList(this.GetData(startDate, endDate, IsSelectAll, sql));
}

这基本上将您的函数简化为真正重要的东西:SQL 和序列化函数的名称。

【讨论】:

  • 因此,您仍在复制/粘贴每个方法。泛型或抽象方法可以产生更少重复的结果。
  • @Abel:是的。尽管如此,每个函数的 SQL 都不同,所以 那部分 不能一概而论。
  • 嗨,这很好,但它不起作用。它给了我一个关于“IsSelectAll”不是字符串的错误
  • @Funky:抱歉,缺少括号。现在应该修好了。
【解决方案2】:

看起来你有机会制作一个通用方法。如果我有语法错误,我会提前道歉。

private List<T> GetData<T>(DateTime? startDate, DateTime? endDate, bool IsSelectAll) where T : ENT_Data
{
    List<T> ret = new List<T>();
    string sql = "";

    if (IsSelectAll)
    {
        sql = "select * from " + T.table;
    }
    else
    {
        sql = " Select * from " + T.table + " where created_Date between @start_Date and @end_date";
    }

    return Converter.Serialize<T>(this.GetData(startDate, endDate, IsSelectAll, sql));    
}

注意我做的一些事情:

  1. 泛型方法有一个约束,即 T 是 ENT_Data 的子类,ENT_Data 是我刚刚创建的类型。
  2. 我为 ENT_Data 类型提供了一个 table 字段,供您在查询中使用。
  3. Converter.Serialize 方法也以类似的方式进行了通用化。

如果我错过了任何重要的事情,请告诉我。

【讨论】:

  • 您不能将 T 限制为具有名为 table 的静态字段/属性。
  • 你是对的,我的错。您必须创建一个 T 实例,然后获取表名。
  • 只是想使它更通用(编写一次):使用父接口IEnt_Data 和属性table。然后让你的基础具体类ENT_Data 有一个静态方法ENT_Data.Create&lt;T&gt;,它返回该类型的一个实例。然后您可以使用ENT_Data.Create&lt;T&gt;().table 调用它。
【解决方案3】:
private List<T> GetData<T>(DateTime? startDate, DateTime? endDate, bool IsSelectAll)
        {
            List<T> ret = new List<T>();

            string tableName = "";//Select the table name based on T here
            string sql = "";

            if (IsSelectAll)
            {
                sql = "select * from tbl_sensor_data";
            }
            else
            {
                sql = "select * from tbl_sensor_data where Timestamp between @start_date and @end_Date";
            }
            //Build your list using the appropriate converter based on T here        
            return Converter.SerializeToSensorDataList(this.GetData(startDate, endDate, IsSelectAll, sql)); 
        }

从那时起,你打电话给

GetData<ENT_whatever>() 

只有。

【讨论】:

  • tbl_sensor_data 仅与一种方法相关 - 它确实需要依赖于 T.
【解决方案4】:

您可以创建一个类 SqlRetriever,然后使用要发送的特定 SQL 消息对每个类进行子类化。

class SqlRetriever {
    public abstract void SendSqlCmd();
    public ArrayList List {
        get; set;
    }
}

class SqlRetrieverXXX
    public const string SqlCmd = " ... ";
    public override void SendSqlCmd()
    {
       /* ... */
    }
}

另一种可能性,涉及更少的类,设计更差:

class SqlRetriever {
        public const string SQLCmd1 = "...";
        public const string SQLCmd2 = "...";

        public void SendSqlCmd(string sqlcmd)
        {
             /*Send the SQL Cmd and store the result in the list */
        }

        public ArrayList List {
            get; set;
        }

        public static ArrayList DoSqlCmd(string sqlCmd)
        {
             var obj = new SqlRetriever();

             obj.SendSqlCmd( sqlCmd );

             return obj.List;
        }
}

【讨论】:

    【解决方案5】:

    如果您将 sql 语句和委托方法存储在键为某种类型 T 的映射中,然后使您的方法成为通用方法(方法采用类型 T 并返回 T 的列表),您可以进行映射查找以获取正确的sql语句和委托方法。

    【讨论】:

      【解决方案6】:

      您可以拥有一个所有 ENT_* 类都继承自的基类。然后制作一种方法 ListGetData(yourParams)。创建一个工厂方法,根据 ENT_Base 的类型为您获取实体列表。 您的 GetData() 调用它并返回它。 您不会绕过 switch-case 或 if-then 来确定您必须返回哪种实体。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-08-28
        • 1970-01-01
        • 2015-08-14
        • 2019-10-10
        • 1970-01-01
        • 2021-10-13
        • 1970-01-01
        相关资源
        最近更新 更多