访问对象链的最佳实践

时间:2013-10-17 12:43:39

标签: c# oop nhibernate

在我们的系统中,我们的域名只是一堆沼泽标准相关的poco,我们使用NHibernate和所有延迟加载等来保持和持久化对象数据

假设我在域中有结构。 (这是我在飞行中创建的一个纯粹的例子)

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Postcode { get; set; }
    public Region Region { get; set; }
}

public class Region
{
    public int Id { get; set; }
    public string Name { get; set; }
    public CountryCode CountryCode { get; set; }
}

public class CountryCode
{
    public string Name { get; set; }
    public string Code { get; set; }
    public CostCode CostCode { get; set; }
}

public class CostCode
{
    public string Name { get; set; }
    public string Description { get; set; }
}

现在说客户想要获取自己的CostCode.Name值,在Customer中有一个方法更好吗

    public string GetCountryCostName()
    {
        return Address.Region.CountryCode.CostCode.Name; 
    }

或者它会更好,这样做客户和其他对象具有获取信息的功能

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }

    public string GetCountryCostName()
    {
        return Address.GetCountryCostCodeName();
    }
}

public class Address
{
    public int Id { get; set; }
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Postcode { get; set; }
    public Region Region { get; set; }

    public string GetCountryCostCodeName()
    {
        return Region.GetCountryCostCodeName();
    }
}

public class Region
{
    public int Id { get; set; }
    public string Name { get; set; }
    public CountryCode Country { get; set; }

    public string GetCountryCostCodeName()
    {
        return Country.GetCostCodeName();
    }

}

public class CountryCode
{
    public string Name { get; set; }
    public string Code { get; set; }
    public CostCode CostCode { get; set; }

    public string GetCostCodeName()
    {
        return CostCode.Name;
    }
}

public class CostCode
{
    public string Name { get; set; }
    public string Description { get; set; }
}

现在用第一种方式,Nhibernate将创建一个单独的sql来保护它创建的所有代理,但第二种方式如果我在如何延迟加载与NHibernate一起使用它将创建许多较小的sql语句,因为函数被访问。那么哪种方式是OO设计和性能观点的最佳方法呢?

问候

CD

1 个答案:

答案 0 :(得分:2)

从SQL和“代码执行方式”的角度来看,这两种方法是相同的。 NHibernate不会为包含Address.Region.CountryCode.CostCode.Name的方法创建特殊覆盖。这需要在NHibernate上进行一些非常复杂的代码内省。相反,每个many-to-one属性都使用延迟加载代理对象进行设置。每当您访问代理对象上的非id属性时,NHibernate将确保该对象已从数据库初始化。

从设计的角度来看,你所询问的几乎是Law of Demeter的本质。得墨忒耳法则会说第二种选择,即每个阶级只与其直接邻居交谈,这种选择更好。但是,问题的每一方都有利弊。阅读维基百科文章了解更多详情。

我认为重点是尽量减少受给定更改影响的代码量。如果关于如何访问该值的某些内容必须在该链的中间发生变化,那么您只需要更改一半的类。但是,我们假设客户是唯一真正需要此信息的类。在这种情况下,我认为最好选择第一个选项,忽略德米特定律。是的,您正在向客户提供有关数据结构其余部分的深入信息,但如果该结构发生变化,您只需在Customer中更改一个方法,剩下的就是其他方法。

对于空值,特别是如果数据库允许这些值为null,则不希望将其留给异常处理。对选项#1执行此操作会更好:

public virtual string GetCountryCostName()
{
    if (Address == null || Address.Region == null || Address.Region.CountryCode == null || Address.Region.CountryCode.CostCode == null)
        return null;
    return Address.Region.CountryCode.CostCode.Name; 
}

对于选项#2:

public virtual string GetCostCodeName()
{
    if (Address == null)
        return null;
    return Address.GetCostCodeName();
}

// etc...

现在,如果所有这些关系都需要NOT NULL并由数据库中的外键强制执行,您可以选择跳过if (... == null)检查,因为在该场景中遇到空值确实会是一个例外情况,这就是例外情况。

P.S。我非常喜欢this answer