Search code examples
c#interfacemvpsolid-principlespresenter

How to handle different interface implementations using MVP pattern?


I have a design that I am working on and wanted to be clear on how one should handle multiple implementations of an interface using MVP pattern. Here is my situation:

There is a base "Tank" interface which defines basic functionality of what a tank should do.

public interface ITankView
{
  public string TankName
  public double TankLevel
  public double TankSize
  ... ext ...
}

My presenter accepts implementations of this interface (i.e. - the "Tank" view):

public class TankPresenter
{
  ITankView tank;
  public void TankPresenter(ITankView tank)
  {
    this.tank = tank;
  }

  public void DoStuffWithaBasicTank()
  {
     tank.TankName = "This is a basic Tank!"
     tank.TankSize = 100;
     tank.TankLevel = 50;
  }
}

I also have multiple implementations of the ITankView interface:

// Just a basic tank with a level:
public class BasicTankView, ITankView
{
   public string TankName {get; set;}
   public double TankLevel {get; set;}
   public double TankSize {get; set;}
}

// The "Advanced" version of a tank:
public class MixableTankView, ITankView
{
   public double TankName {get; set;}
   public double TankLevel {get; set;}
   public double TankSize {get; set;}
   public double MixingSpeed {get; set;}
}

So my question is how do I handle the "Advanced" MixingTankView in the TankPresenter while adhering to the MVP patter and best practice?

For example, if I instantiate the TankPresenter with a MixingTankView how would you access the special "Advanced" functionality?

public MixingTankView view = new MixingTankView();
public TankPresenter Presenter = new TankPresenter(view );


public class TankPresenter
{
  ITankView tank;
  public void TankPresenter(ITankView tank)
  {
    this.tank = tank;
  }

  public void DoStuffWithaMixingTank()
  {
     tank.TankName = "This is a mixing Tank!"
     tank.TankSize = 100;
     tank.TankLevel = 50;
     tank.MixingSpeed = 75; // This does not work as it's not declared in ITankView!!
  } 
}

I'm thinking I would need to add in a presenter for each and every type of Tank. So in this example that would be a IBasicTankPresenter and a IMixingTankPresenter but I'm still a little confused on how that would work. Also 90% of the functionality between the two tanks is identical so it seems redundant. In reality I have more than just 2 tanks and the actual implementation is much more complex but each tank type only has slight differences.

My Goals are:

  1. To reduce code complexity and coupling/inter-dependance of code
  2. Adhere to the MVP patter to some degree
  3. Not repeat myself a bunch of times with each tank type.
    • I'd like to reduce the amount of code for my own sanity
    • Do I really need 1 presenter for every 1 tank view?

Solution

  • First, you will need to add an interface for the MixableTankView -

    public interface IMixableTankView : ITankView
    {
        double MixingSpeed {get;set;}
    }
    

    Then, you can simply use generics. You start with your basic presenter, only now you change the ITankView to a type parameter:

    public class TankPresenter<TTankView> where TTankView : ITankView
    {
        TTankView tank;
        // basic implementation here
    }
    

    and add another presenter that inherits it:

    public class MixingTankPresenter : TankPresenter<IMixableTankView>
    {
        // IMixableTankView special implementation here
    }
    

    You do this for every different implementation of the ITankView - This way, most of the code will still be only in the base TankPresenter, and you get to keep MVP principles throughout your project.