cultureinforegioninfo

Refactoring two basic classes


How would you refactor these two classes to abstract out the similarities? An abstract class? Simple inheritance? What would the refactored class(es) look like?

public class LanguageCode
{
    /// <summary>
    /// Get the lowercase two-character ISO 639-1 language code.
    /// </summary>
    public readonly string Value;

    public LanguageCode(string language)
    {
        this.Value = new CultureInfo(language).TwoLetterISOLanguageName;
    }

    public static LanguageCode TryParse(string language)
    {
        if (language == null)
        {
            return null;
        }

        if (language.Length > 2)
        {
            language = language.Substring(0, 2);
        }

        try
        {
            return new LanguageCode(language);
        }
        catch (ArgumentException)
        {
            return null;
        }
    }
}

public class RegionCode
{
    /// <summary>
    /// Get the uppercase two-character ISO 3166 region/country code.
    /// </summary>
    public readonly string Value;

    public RegionCode(string region)
    {
        this.Value = new RegionInfo(region).TwoLetterISORegionName;
    }

    public static RegionCode TryParse(string region)
    {
        if (region == null)
        {
            return null;
        }

        if (region.Length > 2)
        {
            region = region.Substring(0, 2);
        }

        try
        {
            return new RegionCode(region);
        }
        catch (ArgumentException)
        {
            return null;
        }
    }
}

Solution

  • Unless you have a strong reason for refactoring (because you are going to add more classes like those in near future) the penalty of changing the design for such a small and contrived example would overcome the gain in maintenance or overhead in this scenario. Anyhow here is a possible design based on generic and lambda expressions.

    public class TwoLetterCode<T>
    {
        private readonly string value;
    
        public TwoLetterCode(string value, Func<string, string> predicate)
        {
            this.value = predicate(value);
        }
    
        public static T TryParse(string value, Func<string, T> predicate)
        {
            if (value == null)
            {
                return default(T);
            }
    
            if (value.Length > 2)
            {
                value = value.Substring(0, 2);
            }
    
            try
            {
                return predicate(value);
            }
            catch (ArgumentException)
            {
                return default(T);
            }
        }
    
        public string Value { get { return this.value; } }
    }
    
    public class LanguageCode : TwoLetterCode<LanguageCode>  {
        public LanguageCode(string language)
            : base(language, v => new CultureInfo(v).TwoLetterISOLanguageName)
        {
        }
    
        public static LanguageCode TryParse(string language)
        {
            return TwoLetterCode<LanguageCode>.TryParse(language, v => new LanguageCode(v));
        }
    }
    
    public class RegionCode : TwoLetterCode<RegionCode>
    {
        public RegionCode(string language)
            : base(language, v => new CultureInfo(v).TwoLetterISORegionName)
        {
        }
    
        public static RegionCode TryParse(string language)
        {
            return TwoLetterCode<RegionCode>.TryParse(language, v => new RegionCode(v));
        }
    }