Я работаю над перефакторингом определенных аспектов существующего веб-сервиса. Реализация сервисных API-интерфейсов осуществляется с помощью своего рода «конвейера обработки», в котором есть задачи, которые выполняются последовательно. Неудивительно, что более поздним задачам может потребоваться информация, вычисленная предыдущими задачами, и в настоящее время способ сделать это - добавить поля в класс «состояние конвейера».
Я думал (и надеюсь?), Что есть лучший способ обмена информацией между этапами конвейера, чем наличие объекта данных с миллионным полем, некоторые из которых имеют смысл для одних этапов обработки, а не для других. Было бы очень трудно сделать этот класс поточно-ориентированным (я не знаю, было бы это вообще возможно), нет способа рассуждать о его инвариантах (и, вероятно, у него его нет).
Я пролистывал книгу по шаблонам дизайна Gang of Four, чтобы найти какое-то вдохновение, но я не чувствовал, что там есть какое-то решение (Memento был несколько в том же духе, но не совсем). Я также посмотрел онлайн, но в тот момент, когда вы выполняете поиск по «конвейеру» или «рабочему процессу», вы получаете информацию о каналах Unix или запатентованные механизмы и платформы рабочих процессов.
У меня вопрос - как бы вы подошли к вопросу записи состояния выполнения конвейера программной обработки, чтобы в более поздних задачах использовалась информация, вычисленная ранее? Я предполагаю, что основное отличие от конвейеров Unix состоит в том, что вы не просто заботитесь о результатах непосредственно предшествующей задачи.
В соответствии с просьбой, некоторые псевдокод для иллюстрации моего варианта использования:
Объект "конвейерного контекста" имеет несколько полей, которые могут заполнять / читать различные шаги конвейера:
public class PipelineCtx {
... // fields
public Foo getFoo() { return this.foo; }
public void setFoo(Foo aFoo) { this.foo = aFoo; }
public Bar getBar() { return this.bar; }
public void setBar(Bar aBar) { this.bar = aBar; }
... // more methods
}
Каждый из этапов конвейера также является объектом:
public abstract class PipelineStep {
public abstract PipelineCtx doWork(PipelineCtx ctx);
}
public class BarStep extends PipelineStep {
@Override
public PipelineCtx doWork(PipelieCtx ctx) {
// do work based on the stuff in ctx
Bar theBar = ...; // compute it
ctx.setBar(theBar);
return ctx;
}
}
Точно так же для гипотетического FooStep
, которому может потребоваться Bar, вычисленный BarStep перед этим, наряду с другими данными. И тогда у нас есть настоящий вызов API:
public class BlahOperation extends ProprietaryWebServiceApiBase {
public BlahResponse handle(BlahRequest request) {
PipelineCtx ctx = PipelineCtx.from(request);
// some steps happen here
// ...
BarStep barStep = new BarStep();
barStep.doWork(crx);
// some more steps maybe
// ...
FooStep fooStep = new FooStep();
fooStep.doWork(ctx);
// final steps ...
return BlahResponse.from(ctx);
}
}