Щось у моїй роботі зараз виникає досить багато, - це те, що існує узагальнений процес, який має відбутися, але тоді непарна частина цього процесу повинна відбуватися дещо інакше залежно від значення певної змінної, і я не цілком впевнений, який найелегантніший спосіб впоратися з цим.
Я буду використовувати приклад, який у нас зазвичай є, який робить дещо інакше залежно від країни, з якою ми маємо справу.
Тож у мене клас, назвемо його 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 класів зі злегка різною логікою також почуваються незграбними.
В основному я думаю, що я потрапив у якийсь вузол OOP і не зовсім знаю хорошого способу розплутати його. Мені було цікаво, чи існує там схема, яка допомогла б цьому процесу?
if (country == "DEU")вас перевіряють if (config.ShouldRemovePunctuation).
country
PreProcessфункціонал, який можна було б реалізувати по-різному залежно від деяких країн,DetermineSeparatorможе бути там для всіх, і aPostProcess. Усі вони можуть бутиprotected virtual voidз типовою реалізацією, і тоді ви можете мати конкретнуProcessorsкраїну