Thay thế Mã loại bằng Lớp (Từ tái cấu trúc [Fowler])


9

Chiến lược này liên quan đến việc thay thế những thứ như thế này:

public class Politician
{
    public const int Infidelity = 0;
    public const int Embezzlement = 1;
    public const int FlipFlopping = 2;
    public const int Murder = 3;
    public const int BabyKissing = 4;

    public int MostNotableGrievance { get; set; }
}

Với:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public class MostNotableGrievance
{
    public static readonly MostNotableGrievance Infidelity = new MostNotableGrievance(0);
    public static readonly MostNotableGrievance Embezzlement = new MostNotableGrievance(1);
    public static readonly MostNotableGrievance FlipFlopping = new MostNotableGrievance(2);
    public static readonly MostNotableGrievance Murder = new MostNotableGrievance(3);
    public static readonly MostNotableGrievance BabyKissing = new MostNotableGrievance(4);

    public int Code { get; private set; }

    private MostNotableGrievance(int code)
    {
        Code = code;
    }
}

Tại sao chính xác điều này lại thích hợp hơn để làm cho kiểu liệt kê, như vậy:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public enum MostNotableGrievance
{
    Infidelity = 0,
    Embezzlement = 1,
    FlipFlopping = 2,
    Murder = 3,
    BabyKissing = 4
}

Không có hành vi nào liên quan đến loại và nếu có, bạn sẽ sử dụng một loại tái cấu trúc khác, ví dụ: 'Thay thế Mã loại bằng các lớp con' + 'Thay thế có điều kiện bằng Đa hình'.

Tuy nhiên, tác giả giải thích lý do tại sao anh ta cau mày với phương pháp này (trong Java?):

Mã loại số, hoặc liệt kê, là một tính năng phổ biến của các ngôn ngữ dựa trên C. Với tên tượng trưng họ có thể khá dễ đọc. Vấn đề là tên tượng trưng chỉ là bí danh; trình biên dịch vẫn nhìn thấy số cơ bản. Kiểu trình biên dịch kiểm tra bằng cách sử dụng số 177 không phải là tên tượng trưng. Bất kỳ phương thức nào lấy mã loại làm đối số đều mong muốn một số và không có gì để buộc một tên tượng trưng được sử dụng. Điều này có thể làm giảm khả năng đọc và là một nguồn lỗi.

Nhưng khi cố gắng áp dụng tuyên bố này cho C #, tuyên bố này dường như không đúng: nó sẽ không chấp nhận một con số vì một phép liệt kê thực sự được coi là một lớp. Vậy đoạn mã sau:

public class Test
{
    public void Do()
    {
        var temp = new Politician { MostNotableGrievance = 1 };
    }
}

Sẽ không biên dịch. Vì vậy, việc tái cấu trúc này có thể được coi là không cần thiết trong các ngôn ngữ cấp cao mới hơn, như C #, hoặc tôi không xem xét điều gì?


var temp = new Politician { MostNotableGrievance = MostNotableGrievance.Embezzlement };
Robert Harvey

Câu trả lời:


6

Tôi nghĩ rằng bạn gần như đã trả lời câu hỏi của riêng bạn ở đó.

Đoạn mã thứ hai được ưa thích hơn mã đầu tiên vì nó cung cấp loại an toàn. Với phần đầu tiên, nếu bạn có một bảng liệt kê tương tự về Cá thì bạn có thể nói điều gì đó như

MostNotableGrievance grievance = Fish.Haddock;

và trình biên dịch sẽ không quan tâm. Nếu Fish.Haddock = 2 thì ở trên sẽ hoàn toàn tương đương với

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

nhưng rõ ràng là không thể đọc ngay lập tức như vậy.

Lý do liệt kê thường không tốt hơn là vì chuyển đổi ngầm định và từ int để lại cho bạn cùng một vấn đề chính xác.

Nhưng, trong C #, không có chuyển đổi ngầm định như vậy, vì vậy một enum là một cấu trúc tốt hơn để sử dụng. Bạn sẽ hiếm khi thấy một trong hai cách tiếp cận đầu tiên được sử dụng.


5

Nếu không có hành vi nào liên quan đến giá trị và không có thêm thông tin mà bạn cần lưu trữ cùng với nó ngôn ngữ không hỗ trợ chuyển đổi ngầm định thành số nguyên, tôi sẽ sử dụng một enum; đó là những gì họ đang ở đó.


1

Một ví dụ khác có thể giúp bạn có thể được tìm thấy trong Java hiệu quả (mục 30, nếu bạn muốn tìm kiếm nó). Nó là hợp lệ cho C # là tốt.

Giả sử bạn sử dụng hằng số int để mô hình các động vật khác nhau, giả sử rắn và chó.

int SNAKE_PYTHON=0;
int SNAKE_RATTLE=1;
int SNAKE_COBRA=2;

int DOG_TERRIER=0;
int DOG_PITBULL=1;

Giả sử đây là các hằng số và thậm chí có thể được định nghĩa trong các lớp khác nhau. Điều này có vẻ tốt với bạn và nó thực sự có thể giải quyết. Nhưng hãy xem xét dòng mã này:

snake.setType(DOG_TERRIER);

Rõ ràng, đây là một lỗi. Trình biên dịch sẽ không phàn nàn và mã sẽ thực thi. Nhưng con rắn của bạn sẽ không biến thành một con chó. Đó là bởi vì trình biên dịch chỉ nhìn thấy:

snake.setType(0);

Con rắn của bạn thực sự là một con trăn (và không phải là một con chó sục). Tính năng này được gọi là an toàn kiểu và nó thực sự là lý do tại sao bạn ví dụ mã

var temp = new Politician { MostNotableGrievance = 1 };

sẽ không biên dịch. MostNotableGrievance là MostNotableGrievance không phải là số nguyên. Với enums bạn có thể thể hiện điều này trong hệ thống loại. Và đó là lý do tại sao Martin Fowler và tôi nghĩ rằng việc liệt kê là tuyệt vời.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.