user3093257
user3093257

Reputation: 11

Strategy pattern method that takes other interface

I am looking to utilize the strategy pattern for a .net application. I am simplifying the code, but the idea is the same. There is a transport interface, which will have 2 concrete classes, Car & Motorcycle, both with the only requirement being to implement the driveable method. To implement it, I want to use the strategy pattern and the IDriveableStrategy interface, which will take a class that implements ITransport and use information in that class to determine if the ITransport is driveable. I saw some posts questioning the practice of sending the context to the strategy, but I want to ignore that debate for now. If I implement a class called MotorcycleDriveableStrategy, I want to use that as a strategy for the Motorcycle class.

In the code below, you'll notice that I have two additional properties specific to the Motorcycle class, that I would like to use to evaluate whether or not the ITransport is driveable. In the MotorcycleDriveableStrategy I have commented out how I'd ideally like to implement the class, (have isDriveable implement the interface by taking a Motorcycle, because Motorcycle does implement ITransport) but I get a compilation error when I try that, so I have to keep the method expecting the ITransport interface.

I could achieve the coupling that I wanted by making the IDriveableStrategy interface take a generic that implements ITransport

IDriveableStrategy<T> where T : ITransport

and declare

MotorcycleDriveableStrategy<Motorcycle>

but I want to know if I'm missing the bigger picture. How would this be achieved before generics? Should the strategy pattern always evaluate the context based off of the exposed interface implementations? Should the strategy interface not take an interface as a parameter? Is there another way to achieve this without adding numWheels to the ITransport interface or making the IDriveableStrategy generic? Thank you in advance for any advice or insight.

public interface ITransport
  {
        boolean driveable();
  }

public class Car : ITransport
{
     private driveableStrategy {get;set;}
     public boolean driveable()
     {
         return driveableStrategy.isDriveable();
     }

}

public class Motorcycle: ITransport
{
     private driveableStrategy {get;set;}
     private int numWheels {get;set;}
     private string weatherExceptions {get;set;}
     public Motorcycle(IDriveableStrategy driveableStrategy,int numWheels,string weatherExceptions)
     {
         this.driveableStrategy = driveableStrategy;
         this.numWheels = numWheels;
         this.weatherExceptions = weatherExceptions;
     }

     public boolean driveable()
     {
         return driveableStrategy.isDriveable();
     }

}

public interface IDriveableStrategy
{
        boolean isDriveable(ITransport transport);
}

public class MotorcycleDriveableStrategy
{
        //What i would like to do
        /*public boolean isDriveable(Motorcycle transport)
           {
               return transport.numWheels > 2;
           }
         */
        public boolean isDriveable(ITransport transport)
        {

        }
}

Upvotes: 1

Views: 686

Answers (1)

narendra
narendra

Reputation: 163

Let me start by saying I have no problem with send the context to the strategy, IMO the point of the individual strategy is to abstract away the details so you can plug in another at whim. The IStrategy interface methods allow user classes to execute the same command for all strategies, but I would not restrict the strategy itself to the interface methods. The individual strategy should have intimate knowledge of the context. Please let me know if I completely missed ur point, also I'd be very interested if someone has a differing opinion btw.

In your example the MotorcycleDriveableStrategy is the only place we should be making decisions about the driveability of the motorcycle. Hence:

public class MotorcycleDriveableStrategy : IDriveableStrategy
{
    //What i would like to do
    public bool IsDriveable(ITransport transport)
    {
        var mc = transport as Motorcycle;
        if (mc == null) return false;
        return mc.NumWheels > 2;
    }
}

And if you are interested in the whole thing:

public interface ITransport { bool Driveable();}
public interface IDriveableStrategy { bool IsDriveable(ITransport transport);}

public class Car : ITransport
{
    private IDriveableStrategy Strategy { get; set; }
    public bool Driveable()
    {
        return Strategy.IsDriveable(this);
    }
}

public class Motorcycle : ITransport
{
    private IDriveableStrategy Strategy { get; set; }
    public int NumWheels { get; set; }
    public string WeatherExceptions { get; set; }

    public Motorcycle(IDriveableStrategy driveableStrategy, int numWheels, string weatherExceptions)
    {
        Strategy = driveableStrategy;
        NumWheels = numWheels;
        WeatherExceptions = weatherExceptions;
    }

    public bool Driveable()
    {
        return Strategy.IsDriveable(this);
    }
}

public class MotorcycleDriveableStrategy : IDriveableStrategy
{
    //What i would like to do
    public bool IsDriveable(ITransport transport)
    {
        var mc = transport as Motorcycle;
        if (mc == null) return false;
        return mc.NumWheels > 2;
    }
}

// And for the Car strategy
public class CarDriveableStrategy : IDriveableStrategy
{
    //place ur implementation here ...
    public bool IsDriveable(ITransport transport) {return true;}
}

public class TestMotorcycle
{
    public TestMotorcycle()
    {
        var mcs = new MotorcycleDriveableStrategy();
        var mc = new Motorcycle(mcs, 2, "abc");
        Console.WriteLine("Motorcycle is {0}drivable", mc.Driveable()?"":"not ");
    }
}

Upvotes: 1

Related Questions