【问题标题】:Avoiding If Else conditions [closed]避免If Else条件[关闭]
【发布时间】:2014-03-27 04:51:16
【问题描述】:

我想重构以下代码以避免 if...else 这样我就不必在每次进入新的调查类型时都更改方法(打开/关闭原则)。以下是我正在考虑重构的一段代码:

if (surveyType == SurveySubType.Anonymous)
{
    DoSomething(param1, param2, param3);

}
else if (surveyType == SurveySubType.Invitational)
{
    DoSomething(param1);
}
else if (surveyType == SurveySubType.ReturnLater)
{    
    DoSomething(param1);
}

为了解决这个问题,我添加了以下类:

    public abstract class BaseSurvey
{
            public string BuildSurveyTitle()
            {
             ...doing something here
            }

    public abstract void DoSomething(int? param1,int?  param2,int?  param3);
}
public class InvitationalSurvey: BaseSurvey
{
    public override void DoSomething(int? param1,int?  param2,int?  param3)
    {
    //I don't need param2 and param3 here

    }
}


public class ReturnLaterSurvey: BaseSurvey
{
    public override void DoSomething(int? param1,int?  param2,int?  param3)
    {
    //I don't need param2 and param3 here

    }
}


public class AnonymousSurvey: BaseSurvey
{
    public override void DoSomething(int? param1,int?  param2,int?  param3)
    {

    //I need param2 and param3 here
    //do something
    }

}

这就是我的代码的最终结果:

var survey = SurveyFactory.Create();
survey.DoSomething(param1,param2,param3);

我的问题是如何避免将 param2 和 param3 传递给 InvitationalSurvey 和 ReturnLaterSurvey 类?

【问题讨论】:

  • 我能想到的最好的方法就是为参数添加一个类似的类结构......换句话说,制作一个类似于 BaseSurveyArguments 基类的东西并根据需要扩展它。不过不是很优雅。
  • 看到这个我很高兴 Delphi 有case 开关。 C#中没有这样的替代方案吗?
  • C# 确实有 case 语句,switch 语句也在 this question 中提到。尽管我自己还没有体验过,但可能有一些巧妙的方法可以使用委托或实体引用使其更加时尚。
  • 我也认为switch 加上默认参数是一种解决方案。在 Python 中,您可以为所有参数分配默认值并提供更新参数的列表。另一个问题是,是否应该只有一个 DoSomething 方法。为什么不使用ProcessLateSurveyProcessAnonSurvey 等方法?
  • 这个问题似乎是题外话,因为它是关于CodeReview

标签: c# oop solid-principles open-closed-principle


【解决方案1】:

如果param2param3AnonymousSurvey 的具体要求,那么它们不应该是接口的一部分,而是具体类的一部分:

public abstract class BaseSurvey
{
    public abstract void DoSomething(param1);
}

public class InvitationalSurvey: BaseSurvey
{
    public void DoSomething(param1)
    {
    }
}


public class ReturnLaterSurvey: BaseSurvey
{
    public void DoSomething(param1)
    {
    }
}


public class AnonymousSurvey: BaseSurvey
{
    private readonly object param2;
    private readonly object param3

    public AnonymousSurvey(param2, param3)
    {
        this.param2 = param2;
        this.param3 = param3;
    }

    public void DoSomething(param1)
    {
        // use this.param2 and this.param3 here
    }
}

【讨论】:

  • 不应该将DoSomething提取为接口,然后您可以使用依赖注入吗?
  • 你可以这样做。那将是我首选的设计方法,但正如给定的那样,BaseSurvey 是一种纯抽象类型,因此从客户的角度来看,它在组合上等同于接口。
  • @MarkSeemann 你能给我举个例子,说明客户如何调用 DoSomething 吗?需要对 AnonymousSurvey 进行类型检查以传递 param2 和 param3 对吗?
  • 何时创建InvitationalSurveyReturnLaterSurveyAnonymousSurvey 对象?谁拥有 param2param3 值?
  • @MarkSeemann 如上所示,工厂方法创建对象。 param2 和 param3 仅与 AnonymousSurvey 类相关。
【解决方案2】:

为什么不添加重载

doSometing(Param1){
 doSomething(Param1, null, null)
}

【讨论】:

    【解决方案3】:

    了解参数类型是什么会有所帮助。如果它们都相同,那么您至少可以在 C# 中使用 params 关键字并根据需要发送尽可能多的参数。如果不是,那么您可能需要传递一个参数字典,然后将其留给实现类将对象转换为正确的类型。

    public abstract class BaseSurvey
    {
        public abstract void DoSomething(params string[] parameters);
    }
    
    public abstract class BaseSurvey
    {
        public abstract void DoSomething(Dictionary<string,object> parameters);
    }
    

    也许更好的方法是将参数合并到工厂方法调用中,并让工厂在创建时将值设置为正确的类型,然后您可以在没有任何参数的情况下调用该方法。

    var survey = surveyFactory.CreateAnonymousSurvey(param1, param2, param3);
    survey.DoSomething();
    

    var survey = surveyFactory.CreateReturnLaterSurvey(param1);
    survey.DoSomething();
    

    【讨论】:

    • 那么再次需要引入if...else类型检查来判断调用哪个工厂方法。
    • @fahmi - 没办法。但它确实在创建代码中将其本地化。
    【解决方案4】:

    好像是Overloading的情况,不过已经有人建议了。因此,作为替代方案,您为什么不这样做,这意味着为参数分配默认值 使其成为可选。看看下面的例子。

    我演示了一个整数类型,您可以更改类型并设置最适合您的默认值。

    Live Demo

    using System;
    
    public class Test
    {
        public static void Main()
        {
            // your code goes here
            InvitationalSurvey iservey = new InvitationalSurvey();
            iservey.DoSomething(1, 1, 1);
            iservey.DoSomething(1);
        }
    }
    
    public abstract class BaseSurvey
    {
         
    }
    public class InvitationalSurvey: BaseSurvey
    {
        public void DoSomething(int param1, int param2 = 0, int param3 = 0)
        {
        //I don't need param2 and param3 here
        Console.WriteLine(string.Format("{0},{1},{2}",param1, param2, param3));
        }
    }
    

    【讨论】:

      【解决方案5】:

      您发布的代码不是。不管怎样,听起来你想要一个Option type

      【讨论】:

        【解决方案6】:

        您可以有另一个抽象类扩展BaseSurveyInvitationalSurveyReturnLaterSurvey 都扩展。这个抽象类可以通过调用自己的抽象方法DoSomething(param1)来实现DoSomething(param1,param2,param3)InvitationalSurveyReturnLaterSurvey可以扩展DoSomething(param1,param2,param3)的intead

        public abstract class BaseSurvey
        {
            public abstract void DoSomething(param1, param2, param3);
        }
        
        public abstract class SpecialSurvey : BaseSurvey
        {
            public abstract void DoSomething(param1);
        
            public void DoSomething(param1, param2, param3)
            {
                DoSomething(param1);
            }
        }
        
        public class InvitationalSurvey: SpecialSurvey
        {
            public void DoSomething(param1)
            {
                 ReallyDoSomething();
            }
        }
        

        【讨论】:

        • 你的头像背后有什么样的宣传?
        • 我认为@nurettin 暗示你可能正在通过你的头像推送政治信息
        • @DavidWallace 如果您想向网站上的某个人提出问题,您可以使用 @ 符号,以便他们收到警报。假设这是针对我的,我显然是在建议您使用宣传图片并询问其含义。
        • @nurettin 我想你很清楚这意味着什么,你只是想开始战斗。我不认为 Stack Overflow 是合适的论坛,对吗?
        • @DavidWallace 我同意,stackoverflow 不适合你的宣传。我们为此提供了 reddit。
        猜你喜欢
        • 2012-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-26
        相关资源
        最近更新 更多