3

特定の国の郵便番号が必須かどうかを、countryid提供されたコードに基づいて確認する必要があります。現在、私はswitchステートメントでこれを行っていますが、このコードは Open/Closed SOLID の原則を破っています。switchこのシナリオでを取り除く方法を知りたいです。

public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory {
    public bool IsPostCodeRequired(int countryId, string region)
    {
        switch (countryId) {
            case 1:     //UK
            case 12:    //Australia
            case 29:    //Brazil
            case 31:    //Brunei
            case 37:    //Canada
            case 56:    //Denmark
            case 105:   //Japan
            case 110:   //South Korea
            case 114:   //Latvia
            case 136:   //Moldova
            case 137:   //Monaco
            case 145:   //Netherlands
            case 165:   //Poland
            case 166:   //Portugal
            case 183:   //Slovak Republic (Slovakia)
            case 189:   //Spain
            case 196:   //Sweden
            case 197:   //Switzerland
            case 199:   //Taiwan Region
            case 213:   //Ukraine
            case 215:   //USA
            case 221:   //Vietnam
                return true;
            case 232:   //Ireland
                return region == "Dublin";
            default:
                return false;
        }
    }

}
4

4 に答える 4

3

ステートメントswitchは、整数をブール値に効果的にマップし、デフォルト値はfalseです。

したがって、この場合はDictionary<int,bool>、適切な値を持つ を作成するだけです。値はほぼ固定されているため、宣言で初期化できます。

Dictionary<int, bool> dict = new Dictionary<int, bool>() {
  {  1 /* UK */,        true  }, 
  { 12 /* Australia */, false } 
    ...etc...
};

@Nick が指摘するように、アイルランドの場合は、まだ追加のロジックが必要であることを意味するため、Dictionary を にし、メソッドprivateを介して答えにアクセスできるようにする必要がありますIsPostCodeRequired(int,strnig)

編集:
@JustinHarvey が指摘しているように、データベースからこれらの値を取得するのがおそらく最善でしょう。

Open/Closed Principle について非常に厳密にしたい場合は、 Strategyデザイン パターンを使用できます。国ごとに個別の ConcreteStrategy オブジェクトを作成します。新しい国が追加された場合、その国用に新しい ConcreteStrategy オブジェクトを作成します。そうすれば、元のコードに手を加えることなく、特別なルールを持つ国のロジックを追加できます。
ただし、特別なルールを持つ国の数はおそらく非常に少ないため、本番環境でコードを変更できない場合を除き、これはオーバーエンジニアリングです.

于 2012-09-20T12:10:23.857 に答える
1

私はおそらく、c2 Wiki ページの「Switch ステートメントの匂い」からのアドバイスに従います。

データベースまたは TableOrientedProgramming を使用することが、ポリモーフィズムではなく適切な「修正」である場合があります。たとえば、ストア製品の分類は、case ステートメントではなく、多対多のカテゴリ テーブルを持つデータベースで処理するのが最適です。

あなたは次のようなものを持つことができます:

public class Country 
{
  public List<Region> Regions { get; set; }

  public bool IsPostCodeRequiredByDefault { get; set; }
}

public class Region
{
  private bool? _isPostCodeRequired;

  public Country Country { get; set; }

  public bool IsPostCodeRequired 
  {
    get { return _isPostCodeRequired ?? Country.IsPostCodeRequiredByDefault; }
  }
}

これには、地域を単なる文字列ではなくファーストクラスのドメイン概念にすることで、二次的な「原始的な強迫観念」の匂いに対処するという利点もあります。

于 2012-09-20T14:48:30.350 に答える
1

多分このようなもの:

            private Dictionary<int, string> _dict;
            protected Dictionary<int, string> CountryDictionary
            {
                get
                {
                    if (_dict == null)
                    {
                        _dict = new Dictionary<int, string>();
                        _dict.Add(1, "UK");
                        _dict.Add(12, "Australia");
                        // and so on
                    }

                    return _dict;
                }
            }

            public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory
            {
                public bool IsPostCodeRequired(int countryId, string region)
                {
                    return CountryDictionary.ContainsKey(countryId);
                }
            }
于 2012-09-20T12:18:22.140 に答える
0

これを試して:

public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory
{
    public List<Func<int, string, bool>> Rules { get; private set; }

    public PostCodeVerifyMandatory()
    {
        Rules = new List<Func<int, string, bool>>();
    }

    public bool IsPostCodeRequired(int countryId, string region)
    {
        if(Rules == null)
            return false;

        return (Rules.Any(r => r(countryId, region)));
    }
}

ルールセットを使用する前に、ルールセットをロードする必要があります。

var simpleCountries = new List<int> 
                {
                    1,  // UK
                    12,  // Australia
                    29,  // Brazil
                    56,   // Brunei
                    //..
                    //..
                    215,   //USA
                    221   //Vietnam
                };

var postCodeVerifier = new PostCodeVerifyMandatory();

// 1 rule for simple countries
postCodeVerifier.Rules.Add((id, region) => simpleCountries.Contains(id)); 

// Special rule for Ireland
postCodeVerifier.Rules.Add((id, region) => id == 232 && region.Equals("Dublin")); 

var a = postCodeVerifier.IsPostCodeRequired(232, "Dublin");

または、完全にデータ駆動型にする(例として辞書を使用):

var countries = new Dictionary<int, string> 
                {
                    { 1, null },      // UK
                    { 12, null },     // Australia
                    { 29, null },     // Brazil
                    { 56, null },     // Brunei
                    //..
                    //..
                    { 215, null },    //USA
                    { 221, null },    //Vietnam
                    { 232, "Dublin" } // Ireland
                };

var postCodeVerifier = new PostCodeVerifyMandatory();

// 1 rule for all
postCodeVerifier.Rules.Add((id, region) => 
                              countries.ContainsKey(id) && 
                              (countries[id] ?? region) == region);
于 2012-09-28T12:19:50.380 に答える