我正在研究遗留代码,我看过很多这样的代码:
public class Person
{
public Person(PersonData data)
{
this.Name = data.Name;
this.Gender = data.Gender ;
}
public String Name { get; private set;}
public String Gender { get; private set;}
}
public class PersonData
{
public String Name;
public String Gender;
}
public static Person ReadPerson(Reader reader)
{
PersonData data = new PersonData;
data.Name = reader.ReadString();
data.Gender = reader.ReadString();
Person p = new Person(data);
return p;
}
PersonData类用于在其构造函数中设置Person类中的私有字段。除此之外,PersonData类引入了冗余代码,正如您所看到的,现在您在Person和PersonData类中都有Name和Sex。
在我的观点中,这种设计不能扩展:现在我有一个新的字段“Age”来阅读,我必须在两个不同的地方添加“Age”属性。
这是一个有效的设计选择(假设我在遗留代码中看到很多代码)吗? 我怎么能重构这个?
修改
这两个类是真实代码的简化版。所以请原谅使用字符串而不是枚举用于性别。
在真实代码中,PersonData有超过10个字段,因此是Person类。
答案 0 :(得分:3)
在使用Constructor Injection时,使用参数对象是一种有效的方法,并且在构造函数中开始获取大量参数 - 但是当您拥有的参数较少时,这是不必要的。
这是一个建议:
public class Person
{
public Person(string name, string sex)
{
_name = name;
_sex = sex;
}
public string Name { get {return _name; }}
public string Sex { get {return _sex; }}
private readonly string _name, _sex;
}
这使得课程immutable。
答案 1 :(得分:1)
如果它是某种面向外部的对象(与您的情况似乎不同的数据传输对象),您可以考虑使用流畅的接口来构建它们,它不会减少类的数量,但会让您构造对象以更加花哨的方式,更好地控制所需要的和可选的。
如果感兴趣,请参阅标有fluent-interface的帖子。即Conditional Builder Method Chaining Fluent Interface:
var person = PersonBuilder
.CreatePerson()
.Named(reader.ReadString())
.Sex(reader.ReadString())
.Build()
答案 2 :(得分:0)
一种方法是代替
public String Name { get; private set;}
public String Sex { get; private set;}
公开PersonData
public class Person
{
public PersonData PersonData { get; }
}
另外,您可以查看从Person
中获取PersonData
。
答案 3 :(得分:0)
摆脱PersonData
并将Reader
提供给构造函数:
public sealed class Person
{
public Person(Reader reader)
{
this.Name = reader.ReadString();
this.Sex = reader.ReadString();
}
public string Name { get; private set; }
public string Sex { get; private set; }
}
答案 4 :(得分:0)
一般来说,我会回到你正在建模的现实世界(或商业:)系统。如果班级匹配那个世界中的某些东西,那就没关系了。如果这个类纯粹是编程系统的一个工件而且看起来没必要,那我就把它扔掉。使用“data”类还可以隐藏使用显式参数捕获的各种问题。例如,当您添加“年龄”时,您将如何检测到所有案例都被发现?如果将其添加为构造函数参数,则每个丢失的案例都会出错。