patterncsharpModerate
Building car factory with custom features per car
Viewed 0 times
carperfactorywithbuildingcustomfeatures
Problem
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:
This is my code, I have a general
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();
}
internal void AddFeature(Feature feature)
{
this._features.Add(feature);
this.price += feature.Install();
}
internal List _features;
public List Features
{
get
{
return _features;
}
}
public bool HasFeature(Feature feature)
{
return t
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();
}
internal void AddFeature(Feature feature)
{
this._features.Add(feature);
this.price += feature.Install();
}
internal List _features;
public List Features
{
get
{
return _features;
}
}
public bool HasFeature(Feature feature)
{
return t
Solution
In 30 min I could only create a draft. It's crazy what they requrie in an interview. It usually takes hours to come up with a resonable design.
Anyways this design has one very bad blocker that prevents it from being extendable. It's the
I would drop the Family/Sport-Car stuff and replace it with Coupe, Combi, SUV etc as they are the real car types. Those types should be classes derived from the
Whether a car is a family or a sport car is usually defined by the features it has - sport seats, engine with more power etc. and it's rather subjective.
With this structure you can create new car types anytime.
The feautures could be created with the composition pattern.
Then I would reduce the factory to just:
and use it like this:
You could configure the feature set by composition like:
Feature composition
First we need an abstract class for other features and a helper
Next we need some contrete features:
To build a feature chain we need a composite feature that will store the chain and it will also enumerate it:
With an extension like this we can easily build the chain:
Usage:
It's a one big feature that is composed of many other features.
I've added the
One of my favourite patterns ;-)
Decorator
You can also build different cars with a decorator pattern instead of an inheritance.
You leave the
but then instead of more abstract classes you wrap a
Example:
Finding features
To find a feature I would create an extension like this one:
Example:
You can virtually add anything you want.
Anyways this design has one very bad blocker that prevents it from being extendable. It's the
CarManufactures switch. You have a fixed number of those types and you cannot add new ones without modifying the switch and the enum.I would drop the Family/Sport-Car stuff and replace it with Coupe, Combi, SUV etc as they are the real car types. Those types should be classes derived from the
Car so that you can add new ones easily.Whether a car is a family or a sport car is usually defined by the features it has - sport seats, engine with more power etc. and it's rather subjective.
abstract class Car
{
protected Car(string make) { ... }
public string Make { get; }
public Feature Feature { get; set; }
}
abstract class Combi : Car {}
abstract class Hatchback : Car {}
class FocusCombi : Combi
{
public Focus() : base("Ford") {}
}
class FocusHatchback : Hatchback
{
public Focus() : base("Ford") {}
}With this structure you can create new car types anytime.
The feautures could be created with the composition pattern.
Then I would reduce the factory to just:
public static class CarFactory
{
public static Car CreateCar(Feature feature) where TCar : Car, new()
{
return new TCar()
{
Feature = feature
};
}
}and use it like this:
CarFactory.CreateCar(Feature.Family);You could configure the feature set by composition like:
abstract class Feature
{
public static Feature Family => Feature.Empty
.Add()
.Add();
}Feature composition
First we need an abstract class for other features and a helper
Empty property. ForEach will us allow to get all features.abstract class Feature
{
public static Feature Empty => new CompositeFeature();
public virtual void ForEach(Action feature) { feature(this); }
}Next we need some contrete features:
class PowerWindows : Feature { }
class InjectionA : Feature { }To build a feature chain we need a composite feature that will store the chain and it will also enumerate it:
class CompositeFeature : Feature
{
private readonly Feature[] _features;
public CompositeFeature(params Feature[] feautures)
{
_features = feautures;
}
public override void ForEach(Action feature)
{
foreach (var f in _features)
{
f.ForEach(feature);
}
}
}With an extension like this we can easily build the chain:
static class FeatureComposition
{
public static Feature Add(this Feature feature) where TFeature : Feature, new()
{
return new CompositeFeature(feature, new TFeature());
}
}Usage:
var feature = Feature.Empty
.Add()
.Add();
feature.ForEach(x => /* do somehting with x */ );It's a one big feature that is composed of many other features.
I've added the
ForEach so that we can get them all and do something later with each of them.One of my favourite patterns ;-)
Decorator
You can also build different cars with a decorator pattern instead of an inheritance.
You leave the
Car untouched:abstract class Car
{
protected Car(string make) { ... }
public string Make { get; }
public Feature Feature { get; set; }
}but then instead of more abstract classes you wrap a
Focus as a Combiclass Focus : Car
{
public Focus() : base("Ford") {}
}
class Combi : Car
{
private readonly _car;
public Combi(Car car) { _car = car; }
}Example:
var focus = new Focus();
var focusCombi = new Combi(focus);Finding features
To find a feature I would create an extension like this one:
static class FeatureExtensions
{
public static T Find(this Feature feature) where T : Feature
{
var result = default(T);
feature.ForEach(f =>
{
if (f.GetType() == typeof(T))
{
result = (T)f;
return;
}
});
return result;
}
}Example:
var injectionA = feature.Find();
var injectionB = feature.Find();
...do the comparisonYou can virtually add anything you want.
Code Snippets
abstract class Car
{
protected Car(string make) { ... }
public string Make { get; }
public Feature Feature { get; set; }
}
abstract class Combi : Car {}
abstract class Hatchback : Car {}
class FocusCombi : Combi
{
public Focus() : base("Ford") {}
}
class FocusHatchback : Hatchback
{
public Focus() : base("Ford") {}
}public static class CarFactory
{
public static Car CreateCar<TCar>(Feature feature) where TCar : Car, new()
{
return new TCar()
{
Feature = feature
};
}
}CarFactory.CreateCar<FocusCombi>(Feature.Family);abstract class Feature
{
public static Feature Family => Feature.Empty
.Add<NinjectInjection>()
.Add<PowerWindows>();
}abstract class Feature
{
public static Feature Empty => new CompositeFeature();
public virtual void ForEach(Action<Feature> feature) { feature(this); }
}Context
StackExchange Code Review Q#139984, answer score: 10
Revisions (0)
No revisions yet.