В вашем 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
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;
}
Вы можете преобразовать шаги в интерфейс, чтобы каждый шаг, вместо того, чтобы быть методом, предоставлял метод Run () и свойство Status - и вы могли запускать их в цикле. , пока не произойдет исключение. Таким образом, вы можете сохранить полную информацию о том, какой шаг был выполнен и какой статус имел каждый шаг.
Вы можете выполнить обработку в разделе 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);
}
}
Получить класс кортежа. Затем выполните:
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
Расширение описанного выше подхода к интерфейсу:
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
}
}
}
Можете ли вы заставить performStep1
и performStep2
генерировать разные исключения?
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 {
// ...
}
Не зная большей части ваших логических требований, я бы начал с создания абстрактного класса, который действовал бы как базовый объект для выполнения определенного шага и возврата статуса выполнения. . Он должен иметь переопределяемые методы для реализации выполнения задачи, действий при успехе и действий при неудаче. также обрабатывать логику. Помещение логики в этот класс для обработки успеха или неудачи задачи:
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;
}
Я оставил обработку ошибок, так как не уверен, что ваши ошибки перехвата при выполнении задачи или просто поиск исключения, которое вы генерировали на конкретном шаге, завершились ошибкой.
Я бы посоветовал провести некоторый рефакторинг шагов в отдельные классы, в конце концов, ваш класс в любом случае должен иметь только одну ответственность. Я думаю, это звучит так, как будто это ответственность должна контролировать ход шагов.
а что насчет вложенных, если?
мог работать и не мог работать
он удалял бы каждый возврат, чтобы оставить только один
if(performStep1())
{
if(performStep2())
{
//..........
}
else
returnStatus = Status.Warning;
}
else
returnStatus = Status.Error;