2

我正在尝试将带有意大利面条 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 方式”的不同意见。
有人说你根本不应该暴露 getter 和 setter。我理解为什么这对设置器来说是个坏主意,这就是为什么CountryCode只能从构造函数中设置。

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

然后是类似 的东西IsoCode,它也与任何其他属性都没有联系(因此无需使用SetXXX方法将其与其他属性一起设置)。但它包含一些验证,所以我可以创建一个SetXXX方法,或者只是在 setter 中进行验证(并在出现问题时抛出异常)。

抛出异常甚至是通知调用者错误的最佳方式吗?有人说这很好有人说你应该只为“异常”情况抛出异常
IMO 当有人输入无效的 ISO 代码时,这并不是什么特别的情况,但是我应该如何向客户端获取发生错误的信息(包括人类可读的错误消息!!)?使用带有错误代码和ErrorMessage字符串属性的响应对象会更好吗?

4

2 回答 2

2

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

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

我强烈推荐 Eric Lippert 的这篇文章,关于什么样的条件适合抛出/捕获异常:

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

于 2013-02-24T13:50:49.267 回答
1

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

SetXXX一切的方法都是一个常见的“Javaish”约定(尽管它在许多没有像 C# 这样的 setter 和 getter 的语言中使用)。

人们试图将他们为某种语言学到的一些好的实践强加到他们可以使用的每一种语言中,即使它不一定很适合,或者该语言有更好的替代方案。

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

回到正题

这两种方法在功能上没有区别,将来也不会造成任何伤害。采取您填写的方式,使其更具可读性和令人愉悦的代码。这将比“正确的 OOP 方式”重要得多,因为许多人对“更好的方式”做事都有自己的定义。

验证

如果您正在使用诸如 MVC 之类的结构或体系结构模式,则可以使用数据注释属性来强制验证,并且根据框架,也可以使用它来强制客户端验证。

有关链接,请参阅@Andy Skirows 的答案。

于 2013-02-24T14:11:31.667 回答