当前,我试图找到重构类似于以下内容的类的最佳方法:
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语句或将其减少到仅一个?
答案 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。这里的基本设计似乎有问题。