Hi i'm trying to understand how i could build a readable and also error preventing Fluent-API without to much restriction for the User
to hold it simple let's say we want to change the following class to be fluent
public class Car
{
public int Gallons { get; private set; }
public int Tons { get; private set; }
public int Bhp { get; private set; }
public string Make { get; private set; }
public string Model { get; private set; }
public Car(string make, string model)
{
Make = make;
Model = model;
}
public void WithHorsePower(int bhp)
{
Bhp = bhp;
return this;
}
public void WithFuel(int gallons)
{
Gallons = gallons;
}
public void WithWeight(int tons)
{
Tons = tons;
}
public int Mpg()
{
return Gallons/Tons;
}
}
the problem in this case the user should only be able to access Mpg()
if Weight()
and Fuel()
got called first also the position of HorsePower()
is irrelevant.
Samples:
int mpg =Car.Create().HorsePower().Fuel().Weight().Mpg();
int mpg =Car.Create().Fuel().HorsePower().Weight().Mpg();
int mpg =Car.Create().HorsePower().Fuel().HorsePower().Weight().Mpg();// <- no typo
int mpg =Car.Create().Fuel().Weight().HorsePower().Mpg();
int mpg =Car.Create().Weight().HorsePower().Fuel().Mpg();
int mpg =Car.Create().Weight().Fuel().Mpg();
Is there a easy way to do this without a big bunch of interfaces?
I also doesn't how to implement this nested interfaces in the right way
Here are the interfaces i currently created
interface Start
{
IFuelWeight1 HorsePower();
IHorsePowerWeight1 Fuel();
IHorsePowerFuel1 Weight();
}
interface IFuelWeight1 // Start.HorsePower()
{
IHorsePowerWeight1 Fuel();
IHorsePowerFuel1 Weight();
}
interface IHorsePowerWeight1 // Start.Fuel()
{
IHorsePowerWeight1 HorsePower();
IHorsePowerFuelMpg Weight();
}
interface IHorsePowerFuel1 // Start.Weight()
{
IHorsePowerFuel1 HorsePower();
IHorsePowerWeightMpg Fuel();
}
#region End
interface IHorsePowerFuelMpg
{
IFuelWeightMpg HorsePower();
IHorsePowerWeightMpg Fuel();
int Mpg();
}
interface IHorsePowerWeightMpg
{
IFuelWeightMpg HorsePower();
IHorsePowerFuelMpg Weight();
int Mpg();
}
interface IFuelWeightMpg
{
IHorsePowerWeightMpg Fuel();
IHorsePowerFuelMpg Weight();
int Mpg();
}
#endregion
EDIT for Adam Houldsworth :-)
- Is the Interface above a good one or is there a easier way to do this but hold the restriction for Mpg()?
How to Implement the interface above to do this?:
var k = myMiracle as Start; k.Fuel().Weight(); k.Weight().Fuel(); k.HorsePower().Fuel().Weight(); k.HorsePower().Weight().Fuel(); k.Fuel().HorsePower().Weight(); k.Weight().HorsePower().Fuel();
WithXXX
methods to returnCar
and simplyreturn this;
at the end of each method? Then you canvar car = new Car().WithFuel(100).WithMpg(35).WithHorsePower(200);
. Alternatively, just make these optional constructor arguments :-) – BankruptcyCar
to be initialized withoutWeight
orMpg
for example, then you must have those parameters as part of the constructor... otherwise you'll allow users to create aCar
that's in an invalid state and there's no amount of "niceness" that a fluent-interface can offer to fix that (it's not worth the trade-off). Fluent interfaces can be useful, but in this particular case you're doing it wrong. Sorry! – Drift