替换嵌套if语句的设计模式(箭头反模式)

时间:2018-03-30 13:56:18

标签: oop if-statement design-patterns nested chain-of-responsibility

我注意到我的代码看起来非常难看,很难维护。基本上,我需要做一些人检查。伪代码是这样的(BTW我可以' t" cut"查询中的任何内容,并且它不是我的问题的重点):

List<Person> persons = getPersonsBySomeQuery();

if (checkAnyPersonExists(persons)) {
  if (atLeastOneWithGivenNameExist(persons)) {
    if (noOneWithOtherNameExists(persons)) {
      filteredPersons = filterPersonsWithGivenName(persons);
      if (singleName(filteredPersons)) {
        // single person found
        if (someParameterFromQueryIsTrue) {
          if (someComplicatedLogicIsOK) {
            // found exactly one person, all OK!!!
          } else {
            // found exatcly one person with given name, but not OK
          }
        } else {
           // found exactly one person but parameter from query is not OK
        }
      } else {
        // found only persons with given name, but more then one
      }
    } else {
      // found person(s) with given name, but also some with different name
    }
  } else {
    // found person(s), all have different name
  }
} else {
  // noone found
}

所以我没有那么多设计模式的经验,但我读到了它们,我开始想我可以实现责任链模式。比如,每个if-else都可以是链中的一个元素,第6行的这个过滤元素也可以是&#34; Chainable&#34;。

最后它看起来像是:

AbstractChainElement getCheckChain() {
   return new CheckAnyExist(
          new CheckOneWIthGivenNameExist(
          new CheckNoOneWithOtherNameExist(
          new FilterOnlyGivenName(
          new CheckIsSingleName(
          new CheckOtherParameters(
          new CheckWithSomeComplicatedLogic()))))));          
}

getCheckChain().doChain(persons);

你觉得这样做的好方法还是有更优雅的东西?

有了这个,我可以构建链,用于检查不同的检查类型,使用工厂甚至抽象工厂模式。

类似的东西:

FactoryCheckThesePersonsByTheseParameters implements AbstractFactory {

     List<Person> getPersons() {
       // get persons with THIS query
     }

     AbstractChainElement getCheckChain() {
       // check persons THIS way
     }
}

FactoryCheckThatPersonsByThatParameters implements AbstractFactory {

     List<Person> getPersonsBySomeParameters() {
       // get persons with THAT query
     }

     AbstractChainElement getCheckChain() {
       // check persons THAT way
     }
}

3 个答案:

答案 0 :(得分:1)

这不是关于设计模式,而是编码风格和优化。

您可以将条件合并为一个。即 这样:

if (checkAnyPersonExists(persons)) {
  if (atLeastOneWithGivenNameExist(persons)) {
    if (noOneWithOtherNameExists(persons) {
    }
  }
}

首先,这似乎是对同一主题/集合进行多次查询。如果您使用它来对数据库进行查询,那么效率会很低。例如,您可以通过在单个查询中组合条件来做得更好。

如果这不是对数据库进行查询,那么您可以直接获取具有给定名称的人数,并根据您可以确定要执行的操作的数量而不是重复查询而略有不同。

另一方面,使用模式并不总是绝对是最佳实践,因为它可能会增加开销。一个简单的If语句,switch-case或带有return的if-not-statement比调用另一个函数/方法更好,因为它更快。

我认为你需要更加具体。

编辑: 例如,如果您有类似于以下内容的内容:

if (i == 1) {
    // action 1
} else {
    if (i == 2) {
        // action 2
    } else {
        if (i == 3) {
            // action 3
        } else {
            // action 4
        }
    }
}

您可以使用switch语句

switch (i) {
    case 1:
        // action 1
        break;
    case 2:
        // action 2
        break;
    case 3:
        // action 3
        break;
    default:
        // action 4
        break;
}

另一个例子可能是,如果你有:

if (i == 1) {
    // a couple of sentences action
}
else if (x == 2) {
    // a couple of sentences action
}
else if (y == 3) {
    // a couple of sentences action
}

// no more cope beyond the if statements

这可以重新考虑为:

if (i == 1) {
    // a couple of sentences action
    return;
}

if (x == 2) {
    // a couple of sentences action
    return;
}

等等

另一种技巧,例如:

if(name.equals("abc")){
    do something
} else if(name.equals("xyz")){
    do something different
} else if(name.equals("mno")){
    do something different
} ......
.....
else{ 
   error
}

最好使用Map来映射每个字符串(如果它是您验证的有限字符串的有限集合)到特定处理程序。您还可以考虑将一些代码移动到单独的方法

我从SO的几个页面中获取这些示例,因为您看到没有绝对的规则,这取决于代码本身。所以你可以做的就是有一些分析工具可以和你一起使用代码。我认为JetBrain的IntelliJ IDEA是一款出色的IDE,内置酷炫功能。 Eclipse也有一些像这样的功能,但没有那么多。无论如何,这都伴随着练习。

答案 1 :(得分:1)

我会坚持使用if / else语句(虽然我会像asm建议的那样压扁它们)。如果你真的想在这里使用设计模式,那么责任链就可以完成这项任务。另一种选择是使用状态模式。我已经为C#console app提供了示例实现。

public class Person
{
    public String Name { get; set; }
    public bool IsActive { get; set; }

    public Person(String name, bool isActive)
    {
        this.Name = name;
        this.IsActive = isActive;
    }
}

public class PersonChecker
{
    public PersonCheckerState CurrentState { get; set; }
    public String Name { get; private set; }
    public bool ShouldBeActive { get; private set; }

    public void Check(IEnumerable<Person> persons, string name, bool shouldBeActive)
    {
        this.Name = name;
        this.ShouldBeActive = shouldBeActive;
        CurrentState = new InitialState(this);
        CurrentState.Check(persons);
    }
}

public abstract class PersonCheckerState
{
    protected PersonChecker _personChecker;
    public PersonCheckerState(PersonChecker personChecker)
    {
        this._personChecker = personChecker;
    }
    public abstract void Check(IEnumerable<Person> persons);
}

public class InitialState : PersonCheckerState
{
    public InitialState(PersonChecker personChecker) : base(personChecker)
    {
    }

    public override void Check(IEnumerable<Person> persons)
    {
        if (persons != null && persons.Any())
        {
            _personChecker.CurrentState = new AnyPersonExistsState(_personChecker);
            _personChecker.CurrentState.Check(persons);
        }
        else
        {
            Console.WriteLine("No one found");
        }
    }
}

public class AnyPersonExistsState : PersonCheckerState
{
    public AnyPersonExistsState(PersonChecker personChecker) : base(personChecker)
    {
    }

    public override void Check(IEnumerable<Person> persons)
    {
        if (persons.Any(p => p.Name == _personChecker.Name))
        {
            _personChecker.CurrentState = new AtLeastOneWithGivenNameExistsState(_personChecker);
            _personChecker.CurrentState.Check(persons);
        }
        else
        {
            Console.WriteLine("Found person(s), all have different names");
        }
    }
}

public class AtLeastOneWithGivenNameExistsState : PersonCheckerState
{
    public AtLeastOneWithGivenNameExistsState(PersonChecker personChecker) : base(personChecker)
    {
    }

    public override void Check(IEnumerable<Person> persons)
    {
        if (persons.Any(p => p.Name != _personChecker.Name))
        {
            Console.WriteLine("Found person(s) with given name, but also some with different name");
        }
        else // All persons have the same name
        {
            _personChecker.CurrentState = new NoOneWithOtherNameExistsState(_personChecker);
            _personChecker.CurrentState.Check(persons);
        }
    }
}

public class NoOneWithOtherNameExistsState : PersonCheckerState
{
    public NoOneWithOtherNameExistsState(PersonChecker personChecker) : base(personChecker)
    {
    }

    public override void Check(IEnumerable<Person> persons)
    {
        if (persons.Where(p => p.Name == _personChecker.Name).ToList().Count == 1)
        {
            _personChecker.CurrentState = new SinglePersonFoundState(_personChecker);
            _personChecker.CurrentState.Check(persons);
        }
        else
        {
            Console.WriteLine("Found only persons with given name, but more than one");
        }
    }
}

public class SinglePersonFoundState : PersonCheckerState
{
    public SinglePersonFoundState(PersonChecker personChecker) : base(personChecker)
    {
    }

    public override void Check(IEnumerable<Person> persons)
    {
        Person person = persons.Where(p => p.Name == _personChecker.Name).Single();
        if(person.IsActive == _personChecker.ShouldBeActive)
        {
            Console.WriteLine("Found exactly one person, all OK!!!");
        }
        else
        {
            Console.WriteLine("Found exactly one person but parameter IsActive is not OK");
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Person> persons = new List<Person>();
        persons.Add(new Person("Mike", true));
        //persons.Add(new Person("John", true));
        PersonChecker personChecker = new PersonChecker();
        personChecker.Check(persons, "Mike", true);
        Console.ReadLine();
    }
}

答案 2 :(得分:0)

您可以应用许多技术来减少嵌套。

下面是几个参考链接。

https://blog.jetbrains.com/idea/2017/08/code-smells-deeply-nested-code/ https://blog.codinghorror.com/flattening-arrow-code/

对于您给出的示例,您可以组合条件,或使用Chain of Responsibility pattern传递列表和谓词,或者使用Command pattern