web-dev-qa-db-ja.com

流暢なインターフェースを作成する方法の理解

こんにちは私は、ユーザーに多くの制限を与えることなく、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
_

[〜#〜] edit [〜#〜] AdamHouldsworthの場合:-)

  • 上記のインターフェースは良いものですか、それともこれを行う簡単な方法はありますかしかし 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();
    _
10
WiiMaxx

1つの代替方法は、Mpg()ですべての操作を呼び出すことです。これにより、他の操作を条件付きにすることができます。

これは、コードサンプルを使用してSOですでに回答されています。 Conditional Builder Method Chaining Fluent Interface を参照してください。

投稿は、インターフェースの代わりに、他のすべての操作を条件付きにする呼び出しメソッドを使用して、コンストラクターを使用して同じことが達成される可能性があることを示しています。

8
Consult Yarla

Fluent APIは素晴らしいことですが、あなたの場合は別の方法で進めます。車を作ると、私は ビルダーパターン に引き寄せられます。そうすれば、現在のようにコマンドを受け入れるが質問を受け入れないファクトリ(ファクトリメソッドパターンではない)で作成されている車を非表示にできます。

完成して発表の準備ができていない限り、新車の詳細を知らせるメーカーはありません。したがって、GetMyCar()最初に車をリリースするため、未完成の車でMpgを呼び出すと、例外が発生することは完全に理にかなっています。そして、その流暢なパターンを使用すれば、それでも見栄えがします。

var builder = new CarBuilder();
// each building method returns `CarBuilder`
builder.BuildFrames(size).BuildChassis().AppendWheels(4)...

わかりました、それは私の意見です。ただし、ビルダーが気に入らない場合は、現在の状況についてさらに2つの提案があります。

1)MpgWeightが設定される前にユーザーがFuelを呼び出すと、状況を説明するメッセージとともに例外がスローされます。また、Mpgメソッドの適切なドキュメントを追加します。

2)コンストラクターに他のプロパティに必要なすべてのパラメーターを取得させます。私の意見では、これは最初の解決策よりも優れた解決策です。なぜなら、最初から期待できる状態だからです。

8
Ondrej Janacek