Algo que aparece bastante no meu trabalho atual é que existe um processo generalizado que precisa acontecer, mas a parte estranha desse processo precisa acontecer de maneira um pouco diferente, dependendo do valor de uma determinada variável, e eu não sou tenho certeza de qual é a maneira mais elegante de lidar com isso.
Usarei o exemplo que geralmente temos, que está fazendo as coisas de maneira ligeiramente diferente, dependendo do país com o qual estamos lidando.
Então, eu tenho uma aula, vamos chamá-lo 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);
}
}
Exceto que apenas algumas dessas ações precisam acontecer para determinados países. Por exemplo, apenas 6 países exigem a etapa de capitalização. O caractere no qual dividir pode mudar dependendo do país. A substituição do acentuado 'e'
pode ser necessária apenas dependendo do país.
Obviamente, você poderia resolvê-lo fazendo algo assim:
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);
}
Mas quando você lida com todos os países possíveis do mundo, isso se torna muito complicado. E, independentemente disso, as if
instruções tornam a lógica mais difícil de ler (pelo menos, se você imaginar um método mais complexo que o exemplo), e a complexidade ciclomática começa a surgir rapidamente.
Então, no momento, estou fazendo algo assim:
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);
}
}
Manipuladores:
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; }
}
Do mesmo modo, não tenho certeza se gosto. A lógica ainda é um pouco obscurecida por toda a criação da fábrica e você não pode simplesmente olhar para o método original e ver o que acontece quando um processo "GBR" é executado, por exemplo. Você também acaba criando muitas classes (em exemplos mais complexos que isso) no estilo GbrPunctuationHandler
, UsaPunctuationHandler
etc ... o que significa que você precisa observar várias classes diferentes para descobrir todas as ações possíveis que podem ocorrer durante a pontuação tratamento. Obviamente, não quero uma classe gigante com um bilhão de if
declarações, mas igualmente 20 classes com lógica ligeiramente diferente também parecem desajeitadas.
Basicamente, acho que me envolvi em algum tipo de nó OOP e não conheço uma boa maneira de desenredá-lo. Fiquei me perguntando se havia um padrão lá fora, que ajudaria com esse tipo de processo?
if (country == "DEU")
você verificar if (config.ShouldRemovePunctuation)
.
country
uma string, em vez de uma instância de uma classe que modela essas opções?
PreProcess
funcionalidade, que pode ser implementada de maneira diferente com base em alguns países,DetermineSeparator
pode estar lá para todos eles e aPostProcess
. Todos eles podem serprotected virtual void
com uma implementação padrão, e então você pode ter específicasProcessors
por país