如何在域类中表达域逻辑?

时间:2013-02-24 13:39:08

标签: c# oop class-design business-logic

我正在尝试将带有spaghetti VBA代码的MS Access应用程序移植到C#和OOP,并且我很难找到将域逻辑放入域类的最佳方法。

我将使用Country类作为一个简单示例。它有三个具有不同业务规则的属性:

    创建国家/地区后,
  • CountryCode无法再更改,因为这会导致使用国家/地区的第三方应用出现问题
  • CountryName可以随时更改,无需任何基础逻辑或业务规则
  • IsoCode可以随时更改,但必须长度为2个字符 IsoCode实际上有更多的规则,但在这个例子中,我们假设“必须正好是2个字符”是唯一的规则,为了简单起见)

我创建了两个稍微不同的班级版本 我在面向对象编程方面缺乏经验,所以我需要帮助决定:

  • 我使用哪种方法有用吗?
  • 他们中的一个(或两个)是否有我看不到的问题?
  • 有不同的方式甚至更好吗?

我的两种方法现在对我都很好,但我不知道它们是否会在以后出现问题(有问题的应用程序已经使用了十年,并且可能会存在很长时间)。


版本1:

public class Country1
{
    public string CountryCode { get; private set; }
    public string CountryName { get; set; }
    public string IsoCode { get; private set; }

    public Country1(string countryCode, string countryName, string isoCode)
    {
        this.CountryCode = countryCode;
        this.CountryName = countryName;
        SetIsoCode(isoCode);
    }

    public void SetIsoCode(string isoCode)
    {
        if (isoCode.Length != 2)
        {
            throw new ArgumentException("must be exactly 2 characters!");
        }

        this.IsoCode = isoCode;
    }
}

版本2:

public class Country2
{
    public Country2(string countryCode, string countryName, string isoCode)
    {
        this.countrycode = countryCode;
        this.CountryName = countryName;
        this.isocode = isoCode;
    }

    private readonly string countrycode;
    private string isocode;

    public string CountryCode
    {
        get { return this.countrycode; }
    }

    public string CountryName { get; set; }

    public string IsoCode
    {
        get { return this.isocode; }
        set
        {
            if (value.Length != 2)
            {
                throw new ArgumentException("must be exactly 2 characters!");
            }

            this.isocode = value;
        }
    }
}

关于我为什么要问这个以及我想知道什么的更多背景知识:

我已经阅读了很多关于“正确的OOP方式”的不同意见 有人说你根本不应该暴露吸气剂和制定者。我理解为什么这对setter来说是一个坏主意,这就是为什么CountryCode只能从构造函数中设置的原因。

有人说,您应该使用GetXXXSetXXX方法,而不是使用任何getter和setter。我可以看到在某些情况下这是有意义的(例如,当您有多个需要一起设置的值时,带有多个参数的SetXXX方法)。
但通常有一些简单的值,例如我的例子中的CountryName,这是一个没有任何逻辑的“哑”值。当我在一个课程中有十个这样的东西时,我不想为每个课程创建GetXXXSetXXX方法。

然后有像IsoCode之类的东西,它与任何其他属性都没有任何关系(所以不需要使用SetXXX方法将它与另一个属性一起设置)。但它包含一些验证,所以我可以制作一个SetXXX方法,或者只是在setter中进行验证(并在出现错误时抛出异常)。

即使是通知调用者错误的最佳方法,也会抛出异常? Some say it's finesome say that you should throw exceptions only for "exceptional" cases 当有人输入无效的ISO代码时,IMO并不是特别优秀,但我应该如何向客户端获取发生错误的信息(包括人类可读的错误消息!!)?使用带有错误代码和ErrorMessage字符串属性的响应对象是否更好?

2 个答案:

答案 0 :(得分:2)

我个人认为属性和方法实现之间没有太大区别。当值可以独立设置时,我倾向于使用属性。当值以某种方式相互依赖时,我使用setter方法强制为模型提供多个值(例如,您设置整数x和y,y不能大于x)。

在视图模型中使用简单的验证逻辑(例如,必填字段,字段长度)非常有意义,因为这样可以将验证与用户界面更紧密地联系在一起(例如,您可以突出显示该字段填写后无效。)

我强烈推荐Eric Lippert撰写的这篇文章,说明什么样的条件成为抛出/捕获异常的好候选人:

http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

答案 1 :(得分:1)

我倾向于使用属性进行单值更改和SetXXX方法很少,因为我有一些规则涉及多个必须同时设置的值(就像Andy Skirrow所说)。

所有方法的

SetXXX方法都是一种常见的“Javaish”约定(通过它在很多语言中使用,没有像C#这样的setter和getter。)

人们试图将他们为某种语言学到的一些好的做法强加到他们能够使用的每种语言中,即使它不一定合适,或者语言有更好的选择。

尽量不要成为“OOP疯子”。这只会让你头部疼痛。 OOP不是宗教(甚至宗教也不应该有狂热分子恕我直言,但这是另一个故事)。

回到

这两种方法没有任何功能差异,将来也不会造成任何伤害。采用您填写代码更易读和愉快的方式。这将比“正确的OOP方式”更重要,因为许多人对“更好的”做事方式有自己的定义。

<强>验证

如果您正在使用某些结构或架构模式(如MVC),则可以使用数据注释属性来强制执行验证,并根据框架使用它来强制执行客户端验证。

请参阅@Andy Skirrows的链接答案。