C# 了解如何创建流畅的界面
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18846811/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Understanding of How to Create a Fluent Interface
提问by WiiMaxx
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
嗨,我正在尝试了解如何构建一个可读且错误的防止 Fluent-API 对用户没有太多限制
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.
在这种情况下,问题是用户应该只能在首先被调用时才能访问Mpg()
,Weight()
而且Fuel()
的位置HorsePower()
也无关紧要。
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
EDITfor Adam Houldsworth :-)
为亚当·霍尔兹沃思 (Adam Houldsworth)编辑:-)
- Is the Interface above a good one or is there a easier way to do this buthold 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();
- 上面的接口是一个好的接口还是有更简单的方法来做到这一点, 但保留对 Mpg() 的限制?
如何实现上面的接口来做到这一点?:
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();
采纳答案by Consult Yarla
One alternative could be to invoke all operations on Mpg() which will allow the other operations to be conditional.
一种替代方法是调用 Mpg() 上的所有操作,这将允许其他操作有条件。
This is already answered in SO with a code sample. Please refer to Conditional Builder Method Chaining Fluent Interface
这已经通过代码示例在 SO 中得到了回答。请参考Conditional Builder Method Chaining Fluent Interface
The post indicates, instead of Interfaces, the same might be achieved using constructors, with a calling method that makes all others operations conditional.
该帖子表明,使用构造函数可以实现相同的效果,而不是接口,使用使所有其他操作成为条件的调用方法。
回答by Ondrej Janacek
Fluent API is a nice thing, but I would go a different way in your case. Building a car draws me more towards the Builder pattern. That way you would hide a car being composed in a factory (not the factory method pattern) which accepts commands like you have now, but do not accept questions.
Fluent API 是一件好事,但我会在你的情况下采取不同的方式。建造汽车让我更倾向于建造者模式。这样你就可以隐藏在工厂中组合的汽车(不是工厂方法模式),它接受像你现在这样的命令,但不接受问题。
No manufacturer lets you know details about their new car unless it is completed and prepared to be announced.So you would have to send a command like GetMyCar()
for releasing a car first and it would perfectly make sense that if you call Mpg
on unfinished car you would get an exception. And it would still look good if you use that fluent pattern.
除非已完成并准备发布,否则任何制造商都不会让您知道有关其新车的详细信息。所以你必须先发送一个命令,比如GetMyCar()
先释放汽车,如果你调用Mpg
未完成的汽车,你会得到一个例外,这是完全合理的。如果你使用那种流畅的模式,它看起来仍然不错。
var builder = new CarBuilder();
// each building method returns `CarBuilder`
builder.BuildFrames(size).BuildChassis().AppendWheels(4)...
Ok, that's my opinion. However there are two more suggestions for your current situation you can choose from if you don't like the builder.
好吧,这就是我的意见。但是,如果您不喜欢构建器,还有两个针对您当前情况的建议可供您选择。
1) If user calls Mpg
before Weight
and Fuel
are set, thrown an exception with a message explaining the situation. Also add a proper documentation of the Mpg
method.
1) 如果用户调用Mpg
之前Weight
和Fuel
被设置,则抛出异常并带有说明情况的消息。还要添加该Mpg
方法的适当文档。
2) Make a constructor take all required parameters for other properties. This is, in my opinon, a better solution than the first one, because is states from the very start what you can expect.
2)使构造函数采用其他属性的所有必需参数。在我看来,这是比第一个更好的解决方案,因为从一开始就说明您可以期待什么。