您好我正在努力了解我如何构建可读性和错误防止Fluent-API,而不会对用户造成太多限制
保持简单让我们说我们想要改变下面的课程是否流利
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;
}
}
在这种情况下,如果首先调用Mpg()
和Weight()
并且Fuel()
的位置无关,则用户应该只能访问HorsePower()
。
样品:
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();
如果没有大量接口,是否有一种简单的方法可以做到这一点? 我也不知道如何以正确的方式实现这个嵌套接口
以下是我当前创建的界面
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
编辑: - )
如何实现上面的界面来执行此操作?:
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();
答案 0 :(得分:8)
Fluent API是一件好事,但我会采取不同的方式。建造一辆汽车让我更加关注Builder pattern。这样你就可以隐藏在工厂中组成的汽车(不是工厂方法模式),它接受你现在拥有的命令,但是不接受问题。
没有制造商会让您知道有关新车的详细信息,除非它已完成并准备公布。因此您必须先发送GetMyCar()
之类的命令,以便先发布汽车,如果你在未完成的汽车上打Mpg
,你会得到一个例外,这是完全有道理的。如果你使用那种流畅的模式,它仍然会很好看。
var builder = new CarBuilder();
// each building method returns `CarBuilder`
builder.BuildFrames(size).BuildChassis().AppendWheels(4)...
好的,这是我的看法。但是,如果您不喜欢建设者,还有两种建议可供您选择。
1)如果用户在设置Mpg
和Weight
之前调用Fuel
,则抛出异常,并显示解释该情况的消息。还要添加Mpg
方法的正确文档。
2)使构造函数获取其他属性的所有必需参数。在我看来,这是一个比第一个更好的解决方案,因为从一开始就说明了你所期望的。
答案 1 :(得分:8)
一种替代方法是调用Mpg()上的所有操作,这将允许其他操作成为条件。
这已经在SO中用代码示例回答了。请参阅Conditional Builder Method Chaining Fluent Interface
帖子指出,使用构造函数可以实现相同的操作,而不是接口,调用方法可以使所有其他操作成为条件。