Заявление об ограничении ответственности: я не являюсь экспертом, и люди могут не согласиться с некоторыми из моих мыслей здесь. Трудно дать прямой ответ, потому что это сильно зависит от того, что здесь находится за занавеской. Вероятно, есть также много "правильных" ответов, но все зависит от того, сколько информации нам здесь не хватает. Тем не менее, никто еще не ответил, и я подумал, что могу указать на некоторые вещи, которые могут направить вас в правильном направлении.
Удачи!
У вас есть доступ к Pluralsight? Быстрая покупка месяца стоит того, чтобы просто пройти через Encapsulation и SOLID. Один из моментов "а-а-а", которые у меня были, когда я это пережил, был о том, как взглянуть на сигнатуры ваших методов, чтобы помочь идентифицировать интерфейсы, которые вы могли бы извлечь, чтобы упростить код. Игнорируйте имена, просто посмотрите на параметры.
Я попытаюсь выполнить упражнение с кодом, который вы предоставили, но по ходу дела мне придется делать предположения, которые могут оказаться неверными.
На IFieldCleaner
у вас есть 3 метода с одинаковой сигнатурой:
void CleanAllPhoneFields(FileRow row, FileContents fc);
void MatchObscentities(FileRow row, FileContents fc);
void CleanEmailFields(FileRow row, FileContents fc);
Обратите внимание на то, что все эти методы абсолютно одинаковы. Это говорит о том, что вы можете извлечь один интерфейс с тремя реализациями:
interface IFieldCleaner {
void Clean(FileRow row, FileContents fc);
}
class PhoneFieldCleaner : IFieldCleaner { }
class ObscentitiesFieldCleaner : IFieldCleaner { }
class EmailFieldCleaner : IFieldCleaner { }
Теперь это красиво разделило обязанности по очистке этих полей на классы небольшого размера.
Теперь у вас есть пара других методов очистки:
string CleanPhoneNumber(string phoneNumber);
string CleanAddress(StringBuilder inputAddress);
Они очень похожи, за исключением того, что берется StringBuilder
, предположительно потому, что реализация заботится об отдельных строках? Давайте просто переключим его на string
и предположим, что реализация позаботится о разделении / синтаксическом анализе строк, тогда мы получим тот же результат, что и раньше - два метода с одинаковой сигнатурой:
string CleanPhoneNumber(string phoneNumber);
string CleanAddress(string inputAddress);
Итак, следуя нашей предыдущей логике, давайте также создадим интерфейс, связанный с очисткой строк:
interface IStringCleaner {
string Clean(string s);
}
class PhoneNumberStringCleaner : IStringCleaner { }
class AddressStringCleaner : IStringCleaner { }
Теперь мы разделили эти обязанности на их собственные реализации.
На данный момент у нас остался только один метод:
void CleanFile(FileContents fc);
Я не уверен, что делает этот метод. Почему это часть IAddressCleaner
? Итак, пока я оставлю это вне обсуждения - возможно, это способ прочитать файл, найти адрес, а затем очистить его, и в этом случае вы можете сделать это, вызвав наш новый AddressStringCleaner
.
Итак, давайте посмотрим, где мы находимся.
interface IFieldCleaner {
void Clean(FileRow row, FileContents fc);
}
class PhoneFieldCleaner : IFieldCleaner { }
class ObscentitiesFieldCleaner : IFieldCleaner { }
class EmailFieldCleaner : IFieldCleaner { }
interface IStringCleaner {
string Clean(string s);
}
class PhoneNumberStringCleaner : IStringCleaner { }
class AddressStringCleaner : IStringCleaner { }
Все это мне кажется похожим и чем-то пахнет. Основываясь на ваших исходных именах методов, таких как Очистить Все поля, похоже, что вы можете использовать цикл только для очистки определенных столбцов от FileRow
? Но почему также зависит от FileContents
? Опять же, я не вижу вашей реализации, поэтому не совсем уверен. Возможно, вы собираетесь передать необработанный файл или ввод базы данных?
Я также не вижу, где вы храните очищенные результаты - большинство ваших предыдущих методов возвращали void
, что означает, что вызов метода имеет некоторый побочный эффект (то есть это команда), тогда как пара методов вернула только чистая строка (запрос).
Поэтому я предполагаю, что общая цель состоит в том, чтобы очистить строки независимо от того, откуда они получены, и также где-то их сохранить. Если это так, мы можем еще больше упростить нашу модель:
interface IStringCleaner {
string Clean(string s);
}
class PhoneNumberStringCleaner : IStringCleaner { }
class AddressStringCleaner : IStringCleaner { }
class ObscenitiesStringCleaner : IStringCleaner { }
class EmailStringCleaner : IStringCleaner { }
Обратите внимание, что мы устранили необходимость в IFieldCleaner
, потому что эти очистители строк работают только с входной строкой, которую нужно очистить.
Теперь вернемся к исходному контексту - кажется, вы можете получить данные из файла и что в этих файлах могут быть строки? Эти строки содержат столбцы, значения которых нам нужно очистить. Нам также необходимо сохранить внесенные нами очищенные изменения.
Итак, судя по предоставленной вами услуге, я вижу несколько вещей, которые могут нам помочь:
IRepository
IFileHelper
IFileWriter
IFileParser
Я предполагаю, что мы намерены сохранить очищенные поля обратно - туда, где я не уверен, учитывая, что я вижу «Репозиторий», а затем также «FileWriter».
Неважно, мы знаем, что нам нужно в конечном итоге получить строки из полей, может быть, IFileParser
поможет с этим?
interface IFileParser {
FileContents ReadContents(File file);
FileRow[] ReadRows(FileContents fc);
FileField ReadField(FileRow row, string column);
}
Это может быть сложнее, чем должно быть --_ 21_ может позаботиться о сохранении значения поля, поэтому, по-видимому, вы могли бы собрать все это вместе, чтобы сформировать FileContents
для сохранения на диске.
Итак, теперь мы разделили наши конечные цели (чистый материал), откуда поступает ввод (файлы, база данных и т. Д.) И как мы его сохраняем (обратно в файлы, базу данных и т. Д.).
Теперь вы можете использовать свой сервис, чтобы составить этот поток по своему усмотрению. Например, вы сказали, что сейчас вызываете внешнюю программу для очистки адресов? Без проблем:
class ExternalAddressStringCleaner : IStringCleaner {
// depend on whatever you need here
public string Clean(string s) {
// call external program
return cleanString;
}
}
Теперь вы переключаетесь на хранимую процедуру? Ладно, тоже без проблем:
class DatabaseAddressStringCleaner : IStringCleaner {
// depend on database
DatabaseAddressStringCleaner(IRepository repository) {
}
string Clean(string s) {
// call your database sproc
return cleanString;
}
}
Трудно рекомендовать идеи для вашей службы, но возможно, вы можете разделить ее на отдельные более мелкие службы (FileReaderService
, FileCleaningService
и FileStoreService
) или упростить зависимости, которые вы принимаете.
Теперь, когда у вас есть только один интерфейс IStringCleaner
, вы можете просто объявить нужные вам очистители и поменять их / менять в любое время.
public FileCleanerService {
private IStringCleaner _addressCleaner;
private IStringCleaner _phoneCleaner;
private IStringCleaner _obscenityCleaner;
private IStringCleaner _emailCleaner;
ctor(IFileParser parser, /* deps */) {
_parser = parser;
_addressCleaner = new ExternalAddressStringCleaner(/* deps */);
_phoneCleaner = new PhoneStringCleaner();
_obscenityCleaner = new ObscenityStringCleaner();
_emailCleaner = new EmailStringCleaner();
}
public void Clean(FileContents fc) {
foreach(var row in _parser.ReadRows(fc)) {
var address = _parser.ReadField(row, "Address");
var phone = _parser.ReadField(row, "Phone");
var post = _parser.ReadField(row, "PostContent");
var email = _parser.ReadField(row, "Email");
// assumes you want to write back to the field?
// handle this however you want
address.Value = _addressCleaner.Clean(address.Value);
phone.Value = _phoneCleaner.Clean(phone.Value);
post.Value = _obscenityCleaner.Clean(post.Value);
email.Value = _emailCleaner.Clean(email.Value);
}
}
Я сделал много предположений относительно вашего процесса и кода, так что это, вероятно, намного сложнее, чем я предполагал. Не имея всей информации, сложно дать рекомендации, но есть еще базовые вещи, о которых вы можете рассуждать, просто глядя на интерфейсы и имена, и я надеюсь, что я это продемонстрировал. Иногда вам просто нужно посмотреть сквозь поверхность, чтобы увидеть единицы и нули за Матрицей, и тогда все это имеет смысл;)
Извиняюсь за длинный пост, но я полностью понимаю, откуда вы. Выяснение того, как провести рефакторинг, пугает, сбивает с толку, и, похоже, никто не может помочь. Надеюсь, это даст вам с чего начать при рефакторинге. Это непростая задача, но просто придерживайтесь некоторых простых рекомендаций и шаблонов, и ее, вероятно, будет намного проще поддерживать, в зависимости от приложенных вами усилий. И снова я абсолютно рекомендую PluralSight курс.
person
kamranicus
schedule
21.07.2016
IFieldCleaner
мог бы иметь составной очиститель (скажем,IEnumerable<ISingleFieldCleaner>
), у которого был бы список из 3 методов для очистки различных полей, а не 3 отдельных метода, которые у вас есть на данный момент.IFieldCleaner
должен будет перебирать этот список, вызывая каждый метод с соответствующими аргументами. Я не уверен, насколькоCleanPhoneNumber()
вписывается в эту схему ... - person Matthew Watson   schedule 08.06.2016MatchObscentities
не принадлежитIFieldCleaner
, поскольку он имеет низкую связь с другими операциями и с концепцией очистки поля. Также ясно, чтоFileRow
иFileContents
, вероятно, не подходящие абстракции дляFieldCleaner
. Я ожидал, что он будет работать с чем-то более абстрактным, например,Record
илиList<Record>
. - person plalx   schedule 09.06.2016IFieldCleaner
делает слишком много самостоятельно в соответствии с SRP, зависит от того, насколько взаимосвязан процесс очистки различных полей. Если процесс очистки разных полей может измениться независимо друг от друга, я бы сказал, чтоIFieldCleaner
в настоящее время имеет слишком много ответственности. Вместо этого вы можете реализоватьIRecordCleaner
, который объединяет столько стратегий очистки, сколько полей. Что касается службыReadFileService
, зачем ей нужна такая зависимость, какIFileWriter
? Чтение должно быть идемпотентной операцией. - person plalx   schedule 09.06.2016CleaningCompleted
, и подписчик на это событие отправил бы электронное письмо. Это устранило бы зависимость от почтовой службы. Если вы объясните общую проблему предметной области с помощью правил и поведения, я могу помочь вам с дизайном. - person plalx   schedule 09.06.2016