jeudi 30 novembre 2023

How to solve multiple class inheritance without duplicating code in my case?

My concrete classes can be one of the type permutations shown below. But, since I cannot inherit from two classes (case 4 and 5) I had to convert one of them to interface which introduce code duplication shown in FIX section below. I know someone will say "use composition", but I don't see how to change the design to take this into account.

Type permutations:

  1. ControllerSimulator
  2. MovingControllerSimulator
  3. RegistersControllerSimulator
  4. ControllerSimulator + RegistersControllerSimulator
  5. MovingControllerSimulator + RegistersControllerSimulator

CODE

public abstract class ControllerSimulator
{
   protected virtual void OnTurningOn() { }
   public void TurnOn()
   { 
     ...
   }
   
   protected virtual void OnTurningOff() { }
   public void TurnOff()
   { 
     ...
   }

   // more base methods
   ...
}

public abstract class MovingControllerSimulator : ControllerSimulator
{
   public virtual void Disable() { }

   public void SetMotionDuration(TimeSpan duration)
   {
       _motionDuration = duration;
   }

    protected void OnMoveStarted()
    {
       MoveStarted?.Invoke();
    }

    // more base methods
    ...
}

public abstract class RegistersControllerSimulator : ControllerSimulator
{
   protected Dictionary<string, object> Registers;
   
   protected abstract void OnRegisterChanged(string regName, object regVal); 
   public void UpdateRegister(string regName, object regVal)
   {
      Registers[regName] = regVal;
      OnRegisterChanged(...);
   }

   // more base methods
   ...
}

FIX

Since I cannot inherit from two classes I decided to convert RegistersControllerSimulator to an interface and create RegistersMovingControllerSimulator and RegistersNonMovingControllerSimulator. But, this requires code duplication which I potentially can solve by moving it to utils.

interface IRegistersControllerSimulator
{
   void UpdateRegister(string regName, object regVal);
}

public abstract class RegistersMovingControllerSimulator : MovingControllerSimulator, IRegistersControllerSimulator
{
   protected Dictionary<string, object> Registers;
   
   protected abstract void OnRegisterChanged(string regName, object regVal); 
   public void UpdateRegister(string regName, object regVal)
   {
      Registers[regName] = regVal;
      OnRegisterChanged(...);
   }
}    

public abstract class RegistersNonMovingControllerSimulator : ControllerSimulator, IRegistersControllerSimulator
{
   protected Dictionary<string, object> Registers;
   
   protected abstract void OnRegisterChanged(string regName, object regVal); 
   public void UpdateRegister(string regName, object regVal)
   {
      Registers[regName] = regVal;
      OnRegisterChanged(...);
   }
}

public class Concrete_Registers_Moving_Controller : RegistersMovingControllerSimulator
{
    // Uses "await Task.Delay(_motionDuration)" to imitate motion
}

public class Concrete_Registers_NonMoving_Controller : RegistersNonMovingControllerSimulator
{
}

public class Concrete_NonRegisters_Moving_Controller : MovingControllerSimulator
{
    // Uses "await Task.Delay(_motionDuration)" to imitate motion
}

public class Concrete_NonRegisters_NonMoving_Controller : ControllerSimulator
{
}

Aucun commentaire:

Enregistrer un commentaire