11

I have a simulation that runs at a specific height and a specific temperature:

interface IGeneratable
{
    string Name { get; }
    void Generate();
}    

interface ISimulation : IGeneratable
{
    int Height { get; }
    int Temperature { get; }        
}

The Generate() process for a simulation typically involves multiple steps:

void Generate()
{
    Step1();
    Step2();
    Step3();
}

Now, it is possible for the user to specify multiple heights and/or multiple temperatures.

In this case, multiple simulations (sub-simulations) are spawned off, one per each height/temperatue combination.

interface IMultiSimulation : IGeneratable
{
    ISimulation[] SubSimulations { get; }       
}

However, in this case, the sub-simulation's Generate() method deviates from the Step1, Step2, Step3 order:

  • If multiple temperatures are specified, then Step2() needs to be performed only once for all sub-simulations, and not per temperature (i.e. once per multi-simulation).
  • If multiple heights are specified, then:
    • Step1() is pre-computed first for all sub-simulations.
    • Step2, Step3,..etc are then performed.
  • It is possible to have a grand simulation with multiple heights AND multiple temperatures. This means that 2 above criteria need to be satisfied.

General notes

  • A step's implementation is encapsulated in IStep, which implements IGeneratable. So it is possible for a simulation to return a list of steps for example.
  • The number of steps can be fairly large.

I've been trying to use the decorator pattern but with no success.

I'm looking for a proper pattern with a scalable solution that would handle the generation of a single simulation as well as multiple simulations.

Thanks.

alhazen
  • 1,907
  • 3
  • 22
  • 43
  • 1
    A simulation interface which abstracts one or many simulations, sounds like a [Composite](https://sourcemaking.com/design_patterns/composite). – jaco0646 May 06 '16 at 04:23

4 Answers4

1

In your case, I would use the design pattern composite. The generate method would check it it has any components. If it doesn't it will simply call

void Generate()
{
    Step1();
    Step2();
    Step3();
}

but if it does have components it means it has multiple simulations. Then the code will be something like:

void Generate()
{
if(this.simulations.Count==0)
{
  Step1();
  Step2();
  Step3();
}
else
 {
    if(multipleHeights)
    {
      precomputeStep1();
      if(multipleHeights)
      {
        createSingletonForStep2(this);
      }
      else
      {
        Step2();
      }
      Step3();
    }
  }
}

And for the step 2 i would simply call a singleton that receives this composite as a parameter, so for this group of simulations there will be only one step2.

bns
  • 392
  • 2
  • 9
1

Interfaces:

interface IGeneratable
{
    string Name { get; }
    void Generate();
} 

interface IGeneratableOnce : IGeneratable
{
    bool HasRunned { get; set; }
}   

interface ISimulation : IGeneratable
{
    int Height { get; }
    int Temperature { get; }
    IMultiSimulation MultiSimulation{ get; }
    IGeneratable[] Steps{ get; }

}

interface IMultiSimulation : IGeneratable
{
    ISimulation[] SubSimulations { get; }       
}

Typical MultiSimulation Generate:

void Generate(){
    for (ISimulation simulation in SubSimulations){
        simulation.Generate();
    }
}

Typical ISimulation Generate:

void Generate(){
    for (IGeneratable step in Steps){
        step.Generate();
    }
}

ISimulation Generate avoiding second Step run twice:

void Generate(){
    for (int i=0;i<Steps.Length;i++){
        IGeneratable step = Steps[i];
        if (i!=1){
            if (step is IGeneratableOnce && !(step as IGeneratableOnce).HasRunned){
                step.Generate();
                step.HasRunned = true;
            }
        }else{
            step.Generate();
        }
    }
}

You can add a few other flags if you wish, maybe even some sort of method like CanRun(int Height ,int Temperature) in case your logic gets too complex. However in your situation, I believe you should use a composite pattern variation like this sample of code.

EDIT: Here another interesting pattern that you may use

Ronaldo Felipe
  • 402
  • 2
  • 13
1

Your task is not so small, so the answer is not a single design pattern, but instead multiple. I personally would not emphasize on patterns, but rather on intuitive and intention-revealing implementation (e.g. I wrote about it here: http://www.tutisani.com/software-architecture/intuitive-object-models.html). I think your design is not intuitive and self-describing though, so you are not solving the right problem by asking the question you've asked.

Anyway, since you are asking to have design patterns identified, I will help you with that too.

  • Fact that all your derived types (specifically interfaces) implement IGeneratable, that is called Super Type design pattern.
  • As some suggested, simulation containing other simulations inside is a Composite pattern. However, it's not entirely accurate since IMultiSimulation does not implement ISimulation. It's some kind of composite anyway, because both parent and children implement IGeneratable at least.
  • IStep sounds like a Strategy pattern, but I guess you are not implementing it correctly.

Now, I want to suggest you to re-think your design approach, because your interfaces are not intuitive. Here are the issues I see, which you need to re-think:

  • ISimulation has Height and Temperature, and it also has Generate(). Generate() most likely is supposed to use Height and Temperature - that's how I understood your description, but then it's not the right way to express that. If Generate() depends on Height and Temperature, then pass them as arguments, and don't define them as properties. Besides, interface can better express behavior, not state. Properties represent state, which I would express as a class, not an interface.
  • If simulation is going to execute steps, don't define them on the ISimulation itself - that's again not so intuitive design. Pass them as arguments, and that will make it a Strategy design pattern (that's why I said above it's not implemented correctly).

I would go further, but I don't know the whole picture. From what you've expressed, your presented implementation is incorrect. Please re-think.

Tengiz
  • 8,011
  • 30
  • 39
0

its seems to me there is a very specific use case to implement so I suggest to use a class that will include imlementation for Generate() (I hope I understood the requirements right)

class Simulation
{
    string Name { get; }
    int[] Heights { get; }
    int[] Temperatures { get; }

    void Generate() {
         for (int i = 0; i < Temperatures.Count; i++) {
             for (int j = 0; j < Heights.Count; j++) {
                  GenerateStep1();
             }
             GenerateStep2();
             GenerateStep3();
         }
    }
}
chenop
  • 4,743
  • 4
  • 41
  • 65