Я працюю над переосмисленням певних аспектів існуючої веб-служби. Те, як реалізуються API сервісних послуг, має своєрідний "конвеєр обробки", де є завдання, які виконуються послідовно. Не дивно, що для пізніших завдань може знадобитися інформація, обчислена попередніми завданнями, і в даний час так це робиться шляхом додавання полів до класу "стан конвеєра".
Я думав (і сподіваюся?), Що існує кращий спосіб обміну інформацією між кроками конвеєра, ніж наявність об'єкта даних із мільйонами полів, деякі з яких мають сенс для деяких етапів обробки, а не для інших. Це було б великим болем зробити цей клас потоком безпечним (я не знаю, чи це було б можливо), немає ніяких причин міркувати про його інваріантів (і це, швидше за все, у нього немає).
Я прогулював книгу зразків дизайну «Банда чотирьох», щоб знайти деяке натхнення, але мені не здалося, що там є рішення (Мементо дещо в тому ж дусі, але не зовсім). Я також дивився в Інтернеті, але вдруге, коли ви шукаєте "трубопровід" або "робочий процес", ви потрапляєте або з інформацією про труби 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
, який, можливо, потребуватиме Бар, обчислений 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);
}
}