I work on a simulator that simulates patients, so that specific processes can be validated on "virtual" patients, before testing on real patients. The simulator is essentially a mathematical model which consists of several ordinary differential equations (ODE), which can be calculated using a numerical method. These mathematical models are calculated step by step, which means that you need the previously calculated values to calculate new values. These steps can be small or larger, the smaller the step, the better the result but the longer it takes to calculate.
At first, development went well, but gradually the simulator became a mess, we introduced a second mathematical model and a second ODE solver. Who had to work with the same "virtual" patients. But the second mathematical model required the patient to have additional fields and the vector table of calculated values became a little larger (from 7 entries per step to 10 entries). It worked, but we are now thinking of adding another mathematical model, which requires even more properties in a patient and the calculated values could not be stored in the same vector table, since the first 3 entries are for different values of the other models. We are now considering leaving the first 3 entries 0 / null and adding 3 entries at the end of the vector array for the most recent model.
Here is our simplified class diagram to show the problem:
The class diagram shows only 2 concrete models and 2 concrete solvers, but we must add at least 1 additional model. As you can see, the patient has 7 variables. The first model requires the first 5, the seconds all and the last model requires only the last 4 (and 2 additional not yet added). Below is a program with "life events" of the patient which have an influence on the calculated values of the patient. Below is the vector table which has 10 entries. the first 7 are only used by the mathematical model X, the 10 are used by the Y model and the most recent model would require the addition of 3 more at the end, and the first 3 would not be used by this model .
What is a design model that would work here?
I have tried to use inheritance. The second model uses the first model as the base class, it works. But when a third model is added that doesn't need certain values / functions, is this still a good way to go? The patient's problem still existed, because it also had to be inherited or additional attributes simply had to be added.
Also tried, but had the same problems as inheritance.
Does anyone see a solution to this design problem?
Then there is a second problem
This problem concerns the creation of the simulator mentioned above. As you can see, creating patients is done via a CreatePatient () function in the simulator, which requires a list of patient values (variables 1 to 7 in patient) which increases with the addition of models. And a calendar with "life events" that influence the values calculated by the patient.
The creation of the simulator, the planning, the model and the solver is now done in a CLI and a GUI project which reference the Simlator project. There are 2 problems I see, the first concerns the growing list of attributes / properties and calculated values vector array when adding a model. This makes it more difficult to create a patient. To resolve this issue, the first issue discussed above must first be resolved in my opinion. Second, the creation is a bit weird now. The CLI requests a file path from the user which points to an .ini file with the configuration of the simulator. This .ini file includes a configuration like the duration of the simulator, the size of the step size (remember the models mentioned above and how they are solved), if the patients have to be exported, the seeds. It also gives for each possible patient value (attribute / patient property) the range of that value, and you can also add a list of paths to .ini files that contain hard-coded patient values.
So you could say I want 50 patients and then give the point to 25 .ini files in the .ini configuration file for the simulator and the rest of the patients will be generated using the range specified by value. The same is done for the calendar which must be generated for each patient.
The problem I see with the above is that a patient is created via the simulator object after it is created, but the patient's calendar is not. The reason that scheduling is not is that it is now generated in the CLI / GUI interface based on certain parameters of the .ini file. Should these creations be done differently? does the creation of a patient even have to be done via a simulator object? Should there be a factory method for each object (calendar, patient)?
Summary | Explanation of high leverage
I am currently trying to refactor a project so that it is more extensible. The basis of the project consists of a "patient" model with a set of attributes and an array of vectors. The attributes will be filled during creation, but are too tightly coupled to the classes that require these attributes. For example: class 1 only needs the 1-5 attribute and class 2 only needs 4-10.
I'm looking for a solution that makes it easier to add class3, class4 and class5 but doesn't inflate the patient class with even more attributes.
One solution I have tried is to have different patient classes per class1, class2, etc. This works to some extent, as the patient class must be used in other places that would require the if / switch statement to find out what attributes they can get from the patient for example. 1 to 5 or 4 to 10.
Possible solution 1
To work with the ever-changing amount of patient attributes, I will now build a parameter structure. When creating the simulator object, I will check which mathematical model is used and based on that, I will create a structure containing the patient values required by this specific model. The structure will inherit from IPatientParameter which can then be used to store it in the patient object.