1. 程式人生 > 其它 >SOLID 學習筆記 - 單一職責

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

我們使用讀取、解析和寫入的通用方法名稱,因為我們不知道將獲得什麼型別的資料。

現在,我們可以很容易地對這段程式碼進行單元測試。

我們還可以輕鬆修改解析程式碼,如果我們引入新的錯誤,它不會影響讀取和寫入程式碼。

另一個好處是,我們已經鬆散地耦合了實現。

所以,你有它。我們採用了相當常見的程式程式碼,並使用單一責任原則對其進行了重構。