Данные, которые часто изменяют, выделяют отдельно и называют конфигурацией. Благодаря отделению конфигурацию проще создавать, редактировать, хранить. Конфигурация может быть реализована обособленным программным кодом, таблицей в базе данных, json-файлом или бинарником. Формат хранения не так важен. Важно, что данные отделяются от кода, обрабатывающего эти данные устраняя опасность его повреждения, устраняя дублирование. Код становится более документируемым. Изменение конфигурации выполняется отдельно от кода по ее обработке и более простым способом.
Приведем простой пример применения шаблона «конфигурация». Мы имеем enum и условие вида:
1 2 3 4 5 6 7 8 |
if (anymalType == AnymalType.Monkey || anymalType == AnymalType.Cow || anymalType == AnymalType.Wolf || anymalType == AnymalType.Pig || anymalType == AnymalType.Leopard) { Console.WriteLine(“Кормит детенышей молоком»); } |
Выделим конфигурацию:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var mammals = new AnymalType[] { AnymalType.Monkey, AnymalType.Cow, AnymalType.Wolf, AnymalType.Pig, AnymalType.Leopard }; if (mammals.Contains(anymalType)) { Console.WriteLine(“Кормит детенышей молоком»); } |
В результате код стал самодокументированным.
На практике часто возникает ситуация, когда нужно перечислить млекопитающих, вывести на экран и т.п. С данной целью выделим переменную «Млекопитающие» в члены класса расширяющий enum AnymalType:
1 2 3 4 5 6 7 |
public static class AnymalType { public static bool IsMammals(this AnymalType anymalType) { return Mammals.Contains(anymalType); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public AnymalType[] Mammals { get { return new AnymalType[] { AnymalType.Monkey, AnymalType.Cow, AnymalType.Wolf, AnymalType.Pig, AnymalType.Leopard }; } } if(anymalType.IsMammals()) { Console.WriteLine("Кормит детенышей молоком"); } |
Рассмотрим более сложный пример ближе к реальным проектам. Часто в наличии схожие действия, ветвления, вызовы одних и тех же функций с разными аргументами. Например, рассмотрим фабричный метод создающий импортер по имени файла:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public class ImporterFabric : IImporterFabric private ILog _logger; public ImporterFabric(ILog logger) { _logger = logger; } public IImporter CreateImporter(string fileName) { IImporter importer = null; if (fileName.StartsWith("Наценка")) { importer = new ProfitImporter(); } else if (fileName.StartsWith("ПродажиВесСумма")) { importer = new SumWeightByDayImporter(); } else if (fileName.StartsWith("ПродажаПродукции_НастройкаПродажаПродукцииВозвраты")) { importer = new CauseReturnImproter(); } else if (fileName.StartsWith("ПродажаПродукции_НастройкаПродажаПродукцииДоВычетаВозвратов")) { importer = new BeforeReturnImproter(); } else if (fileName.StartsWith("ПросроченнаяЗадолженностьПокупателейПоКонтрагентам"))////// { importer = new PastDueAccountsImporterByContacts(); } ... else { _logger.AddError(String.Format("Импорт для файла {0} не предусмотрен", fileName)); } return importer; } } |
После выделения «Конфигурации» и применения лямбда-выражений получим код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
public class ImporterFabric : IImporterFabric { private ILog _logger; public ImporterFabric(ILog logger) { _logger = logger; } private static Dictionary<string, Func<IImporter>> ImportersMapping { get { return new Dictionary<string, Func<IImporter>>() { {"Наценка", () => new ProfitImporter()}, {"ПродажиВесСумма", () => new SumWeightByDayImporter()}, {"ПродажаПродукции_НастройкаПродажаПродукцииВозвраты", () => new CauseReturnImproter()}, {"ПродажаПродукции_НастройкаПродажаПродукцииДоВычетаВозвратов", () => new BeforeReturnImproter()}, {"ПросроченнаяЗадолженностьПокупателейПоКонтрагентам", () => new PastDueAccountsImporterByContacts()}, ... }; } } public IImporter CreateImporter(string fileName) { if (string.IsNullOrEmpty(fileName)) { throw new ArgumentException("Пустое имя файла. Задайте его", "fileName"); } const string pattern = @"(?<n>[А-Яа-я]+)(_[А-Яа-я]+)?"; var match = Regex.Match(fileName, pattern); if (!match.Success) { _logger.AddError(string.Format("Имя файла '{0}' не подходит под шаблон '{1}'", fileName, pattern)); return null; } var searchkey = ImportersMapping.ContainsKey(match.Value) ? match.Value : (ImportersMapping.ContainsKey(match.Groups[0].Value) ? match.Groups[0].Value : null); if (searchkey == null) { _logger.AddError(string.Format("Неожиданное имя файла '{0}'", fileName)); return null; } try { var creator = ImportersMapping[searchkey]; var importer = creator(); return importer; } catch (Exception e) { _logger.AddError(string.Format("При создании импортера '{0}' произошла ошибка: {1}", searchkey, e)); return null; } } } |