Осуществите рефакторинг метод с несколькими точками возврата

В вашем config.ru:

root = ::File.dirname(__FILE__)
logfile = ::File.join(root,'logs','requests.log')

require 'logger'
class ::Logger; alias_method :write, :<<; end
logger  = ::Logger.new(logfile,'weekly')

use Rack::CommonLogger, logger

require ::File.join(root,'myapp')
run MySinatraApp.new # Subclassed from Sinatra::Application
11
задан Odrade 29 October 2009 в 18:19
поделиться

10 ответов

Don't use exceptions to control the flow of your program. Personally, I would leave the code as it is. To add the new functionality at the end you could do:

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            if (!performStep1())
                returnStatus = Status.Error;
            else if (!performStep2())
                returnStatus = Status.Warning;

            //.. More steps, some of which could change returnStatus..//

            else if (!performStep3())
                returnStatus = Status.Error;
        }
        catch (Exception ex)
        {
            log(ex);
            returnStatus = Status.Error;
        }
        finally
        {
            //some necessary cleanup
        }

        // Do your FinalProcessing(returnStatus);

        return returnStatus;
    }
9
ответ дан 3 December 2019 в 08:04
поделиться

Вы можете преобразовать шаги в интерфейс, чтобы каждый шаг, вместо того, чтобы быть методом, предоставлял метод Run () и свойство Status - и вы могли запускать их в цикле. , пока не произойдет исключение. Таким образом, вы можете сохранить полную информацию о том, какой шаг был выполнен и какой статус имел каждый шаг.

3
ответ дан 3 December 2019 в 08:04
поделиться

Вы можете выполнить обработку в разделе finally . finally забавен тем, что даже если вы вернулись в своем блоке try , блок finally все равно будет выполнен до того, как функция действительно вернется. Тем не менее, он запомнит, какое значение было возвращено, так что вы также можете отказаться от return в самом конце вашей функции.

public void processStuff()
{
    Status returnStatus = Status.Success;
    try
    {
        if (!performStep1())
            return returnStatus = Status.Error;

        if (!performStep2())
            return returnStatus = Status.Warning;

        //.. the rest of the steps ..//
    }
    catch (Exception ex)
    {
        log(ex);
        return returnStatus = Status.Exception;
    }
    finally
    {
        //some necessary cleanup

        FinalProcessing(returnStatus);
    }
}
2
ответ дан 3 December 2019 в 08:04
поделиться

Получить класс кортежа. Затем выполните:

var steps = new List<Tuple<Action, Status>>() {
  Tuple.Create(performStep1, Status.Error),
  Tuple.Create(performStep2, Status.Warning)
};
var status = Status.Success;
foreach (var item in steps) {
  try { item.Item1(); }
  catch (Exception ex) {
    log(ex);
    status = item.Item2;
    break;
  } 
}
// "Finally" code here

Да, вы можете использовать анонимные типы для кортежей:

var steps = new [] {                        
    { step = (Action)performStep1, status = Status.Error }, 
    { step = (Action)performStep2, status = Status.Error }, 
};                                          
var status = Status.Success          
foreach (var item in steps) {               
  try { item.step(); }                     
  catch (Exception ex) {                    
    log(ex);                                
    status = item.status;                    
    break;                                  
  }                                         
}                                           
// "Finally" code here                      
1
ответ дан 3 December 2019 в 08:04
поделиться

Расширение описанного выше подхода к интерфейсу:

public enum Status { OK, Error, Warning, Fatal }

public interface IProcessStage {
    String Error { get; }
    Status Status { get; }
    bool Run();
}

public class SuccessfulStage : IProcessStage {
    public String Error { get { return null; } }
    public Status Status { get { return Status.OK; } }
    public bool Run() { return performStep1(); }
}

public class WarningStage : IProcessStage {
    public String Error { get { return "Warning"; } }
    public Status Status { get { return Status.Warning; } }
    public bool Run() { return performStep2(); }
}

public class ErrorStage : IProcessStage {
    public String Error { get { return "Error"; } }
    public Status Status { get { return Status.Error; } }
    public bool Run() { return performStep3(); }
}

class Program {
    static Status RunAll(List<IProcessStage> stages) {
        Status stat = Status.OK;
        foreach (IProcessStage stage in stages) {
            if (stage.Run() == false) {
                stat = stage.Status;
                if (stat.Equals(Status.Error)) {
                    break;
                }
            }
        }

        // perform final processing
        return stat;
    }

    static void Main(string[] args) {
        List<IProcessStage> stages = new List<IProcessStage> {
            new SuccessfulStage(),
            new WarningStage(),
            new ErrorStage()
        };

        Status stat = Status.OK;
        try {
            stat = RunAll(stages);
        } catch (Exception e) {
            // log exception
            stat = Status.Fatal;
        } finally {
            // cleanup
        }
    }
}
1
ответ дан 3 December 2019 в 08:04
поделиться

Можете ли вы заставить performStep1 и performStep2 генерировать разные исключения?

0
ответ дан 3 December 2019 в 08:04
поделиться

You could reverse your status setting. Set the error status before calling the exception throwing method. At the end, set it success if no exceptions.

Status status = Status.Error;
try {
  var res1 = performStep1(); 
  status = Status.Warning;
  performStep2(res1);
  status = Status.Whatever
  performStep3();
  status = Status.Success; // no exceptions thrown
} catch (Exception ex) {
  log(ex);
} finally {
 // ...
}
0
ответ дан 3 December 2019 в 08:04
поделиться

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

abstract class TaskBase
{
    public Status ExecuteTask()
    {
        if(ExecuteTaskInternal())
            return HandleSuccess();
        else
            return HandleFailure();
    }

    // overide this to implement the task execution logic
    private virtual bool ExecuteTaskInternal()
    {
        return true;
    }

    // overide this to implement logic for successful completion
    // and return the appropriate success code
    private virtual Status HandleSuccess()
    {
        return Status.Success;
    }

    // overide this to implement the task execution logic
    // and return the appropriate failure code
    private virtual Status HandleFailure()
    {
        return Status.Error;
    }
}

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

public void processStuff()
{
    Status returnStatus 
    SortedList<int, TaskBase> list = new SortedList<int, TaskBase>();
    // code or method call to load task list, sorted in order of execution.
    try
    {
        foreach(KeyValuePair<int, TaskBase> task in list)
        {
            Status returnStatus task.Value.ExecuteTask();
            if(returnStatus != Status.Success)
            {
                break;
            }
        }
    }
    catch (Exception ex)
    {
        log(ex);
        returnStatus = Status.Error;
    }
    finally
    {
        //some necessary cleanup
    }

    return returnStatus;
}

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

0
ответ дан 3 December 2019 в 08:04
поделиться

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

0
ответ дан 3 December 2019 в 08:04
поделиться

а что насчет вложенных, если?

мог работать и не мог работать

он удалял бы каждый возврат, чтобы оставить только один

if(performStep1())
{
  if(performStep2())
  {
      //..........
  }
  else 
    returnStatus = Status.Warning;
}
else
  returnStatus = Status.Error;
-1
ответ дан 3 December 2019 в 08:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: