尝试破译适当的OO设计以实现。基本情况是你有一个PstnNumber,它本质上是一个10位数的电话号码,始终以0开头(例如0195550000)。如果前导0丢失(例如195550000),则引入规则以允许自动校正数字。
START EDIT
我意识到原来的问题可能被误解了(恭喜已经回答过那些已经回答的人),所以我编辑过来试着更好地解释这个场景。
结束编辑
我开始玩一些初步的概念然后想我会问是否有更合适的方法去做或者做其中一个就足够了(在某种程度上)?
概念1
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(已删除)
概念3
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;
}
}
我认为Concept 1可能违反了Liskov Substitution规则,因为子类改变了Number属性的行为(很高兴知道我是否误解了这一点)。
任何其他建议都会很愉快地收到。
答案 0 :(得分: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'); } }
}
答案 1 :(得分: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。例如,您不需要测试短与长,长与短,短与短,长与长的比较。
不同方面
你需要区分“autoformatted”和“normal”数字,还是仅仅是输入和输出的问题 - 即
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中。
答案 2 :(得分:1)
在选项1和选项2中,您无论如何都不保留原始数字,使子类毫无价值(除非知道它在某些时候是自动编码的,这似乎不是有用的信息)。使这些选项更有用的替代方法是格式化Get而不是Set。
因此,选项3是这三个选项中的首选模式,但我也会问 - 为什么PstnNumber还不能简单地检测数字位数和autoformat?
答案 3 :(得分:1)
如果你遵守规则 - 有一个说“每个例程(阅读课程)应该只做一件事并且做得好”。
根据我的意思,我会让PstnNumber
只保留数字,并创建一些产生正确数字的工厂。
在同一个类中执行这两个操作意味着您正在编织域逻辑和表示。我更喜欢将它们分开。
答案 4 :(得分:1)
我会问为什么你的班级名字如此神秘。 “数字”对我来说很清楚,而“P”表示“电话”,但是什么是“stn”告诉我的?一些额外的击键会使这个课程更加自我记录。
我还会询问默认构造函数的逻辑,它不会将基础数据成员初始化为某个值。我认为默认构造函数应尽可能具有合理的默认值。
我觉得选项1有点矫枉过正。我不认为继承使这个模型更清晰或更好。我没有看到它如何打破Liskov替换,它要求你可以在任何需要基类的情况下使用子类。就我所见,这些方法以1:1的比例映射。 Liskov是如何违反的?
选项2说这是两个没有关系的独立类。这对我来说似乎不对。
所有这些工作都表明您的问题需要您同时使用这两个类。您将遇到不需要前导零的情况以及其他情况。真的吗?或者你总是要求领先的零?
我不关心你的任何选择。我更喜欢接口或静态工厂,甚至可以修改你所建议的任何类。这感觉就像一个格式问题。您是否存储前导零的数字?如果没有,也许这只是一个观点问题。
答案 5 :(得分:1)
你是否真的有理由拥有一个二传手而不是你的会员?如果没有,这可能比三者之间的任何其他变化都要大。
所以我会选择无状态#3,这意味着将数字设为最终并取消autoFormat变量。
为简单起见,我只有一个getNumberRaw和getNumberFormatted
更好的是,您可以使用getNumberRaw和getNumber(formatType),其中formatType实际上包含格式化数字的代码,因为格式可能在将来再次更改,并且格式化(视图)与您的电话号码(模型)不是最佳的。
(PS / EDIT):只是电话号码可以改变的事实并不是设置二传手的好理由!创建一个新的电话号码对象并替换旧的电话号码对象几乎总是有效!
答案 6 :(得分: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'));
}
}
当然,如果我知道属性如何工作,我可能会采用不同的方式:)
答案 7 :(得分:0)
我会使用更简单的版本,覆盖ToString方法,甚至创建一个ToString重载,它接收bool
参数,指示应该格式化数字。