3
\$\begingroup\$

I am learning design patterns and I have implemented the abstract factory using generics C#.NET. Is this implementation correct or not? Or is there any SOLID principle violation?

using System;
namespace GenericAbstractFactory
{
    public enum vehicles { car, bicycle, truck }

    interface IFourWheeler
    {
        void DriveFourWheeler();
    }

    interface ITwoWheeler
    {
        void DriveTwoWheeler();
    }

    interface IVehcileAbstractFactory<VehicleTypeFactory> //Abstract Factory
    {
        VehicleTypeFactory GetVehicleType();
    }

    class FourWheeler<VehicleType> : IVehcileAbstractFactory<IFourWheeler>
        where VehicleType : IFourWheeler, new() // ConcreteFactory
    {
        public IFourWheeler GetVehicleType()
        {
            return new VehicleType();
        }
    }

    class TwoWheeler<VehicleType> : IVehcileAbstractFactory<ITwoWheeler>
        where VehicleType : ITwoWheeler, new() // ConcreteFactory
    {
        public ITwoWheeler GetVehicleType()
        {
            return new VehicleType();
        }
    }

    class Car : IFourWheeler
    {
        public void DriveFourWheeler()
        {
            Console.WriteLine("Drive Car");
        }
    }

    class Truck : IFourWheeler
    {
        public void DriveFourWheeler()
        {
            Console.WriteLine("Drive Truck");
        }
    }

    class Bicycle : ITwoWheeler
    {
        public void DriveTwoWheeler()
        {
            Console.WriteLine("Drive Bicycle");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            vehicles veh = vehicles.bicycle;
            IVehcileAbstractFactory<IFourWheeler> getFourWheeler = null;
            IVehcileAbstractFactory<ITwoWheeler> getTwoWheeler = null;
            IFourWheeler actionFourWheeler = null;
            ITwoWheeler actionTwoWheeler = null;
            if (veh == vehicles.car)
            {
                getFourWheeler = new FourWheeler<Car>();
                actionFourWheeler = getFourWheeler.GetVehicleType();
            }
            else if (veh == vehicles.bicycle)
            {
                getTwoWheeler = new TwoWheeler<Bicycle>();
                actionTwoWheeler = getTwoWheeler.GetVehicleType();
            }
            else if (veh == vehicles.truck)
            {
                getFourWheeler = new FourWheeler<Truck>();
                actionFourWheeler = getFourWheeler.GetVehicleType();
            }
            else
            {
                throw new Exception("vehicle type not supported");
            }

            if (actionFourWheeler != null)
            {
                actionFourWheeler.DriveFourWheeler();
            }
            else if (actionTwoWheeler != null)
            {
                actionTwoWheeler.DriveTwoWheeler();
            }
            Console.Read();
        }
    }
}
\$\endgroup\$
1
\$\begingroup\$

I'm not sure, but I don't think a generic approach is suitable for the abstract factory pattern, because the client has to specify the concrete class type in order to call the factory, and then the whole point of that pattern is gone?

The way you've tried to implement it works, but you have effectively two parallel factories (one for four wheels, and one for two) that have nothing or just very little to do with each other (DriveFourWheeler() vs DriveTwoWheeler()).

Below I've tried to fiddle a little bit with you code in order to show my understanding of the pattern and still with a generic touch.

using System;
namespace GenericAbstractFactory
{
  public enum Vehicles { car, bicycle, truck }

  interface IWheeler
  {
    void Drive();
  }

  interface IFourWheeler : IWheeler
  {
  }

  interface ITwoWheeler : IWheeler
  {
  }

  interface IVehicleAbstractFactory
  {
    IWheeler GetVehicle();
  }

  class FourWheelerFactory<FourWheeler> : IVehicleAbstractFactory where FourWheeler: IFourWheeler, new()
  {
    public IWheeler GetVehicle()
    {
      return new FourWheeler();
    }
  }

  class TwoWheelerFactory<TwoWheeler> : IVehicleAbstractFactory where TwoWheeler: ITwoWheeler, new()
  {
    public IWheeler GetVehicle()
    {
      return new TwoWheeler();
    }
  }

  class Car : IFourWheeler
  {
    public void Drive()
    {
      Console.WriteLine("Drive Car");
    }
  }

  class Truck : IFourWheeler
  {
    public void Drive()
    {
      Console.WriteLine("Drive Truck");
    }
  }

  class Bicycle : ITwoWheeler
  {
    public void Drive()
    {
      Console.WriteLine("Drive Bicycle");
    }
  }

  class Program
  {
    static IVehicleAbstractFactory GetFactory(Vehicles vehicle)
    {
      switch (vehicle)
      {
        case Vehicles.car:
          return new FourWheelerFactory<Car>();
        case Vehicles.truck:
          return new FourWheelerFactory<Truck>();
        case Vehicles.bicycle:
          return new TwoWheelerFactory<Bicycle>();
      }
    }

    static void Main(string[] args)
    {
      IVehicleAbstractFactory factory = GetFactory(Vehicles.bicycle);
      IWheeler vehicle = factory.GetVehicle();
      vehicle.Drive();


      Console.Read();
    }
  }
}

I'm not saying this is the right way for the factory pattern, but may be a little clearer. The client still has to know which actual class he wants to create due to the nature of generics.

\$\endgroup\$
0
\$\begingroup\$

I also think there should be only one factory. The GetVehicle method should get the enum value and decide which class is created. If you don't want the factory to know the concrete implementations of the single classes, you could pass them as generic types.

This would be my approach:

namespace GenericAbstractFactory
{
    public enum Vehicles { car, bicycle, truck }

    interface IWheeler
    {
        void Drive();
    }

    interface IFourWheeler : IWheeler
    {
        bool IsWheelFrontLeftFlat { get; }
        bool IsWheelFrontRightFlat { get; }
        bool IsWheelBackLeftFlat { get; }
        bool IsWheelBackRightFlat { get; }
    }

    interface ITwoWheeler : IWheeler
    {
        bool IsWheelFrontFlat { get; }
        bool IsWheelBackFlat { get; }
    }

    interface IVehicleAbstractFactory
    {
        IWheeler GetVehicle(Vehicles vehicle);
    }

    class WheelerFactory<TCar, TTruck, TBiccycle> : IVehicleAbstractFactory 
        where TCar : IFourWheeler, new()
        where TTruck : IFourWheeler, new()
        where TBiccycle : ITwoWheeler, new()
    {
        public IWheeler GetVehicle(Vehicles vehicle)
        {
            switch (vehicle)
            {
                case Vehicles.car:
                    return new TCar();
                case Vehicles.truck:
                    return new TTruck();
                case Vehicles.bicycle:
                    return new TBiccycle();
                default:
                    return null;
            }
        }
    }

    class Car : IFourWheeler
    {
        public bool IsWheelBackLeftFlat { get { return false; } }
        public bool IsWheelBackRightFlat { get { return false; } }
        public bool IsWheelFrontLeftFlat { get { return false; } }
        public bool IsWheelFrontRightFlat { get { return false; } }

        public void Drive()
        {
            Console.WriteLine("Drive Car");
        }
    }

    class Truck : IFourWheeler
    {
        public bool IsWheelBackLeftFlat { get { return false; } }
        public bool IsWheelBackRightFlat { get { return false; } }
        public bool IsWheelFrontLeftFlat { get { return false; } }
        public bool IsWheelFrontRightFlat { get { return false; } }

        public void Drive()
        {
            Console.WriteLine("Drive Truck");
        }
    }

    class Bicycle : ITwoWheeler
    {
        public bool IsWheelBackFlat { get { return false; } }
        public bool IsWheelFrontFlat { get { return false; } }

        public void Drive()
        {
            Console.WriteLine("Drive Bicycle");
        }
    }

    class Program
    {
        private static IVehicleAbstractFactory factory = new WheelerFactory<Car, Truck, Bicycle>();

        static void Main(string[] args)
        {
            IWheeler vehicle = factory.GetVehicle(Vehicles.bicycle);

            bool canDrive = false;
            ITwoWheeler vehicleTwoWheeler = vehicle as ITwoWheeler;
            canDrive = (vehicleTwoWheeler != null)
                       && !vehicleTwoWheeler.IsWheelBackFlat && !vehicleTwoWheeler.IsWheelFrontFlat;

            if (!canDrive)
            {
                IFourWheeler vehicleFourWheeler = vehicle as IFourWheeler;
                canDrive = (vehicleFourWheeler != null)
                           && !vehicleFourWheeler.IsWheelFrontLeftFlat && !vehicleFourWheeler.IsWheelFrontRightFlat
                           && !vehicleFourWheeler.IsWheelBackLeftFlat && !vehicleFourWheeler.IsWheelBackRightFlat;
            }

            if (canDrive) vehicle.Drive();

            Console.Read();
        }
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.