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 指出的那样,最好从数据库中获取这些值。

如果您想对开放/封闭原则非常严格,您可以使用策略设计模式 - 您将为每个国家/地区创建一个单独的具体策略对象。如果添加了一个新国家,您将为该国家创建一个新的 ConcreteStrategy 对象。这样您就可以为具有特殊规则的国家/地区添加逻辑,而无需触及您的原始代码。
但是,有特殊规则的国家可能很少,所以除非你真的不能在生产后更改代码,否则这就是过度工程。

于 2012-09-20T12:10:23.857 回答
1

Maybe something like this:

            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 回答
1

我可能会遵循 c2 Wiki 页面“ Switch statements 气味”中的那条建议:

使用数据库或 TableOrientedProgramming 有时是适当的“修复”,而不是多态性。例如,商店产品分类最好在具有多对多类别表的数据库中处理,而不是案例语句。

你可以有类似的东西:

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 回答
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 回答