Algo que surge bastante en mi trabajo actual es que hay un proceso generalizado que debe suceder, pero luego la parte extraña de ese proceso debe suceder de manera ligeramente diferente dependiendo del valor de una determinada variable, y no estoy Estoy bastante seguro de cuál es la forma más elegante de manejar esto.
Usaré el ejemplo que usualmente tenemos, que es hacer las cosas de manera ligeramente diferente dependiendo del país con el que estemos tratando.
Entonces tengo una clase, llamémosla 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);
}
}
Excepto que solo algunas de esas acciones tienen que suceder en ciertos países. Por ejemplo, solo 6 países requieren el paso de capitalización. El personaje para dividirse puede cambiar según el país. Sustitución de lo acentuado'e'
solo puede ser necesario dependiendo del país.
Obviamente, podrías resolverlo haciendo algo como esto:
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);
}
Pero cuando se trata de todos los países posibles del mundo, eso se vuelve muy engorroso. Y a pesar de todo, las if
declaraciones hacen que la lógica sea más difícil de leer (al menos, si imagina un método más complejo que el ejemplo), y la complejidad ciclomática comienza a ascender bastante rápido.
Así que por el momento estoy haciendo algo como esto:
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);
}
}
Manejadores:
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; }
}
Lo que tampoco estoy seguro de que me guste. La lógica todavía está algo oscurecida por toda la creación de fábrica y no puede simplemente mirar el método original y ver qué sucede cuando se ejecuta un proceso "GBR", por ejemplo. También terminas creando muchas clases (en ejemplos más complejos que este) en el estilo GbrPunctuationHandler
, UsaPunctuationHandler
etc., lo que significa que tienes que mirar varias clases diferentes para descubrir todas las acciones posibles que podrían ocurrir durante la puntuación manejo. Obviamente no quiero una clase gigante con mil millonesif
declaraciones, pero igualmente 20 clases con una lógica ligeramente diferente también se siente torpe.
Básicamente, creo que me he metido en algún tipo de nudo OOP y no conozco una buena manera de desenredarlo. Me preguntaba si había un patrón que ayudaría con este tipo de proceso.
PreProcess
funcionalidad, que podría implementarse de manera diferente en función de algunos de los países,DetermineSeparator
puede estar disponible para todos ellos, y aPostProcess
. Todos ellos pueden tenerprotected virtual void
una implementación predeterminada, y luego puede tener una especificaciónProcessors
por paísif (country == "DEU")
comprobarif (config.ShouldRemovePunctuation)
.country
una cadena en lugar de una instancia de una clase que modela esas opciones?