重構系列整理-搭配Resharper(三)

重構

一、Self Encapsulate Field

用途:在物件內部運算時候不要引用自己屬性變數,而將屬性變數楓裝成set和get方法,為程式所用,這樣設計對日後擴展很方便。

//前
public class Employee
{
    //Step1 Ctrl+R+S
    //Step2 選Encapsulate Field
    public int _salary;

    public void SetSalary(int salary)
    {
        _salary = salary;
    }

    public int CalculatePay()
    {
        return _salary;
    }
}
 //後
 public class Employee
 {
     public int Salary { get; set; }

     public void SetSalary(int salary)
     {
         Salary = salary;
     }

     public int CalculatePay()
     {
         return Salary;
     }
 }

二、Replace Data Value With Object

用途:在值物件中某個屬性,起初是一個數值(整數、字串、日期…等)隨者值擴展狀況,用一個物件來替換。

例如:客戶的這個物件當中有地址(省、市、郵遞區號、地址)後來擴展成地址物件。

    //前
    public class Customer
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string Province { get; set; }
        public string City { get; set; }
        public string Street { get; set; }

    }
    //後
    public class Customer
    {
       
        public string Id { get; set; }
        public string Name { get; set; }
        public CustomerAddress CustomerAddress { get; set; }
    }

    public class CustomerAddress
    {
        public string Address { get; set; }
        public string Province { get; set; }
        public string City { get; set; }
        public string Street { get; set; }
    }

三、Change Value to Reference

用途:當值物件某個屬性物件存在大量重複的物件時(大量訂單物件下的客戶物件都是同一個客戶),不要為每個值物件建立這個屬性物件,而是用引用方式來指定共同屬性的物件。

四、Change Reference to Value

用途:當值物件中某個引用物件存在大量重複或日後維護中隊引用物件的修改不同步時(如物件A和B引用物件R,但A和B的更新不同步,使其R變成R1和R2),須調整為屬性物件以降低系統複雜度。(Change Value to Reference反向操作)

五、Replace Array with Object

用途:用各種值雜亂的陣列換成有意義的各種屬型的值物件。

string[] person = new string[2];
person[0] = "Alice"; // Name
person[1] = "30";    // Age
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}
//EX:Person person = new Person("Alice", 30);

六、Duplicate Observed Data

用途:如果一個類既負責觀察資料變化,又負責顯示資料時,運用觀察者模式給予分離、這個過程就跟MVC模式一樣,M負責讀取資料、V負責展示資料、C負責完成對應操作。

七、Change Unidirectional Assoication to Bidirectional

用途:當值物件A有屬性物件B,但物件B沒有這個屬性物件A時,我們稱從A到B的單向關聯。

註記:如果員工物件有屬性部門物件,因此員工容易找到自己部門,但部門不容易獲得其下的所有員工,因此,為部門物件建立集合屬性物件員工,從而形成員工與部門的雙向關聯。

public class Department
{
    public string Name { get; set; }
    public List<Employee> Employees { get; private set; }

    public Department(string name)
    {
        Name = name;
        Employees = new List<Employee>();
    }

    public void AddEmployee(Employee employee)
    {
        if (!Employees.Contains(employee))
        {
            Employees.Add(employee);
            employee.Department = this; // 設定員工的部門
        }
    }
}
public class Employee
{
    public string Name { get; set; }
    public Department Department { get; set; }

    public Employee(string name)
    {
        Name = name;
    }
}
Department department = new Department("IT");
Employee employee = new Employee("John Doe");

department.AddEmployee(employee);

八、Change Bidirectional Association to Unidirectional

用途:當值物件A有個屬性物件B,而物件B中又有這個屬性物件A時,我們稱為A與B建立雙向關聯,雙向關聯有時候會帶來問題,變成一個死循環。

註記:例如部門與主管一對一關係,建立部門時會建立這個主管,但建立這個主管時,又建立這個部門,如此反複,直到系統崩潰,不要頻繁建立雙向關聯,而是改成單向關聯。

public class Department
{
    public string Name { get; set; }
    public Manager Manager { get; set; }

    public Department(string name)
    {
        Name = name;
    }

    public void AssignManager(Manager manager)
    {
        Manager = manager;
    }
}
public class Manager
{
    public string Name { get; set; }

    public Manager(string name)
    {
        Name = name;
    }
}
Department department = new Department("IT");
Manager manager = new Manager("John Doe");

department.AssignManager(manager);

九、Replace Magic Number with Symbolic Constant

用途:將程式一些有特殊意義的數字,如3.1415,改為常數PAI,並以數字的意義來命名這些常數。

 public class CirCle
 {
     public double area(double r)
     {
         //Step 1 Shift+Ctrl+R => 3.1415區塊選取
         //Step 2 Introduct Field
         return 3.1415 * r * r;
     }
 }
  public class CirCle
  {
      private static double PAI = 3.1415;

      public double area(double r)
      {
          //Step 1 Shift+Ctrl+R => 3.1415區塊選取
          //Step 2 Introduct Field
          return PAI * r * r;
      }
  }

十、Encapsulate Field

用途:在物件中,不要直接對客戶程式開放屬性變數,而是將封裝成set與get方法,將這些方法開放給客戶。

public class Person
{
    // 私有字段
    private string name;

    // 公共的getter和setter方法
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    // 其他類的成員...
}

class Program
{
    static void Main(string[] args)
    {
        Person person = new Person();
        
        // 使用setter設置name的值
        person.Name = "Alice";

        // 使用getter獲取name的值
        Console.WriteLine(person.Name);
    }
}

十一、Encapsulate Collection

用途:在一些物件中通過set方法直接向Clinet程式提供對集合物件的管理,這樣的設計不好,應封裝這些集合物件管理,而向Clinet程式提供add、remove…等方法。

using System.Collections.Generic;

public class Classroom
{
    // 私有的集合
    private List<string> students = new List<string>();

    // 公共方法來增加學生
    public void AddStudent(string name)
    {
        if (!string.IsNullOrWhiteSpace(name) && !students.Contains(name))
        {
            students.Add(name);
        }
    }

    // 公共方法來移除學生
    public void RemoveStudent(string name)
    {
        if (students.Contains(name))
        {
            students.Remove(name);
        }
    }

    // 公共方法來獲取學生列表的副本
    public List<string> GetStudents()
    {
        return new List<string>(students);
    }

    // 其他類的成員...
}

class Program
{
    static void Main(string[] args)
    {
        Classroom classroom = new Classroom();

        // 添加學生
        classroom.AddStudent("Alice");
        classroom.AddStudent("Bob");

        // 獲取學生列表
        foreach (var student in classroom.GetStudents())
        {
            Console.WriteLine(student);
        }

        // 移除學生
        classroom.RemoveStudent("Alice");

        // 再次獲取學生列表
        foreach (var student in classroom.GetStudents())
        {
            Console.WriteLine(student);
        }
    }
}

十二、Replace Record With Data Class

用途:由傳統的資料庫返回的結果記錄,轉換成一個簡單值物件。

十三、Replace Type Code with Class

用途:將程式中的類型程式替換成一個類別或列舉。

public class Employee
{
    public static int MANAGER = 0;
    public static int ENGINEER = 1;
    public static int INTERN = 2;

    public int Type { get; set; }
}
public enum EmployeeType
{
    Manager,
    Engineer,
    Intern
}
public class Employee
{
    public EmployeeType Type { get; set; }

    public Employee(EmployeeType type)
    {
        Type = type;
    }
}
var manager = new Employee(EmployeeType.Manager);
var engineer = new Employee(EmployeeType.Engineer);
var intern = new Employee(EmployeeType.Intern);

十四、Replace Type Code with Subclass

用途:將程式中類型代碼(員工類型:程式設計師、業務人員),替換成一個父類別(員工類別)下的多個子類別(程式設計師類別、業務人員類別)。

 

元哥的筆記