vendredi 21 janvier 2022

Whats a good way to implement an extended data structure in a pipeline pattern?

I want to implement a pipeline pattern. That means i want to implement multiple stages working on a common job in a sequential manner. The stages themselves get a reference to the jobs data, and can modify this data:

class PipelineJob{
  final JobData data;
  final stages = [FirstStage(), SecodnStage()];
  run(){
    for(stage in stages){
      stage.run(data);
    }
  }
}

class PipelineStage{
  run(JobData data){
    // do something with data
  }  
}

class Pipeline{
  InputData input;
  OutputData result;
  start(InputData input){
    final job = PipelineJob.fromInput(input);
    job.run(); 
  }
}

So far, so good. But now i might have some data in the job, that is conditional on some type. And stages want to process this data differently depending on this type. Specifically, lets say, job data is structured in the following way:

class JobData {
  List<Info> infos;
}

class Info {
  DateTime time;
}

class ExtendedInfo extends Info {
  int count;
}

Depending on the type of info a stage mgith want to modify an Info object or might expect an ExtendedInfo, but might not want to change the type of the object. So i can check if an object is an ExtendedInfo with is ExtendedInfo and i can run the stage conditionally.

But for a general stage, that would process Info in general: How can i guarantee, that my stage will create ExtendedInfo objects, when my stage is trying to replace an existing ExtendedInfo object in the jobs job.data.infos?

  1. Inheritance: One way would be to let the stage always check with is ExtendedInfo and create the appropiate type and then store it.
class Info{
  DateTime time;
  int? counter;
}

class ExtendedInfo extends Info {
  int count;
}

class JobData{
  List<Info> infos;
}

class FirstStage implements PipelineStage{
  run(JobData data){
    for(int i = 0; i<data.infos.length; i++){
      if(data.infos[i] is ExtendedInfo){
        data.infos[i] = processExtendedInfo(i);
      }
    }
  }

  ExtendedInfo processExtendedInfo(ExtendedInfo i){
    // process it, create and return result
  }  
}
  1. Optional parameters: Another way, would be to merge ExtendedInfo and Info into one Info class with more optional fields:
class Info{
  DateTime time;
  int? counter;
}

class JobData{
  List<Info> infos;
}

that would give better type safety, but in general this practice would make null checks necessary every time one tries to use an extended info.

  1. Extension class: I could move the "extension" of Info into an optional extension class
class Info{
  DateTime time;
}

class InfoExtension{
  int counter;
}

class JobData{
  List<Info> infos;
  List<InfoExtension?> infoExtensions;
}

this preserves type safety, requires handling the lists together doe, which is kinda awkward. One could easily forget to update infoExtensions alongside with infos.

Is there a best practice?

Aucun commentaire:

Enregistrer un commentaire