web-dev-qa-db-ja.com

静的クラスの「インターフェース」を作成する

ヘルパーメソッドを呼び出すことができるように、郵便番号検証ライブラリを作成しています

var result = Postcode.IsValid(postcode, country)

そのためには、サポートされている国を表す「クラス」を用意し、それぞれを検証する方法を知る必要があります。現在、私はこうしてインターフェースを持っています:

public interface IPostcode {
    bool IsValid(string postcode);
}

と私は各国のためのクラスを持っている必要があります。

public class USA : IPostcode {
  public bool IsValid(string postcode) {
     // ... validate here
  }

ヘルパーメソッドは、国コードに基づいて関連するIPostcodeを選択します。

問題は、状態やプロパティを持たないクラス、つまりstaticの場合にはるかに優れたメソッドをインスタンス化する必要があるのは間違っていると感じていることです。しかし、もちろん静的クラスはインターフェースを持つことができません。これによりよいパターンはありますか?

7
Quango

状態のないクラスがあることに問題はないと思います。クラスの動作が異なる(ポストコード検証ロジックが異なる)限り、それらは正当化されます。

あなたcould種類は静的メソッドで同じことを達成し、インターフェイスではなくデリゲートシグネチャを使用します(とにかくインターフェイスは単一の関数シグネチャしかないため)クラスを使用する方がより自然で簡単です。

5
JacquesB

インターフェイスのポイントは、複数の実装が同じ機能コントラクトを公開できるようにすることです。

IPostCodeの実装には状態がない場合があるため、メソッドを静的にすることでそれを強制しようとする傾向があります。データベースにヒットする実装があり、接続文字列などが必要な場合があります。

必要な場合でも、静的メソッドをラップできます

USA : IPostcode
{
    private static bool isValid(string postcode) {...}
    public bool IsValid(string postcode)
    {
        return isValid(postcode);
    }
}

または、他の場所で静的に宣言できるFuncを注入する

USA : IPostcode
{
    public USA(Func<string, bool> postcodeValidator) {...}
    public bool IsValid(string postcode)
    {
        return postcodeValidator(postcode);
    }
}

ただし、IPostCodeインターフェースを捨てないでください。これには多くの利点があり、特にテストで他の実装を使用できるようになります。標準の検証をダミーに置き換えたい場合

MockCountry : IPostcode
{
    public bool IsValid(string postcode)
    {
         return true;
    }
}

これは静力学だけでは不可能に近いでしょう

5
Ewan

次の提案は、個々の国のバリデーターが実行時にデータベースからデータをフェッチしない場合、つまり不変のデータにのみアクセスする場合にのみ使用してください。

それらがそうであると仮定すると、ここでの最も簡単な解決策は、インターフェイス実装を介してポリモーフィズムのアイデアを捨て、代わりに関数の辞書の使用に切り替えることです。

public static class Postcode
{
    private static readonly Dictionary<string, Func<string, bool>> Validators = 
        new Dictionary<string, Func<string, bool>>
        {
            ["USA"] : USAPostcode.IsValid,
            ["UK"] : UKPostcode.IsValid,
            ...
        };

    public static bool IsValid(string postcode, string country) =>
        Validators.TryGetValue(country, out var validator) 
        ? validator(postcode) 
        : throw new ArgumentException("No such country", nameof(country));
}

静的クラスはインターフェースをサポートしていませんが、静的メソッドはFunc<>デリゲートで使用するために理想的に設計されています。代わりにその方法で「一緒に配管」してください。

覚えておいてください:状態へのアクセスまたは変更に静的メソッドを使用しないでください。たとえば、実行時にデータベースからデータをフェッチします。そのため、バリデーターのセットとそのルールが不変である場合にのみ、このパターンを使用してください。

1
David Arno