Что означает "Функция должна выполнять одно и только одно действие"?
Тезис из заголовка самим автором разъясняется, на сколько я знаю, так: "Если функция выполняет только те действия, которые находятся на одном уровне абстракции под объявленным именем функции, то эта функция выполняет одну операцию". Так как программировать я учусь недавно(2 месяца), то прошу знающих людей объяснить, как это должно выглядеть на простом примере:
Есть метод (вызываемый из метода Main), принимающий две строки и возвращающий третью строку, содержащую текст, который не встречается в исходных строках (на деле чуть сложнее, но для примера сойдет и такое объяснение). Внутри метода исходные строки разбиваются на два списка по определенному ключевому слову, затем определяется пересечение двух списков и поочередно удаляется из каждого. Далее создаются два словаря, они сравниваются по заголовкам, и их содержимое добавляется в StringBuilder, а затем возвращается из метода в виде одной строки.
По сути, этот метод выполняет одно действие - находит различия между строками. Но внутри он выполняет несколько разных действий, а потому меня интересует, как можно правильно оформить такой метод, и нужно ли пытаться это сделать. Мне это представляется следующим образом:
- Вместо одного метода их должно быть несколько. Первый вызывается из метода Main, а все остальные вызываются им самим;
- Все функции (создание списков, поиск пересечений, удаление пересечений, создание словарей и т.д.) должны быть оформлены в отдельные (дочерние) методы, вызываемые в оперделенном порядке из первого. В результате в первом методе будет содержаться только перечисление отдельных действий, а сами действия будут подробно расписаны в отдельных методах.
На словах выглядит хорошо, только вот первый метод принимает 4 аргумента, и вызов дочерних методов потребует передачи каждому из них этих аргументов. В результате получается ни сколько не короче и не чище... Выходом было бы не создавать отдельные методы, а оформить их в виде локальных функций первого - тогда они смогли бы использовать его аргументы. Либо вынести используемые аргументы в поля класса.
Ещё что-то слышал про передачу нескольких аргументов через параметр object, но как это правильно делается не знаю.
Вот такой пример. Прошу знающих людей подсказать, как правильно оформлять такой или подобный код, чтобы голова не начинала болеть после первого прочтения)) Заранее спасибо!
Метод Main:
static void Main(string[] args)
{
Console.Write("Введите путь к исходному файлу реестра: ");
string originalRegistry = File.ReadAllText(Console.ReadLine().Replace("\"", ""));
Console.WriteLine();
Console.Write("Введите путь к измененному файлу реестра: ");
string modifiedRegistry = File.ReadAllText(Console.ReadLine().Replace("\"", ""));
Console.WriteLine();
string paragraphSeparator = "[HKEY";
string keyValueSeparator = "\r\n";
File.WriteAllText("RegistryTweaks.reg", FindDifferencesBetweenFiles(originalRegistry, modifiedRegistry, paragraphSeparator, keyValueSeparator));
Console.WriteLine("Готово!");
Console.ReadKey();
}
Метод для анализа входных текстовых файлов:
static string FindDifferencesBetweenFiles(string originalRegistry, string modifiedRegistry, string paragraphSeparator, string keyValueSeparator)
{
var originalList = originalRegistry.Split(new string[] { paragraphSeparator }, StringSplitOptions.None).ToList();
var modifiedList = modifiedRegistry.Split(new string[] { paragraphSeparator }, StringSplitOptions.None).ToList();
var intersection = originalList.Intersect(modifiedList).ToList();
intersection.ForEach(item => originalList.RemoveAt(originalList.IndexOf(item)));
intersection.ForEach(item => modifiedList.RemoveAt(modifiedList.IndexOf(item)));
var originalDictionary = originalList.ToDictionary(item => paragraphSeparator + item.Substring(0, item.IndexOf(keyValueSeparator)), item => paragraphSeparator + item);
var modifiedDictionary = modifiedList.ToDictionary(item => paragraphSeparator + item.Substring(0, item.IndexOf(keyValueSeparator)), item => paragraphSeparator + item);
var builder = new StringBuilder("Windows Registry Editor Version 5.00\r\n\r\n");
modifiedDictionary.Values.ToList().ForEach(item => builder.Append(item));
foreach (string originalDictionaryKey in originalDictionary.Keys)
{
if (!modifiedDictionary.ContainsKey(originalDictionaryKey))
{
builder.Append(originalDictionary[originalDictionaryKey].Insert(1, "-"));
}
}
return builder.ToString();
}
Пример данных, с которыми работает программа:
[HKEY_LOCAL_MACHINE\HARDWARE\RESOURCEMAP\System Resources]
[HKEY_LOCAL_MACHINE\HARDWARE\RESOURCEMAP\System Resources\Loader Reserved]
".Raw"=hex(8):01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,0b,00,00,00,03,\
0e,00,00,00,00,00,00,00,02,00,00,00,00,00,03,01,00,00,00,20,10,00,00,00,00,\
00,00,70,09,00,00,00,00,0064,03,01,00,00,00,00,d0,09,00,00,00,00,00,00,30,00,\
[HKEY_LOCAL_MACHINE\HARDWARE\RESOURCEMAP\System Resources\Physical Memory]
".Translated"=hex(8):01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,09,00,00,\
00,03,01,00,00,00,10,00,00,00,00,00,00,00,c0,09,00,00,00,00,00,03,01,00,00,\
00,00,10,00,00,00,00,00,00,24640,00,00,00,00,00,00,03,01,00,00,00,30,10,00,00,\
00,50,c8,db,00,00,00,00,00,10,18,00,00,00,00,00,03,01,00,00,00,e0,d7,dc,00,\
00,00,00,00,20,28,02,00,00,00,00,07,01,00,02,00,00,00,00,01,00,00,00,00,38,\
1f,03,00,00,00,00
[HKEY_LOCAL_MACHINE\HARDWARE\RESOURCEMAP\System Resources\Reserved]
".Translated"=hex(8):01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,02,00,00,\
00,03,01,00,00,00,10,00,00,00,00,00,00,00,c0,09,00,00,00,00,00,03,01,00,00,\
00,30,10,00,00,00,00,00,00,60,09,00,00,00,00,00
Если ключ (строка, начинающаяся на "[HKEY" и до символа переноса "\r\n") отсутствует в измененном файле реестра, значит этот ключ вместе с содержимым нужно добавить в итоговый файл, добавив к нему "-" ("[-HKEY");
Если ключ вместе с содержимым* присуствует обоих анализируемых файлах без изменений, то он игнорируется (в выходной файл попасть не должен);
Если ключ присуствует в обоих анализируемых файлах, но содержимое отличается, то в итоговый файл нужно поместить этот ключ вместе с содержимым из измененного снимка реестра.
Ответы (1 шт):
Вот простое объяснение.
Метод что делает?
- Сплитит строку на части
- Создает 2 словаря
- Скармливает словари билдеру
Это можно подвести под одну общую стадию обработки данных, здесь именно логически разбивать метод нет смысла.
По стадиям обработки - 2 этапа: парсинг и сборка. Но разбивать был бы смысл, если бы промежуточные данные между этими этапами нужны были бы где-то ещё.
Итого, я рекомендую оставить как есть.
Раз уж написал ответ, вот немного ускоренная версия метода
static string FindDifferencesBetweenFiles(string originalRegistry, string modifiedRegistry, string paragraphSeparator, string keyValueSeparator)
{
var separator = new string[] { paragraphSeparator };
var original = originalRegistry.Split(separator, StringSplitOptions.None);
var modified = modifiedRegistry.Split(separator, StringSplitOptions.None);
var originalDictionary = original.Except(modified).ToDictionary(item => paragraphSeparator + item.Substring(0, item.IndexOf(keyValueSeparator)), item => item);
var modifiedDictionary = modified.Except(original).ToDictionary(item => paragraphSeparator + item.Substring(0, item.IndexOf(keyValueSeparator)), item => item);
var builder = new StringBuilder("Windows Registry Editor Version 5.00\r\n\r\n");
foreach (var item in originalDictionary.Values)
{
builder.Append(paragraphSeparator).Append(item);
}
var paragraphRemoveSeparator = paragraphSeparator.Insert(1, "-");
foreach (var originalDictionaryPair in originalDictionary)
{
if (!modifiedDictionary.ContainsKey(originalDictionaryPair.Key))
{
builder.Append(paragraphRemoveSeparator).Append(originalDictionaryPair.Value);
}
}
return builder.ToString();
}
Если же исходить из обработки структуры данных реестра системы, то я бы делал полную симуляцию базы реестра на основе какой-то БД типа SQLite, это не космически сложно. И работал уже бы с полностью детализированными данными, а не строками.
Скажем так, ваша реализация очень грубая и узконаправленная. Понимаю, что это сделано в первую очередь для производительности, но здесь уже вопрос в том - как именно вы собираетесь приложение развивать. Вообще можно начать и напрямую с реестром работать, это может оказаться ещё быстрее.