God object - decrease coupling to a 'master' object
我有一个名为参数的对象,它在调用树上下、上、下、跨包边界从一个方法传递给另一个方法。它有大约50个状态变量。每个方法可能使用一个或两个变量来控制其输出。
我认为这是个坏主意,因为我不容易看出一个方法需要什么样的功能,甚至不知道如果模块y的某些参数组合与我当前的模块完全无关,会发生什么。
有什么好的技术可以减少与这个上帝物体的耦合,或者理想地消除它?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 { |
打电话的人会这样做:
1 2 | PortefeuillesReference pr = new PortefeuillesReference(); pr.ExporterExcelParFonds(parametres); |
首先,冒着陈述显而易见的风险:传递方法使用的参数,而不是上帝对象。
然而,这可能会导致某些方法需要大量参数,因为它们调用其他方法,而其他方法反过来调用其他方法,等等。这可能是把所有东西都放在神的物体里的灵感。我将给出一个带有太多参数的这种方法的简化示例;您必须想象"太多"==3这里:—)
1 2 3 4 5 6 | public void PrintFilteredReport( Data data, FilterCriteria criteria, ReportFormat format) { var filteredData = Filter(data, criteria); PrintReport(filteredData, format); } |
所以问题是,我们如何减少参数的数量而不求助于上帝的物体?解决方法是摆脱程序化编程,充分利用面向对象的设计。对象可以彼此使用,而无需知道用于初始化其合作者的参数:
1 2 3 4 5 6 7 8 9 10 | // 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方法只能用一个参数实现:
1 2 3 4 5 | public void Print(data) { var filteredData = this.dataFilter.Filter(data); this.reportPrinter.Print(filteredData); } |
顺便说一句,这种关注点分离和依赖注入不仅有利于消除参数。如果您通过接口访问合作者对象,那么这将使您的类
- 非常灵活:您可以使用任何可以想象的过滤器/打印机实现来设置filteredreportprinter
- 非常可测试:您可以通过具有屏蔽响应的模拟合作者,并验证它们在单元测试中的使用是否正确。
如果您所有的方法都使用同一个
开始重构这个上帝类的一个好方法是将它分成更小的部分。查找相关的属性组,并将它们分解为自己的类。
然后您可以重新访问依赖于
如果没有一些代码示例和实际情况,很难给出一个好的解决方案。
对于指定行为的参数,可以实例化显示已配置行为的对象。然后,客户机类只使用实例化的对象——客户机和服务都不需要知道参数的值是什么。例如,对于一个告诉从何处读取数据的参数,让flatfilereader、xmlfilereader和databasereader都继承相同的基类(或实现相同的接口)。根据参数的值实例化其中一个,然后读卡器类的客户机只需要向实例化的读卡器对象请求数据,而不知道数据是来自文件还是来自数据库。
首先,您可以将大型的ParameterExecution类划分为几个类,每个包一个类,这些类只保存包的参数。
另一个方向可能是在构建时传递ParameteReseExecution对象。您不必在每次函数调用时都传递它。
听起来您没有在设计中应用面向对象(OO)原则。既然你提到了"对象"这个词,我想你是在某种OO范式中工作的。我建议您将"调用树"转换为对您正在解决的问题进行建模的对象。"上帝的物体"绝对是要避免的。我认为你可能遗漏了一些基本的东西…如果您发布一些代码示例,我可以更详细地回答。
查询每个客户机的所需参数并注入它们?
示例:每个需要"参数"的"对象"都是一个"客户机"。每个"客户机"都公开一个接口,"配置代理"通过该接口查询客户机所需的参数。然后,配置代理"注入"参数(并且只注入客户机所需的参数)。
(我假设这是在Java或.NET环境中)将类转换为单体。添加一个名为"getInstance()"的静态方法或类似于调用的方法来获取名称值包(并停止"践踏"它——参见"代码完成"一书的第10章)。
现在是最难的部分。可能是在Web应用程序或其他非批处理/单线程环境中。因此,要在对象不是真正的单例对象时访问正确的实例,必须将选择逻辑隐藏在静态访问器中。
在爪哇中,可以设置一个"线程本地"引用,并在每个请求或子任务启动时初始化它。然后,根据该线程本地对访问器进行编码。我不知道.NET中是否存在类似的东西,但您可以使用字典(哈希、映射)来伪造它,字典使用当前线程实例作为键。
这是一个开始…(blob本身总是有分解,但我构建了一个框架,其中有一个非常相似的半全局值存储)