重构方法以减少切换语句的数量-C#

时间:2019-01-18 23:06:53

标签: c#

当前,我试图找到重构类似于以下内容的类的最佳方法:

public static SearchModel GetSearchResults(SearchModel model)
    {
        List<ResultModel> results = new List<ResultModel>();

        try
        {
            string sqlCommand = string.Empty;

            switch (model.Attribute)
            {
                case "Users":
                    sqlCommand = "GeneralUserSearch";
                    break;
                case "Favorites":
                    sqlCommand = "UserFavorites";
                    break;
                case "Email":
                    sqlCommand = "EmailSearch";
                    break;                                 
            }

            using (SqlConnection conn = new SqlConnection("connection string"))
            {
                conn.Open();

                using (SqlCommand cmd = AdoBase.GetSqlCommand(sqlCommand, conn))
                {                        
                    switch (model.Attribute)
                    {
                        case "Users":
                            if(!string.IsNullOrWhiteSpace(model.Name)) {
                                cmd.Parameters.AddWithValue("Name", model.Name);
                            }
                            if(!string.IsNullOrWhiteSpace(model.Username)) {
                                cmd.Parameters.AddWithValue("Username", model.Username);
                            }                                                           
                            break;
                        case "Favorites":
                            cmd.Parameters.AddWithValue("Favorites", model.Favorites);
                            break;
                        case "Email":
                            cmd.Parameters.AddWithValue("Email", model.Email);
                            break;                                             
                    }

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            while (reader.Read())
                            {
                                ResultModel result = new ResultModel();

                                switch (model.Attribute)
                                {
                                    case "Users":
                                        result.Users.Add(reader["User"]);                                       
                                        break;
                                    case "Favorites":
                                        result.User = reader["User"];
                                        result.Favorites = reader["Favorites"];
                                        break;
                                    case "Email":
                                        result.User = reader["User"];
                                        result.Email = reader["Email"];
                                        break;                                             
                                }

                                results.Add(result);
                            }
                        }
                    }

                }
            }
        }
        catch (Exception ex)
        {
            return ex;
        }

        return model;
    }

由于搜索根据model.Attribute中的值而变化,因此将使用switch语句。但是,大多数代码不依赖于Attribute。有没有一种方法可以重构此规则以消除switch语句或将其减少到仅一个?

4 个答案:

答案 0 :(得分:1)

开关是一种代码气味,表明您的代码不是OO。在这种情况下,您可以使用SearchModel的不同类型的search方法来实现每种类型。

答案 1 :(得分:1)

我同意TKK

话虽如此,并根据提供的代码回答您的问题,我只看到一种消除该方法中的switch语句的方法。

第一个switch语句将设置sqlcommand和参数,第二个语句将在while循环中保持不变。

在您的方法顶部声明这些:

`SqlConnection conn = new SqlConnection("connection string");
 SqlCommand cmd = AdoBase.GetSqlCommand("", conn);`

更改第一个switch语句以将参数添加到sql命令:

switch (model.Attribute)
{
    case "Users":
        sqlCommand = "GeneralUserSearch";
        if (!string.IsNullOrWhiteSpace(model.Name))
        {
            cmd.Parameters.AddWithValue("Name", model.Name);
        }

        if (!string.IsNullOrWhiteSpace(model.Name))
        {
            cmd.Parameters.AddWithValue("Username", model.Username);
        }

        break;
    case "Favorites":
        sqlCommand = "UserFavorites";
        cmd.Parameters.AddWithValue("Favorites", model.Favorites);
        break;
    case "Email":
        sqlCommand = "EmailSearch";
        cmd.Parameters.AddWithValue("Email", model.Email);
        break;
}

删除此代码块:

switch (model.Attribute)
{
    case "Users":
        if(!string.IsNullOrWhiteSpace(model.Name)) {
            cmd.Parameters.AddWithValue("Name", model.Name);
        }
        if(!string.IsNullOrWhiteSpace(model.Name)) {
            cmd.Parameters.AddWithValue("Username", model.Username);
        }                                                           
        break;
    case "Favorites":
        cmd.Parameters.AddWithValue("Favorites", model.Favorites);
        break;
    case "Email":
        cmd.Parameters.AddWithValue("Email", model.Email);
        break;                                             
}

在捕获后添加一个final,以清理连接和命令对象:

 finally
 {
    conn.Dispose();
    cmd.Dispose();
 }

答案 2 :(得分:1)

我简化了一点,只删除了一个开关,但是删除两个开关是非常不可能的,也许您可​​以使用它:

public static SearchModel GetSearchResults(SearchModel model)
{
    List<ResultModel> results = new List<ResultModel>();
    SqlCommand cmd = new SqlCommand(); // only for compile for finnaly dispose
    try
    {
        using (SqlConnection conn = new SqlConnection("connection string"))
        {
            conn.Open();
            string sqlCommand = string.Empty;

            switch (model.Attribute)
            {
                case "Users":
                    cmd = AdoBase.GetSqlCommand("GeneralUserSearch", conn);
                    if (!string.IsNullOrWhiteSpace(model.Name)) {
                        cmd.Parameters.AddWithValue("Name", model.Name);
                    }
                    if (!string.IsNullOrWhiteSpace(model.Name)) { // redundant if or mistake, and there should be model.Username
                        cmd.Parameters.AddWithValue("Username", model.Username);
                    }
                    break;
                case "Favorites":
                    cmd = AdoBase.GetSqlCommand("UserFavorites", conn);
                    cmd.Parameters.AddWithValue("Favorites", model.Favorites);
                    break;
                case "Email":
                    cmd = AdoBase.GetSqlCommand("EmailSearch", conn);
                    cmd.Parameters.AddWithValue("Email", model.Email);
                    break;
            }
            SimpleMethod(results, cmd, model);
        }
    }
    catch (Exception ex)
    {
        return ex;
    }
    finally
    {
        cmd.Dispose();
    }
    return model;
}

SimpleMethod:

public static void SimpleMethod(List<ResultModel> results, SqlCommand cmd, SearchModel model)
{
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        if (reader.HasRows)
        {
            while (reader.Read())
            {
                ResultModel result = new ResultModel();

                switch (model.Attribute)
                {
                    case "Users":
                        result.Users.Add(reader["User"]);
                        break;
                    case "Favorites":
                        result.User = reader["User"];
                        result.Favorites = reader["Favorites"];
                        break;
                    case "Email":
                        result.User = reader["User"];
                        result.Email = reader["Email"];
                        break;
                }
                results.Add(result);
            }
        }
    }
}

答案 3 :(得分:0)

其他答案充满了反模式。对switch语句的需求(尤其是如此频繁的重复)似乎是朝着更面向对象的方向发展的机会。

我还没有重构整个过程,只是为了给您一个想法。

public class SearchHelper
{
    //why does this need to return the model at all? the model isn't altered 
    //and would already be in scope for whatever is calling this method
    public static void GetSearchResults(SearchModel model)
    {
        List<ResultModel> results = new List<ResultModel>();

        try
        {
            using (SqlConnection conn = new SqlConnection("connection string"))
            {
                conn.Open();

                using (SqlCommand cmd = AdoBase.GetSqlCommand(model.SqlCommandName, conn))
                {
                    //this will mutate the object, so you don't need a return type. I'd suggest refactoring this further.
                    model.BuildSqlCommand(cmd);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        //your code sample wasn't returning this, but maybe you intended to?
                        BuildResultSet(reader);
                    }

                }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    private static IEnumerable<ResultModel> BuildResultSet(SqlDataReader reader)
    {
        var results = new List<ResultModel>();

        if (!reader.HasRows) { return results; }

        while (reader.Read())
        {
            ResultModel result = new ResultModel();

            //  ...the result composition would need to be refactored in a similar way as well
            results.Add(result);
        }
        return results;
    }
}

public abstract class SearchModel
{
    public string SqlCommandName { get; private set; }

    private SearchModel() { }

    protected SearchModel(string sqlCommandName)
    {
        SqlCommandName = sqlCommandName;
    }

    public abstract void BuildSqlCommand(SqlCommand command);
}

public class UserSearchModel : SearchModel
{
    public string Name { get; set; }

    public string Username { get; set; }

    public UserSearchModel() : base("GeneralUserSearch")
    {
    }

    //warning...this mutates the input parameter
    public override void BuildSqlCommand(SqlCommand command)
    {
        if (!string.IsNullOrWhiteSpace(Name))
        {
            command.Parameters.AddWithValue(nameof(Name), Name);
        }
        if (!string.IsNullOrWhiteSpace(Username))
        {
            command.Parameters.AddWithValue(nameof(Username), Username);
        }
    }
}

采用这种方法,维护较少,因为如果您需要插入其他类型的模型,则无需修改许多不同的switch语句,并且可以更轻松地识别给定搜索类型的逻辑所在。就是说,我不喜欢输入参数的突变,并且认为可以进一步重构。

也就是说,您可以看到这是主观的,因此可能不适用于SO。这里的基本设计似乎有问题。