Бог объект - уменьшить связь с "главным" объектом
У меня есть объект с именем Parameters, который перебрасывается из метода в метод вниз и вверх по дереву вызовов через границы пакета. В нем около пятидесяти переменных состояния. Каждый метод может использовать одну или две переменные для управления своим выводом.
Я думаю, что это плохая идея, потому что я не могу легко понять, что метод должен функционировать, или даже что может произойти, если с определенной комбинацией параметров для модуля Y, который полностью не связан с моим текущим модулем.
Каковы хорошие методы уменьшения связи с этим божественным объектом или идеального устранения этого?
public void ExporterExcelParFonds(ParametresExecution parametres)
{
ApplicationExcel appExcel = null;
LogTool.Instance.ExceptionSoulevee = false;
bool inclureReferences = parametres.inclureReferences;
bool inclureBornes = parametres.inclureBornes;
DateTime dateDebut = parametres.date;
DateTime dateFin = parametres.dateFin;
try
{
LogTool.Instance.AfficherMessage(Variables.msg_GenerationRapportPortefeuilleReference);
bool fichiersPreparesAvecSucces = PreparerFichiers(parametres, Sections.exportExcelParFonds);
if (!fichiersPreparesAvecSucces)
{
parametres.afficherRapportApresGeneration = false;
LogTool.Instance.ExceptionSoulevee = true;
}
else
{
Звонящий сделает:
PortefeuillesReference pr = new PortefeuillesReference();
pr.ExporterExcelParFonds(parametres);
6 ответов
Во-первых, рискуя указать очевидное: передать параметры, которые используются методами, а не объект бога.
Это, однако, может привести к тому, что некоторым методам потребуется огромное количество параметров, потому что они вызывают другие методы, которые в свою очередь вызывают другие методы, и так далее. Вероятно, это послужило вдохновением для того, чтобы поместить все в объект бога. Я приведу упрощенный пример такого метода со слишком большим количеством параметров; Вы должны будете представить, что "слишком много" == 3 здесь:-)
public void PrintFilteredReport(
Data data, FilterCriteria criteria, ReportFormat format)
{
var filteredData = Filter(data, criteria);
PrintReport(filteredData, format);
}
Итак, вопрос в том, как мы можем уменьшить количество параметров, не прибегая к божественному объекту? Ответ состоит в том, чтобы избавиться от процедурного программирования и эффективно использовать объектно-ориентированный дизайн. Объекты могут использовать друг друга без необходимости знать параметры, которые использовались для инициализации их соавторов:
// dataFilter service object only needs to know the criteria
var dataFilter = new DataFilter(criteria);
// report printer service object only needs to know the format
var reportPrinter = new ReportPrinter(format);
// filteredReportPrinter service object is initialized with a
// dataFilter and a reportPrinter service, but it doesn't need
// to know which parameters those are using to do their job
var filteredReportPrinter = new FilteredReportPrinter(dataFilter, reportPrinter);
Теперь метод FilteredReportPrinter.Print может быть реализован только с одним параметром:
public void Print(data)
{
var filteredData = this.dataFilter.Filter(data);
this.reportPrinter.Print(filteredData);
}
Между прочим, этот вид разделения интересов и внедрения зависимостей полезен не только для устранения параметров. Если вы получаете доступ к объектам соавтора через интерфейсы, то это делает ваш класс
- очень гибкий: вы можете настроить FilteredReportPrinter с любой реализацией фильтра / принтера, которую вы можете себе представить
- очень тестируемый: вы можете передать имитированным соавторам готовые ответы и убедиться, что они использовались правильно в модульном тесте
Если все ваши методы используют один и тот же Parameters
Затем, возможно, это должна быть переменная-член класса с соответствующими методами, тогда вы можете передать Parameters
в конструктор этого класса, назначьте его переменной-члену, и все ваши методы могут использовать его, передавая его в качестве параметра.
Хороший способ начать рефакторинг этого класса богов - разделить его на более мелкие части. Найдите группы свойств, которые связаны, и разбейте их на свои собственные классы.
Затем вы можете вернуться к методам, которые зависят от Parameters
и посмотрим, сможете ли вы заменить его одним из созданных вами меньших классов.
Трудно дать хорошее решение без примеров кода и реальных ситуаций.
Запросить у каждого клиента необходимые параметры и ввести их?
Пример: каждый "объект", которому требуются "параметры", является "Клиентом". Каждый "Клиент" предоставляет интерфейс, через который "Агент конфигурации" запрашивает у Клиента его обязательные параметры. Затем агент конфигурации "внедряет" параметры (и только те, которые требуются клиенту).
Для параметров, которые определяют поведение, можно создать объект, который демонстрирует настроенное поведение. Затем клиентские классы просто используют экземпляр объекта - ни клиент, ни служба не должны знать, каково значение параметра. Например, для параметра, который сообщает, откуда следует читать данные, все FlatFileReader, XMLFileReader и DatabaseReader наследуют один и тот же базовый класс (или реализуют один и тот же интерфейс). Создание одного из них основано на значении параметра, затем клиенты класса считывателя просто запрашивают данные для экземпляра объекта считывания, не зная, поступают ли данные из файла или из БД.
Для начала вы можете разбить ваш большой класс ParametresExecution на несколько классов, по одному на пакет, которые содержат только параметры для пакета.
Другим направлением может быть передача объекта ParametresExecution во время создания. Вам не придется передавать его при каждом вызове функции.
Похоже, вы не применяете объектно-ориентированные (ОО) принципы в своем дизайне. Поскольку вы упоминаете слово "объект", я предполагаю, что вы работаете в какой-то парадигме ОО. Я рекомендую вам преобразовать ваше "дерево вызовов" в объекты, которые моделируют проблему, которую вы решаете. "Объект бога" - это определенно то, чего следует избегать. Я думаю, что вы можете упустить что-то фундаментальное... Если вы опубликуете несколько примеров кода, я смогу ответить более подробно.
(Я предполагаю, что это в среде Java или.NET). Преобразуйте класс в одноэлементный. Добавьте статический метод с именем "getInstance()" или что-то похожее на вызов, чтобы получить пакет "имя-значение" (и прекратите "протаскивать" его - см. Гл. 10 книги "Code Complete").
Теперь самая сложная часть. Предположительно, это в веб-приложении или в какой-либо другой среде, не относящейся к пакетным / однопоточным программам. Итак, чтобы получить доступ к нужному экземпляру, когда объект на самом деле не является истинным одноэлементным, вам нужно скрыть логику выбора внутри статического метода доступа.
В Java вы можете установить ссылку на локальный поток и инициализировать ее при запуске каждого запроса или подзадачи. Затем закодируйте метод доступа в терминах этого локального потока. Я не знаю, существует ли что-то аналогичное в.NET, но вы всегда можете подделать его с помощью словаря (Hash, Map), который использует текущий экземпляр потока в качестве ключа.
Это начало... (всегда есть разложение самого BLOB-объекта, но я построил фреймворк, в котором есть очень похожее полуглобальное хранилище значений)