1

试图破译一个合适的面向对象设计来实现。基本情况是您有一个 PstnNumber,它本质上是一个始终以 0 开头的 10 位电话号码(例如 0195550000)。如果缺少前导 0(例如 195550000),则引入了允许自动更正数字的规则。

开始编辑

我意识到最初的问题可能被误解了(感谢那些已经回答过的人),所以我进行了编辑以尝试更好地解释这个场景。

结束编辑

我开始玩一些初步的概念,然后想我会问是否有更合适的方法可以走或做其中一个就足够了(在某种程度上)?

概念一

public class PstnNumber
{
    public virtual string Number { get; set; }

    public PstnNumber() { }

    public PstnNumber(string number)
    {
        this.Number = number;
    }
}

public class AutoFormattedPstnNumber : PstnNumber
{
    public override string Number
    {
        get { return base.Number; }
        set { base.Number = value.PadLeft(10, '0'); }
    }

    public AutoFormattedPstnNumber() : base() { }

    public AutoFormattedPstnNumber(string number)
    {
        this.Number = number;
    }
}

概念 2(已删除)

概念三

public class PstnNumber
{
    public bool AutoCorrect { get; set; }

    private string number;
    public virtual string Number
    {
        get { return (this.AutoCorrect) ? this.number.PadLeft(10, '0') : this.number; }
        set { this.number = value; }
    }

    public PstnNumber() : this(false) { }

    public PstnNumber(bool autoCorrect)
    {
        this.AutoCorrect = autoCorrect;
    }

    public PstnNumber(string number) : this(false)
    {
        this.Number = number;
    }

    public PstnNumber(string number, bool autoCorrect) : this(autoCorrect)
    {
        this.Number = number;
    }
}

我认为概念 1 可能违反 Liskov 替换规则,因为子类改变了 Number 属性的行为(很高兴知道我是否误解了这一点)。

任何替代建议都会很高兴地收到。

4

8 回答 8

4

实例化对象时是否必须进行自动格式化?如果没有,那怎么办:

  public class PstnNumber
  {
    public virtual string Number { get; set; }
    public PstnNumber() { }
    public PstnNumber(string number) { this.Number = number; }
    public AutoFormatNumber { get { return Numer.PadLeft(10, '0'); } }
  }
于 2009-08-01T15:32:36.950 回答
3

避免 getter-setter-surprise
避免 getter 返回与 setter 接受的值不同的值。想象以下片段:

if (input.Value != current.Number)
{
   NumberChangedAgain = true;
   current.Number = input.Value;
}

一个简单的解决方案是使 PstnNumber 不可变:

 temp = PstnNumber.FromString(input.Value);
 if (temp != current) { ... }

规范格式
如果某些数据具有不同的表示形式,则将其存储在规范表示中会有很多好处,并将格式转换移动到工厂函数和 getter/formatter 中。例如,您无需测试空头与多头、多头与空头、空头与空头、多头与多头的比较。

不同方面
您是否需要区分“自动格式化”和“正常”数字,或者这仅仅是输入和输出的问题 - 即

  • 显示格式(短或长)是否取决于数字的输入方式或显示位置?
  • 0195550000==195550000吗?

如果可能的话,我宁愿将两个类合二为一(即当“输入时可以忘记带或不带 0”):

public class PstnNumber 
{  
   private string m_number; // always in long format
   public static PstnNumber(string s)  { ... } // accepts short and long form

   public string Number { get { return m_number; } }
   public string AutoFormatted { { get { ... } }
}

否则我会选择选项 3,但始终将长格式存储在 m_number 中。

于 2009-08-01T16:32:34.740 回答
1

你有一个非常充分的理由让你的二传手不让你的成员进入决赛吗?如果不是,这可能是比三者之间的任何其他变化更大的问题。

所以我会选择无状态的#3,这意味着使数字成为最终数字并摆脱 autoFormat 变量。

为简单起见,我只有一个 getNumberRaw 和 getNumberFormatted

更好的是,您可以使用 getNumberRaw 和 getNumber(formatType) 其中 formatType 实际上包含格式化数字的代码,因为格式将来可能会再次更改,并且将格式化(视图)与您的电话号码(型号)结合起来并不是最佳的。

(PS / EDIT):电话号码可以更改的事实并不是拥有二传手的好理由!创建一个新的电话号码对象并替换旧的几乎总是可行的!

于 2009-08-01T17:47:28.803 回答
1

在选项 1 和选项 2 中,无论如何您都不会保留原始数字,从而使子类变得毫无价值(除非知道它在某些时候被自动格式化,这似乎不是有用的信息)。使这些选项更有用的替代方法是在 Get 而不是 Set 上进行格式化。

因此,选项 3 是这三个选项中的首选模式,但我还要问 - 为什么 PstnNumber 不能也简单地检测位数并相应地自动格式化?

于 2009-08-01T15:36:47.567 回答
1

如果您遵守规则 - 有一个说“每个例程(阅读课程)应该只做一件事并做好”。

据此,我将PstnNumber只保留数字,并创建某种生产正确数字的工厂。

在同一个类中做这两个意味着你正在编织域逻辑和表示。我更喜欢他们分开。

于 2009-08-01T15:40:29.450 回答
1

我会问为什么你的班级名称如此神秘。“号码”对我来说很清楚,“P”表示“电话”,但“stn”告诉我什么?一些额外的击键将使这个类更加自我记录。

我还会询问默认构造函数的逻辑,它不会将底层数据成员初始化为某个值。我认为如果可能的话,默认构造函数应该有一个合理的默认值。

我觉得选项1是矫枉过正。我不认为继承使这个模型更清晰或更好。我看不出它是如何破坏 Liskov 替换的,这要求您可以在任何需要基类的情况下使用子类。据我所知,这些方法映射为 1:1。Liskov是如何被侵犯的?

选项 2 表示这是两个没有关系的独立类。这对我来说似乎不对。

所有这些工作表明您的问题将要求您同时使用这两个类。您将遇到不需要前导零的情况以及其他情况。真的吗?或者你总是需要前导零?

我不在乎你的任何选择。我更喜欢接口或静态工厂,甚至更喜欢将您拥有的类修改为您建议的任何内容。感觉只是一个格式问题。您是否存储前导零的数字?如果不是,也许这只是一个视图问题。

于 2009-08-01T15:58:56.977 回答
0

我会使用一个更简单的版本,覆盖 ToString 方法,甚至创建一个 ToString 重载来接收bool指示数字应该被格式化的参数。

于 2009-08-01T23:33:34.657 回答
0

我不熟悉c#,但我会这样做:

public class PstnNumber {
  readonly string number;

  public PstnNumber(string number) {
    this.number = number;
  }

  public string getNumber() {
    return number;
  }

  static public PstnNumber createNumber(string number) {
    return new PstnNumber(number.PadLeft(10, '0'));
  }
}

当然,如果我知道 Properties 是如何工作的,我可能会做不同的事情:)

于 2009-08-01T16:01:07.613 回答