如何修改代码以使其符合Demeter法则

时间:2010-04-19 11:23:50

标签: c# law-of-demeter

public class BigPerformance  
{  
    public decimal Value { get; set; }
}  

public class Performance  
{  
    public BigPerformance BigPerf { get; set; }
}  

public class Category    
{  
    public Performance Perf { get; set; }     
}

如果我打电话:

Category cat = new Category();  
cat.Perf.BigPerf.Value = 1.0;  

我认为这打破了Law of Demeter / Principle of Least Knowledge
如果是这样,如果我有大量的内部类属性,我该如何解决这个问题呢?

5 个答案:

答案 0 :(得分:9)

Martin Fowler最喜欢的一句话:

  

我希望它被称为   偶尔有用的建议   Demeter的

http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx

答案 1 :(得分:3)

如果您正在谈论Demeter法,例如“不要打电话给邻居的邻居”,您可以将其委托给其他做您想做的事情的方法。

从你的例子中我猜你要重置性能值或其他东西。您可以修改示例代码,以便它们固有地链接在一起:

Category cat = new Category();

cat.resetPerf();

代码将类似于此:

public class BigPerformance 
{
    //constructors 'n stuff

    public static decimal DEFAULT;

    public decimal Value {get; private set;}

    public void reset() {
        Value = BigPerformance.DEFAULT;
    }
}

public class Performance
{
    //constructors 'n stuff

    private BigPerformance BigPerf {get; set};

    public reset() {
        BigPerf.reset();
    }
}

public class Category
{
    // constructors 'n stuff

    public Performance Perf {get; private set;}

    public resetPerformance() {
        Perf.reset();
    }
}

这样Category类不需要知道如果重置值,以防默认值不同或将来更改类型。

就个人而言,如果变更风险很低,我会选择juharr's answer

答案 2 :(得分:1)

Category cat = new Category();  
cat.Perf.BigPerf.Value = 1.0;  

Category cat = new Category();  
cat.GetPerf().GetBigPerf().SetValue(1.0);  

如果维基百科的定义是正确的,那么它就违反了规则:

  

.. [M]对象O的方法M可能只有   调用以下方法   各种物体:

     
      
  • O本身
  •   
  • M的参数
  •   
  • 在M
  • 中创建/实例化的任何对象   
  • O的直接组件对象
  •   
  • 一个全局变量,可由O访问,范围为M
  •   
     

特别是,对象应该避免调用另一个方法返回的成员对象的方法

如果您担心3紧密耦合,则删除公共访问器并在Category上添加方法以设置值。然后将Performance和BigPerformance重构为私有成员。

答案 3 :(得分:1)

如果你始终牢记你的课程的可测试性并使用IoC,你会发现你不必担心LoD。

以这种方式看待它

  

我将如何测试Category我   不希望它自动创建   使用慢速的Performance   文件系统。我们通过一个   IPerformanceCategory   并取代实际的实施   使用虚拟Performance实例。

     

我如何测试Performance   我不希望它自动化   创建一个BigPerformance制作一个   连接到数据库。我们通过一个   IBigPerformance到...   Performance并替换实际   用虚拟实现   BigPerformance实例。
  ...
  你显然注意到了模式

您的代码将在

行中
BigPerformance BigPerf = new BigPerformance();
BigPerf.Value := 1.0;
Performance Perf = new Performance(BigPerformance);
Category cat = new Category(Performance);

(This would be retrieved from a factory.)

它看起来(并且在短期内可能是)更多的工作,但从长远来看,通过能够孤立地测试你的课程,这些好处将得到回报。

查看Misco Hevery's博客,了解此主题和其他主题。

答案 4 :(得分:0)

这并不违反得墨忒耳法,因为你正在使用公共合同。