Home > database >  Handling polymorphisms with large amount of very similar classes in C#
Handling polymorphisms with large amount of very similar classes in C#

Time:10-20

I am making a simulation with different particles. They all have simular behaviours, are all stored in the same list with other particles of potentially different sub-types, and are treated equally by the rest of the system. I therefore created the class Particle, and attempted different methods of polymorphism. This is a simplified version of what I roughly want to achieve, written in pseudo-code:

//Movement methods
Randomwalk() {//code}
Diffusion() {//code}
Steered_Motion() {//code}

//Special actions
Pollute() {//code}
Cleanup() {//code}

//Particles
Particle_Type       Movement_Method           Special_Action
Oil                 Diffusion()               Pollute()
Sorbent             Diffusion()               Cleanup()
Packman_random      Randomwalk()              Cleanup()
Packman_steered     Steered_Motion()          Cleanup()
... (50 more rows)

I have thought of three methods to achieve this, but two of them only work effectively for a small amount of particle types (<= 10), while the third method was a hopefull dream shattered by the way the DataTable class works in C#.

The first method I tried was defining an Enum with the different particle types, adding a Field to the Particle class which defined what kind of particle it was, and used a switch statement using that field to determine which movement method and which special action to call.

public class Particle {
     public enum ParticleType: int {
          Oil = 0,
          Sorbent = 1,
          //...etc
     }
     
     public readonly ParticleType TypeOfParticle;

     Movement() {
          switch(TypeOfParticle) {
               case ParticleType.Oil:
                    Diffusion();
                    break;
               case ParticleType.Sorbent:
                    Diffusion();
                    break;
          }
     }
}

However, as some types required additional fields, this left a mess of various fields that where used by only one of the sub types. In addition, the Enum type does not have any build in support for requiring uniqueness, which means two particle types could end up triggering the same case in the switch statements if the Enum definition would not be properly checked for the value assignment.

The second method was using inheritance. Here I still used the same Particle class, but had every particle type be a child of the Particle class.

public class Particle {
     virtual public void Movement() {}

     //Define methods
     Randomwalk() {//some code}
     Diffusion() {//some code}
     Steered_Motion() {//some code}
}

public class Oil: Particle {
     override public void Movement() {
          Diffusion();
     }
}

public class Sorbent: Particle {
     override public void Movement() {
          Diffusion();
     }
}

While this solves the problem of having fields only for some of the types, it creates a very long list with a ton of classes. Maybe it is just me, but managing this list of types really feels like a chore. Therefore I started looking for something among the lines of the third method.

The thrird, desired method would be something among the lines of using a DataTable to create a table, with all the different particle types as rows, and the movement methods and special actions as columns. Running the methods would be done using delegates. Unfortunately, DataTables are populated at Run Time, while I need to define the classess at compile time. In addition managing DataTables also does not sound like the most comfortable task.

So I finally have come to ask you what you would do. Is there any type similar to DataTable that I could use? I'm a physics & mathematics student, with not that many hours of programming, so even if something sounds very basic, I might have totally missed it. Really appreciate any input! Thank you in advance :)

CodePudding user response:

You can maybe use delegate to represent your movements and special actions. Or Action, Func which are just delegate too. Depends of your need. In this example I've included the Particle in the delegate so that depending the type of the particle the behavior of the Diffusion delegate may be different by example. Then you just have a ParticleFactory to instantiate your particles properly depending the ParticleType you desire.

        public delegate void SpecialAction(Particle particle);
        public delegate void Movement(Particle particle);

        public static Movement Diffusion = (particle) => { };
        public static Movement RandomWalk = (particle) => { };
        public static Movement Steered_Motion = (particle) => { };

        public static SpecialAction Pollute = (particle) => { };
        public static SpecialAction Cleanup = (particle) => { };

        public enum ParticleType
        {
            Oil = 0,
            Sorbent = 1,
            Packman_random=2,
            Packman_steered=3,
        }

        public class ParticleFactory
        {
            public static Particle BuildParticle(ParticleType particleType) {
                switch(particleType)
                {
                    case ParticleType.Oil:
                        {
                            return new Particle(particleType, Diffusion, Pollute);
                        }
                    case ParticleType.Sorbent:
                        {
                            return new Particle(particleType, Diffusion, Cleanup);
                        }
                    case ParticleType.Packman_random:
                        {
                            return new Particle(particleType, RandomWalk, Cleanup);
                        }
                }
                return null;
            }
        }

        public class Particle {

            private readonly ParticleType particleType;
            private readonly Movement movement;
            private readonly SpecialAction specialAction;


            internal Particle(ParticleType particleType, Movement movement, SpecialAction specialAction)
            {
                this.particleType = particleType;
                this.movement = movement;
                this.specialAction = specialAction;
            }

            public void Move()
            {
                movement.Invoke(this);
            }

            public void Special()
            {
                specialAction.Invoke(this);
            }
        }

CodePudding user response:

I think your second approach is the right one, especially if:

  • You have a relatively fixed number of particle types that you're modeling,
  • Some of the particle types you're modeling have special behavior not shared by other particle types, and/or
  • There is any sort of grouping or commonality between particle types that you want to capture as a part of a class hierarchy
public abstract class Particle
{
    // standard movement behaviors available to any subclass of Particle
    protected void RandomWalk() {...}
    protected void SteeredMotion() {...}
    protected void Diffusion() {...}

    // standard "special actions" available to subclasses of Particle
    protected void Pollute() {...}
    protected void CleanUp() {...}

    // abstract methods expected to call the above methods
    protected abstract void Move();
    protected abstract void SpecialAction();
}

public class OilParticle : Particle
{
    protected override void Move() { Diffusion(); }
    protected override void SpecialAction() { Pollute(); }
}

public class PackmanRandomParticle : Particle
{
    protected override void Move() { RandomWalk(); }
    protected override void SpecialAction() { CleanUp(); }
}
  • Related