【问题标题】:Conditionally Limit Property Access有条件地限制属性访问
【发布时间】:2010-12-10 14:47:44
【问题描述】:

有没有更好的方法来限制对 Occupation 和 Employer 属性的访问?

这个类只是为了收集一个人(潜在客户)的就业信息而设计的。 EmployedStatus 可以是 Employed、SelfEmployed、Unemployed、Retired 等...

我只希望这个类的用户能够设置 Employer 和 Occupation(如果该人确实被雇用)。

public class EmploymentInformation
{
    private const string _EmploymentStatusNotEmployedMessage = "Employment status is not set to employed";

    private string _occupation;

    private Company _employer;

    /// <summary>The person's employment status<example>Employed</example></summary>
    public EmploymentStatus EmploymentStatus { get; set; }

    /// <summary>The person's occupation<example>Web Developer</example></summary>
    public string Occupation
    {
        get
        {
            if (IsEmployed)
            {
                return _occupation;
            }
            throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
        }

        set
        {
            if (IsEmployed)
            {
                _occupation = value;
            }
            throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
        }
    }

    /// <summary>The person's employer</summary>
    public Company Employer
    {
        get
        {
            if (IsEmployed)
            {
                return _employer;
            }
            throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
        }

        set
        {
            if (IsEmployed)
            {
                _employer = value;
            }
            throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
        }
    }

    private bool IsEmployed
    {
        get
        {
            return EmploymentStatus == EmploymentStatus.Employed 
                || EmploymentStatus == EmploymentStatus.SelfEmployed;
        }
    }

    /// <summary>
    /// Constructor for EmploymentInformation
    /// </summary>
    /// <param name="employmentStatus">The person's employment status</param>
    public EmploymentInformation(EmploymentStatus employmentStatus)
    {
        EmploymentStatus = employmentStatus;
    }
}

【问题讨论】:

  • 为什么OccupationEmployer 不能只返回null?为什么在设置属性之一时检查IsEmployed?这不会阻止失业者就业吗?

标签: c# oop design-patterns


【解决方案1】:

如果没有设置值,简单地返回null 有什么问题吗?这是相当普遍的做法。如果Employer 不存在,则其值为空。为什么它是null 可能不相关。此外,强制在类本身的 ctor 中设置雇佣状态。

【讨论】:

  • 我认为他应该返回一个失业类型的公司。如果您确实需要知道 Employee 是否确实失业,只需检查类型 Unemployment。
  • @macke 如果他不为公司工作,他就没有公司;在“特殊”公司对象中包含解释是我更愿意远离的事情。
  • 我不知道,我认为所有公司都是“特殊的”,失业的员工与其他失业人员在一起。更进一步,我建议如果失业的人不再是雇员。虽然我通常更倾向于使用空/空对象而不是使用特殊类型 null,但每当我遇到这样的情况时,我就会开始认为模型可能是错误的,而不是语言受到限制,但我不能说肯定是因为我不太了解这个案子。
【解决方案2】:

强制开发人员按特定顺序设置属性是一种危险的设计:它会使界面产生误导并助长错误。

相反,考虑使 EmploymentInformation 对象不可变:

// Constructor for retired / unemployed people
public class EmploymentInformation(EmploymentStatus status) {}

// Constructor for self-employed people - we know their status
public class EmploymentInformation(string occupation) {}

// Constructor for people employed by others - we know their status
public class EmploymentInformation(string occupation, Company employer) {}

public bool IsEmployed { get; }
public string Occupation { get; }
public Company Employer { get; }

【讨论】:

  • 我喜欢这条路线,但它仍然不强制开发者使用正确的构造函数。
  • 在某种程度上它确实 - 它迫使他们搜索正确的构造函数。当没有任何属性设置器时,想要指定 Employer 的开发人员别无选择,只能寻找让他们这样做的构造函数。 还有一些模棱两可的情况:例如,如果用户提供了一个空雇主,第三个构造函数应该做什么?失败或指定自雇人士?但是应该可以为您的目的找出最合理的实现方式。
【解决方案3】:

首先,如果没有Employer,为什么可以构造EmploymentInformation的对象?

尽量不要让对象在无效状态下被构造。您可以使用Guard ClausesCode Contracts 在对象的构造函数中表达这些约束。

public class EmploymentInformation
{
    public EmoloymentInformation(Employer employerInstance)
    {
        if(employerInstance == null)
            throw new ArgumentNullException();
    }

其次,您可以使用Null Object 模式,这样您就不必抛出异常。只需为EmptyEmployer 创建适当的类并返回它们,如下所示。

        public Company Employer
            {
                get
                {
                    return IsEmployed ? _employer : Employer.Empty; 
// Employer.Empty is static property which return an instance of EmptyEmployer just like string.Empty.


    }

【讨论】:

  • EmploymentStatus 枚举还包含 Unemployed、Student、Retired 等的值...不过,我将研究应用代码合同,谢谢!
【解决方案4】:

一个新的答案:

鉴于该对象严格保存有关用户当前就业状态的数据,它仍然是错误的。

正如@Jeff Sternal 所说,您不应强制开发人员根据特定顺序分配参数。如果对象需要序列化/反序列化,您最终可能会遇到很多错误。

相反,您应该提供一个验证功能。 bool IsValid(); 之类的东西在调用该方法时执行业务逻辑验证以确保对象处于可接受的状态。如果不是,您可以让它简单地返回一个 false,抛出一个异常(请不要),或者让它返回一个状态代码,说明对象当前无效的原因。

通常,您将数据放入对象中,然后在持久化之前验证该对象是否良好。以上只是执行此操作的一种方法。其他包括拥有一个将逻辑与数据类完全分离的业务逻辑库(就我个人而言,我一直不明白你为什么要这样做,但很多人都信誓旦旦。

【讨论】:

    【解决方案5】:

    我在这方面没有任何经验,但我会考虑在代码合同中寻找。

    看看这些链接:

    http://social.msdn.microsoft.com/Forums/en/codecontracts/thread/1ca2d371-4b85-479d-9e00-64c84e372f02

    http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx

    您可以使用适合您的应用程序的“要求”来装饰您的属性。使用起来看起来很酷,而且它似乎已集成到 IDE 中。

    【讨论】:

      【解决方案6】:

      从逻辑的角度来看,这看起来是错误的。

      该对象名为“EmploymentInformation”,并有一个名为“EmploymentStatus”的属性

      这似乎允许就业信息涉及在职或长期雇员的情况;或者,考虑到工作经历。

      如果其中任何一个为真,那么在我看来,您可以拥有一个职业,但无论出于何种原因,您的就业状态都类似于“NotEmployed”。

      毕竟,让我们看看记录最初是在雇用就业状态的地方创建的。然后稍后状态更改为“NotEmployed”下一次你去加载你将丢失数据的对象。

      【讨论】:

      • 我在我的问题摘要中添加了以下解释:这个类只是为了收集一个人(潜在客户)的就业信息而设计的。 EmployedStatus 可以是 Employed、SelfEmployed、Unemployed、Retired 等...我只希望此类的用户能够设置 Employer 和 Occupation(如果该人确实受雇)。
      【解决方案7】:

      如果你是严格的,你可能会争辩说一个失业的人没有职业或雇主,所以一个人对象不应该有这些属性。这导致了这样的事情。

      class Person
      {
          public EmploymentStatus EmploymentStatus { get; set; }
      }
      
      class EmployedPerson : Person
      {
          public string Occupation { get; set; }
          public Company Employer { get; set; }
      }
      

      但在实践中,这种无情的对象模型使用起来会很麻烦,因为在实例化对象之前,您需要知道是否雇用了一个人。在就业和失业之间转换也很困难,因为您必须创建一个新对象并复制所有内容。

      临床区别不值得。我认为向失业人员询问他们的雇主是谁并让他们回答“我没有雇主”而不是一开始就无法提出问题同样正确,实际上更合乎逻辑。

      对我来说,这将是一个更灵活的 person 类。

      class Person
      {
          public Person()
          {
              this.EmploymentStatus = EmploymentStatus.Unemployed;
          }
      
          public void Hire(Company employer, string occupation)
          {
              this.Occupation = occupation;
              this.Employer = employer;
              this.EmploymentStatus = EmploymentStatus.Employed;
          }
      
          public void Fire()
          {
              this.Occupation = null;
              this.Employer = null;
              this.EmploymentStatus = EmploymentStatus.Unemployed;
          }
      
          public EmploymentStatus EmploymentStatus { get; private set; }
          public string Occupation { get; private set; }
          public Company Employer { get; private set; }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-12-23
        • 1970-01-01
        • 1970-01-01
        • 2018-11-25
        • 1970-01-01
        • 2014-04-20
        • 2017-05-28
        • 2016-12-07
        相关资源
        最近更新 更多