문제

유사점을 추상화하기 위해 이 두 클래스를 어떻게 리팩토링하시겠습니까?추상 수업?단순 상속?리팩토링된 클래스는 어떤 모습일까요?

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;
        }
    }
}
도움이 되었습니까?

해결책

리팩터링에 대한 강력한 이유가 없는 한(가까운 미래에 클래스와 같은 더 많은 클래스를 추가할 예정이기 때문에) 이렇게 작고 인위적인 예제에 대한 디자인 변경으로 인한 페널티는 이 시나리오에서 유지 관리 또는 오버헤드의 이득을 극복할 것입니다.어쨌든 여기에는 일반 및 람다 표현식을 기반으로 한 가능한 디자인이 있습니다.

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));
    }
}

다른 팁

그들이 더 많은 작업을 수행하지 않을 경우에는 아마도 그대로 둘 것입니다. 이 경우 IMHO에서 항목을 제외하는 것은 더 복잡할 수 있습니다.

이것은 다소 간단한 질문이고 나에게는 마치 숙제처럼 지독한 냄새가 난다.

코드에서 공통적인 부분을 분명히 볼 수 있으며 이러한 항목을 슈퍼 클래스에 넣어 직접 시도해 볼 수 있다고 확신합니다.

어쩌면 그것들을 다음과 같이 결합할 수도 있습니다. Locale 언어 코드와 지역 코드를 모두 저장하는 클래스에는 지역 및 언어에 대한 접근자와 "en_gb"와 같은 문자열도 허용하는 하나의 구문 분석 함수가 있습니다.

이것이 바로 다양한 프레임워크에서 로케일이 처리되는 것을 본 방법입니다.

이 두 가지 방법은 정적 메서드 때문에 제대로 리팩토링되지 않습니다.

해당 기본 클래스의 유형을 반환하는 기본 클래스에 대한 일종의 팩토리 메서드(나중에 캐스팅이 필요함)로 끝나거나 일종의 추가 도우미 클래스가 필요합니다.

추가 코드의 양과 그에 따른 적절한 유형으로의 캐스팅을 고려하면 그럴 가치가 없습니다.

  1. 일반 기본 클래스를 만듭니다(예: AbstractCode<T>)
  2. 다음과 같은 추상 메서드를 추가하세요.

    protected T GetConstructor(string code);
    
  3. 다음과 같은 기본 클래스에서 재정의

    protected override RegionCode GetConstructor(string code)
    {
        return new RegionCode(code);
    }
    
  4. 마지막으로 동일한 작업을 수행합니다. string GetIsoName(string code), 예:

    protected override GetIsoName(string code)
    {
        return new RegionCode(code).TowLetterISORegionName;
    }
    

그러면 둘 다 리팩터링됩니다.Chris Kimpton은 노력이 그만한 가치가 있는지에 관해 중요한 질문을 제기합니다.

더 나은 제네릭 기반 솔루션이 있다고 확신합니다.그러나 여전히 시도해 보았습니다.

편집하다:의견에서 알 수 있듯이 정적 메서드는 재정의될 수 없으므로 이를 유지하고 TwoLetterCode 개체를 사용하여 캐스팅하는 것이 하나의 옵션이지만 다른 사람이 이미 지적했듯이 이는 다소 쓸모가 없습니다.

이건 어때?

public class TwoLetterCode {
    public readonly string Value;
    public static TwoLetterCode TryParseSt(string tlc) {
        if (tlc == null)
        {
            return null;
        }

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

        try
        {
            return new TwoLetterCode(tlc);
        }
        catch (ArgumentException)
        {
            return null;
        }
    }
}
//Likewise for Region
public class LanguageCode : TwoLetterCode {
    public LanguageCode(string language)
    {
        this.Value = new CultureInfo(language).TwoLetterISOLanguageName;
    }
    public static LanguageCode TryParse(string language) {
        return (LanguageCode)TwoLetterCode.TryParseSt(language);
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top