SOLID 学习笔记 - 单一职责

  六大设计原则

  单一责任原则:

    一个类应该只做一件事,而且只做一件事。

    错误示例:

public class CsvFileProcessor
{
    public void Process(string filename)
    {
        TextReader tr = new StreamReader(filename);
        tr.ReadToEnd();
        tr.Close();

        var conn = new SqlConnection("server=(local);integrated security=sspi;database=SRP");
        conn.Open();

        string[] lines = tr.ToString().Split(new string[] {@"\r\l"}, StringSplitOptions.RemoveEmptyEntries);
        foreach( string line in lines)
        {
            string[] columns = line.Split(new string[] {","}, StringSplitOptions.RemoveEmptyEntries);
            var command = conn.CreateCommand();
            command.CommandText = "INSERT INTO People (FirstName, LastName, Email) VALUES (@FirstName, @LastName, @Email)";
            command.Parameters.AddWithValue("@FirstName", columns[0]);
            command.Parameters.AddWithValue("@LastName", columns[1]);
            command.Parameters.AddWithValue("@Email", columns[2]);
            command.ExecuteNonQuery();
        }
        conn.Close();
    }
}

 这个类做了三件事:

    1. 读取 CSV 文件

    2. 解析 CSV 文件

    3. 存储数据

  这样做如果数据验证和错误记录,该怎么办?该怎么对它进行单元测试。一般情况下我们使用代码重构修改此例子;

public class CsvFileProcessor
{
    public void Process(string filename)
    {
        var csvData = ReadCsv(filename);
        var parsedData = ParseCsv(csvData);
        StoreCsvData(parsedData);
    }

    public string ReadCsv(string filename)
    {
        TextReader tr = new StreamReader(filename);
        tr.ReadToEnd();
        tr.Close();
        return tr.ToString();
    }

    public string[] ParseCsv(string csvData)
    {
        return csvData.ToString().Split(new string[] { @"\r\l" }, StringSplitOptions.RemoveEmptyEntries);
    }

    public void StoreCsvData(string[] csvData)
    {
        var conn = new SqlConnection("server=(local);integrated security=sspi;database=SRP");
        conn.Open();
        foreach (string line in csvData)
        {
            string[] columns = line.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            var command = conn.CreateCommand();
            command.CommandText = "INSERT INTO People (FirstName, LastName, Email) VALUES (@FirstName, @LastName, @Email)";
            command.Parameters.AddWithValue("@FirstName", columns[0]);
            command.Parameters.AddWithValue("@LastName", columns[1]);
            command.Parameters.AddWithValue("@Email", columns[2]);
            command.ExecuteNonQuery();
        }
        conn.Close();
    }
}

我们在 ParseCsv() 方法中将 CSV 文件解析为行,但在 StoreCsvData() 方法中进行了额外的解析,以使每行成为列。

解决此问题的方法是使用存储每行数据的 ContactDTO

下一步是添加 DTO,但我会跳过一个步骤,并将每个方法分解为自己的类。

但我也会在这里提前考虑。如果数据未以 CSV 格式提供,该怎么办?它的XML或JSON或其他东西是什么?

你可以用接口来解决这个问题。

public interface IContactDataProvider
{
    string Read();
}
public interface IContactParser { IList<ContactDTO> Parse(string contactList); }
public interface IContactWriter { void Write(IList<ContactDTO> contactData); }
public class ContactProcessor { public void Process(IContactDataProvider cdp, IContactParser cp, IContactWriter cw) { var providedData = cdp.Read(); var parsedData = cp.Parse(providedData); cw.Write(parsedData); } }
public class CSVContactDataProvider : IContactDataProvider { private readonly string _filename; public CSVContactDataProvider(string filename) { _filename = filename; } public string Read() { TextReader tr = new StreamReader(_filename); tr.ReadToEnd(); tr.Close(); return tr.ToString(); } } public class CSVContactParser : IContactParser { public IList<ContactDTO> Parse(string csvData) { IList<ContactDTO> contacts = new List<ContactDTO>(); string[] lines = csvData.Split(new string[] { @"\r\l" }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) { string[] columns = line.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); var contact = new ContactDTO { FirstName = columns[0], LastName = columns[1], Email = columns[2] }; contacts.Add(contact); } return contacts; } } public class ADOContactWriter : IContactWriter { public void Write(IList<ContactDTO> contacts) { var conn = new SqlConnection("server=(local);integrated security=sspi;database=SRP"); conn.Open(); foreach (var contact in contacts) { var command = conn.CreateCommand(); command.CommandText = "INSERT INTO People (FirstName, LastName, Email) VALUES (@FirstName, @LastName, @Email)"; command.Parameters.AddWithValue("@FirstName", contact.FirstName); command.Parameters.AddWithValue("@LastName", contact.LastName); command.Parameters.AddWithValue("@Email", contact.Email); command.ExecuteNonQuery(); } conn.Close(); } } public class ContactDTO { public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } }

我们使用读取、解析和写入的通用方法名称,因为我们不知道将获得什么类型的数据。

现在,我们可以很容易地对这段代码进行单元测试。

我们还可以轻松修改解析代码,如果我们引入新的错误,它不会影响读取和写入代码。

另一个好处是,我们已经松散地耦合了实现。

所以,你有它。我们采用了相当常见的程序代码,并使用单一责任原则对其进行了重构。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值