I was once asked in an interview to build a factory that makes cars. All cars has common features like price, rating and color and they have some features like fuel injection which can on specific car but not mandatory. Moreover there can be several types of the same feature like fuel-injection v1, v2 ect, and you should be able to compare these features. Features should be added with ease. (Meaning new feature)
I've also added a general car compare.
I had ~30 min, I didn't have time to finish my idea, but what I had in mind is doing a property of a list of features for my base car type.
I didn't pass and I would like to know two things:
- Is there something wrong with the approach of List of Feature for each car?
- Is there something I could do better with my factory design?
This is my code, I have a general CarFactory
instead of manufacture based and all models in one file for the simplicity of showing the code:
BL:
Factory.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace BL
{
public class Car
{
public readonly CarTypes Model;
public readonly CarManufactures Manufacture;
public readonly string Color;
private float price;
public float Price
{
get { return price; }
}
internal Car(CarTypes model, CarManufactures manufacture, string color, float price)
{
this.Model = model;
this.Manufacture = manufacture;
this.Color = color;
this.price = price;
this._features = new List<Feature>();
}
internal void AddFeature(Feature feature)
{
this._features.Add(feature);
this.price += feature.Install();
}
internal List<Feature> _features;
public List<Feature> Features
{
get
{
return _features;
}
}
public bool HasFeature(Feature feature)
{
return this.Features.Any(m => m.GetType() == feature.GetType());
}
public Feature GetFeature(Feature feature)
{
return this.Features.FirstOrDefault(m => m.GetType() == feature.GetType());
}
public override string ToString()
{
return string.Format("{0} {1} {2}", this.Color, this.Model, this.Manufacture);
}
}
public static class CarFactory
{
public static Car CreateCar(CarTypes model, CarManufactures manufacture, string color)
{
Car newCar = null;
switch (manufacture)
{
case CarManufactures.Couppe:
newCar = new Car(model, manufacture, color, 30000);
newCar.AddFeature(CruiseControl.GCruise);
break;
case CarManufactures.Mini:
newCar = new Car(model, manufacture, color, 40000);
newCar.AddFeature(CruiseControl.XCruise);
break;
case CarManufactures.Swift:
newCar = new Car(model, manufacture, color, 20000);
break;
}
switch (model)
{
case CarTypes.FamaliyCar:
newCar.AddFeature(FuelInjection.FuelJet);
break;
case CarTypes.SportCar:
newCar.AddFeature(FuelInjection.Ninjet);
break;
}
Console.WriteLine("Car was created:\n Model: {0}\nManufacture: {1}\nHas {2} features\nPrice:{3}\n*************************\n\n", newCar.Model, newCar.Manufacture, newCar.Features.Count, newCar.Price);
return newCar;
}
}
public enum CarManufactures
{
Swift,
Couppe,
Mini
}
public enum CarTypes
{
SportCar,
FamaliyCar
}
public static class Extensions
{
public static List<Car> CompareCars(this List<Car> cars, bool sort= false)
{
Console.WriteLine("{0,-15} {1,-15} {2,-7} {3,-10}", "Manufacture", "Model", "Rating", "Price");
Console.WriteLine("{0,-15} {1,-15} {2,-7} {3,-10}", "-----------", "-----", "------", "-----");
if (sort)
cars = cars.OrderByDescending(m => m.GetRating()).ToList();
foreach (Car car in cars)
{
Console.WriteLine("{0,-15} {1,-15} *{2,-4} {3,-10}", car.Manufacture, car.Model, car.GetRating(), car.Price);
}
return cars;
}
public static int GetRating(this Car car)
{
int rating = GetBaseRating(car.Manufacture);
rating += car.Features.Sum(m => m.Rating);
return rating;
}
private static int GetBaseRating(CarManufactures manufacture)
{
switch (manufacture)
{
case CarManufactures.Swift:
return 30;
case CarManufactures.Couppe:
return 40;
case CarManufactures.Mini:
return 35;
default:
return 0;
}
}
}
}
Features.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BL
{
public abstract class Feature : IComparable
{
internal Feature(string name, int rating, float cost)
{
this._name = name;
this._rating = rating;
this._cost = cost;
}
internal string _name;
public string Name
{
get { return _name; }
}
internal int _rating;
public int Rating
{
get
{
return _rating;
}
}
internal float _cost;
public float Cost
{
get
{
return _cost;
}
}
public virtual float Install()
{
Console.WriteLine("Base installing Feature of type: {0}.\nName of Feature: {1}", this.GetType(), this.Name);
Thread.Sleep(200);
return Cost;
}
public int CompareTo(Object obj)
{
if (obj == null) return 1;
Feature otherObj = obj as Feature;
if (otherObj == null || otherObj.GetType() != this.GetType())
return 1;
return this.Rating.CompareTo(otherObj.Rating);
}
}
public class FuelInjection : Feature
{
public FuelInjection(string name, int rating, float cost) : base(name, rating, cost) { }
private static FuelInjection _fuelJet;
public static FuelInjection FuelJet
{
get
{
if (_fuelJet == null)
{
_fuelJet = new FuelInjection("FuelJet1", 4, 3000);
}
return _fuelJet;
}
}
private static FuelInjection _ninjet;
public static FuelInjection Ninjet
{
get
{
if (_ninjet == null)
{
_ninjet = new FuelInjection("NinJet2", 17, 9000);
}
return _ninjet;
}
}
public override float Install()
{
Console.WriteLine("Adding current specific feature {0}...", this.Name);
Thread.Sleep(400);
//DO STUFF
return base.Install();
}
}
public class CruiseControl : Feature
{
public CruiseControl(string name, int rating, float cost) : base(name, rating, cost) { }
private static CruiseControl _gCruise;
public static CruiseControl GCruise
{
get
{
if (_gCruise == null)
{
_gCruise = new CruiseControl("G.Cruise", 7, 1000);
}
return _gCruise;
}
}
private static CruiseControl _xCruise;
public static CruiseControl XCruise
{
get
{
if (_xCruise == null)
{
_xCruise = new CruiseControl("X.Cruise", 15, 3000);
}
return _xCruise;
}
}
public override float Install()
{
Console.WriteLine("Adding current specific feature {0}...", this.Name);
Thread.Sleep(500);
//DO STUFF
return base.Install();
}
}
}
Console APP:
using BL;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start building cars\n------------------");
List<Car> myCars = new List<Car>();
myCars.Add(CarFactory.CreateCar(CarTypes.FamaliyCar, CarManufactures.Couppe, "Red"));
myCars.Add(CarFactory.CreateCar(CarTypes.SportCar, CarManufactures.Couppe, "Red"));
myCars.Add(CarFactory.CreateCar(CarTypes.SportCar, CarManufactures.Mini, "UK FLAG"));
myCars.Add(CarFactory.CreateCar(CarTypes.SportCar, CarManufactures.Swift, "Blue"));
myCars.Add(CarFactory.CreateCar(CarTypes.FamaliyCar, CarManufactures.Swift, "Black"));
Console.WriteLine("\n\n---------------------");
Console.WriteLine("/**** Compare cars: *****/");
Console.WriteLine("---------------------");
myCars = myCars.CompareCars(true);
Console.WriteLine("\n\n---------------------");
Console.WriteLine("/**** Specific feature vs car - {0}: *****/", myCars[0]);
Console.WriteLine("---------------------");
foreach (Feature feature in myCars[0].Features)
{
Console.WriteLine("\nComparing feature - {0}: ",feature.GetType());
foreach (Car car in myCars.Skip(1))
{
if (car.HasFeature(feature))
{
Console.WriteLine("Feature exist in {1} and is better: {0} [{2} vs {3}]", car.GetFeature(feature).CompareTo(feature)==1, car, car.GetFeature(feature).Rating, feature.Rating);
}
else
{
Console.WriteLine("Feature does not exist in {0}",car);
}
}
}
}
}
}