Рефакторинг двух основных классов
Как бы вы реорганизовали эти два класса, чтобы абстрагироваться от сходства? Абстрактный класс? Простое наследство? Как бы выглядел рефакторированный класс (ы)?
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;
}
}
}
7 ответов
Если у вас нет веской причины для рефакторинга (потому что вы собираетесь добавить больше классов, подобных классам в ближайшем будущем), штраф за изменение дизайна для такого небольшого и надуманного примера может преодолеть выигрыш в обслуживании или накладных расходах в этом сценарии. В любом случае, здесь возможен дизайн, основанный на обобщенных и лямбда-выражениях.
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));
}
}
Зависит от того, что если они не собираются делать намного больше, я бы, вероятно, оставил их как есть - ИМХО разложить вещи, скорее всего, будет сложнее, в этом случае.
Это довольно простой вопрос, и для меня он ужасно пахнет домашним заданием.
Очевидно, вы можете увидеть общие моменты в коде, и я уверен, что вы можете попробовать сделать это самостоятельно, поместив такие вещи в суперкласс.
Вы можете объединить их в Locale
Класс, в котором хранятся как код языка, так и код региона, имеет средства доступа для региона и языка, а также одну функцию синтаксического анализа, которая также допускает строки типа "en_gb"...
Вот как я видел, как локали обрабатываются в разных рамках.
Эти двое, в том виде, в каком они есть, не будут хорошо проводить рефакторинг из-за статических методов.
Вы либо получите какой-то фабричный метод в базовом классе, который возвращает тип этого базового класса (который впоследствии будет нуждаться в приведении), либо вам понадобится какой-то дополнительный вспомогательный класс.
Учитывая количество дополнительного кода и последующее приведение к соответствующему типу, оно того не стоит.
- Создать базовый базовый класс (например,
AbstractCode<T>
) добавить абстрактные методы, такие как
protected T GetConstructor(string code);
переопределить в базовых классах, таких как
protected override RegionCode GetConstructor(string code) { return new RegionCode(code); }
Наконец, сделайте то же самое с
string GetIsoName(string code)
например,protected override GetIsoName(string code) { return new RegionCode(code).TowLetterISORegionName; }
Это будет рефакторинг обоих. Крис Кимптон поднимает важный вопрос о том, стоит ли это усилий.
Я уверен, что есть лучшее решение на основе дженериков. Но все же дал ему шанс.
РЕДАКТИРОВАТЬ: Как говорится в комментарии, статические методы не могут быть переопределены, поэтому один из вариантов будет сохранить его и использовать объекты 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);
}
}