Лучший подход к рефакторингу условной части кода

Я пытаюсь реорганизовать данный фрагмент кода, как указано ниже:

ИСХОДНЫЙ КОД:

Method(URL link)
{
    if(ConditionA(link))
    {
      MehodA(link);
    }else if(ConditionB(link))
    {
      MehodB(link);
    }else if(ConditionC(link))
    {
      MehodC(link);
    }else if(ConditionD(link))
    {
      MehodD(link);
    }
}

приведенный выше код может увеличиваться по мере появления новых условий в будущем. После рефакторинга я могу разделить код на несколько классов, каждый из которых отвечает за одну задачу, тем самым уменьшив первоначальную сложность следующим образом:

ПОСЛЕ РЕФАКТОРИНГА:

METHOD(URL link)
{
  ConditionalHandlerClass obj = new ConditionalHandlerClass(link);
  ConditionalHandlerClass.HandleLinkProcessing();
}

Class ConditionalHandlerClass
{
  URL link;
  IConditionalProcess process;

  public ConditionalHandlerClass(URL _link)
  {
    link = _link;
  }

  public void HandleLinkProcessing()
  {
     if(ConditionA(link))
        {
          process = new ProcessA(link);
        }else if(ConditionB(link))
        {
          process = new ProcessB(link);
        }else if(ConditionC(link))
        {
         process = new ProcessC(link);
        }else if(ConditionD(link))
        {
         process = new ProcessD(link);
        }
    process.Handle();
  }
}

interface IConditionalProcess
{
  void handle();
}


class ProcessA() : IConditionalProcess
{
   void handle()
  {
   // Business Logic here
  }
}

class ProcessB() : IConditionalProcess
{
   void handle()
  {
   // Business Logic here
  }
}

class ProcessC() : IConditionalProcess
{
   void handle()
  {
   // Business Logic here
  }
}

class ProcessD() : IConditionalProcess
{
   void handle()
  {
   // Business Logic here
  }
}

Но я вижу, что метод HandleLinkProcessing() в классе ConditionalHandlerClass будет по-прежнему увеличиваться по мере добавления нового условия.

Можете ли вы предложить, как я могу улучшить эту реализацию, чтобы класс, такой как ConditionalHandlerClass, не изменялся при добавлении нового потока вызовов ConditionE() и MethodE(). Следовательно, снижается сложность в одном классе, даже если добавляются новые условия.

Я пишу этот код в объективе c.


person Balraj Singh    schedule 05.07.2016    source источник
comment
stackoverflow.com/questions/38184481/   -  person Geka P    schedule 05.07.2016


Ответы (3)


Я думаю, ты на правильном пути. Конечно, обработка может быть выполнена бесконечным числом способов (и с внедрением конструктора через DI вам не нужно иметь дело с большинством, если это так), но вот один подход, который составляет повторно используемые экземпляры класса Handler с условной логикой и логикой выполнения. .

public interface IConditional
{
    bool Evaluate(Url link);
}

public class ConditionA : IConditional
{
    public bool Evaluate(Url link)
    {
        return true; // your conditional logic here
    }
}

public class ConditionB : IConditional
{
    public bool Evaluate(Url link)
    {
        return true; // your conditional logic here
    }
}

public class ConditionC : IConditional
{
    public bool Evaluate(Url link)
    {
        return true; // your conditional logic here
    }
}

public class ConditionD : IConditional
{
    public bool Evaluate(Url link)
    {
        return true; // your conditional logic here
    }
}

public interface IProcessor
{
    void Process(Url link);
}

public class MethodA : IProcessor
{
    public void Process(Url link)
    {
        // whatever A does?
    }
}

public class MethodB : IProcessor
{
    public void Process(Url link)
    {
        // whatever B does?
    }
}

public class MethodC : IProcessor
{
    public void Process(Url link)
    {
        // whatever C does?
    }
}

public class MethodD : IProcessor
{
    public void Process(Url link)
    {
        // whatever D does?
    }
}

public class Handler
{
    private IConditional conditional;
    private IProcessor processor;

    public Handler(
        IConditional conditionalReference,
        IProcessor processorReference)
    {
        this.conditional = conditionalReference;
        this.processor = processorReference;
    }

    public bool Handle(Url link)
    {
        bool handled = false;
        if (this.conditional.Evaluate(link)
        {
            this.processor.Process(link);
            handled = true;
        }

        return handled;
    }
}

public class Program
{
    public static void Main()
    {
        Handler[] orderedHandlers = new []
        {
            new Handler(new ConditionA(), new MethodA()),
            new Handler(new ConditionB(), new MethodB()),
            new Handler(new ConditionC(), new MethodC()),
            new Handler(new ConditionD(), new MethodD()),
        };

        Url link = new Url("xxx");

        foreach(Handler handler in orderedHandlers)
        {
            if(handler.Handle(link))
            {
                break;
            }
        }
    }
}
person Trevor Ash    schedule 05.07.2016

Одним из возможных способов было бы сохранить коллекцию пар Predicate<URL> и Consumer<URL>. Такой структурой может быть, например, LinkedHashMap, которая сохраняет порядок вставки.

LinkedHashMap<Predicate<URL>, Consumer<URL>> ops = new LinkedHashMap<> {{ 
    put(::ConditionA, ::MethodA); 
    put(::ConditionB, ::MethodB); .... 
}}

void processURL(URL link) {
     for(Map.Entry<...,...> entry : ops.entrySet()) {
          if (entry.getKey().test(link)) {
               entry.getValue().accept(link);
               break;
          }
     }
}

Изменить: тег Java исчез, пока я писал свой ответ. Однако я не владею языком Objective C, в основном агностик.

person majk    schedule 05.07.2016
comment
Это по сути то же самое, что я написал в своем ответе. Любое решение избавляется от большого, если это просто список обработчиков, соответственно, ваша карта растет и ее нужно как-то инициализировать. - person DoNuT; 05.07.2016

Может что-то вроде введения стратегии для обработчиков и просто их итерации. Список обработчиков может быть инициализирован во время выполнения или в каком-либо статическом контексте, но, по крайней мере, это позволяет избежать создания конструкции мега-если.

EDIT извините за мой Java-псевдокод...

interface LinkHandler() {

  boolean accepts(URL link);

  void handle(URL link);
}

public static void main( ...) {
  List<LinkHandler> handlers = ...

  URL urlToCheck = new URL( ... );

  for( LinkHandler handler : handlers ) {
     if( handler.accepts(urlToCheck) ) {
       handler.handle(urlToCheck);
       break;
     }
   }
}
person DoNuT    schedule 05.07.2016