Что-то, что встречается довольно часто в моей текущей работе, заключается в том, что существует обобщенный процесс, который должен произойти, но тогда нечетная часть этого процесса должна происходить немного по-другому в зависимости от значения определенной переменной, и я не Я уверен, что это самый элегантный способ справиться с этим.
Я буду использовать пример, который у нас обычно есть, который делает вещи немного по-другому в зависимости от страны, с которой мы имеем дело.
Итак, у меня есть класс, давайте назовем его Processor
:
public class Processor
{
public string Process(string country, string text)
{
text.Capitalise();
text.RemovePunctuation();
text.Replace("é", "e");
var split = text.Split(",");
string.Join("|", split);
}
}
За исключением того, что только некоторые из этих действий должны произойти для определенных стран. Например, только 6 стран требуют шага капитализации. Персонаж для разделения может меняться в зависимости от страны. Замена акцентированного 'e'
может потребоваться только в зависимости от страны.
Очевидно, вы можете решить это, выполнив что-то вроде этого:
public string Process(string country, string text)
{
if (country == "USA" || country == "GBR")
{
text.Capitalise();
}
if (country == "DEU")
{
text.RemovePunctuation();
}
if (country != "FRA")
{
text.Replace("é", "e");
}
var separator = DetermineSeparator(country);
var split = text.Split(separator);
string.Join("|", split);
}
Но когда вы имеете дело со всеми возможными странами мира, это становится очень громоздким. И, тем if
не менее, операторы затрудняют чтение логики (по крайней мере, если вы представите более сложный метод, чем в примере), и цикломатическая сложность начинает довольно быстро нарастать.
Так что на данный момент я делаю что-то вроде этого:
public class Processor
{
CountrySpecificHandlerFactory handlerFactory;
public Processor(CountrySpecificHandlerFactory handlerFactory)
{
this.handlerFactory = handlerFactory;
}
public string Process(string country, string text)
{
var handlers = this.handlerFactory.CreateHandlers(country);
handlers.Capitalier.Capitalise(text);
handlers.PunctuationHandler.RemovePunctuation(text);
handlers.SpecialCharacterHandler.ReplaceSpecialCharacters(text);
var separator = handlers.SeparatorHandler.DetermineSeparator();
var split = text.Split(separator);
string.Join("|", split);
}
}
Обработчики:
public class CountrySpecificHandlerFactory
{
private static IDictionary<string, ICapitaliser> capitaliserDictionary
= new Dictionary<string, ICapitaliser>
{
{ "USA", new Capitaliser() },
{ "GBR", new Capitaliser() },
{ "FRA", new ThingThatDoesNotCapitaliseButImplementsICapitaliser() },
{ "DEU", new ThingThatDoesNotCapitaliseButImplementsICapitaliser() },
};
// Imagine the other dictionaries like this...
public CreateHandlers(string country)
{
return new CountrySpecificHandlers
{
Capitaliser = capitaliserDictionary[country],
PunctuationHanlder = punctuationDictionary[country],
// etc...
};
}
}
public class CountrySpecificHandlers
{
public ICapitaliser Capitaliser { get; private set; }
public IPunctuationHanlder PunctuationHanlder { get; private set; }
public ISpecialCharacterHandler SpecialCharacterHandler { get; private set; }
public ISeparatorHandler SeparatorHandler { get; private set; }
}
Что в равной степени я не уверен, что мне нравится. Логика все еще несколько скрыта при создании фабрики, и вы не можете просто посмотреть на оригинальный метод и посмотреть, что происходит, например, при выполнении процесса «GBR». Вы также в конечном итоге создаете много классов (в более сложных примерах, чем этот) в стиле GbrPunctuationHandler
и UsaPunctuationHandler
т. Д., Что означает, что вам нужно взглянуть на несколько разных классов, чтобы выяснить все возможные действия, которые могут произойти во время пунктуации обработки. Очевидно, я не хочу одного гигантского класса с миллиардом if
утверждений, но в равной степени 20 классов со слегка отличающейся логикой также чувствуют себя неуклюже.
По сути, я думаю, что завелся в какой-то ООП-узел и не знаю, как правильно его распутать. Мне было интересно, есть ли шаблон, который поможет с этим типом процесса?
if (country == "DEU")
вас проверить if (config.ShouldRemovePunctuation)
.
country
PreProcess
функциональность, которая может быть реализована по-разному в зависимости от некоторых стран,DetermineSeparator
может быть доступна для всех из них, и aPostProcess
. Все они могут бытьprotected virtual void
с реализацией по умолчанию, а затем вы можете иметь конкретную дляProcessors
каждой страны