简体   繁体   English

如何重构这个C#代码?

[英]How to refactor this C# code?

I'm working on a legacy code and I have seen a lot of code like this: 我正在研究遗留代码,我看到了很多像这样的代码:

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;
}

The PersonData class exists for setting the private fields in Person class in its constructor. PersonData类用于在其构造函数中设置Person类中的私有字段。 Other than that, the PersonData class introduces redundant code, as you can see now you have Name and Sex in both Person and PersonData class. 除此之外,PersonData类引入了冗余代码,正如您所看到的,现在您在Person和PersonData类中都有Name和Sex。

In my opion, this kind of design doesn't scale: now I have a new field "Age" to read, I have to add the "Age" property in two different places. 在我的观点中,这种设计不能扩展:现在我有一个新的字段“Age”来阅读,我必须在两个不同的地方添加“Age”属性。

Is this a valid design choice (given I have see a lot code like this in the legacy code)? 这是一个有效的设计选择(鉴于我在遗留代码中看到了很多这样的代码)?
How can I refactor this? 我怎么能重构这个?

EDIT: 编辑:

Those two classes are simplified version of the real code. 这两个类是真实代码的简化版。 So please forgive using string instead of enum for gender. 所以请原谅使用字符串而不是枚举性别。

In the real code the PersonData have more than 10 fields so as Person class. 在实际代码中,PersonData有超过10个字段,所以Person类。

Using a parameter object is a valid approach to take when using Constructor Injection and you start getting a large number of parameters in your constructor - however it is unnecessary when there are fewer parameters like you have. 在使用构造函数注入时,使用参数对象是一种有效的方法,并且您在构造函数中开始获取大量参数 - 但是当您拥有的参数较少时,这是不必要的。

Here is a suggestion: 这是一个建议:

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;
}

This makes the the class immutable . 这使得该类不可变

If it would be some sort of externally facing objects (unlike data transfer objects that seem to be in your case) you can consider fluent interfaces to build them out, it not going to decrease number of classes but will let you construct object in more fancy looking way along with better control of what is required and what is optional. 如果它是某种面向外部的对象(与您的情况似乎不同的数据传输对象),您可以考虑使用流畅的接口来构建它们,它不会减少类的数量,但会让您更加花哨地构造对象寻找方式以及更好地控制所需要的和可选的。

See posts tagged with fluent-interface if interested. 如果感兴趣,请参阅使用fluent-interface标记的帖子。 Ie Conditional Builder Method Chaining Fluent Interface : 条件生成器方法链接流畅的界面

var person = PersonBuilder
  .CreatePerson()
    .Named(reader.ReadString())
    .Sex(reader.ReadString())
    .Build()

One way would be instead of 一种方法是代替

public String Name { get; private set;}
public String Sex { get; private set;}

to expose a property of type PersonData 公开PersonData类型的属性

public class Person
{
    public PersonData PersonData { get; }
}

Also you can look at deriving Person from PersonData . 您还可以查看从PersonData派生Person

Get rid of PersonData and feed the Reader to the Constructor: 摆脱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; }
}

In general, I'd go back to the real-world (or business :)) system you are modelling. 一般来说,我会回到你正在建模的真实世界(或商业:)系统。 If the class matches something in that world, then it is fine. 如果班级匹配那个世界中的某些东西,那就没关系了。 If the class is purely an artifact of the programming system and also appears unnecessary, I'd toss it. 如果这个类纯粹是编程系统的一个工件而且看起来没必要,那我就把它扔掉。 Using the "data" class can also hide various problems that using an explicit parameter would catch. 使用“data”类还可以隐藏使用显式参数捕获的各种问题。 For example, when you add "age" how will you detect that all cases are found? 例如,当您添加“年龄”时,您将如何检测到所有案例都被发现? If you add it as a constructor parameter, you will get an error for every missing case. 如果将其添加为构造函数参数,则每个丢失的大小写都会出错。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM