2009-10-16 63 views
8

我有一个对象称为参数,从方法降低到方法,并通过包边界调用树。它有大约五十个状态变量。每种方法可能使用一个或两个变量来控制其输出。上帝的对象 - 减少耦合到'主'对象

我认为这是一个糟糕的主意,因为我无法轻易地看到一个方法需要什么功能,甚至如果对于与我当前模块完全无关的模块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); 
+0

“参数”是一个配置对象吗? – 2009-10-16 20:25:56

+0

是的。用于让UI带有业务层可能需要的任何参数。 – 2009-10-16 20:28:41

回答

9

首先,冒着说明明显的风险:传递所使用的参数方法,而不是上帝的对象。

但是,这可能会导致某些方法需要大量参数,因为它们会调用其他方法,这些方法会依次调用其他方法,等等。这可能是将所有东西放在神物中的灵感。我将给出一个参数太多的这种方法的简单例子;你必须想象“太多” == 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与任何滤波器/打印机实现你能想象的
  • 很可测试:你可以通过与罐头模拟合作者并验证它们在单元测试中的使用是否正确
+1

最佳答案,在我看来。 – 2009-10-17 06:26:08

0

查询每个客户对他们所需要的参数,并注入他们?

示例:需要“参数”的每个“对象”都是“客户端”。每个“客户端”暴露一个接口,通过该接口“配置代理”查询客户端的所需参数。配置代理然后“注入”参数(并且只有客户端需要)。

+0

你可以展开这个,也许有一个例子吗?我认为我同意这一点,但我想确定。 – gn22 2009-10-16 20:31:20

+0

我不明白。 – 2009-10-16 23:32:59

+0

我已添加详细信息。 – jldupont 2009-10-16 23:52:32

1

如果你所有的方法都使用相同的Parameters类,那么也许它应该是一个类的成员变量,并在其中包含相关的方法,那么你可以将Parameters传递给这个类的构造函数,将它赋值给一个成员变量所有的方法都可以使用它作为参数传递它。

开始重构这个神类的一个好方法是将它分解成更小的块。查找相关的属性组,并将它们分解到他们自己的类中。

然后,您可以重新访问依赖于Parameters的方法,并查看是否可以使用您创建的其中一个较小的类来替换它。

很难给出一个好的解决方案,没有一些代码示例和真实世界的情况。

0

这听起来像你没有在你的设计中应用面向对象(OO)的原则。既然你提到了“对象”这个词,我认为你在某种面向对象的范式内工作。我建议你将你的“调用树”转换成模拟你正在解决的问题的对象。一个“神物”绝对是要避免的。我想你可能会错过一些基本的东西......如果你发布了一些代码示例,我可能会更详细地回答。

+0

是的,这是一个遗留系统,它写得很差,我同意。 – 2009-10-16 20:38:28

+1

请发表一个代码示例...你如何处理这种情况取决于细节。 – SingleShot 2009-10-16 20:40:22

-2

(我假设这是在Java或.NET环境中)将类转换为单例。添加一个名为“getInstance()”的静态方法或类似的调用来获取名称值包(并停止“tramping”它 - 参见第10章“代码完成”一书)。

现在很难的部分。据推测,这是在一个网络应用程序或其他非批次/单线程环境。因此,当对象不是真正的单例时,要访问正确的实例,必须隐藏静态访问器内的选择逻辑。

在java中,您可以设置一个“线程本地”引用,并在每个请求或子任务启动时对其进行初始化。然后,根据该线程本地对访问器进行编码。我不知道.NET中是否存在类似的东西,但是你总是可以用一个使用当前线程实例作为关键字的Dictionary(Hash,Map)来伪造它。

这是一个开始......(总是会分解blob本身,但是我构建了一个框架,其中有一个非常类似的半全球价值存储区)

+3

我试图避免有一个单身人士。我实际上不喜欢单身人士,因为我曾经见过它。在这里使用它会退后一步。 – 2009-10-16 23:59:47

+0

-1你需要threadlocal来减少“singleton”的“全局性”这一事实表明你不应该首先使用全局变量。无论如何,OP可能会谈论单线程winforms应用程序。 – 2009-10-17 02:35:49

0

对于支配行为的参数,可以实例化一个展示配置行为的对象。然后,客户端类简单地使用实例化的对象 - 客户端和服务都不必知道参数的值是什么。例如,一个告诉从哪里读取数据的参数,让FlatFileReader,XMLFileReader和DatabaseReader都继承相同的基类(或实现相同的接口)。基于参数的值来实例化其中的一个,然后reader类的客户端只需向实例化的reader对象请求数据,而不知道数据是来自文件还是来自数据库。

要开始您可以将您的大ParametresExecution类分成几个类,每个包只包含包的参数。

另一个方向可能是在施工时传递ParametresExecution对象。在每次函数调用时都不必传递它。