Suy nghĩ và thực tiễn tốt nhất về các lớp và thành viên tĩnh [đã đóng]


11

Tôi rất tò mò về những suy nghĩ và thực tiễn tốt nhất trong ngành liên quan đến các thành viên tĩnh hoặc toàn bộ các lớp tĩnh. Có bất kỳ nhược điểm nào trong việc này, hoặc nó có tham gia vào bất kỳ mô hình chống nào không?

Tôi thấy các thực thể này là "các lớp / thành viên tiện ích", theo nghĩa là chúng không yêu cầu hoặc yêu cầu khởi tạo lớp để cung cấp chức năng.

Những suy nghĩ chung và thực tiễn tốt nhất của ngành về điều này là gì?

Xem các lớp ví dụ dưới đây và các thành viên để minh họa những gì tôi đang tham khảo.

// non-static class with static members
//
public class Class1
{
    // ... other non-static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

// static class
//
public static class Class2
{
    // ... other static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

Cảm ơn bạn trước!


1
Các bài viết MSDN trên lớp tĩnh và phương pháp tĩnh cung cấp một điều trị tốt đẹp.
Robert Harvey

1
@Robert Harvey: Mặc dù bài viết mà bạn tham chiếu là hữu ích, nhưng nó không cung cấp nhiều về mặt thực tiễn tốt nhất và một số cạm bẫy khi sử dụng các lớp tĩnh.
Bernard

Câu trả lời:


18

Nói chung, tránh trạng thái - đặc biệt là bất kỳ loại trạng thái tĩnh.

Tại sao?

  1. Thống kê gây rắc rối với đồng thời. Vì chỉ có một thể hiện, nó được chia sẻ một cách tự nhiên giữa các lần thực thi đồng thời. Những tài nguyên được chia sẻ này là nguyên nhân chính của lập trình đồng thời và thường không cần chia sẻ.

  2. Thống kê gây rắc rối với thử nghiệm đơn vị. Bất kỳ khung kiểm tra đơn vị nào có giá trị, muối sẽ chạy thử nghiệm đồng thời. Sau đó, bạn chạy vào # 1. Tệ hơn nữa, bạn làm phức tạp các bài kiểm tra của mình với tất cả các công cụ thiết lập / phân tích và tin tặc bạn sẽ rơi vào để thử và "chia sẻ" mã thiết lập.

Có rất nhiều điều nhỏ quá. Thống kê có xu hướng không linh hoạt. Bạn không thể giao tiếp với chúng, bạn không thể ghi đè chúng, bạn không thể kiểm soát thời gian xây dựng của chúng, bạn không thể sử dụng chúng tốt trong tổng quát. Bạn thực sự không thể phiên bản chúng.

Chắc chắn có sử dụng thống kê: giá trị không đổi làm việc tuyệt vời. Các phương thức thuần túy không phù hợp trong một lớp duy nhất có thể hoạt động tốt ở đây.

Nhưng nói chung, tránh chúng.


Tôi tò mò đến mức không có khả năng đồng thời bạn có nghĩa là. Bạn có thể mở rộng về điều đó? Nếu bạn có nghĩa là các thành viên có thể được truy cập mà không có sự phân biệt, điều đó có ý nghĩa hoàn hảo. Các trường hợp sử dụng của bạn cho thống kê là những gì tôi thường sử dụng chúng cho: giá trị không đổi và phương thức thuần túy / tiện ích. Nếu đó là trường hợp sử dụng tốt nhất và 99% cho thống kê, thì điều đó mang lại cho tôi một mức độ thoải mái. Ngoài ra, +1 cho câu trả lời của bạn. Thông tin tuyệt vời.
Thomas Stringer

@ThomasStringer - chỉ cần nhiều điểm tiếp xúc giữa các luồng / tác vụ có nghĩa là có nhiều cơ hội hơn cho các vấn đề tương tranh và / hoặc mất nhiều hiệu suất hơn khi thực hiện đồng bộ hóa.
Telastyn

Vì vậy, nếu một luồng hiện đang truy cập một thành viên tĩnh thì các luồng khác cần đợi cho đến khi luồng sở hữu giải phóng tài nguyên?
Thomas Stringer

@ThomasStringer - có thể, có thể không. Thống kê là (rất gần, trong hầu hết các ngôn ngữ) không khác với bất kỳ tài nguyên được chia sẻ nào khác về vấn đề này.
Telastyn

@ThomasStringer: Thật không may, nó thực sự tồi tệ hơn thế này, trừ khi bạn đánh dấu thành viên volatile. Bạn không nhận được sự đảm bảo nào từ mô hình bộ nhớ mà không có dữ liệu dễ bay hơi, do đó, việc thay đổi một biến trong một luồng có thể không phản ánh ngay lập tức hoặc hoàn toàn.
Phoshi

17

Nếu chức năng là "thuần túy" tôi thấy không có vấn đề gì. Một hàm thuần túy chỉ hoạt động trong các tham số đầu vào và cung cấp kết quả dựa trên đó. Nó không phụ thuộc vào bất kỳ nhà nước toàn cầu hoặc bối cảnh bên ngoài.

Nếu tôi nhìn vào ví dụ mã của riêng bạn:

public class Class1
{
    public static string GetSomeString()
    {
        // do something
    }
}

Hàm này không lấy bất kỳ tham số nào. Vì vậy, nó có thể không thuần túy (việc thực hiện thuần túy duy nhất của hàm này sẽ là trả về một hằng số). Tôi cho rằng ví dụ này không đại diện cho vấn đề thực tế của bạn, tôi chỉ đơn thuần chỉ ra rằng đây có lẽ không phải là một hàm thuần túy.

Hãy lấy một ví dụ khác:

public static bool IsOdd(int number) { return (number % 2) == 1; }

Không có gì sai với chức năng này là tĩnh. Chúng tôi thậm chí có thể biến điều này thành một chức năng mở rộng, cho phép mã máy khách trở nên dễ đọc hơn. Chức năng mở rộng về cơ bản chỉ là một loại đặc biệt của chức năng tĩnh.

Telastyn đề cập chính xác đồng thời là một vấn đề tiềm năng với các thành viên tĩnh. Tuy nhiên, vì chức năng này không sử dụng trạng thái chia sẻ, không có vấn đề tương tranh ở đây. Một ngàn luồng có thể gọi hàm này đồng thời mà không có bất kỳ vấn đề tương tranh nào.

Trong .NET framework, các phương thức mở rộng đã tồn tại khá lâu. LINQ chứa rất nhiều hàm mở rộng (ví dụ: Enumerable.Where () , Enumerable.First () , Enumerable.Single () , v.v.). Chúng ta không thấy những điều này là xấu, phải không?

Kiểm thử đơn vị thường có thể có lợi khi mã sử dụng trừu tượng có thể thay thế, cho phép kiểm thử đơn vị thay thế mã hệ thống bằng phép thử kép. Các hàm tĩnh ngăn cấm tính linh hoạt này, nhưng điều này chủ yếu quan trọng ở ranh giới lớp kiến ​​trúc, nơi chúng tôi muốn thay thế, ví dụ, lớp truy cập dữ liệu thực tế bằng lớp truy cập dữ liệu giả .

Tuy nhiên, khi viết một bài kiểm tra cho một đối tượng có hành vi khác nhau, tùy thuộc vào việc một số nào đó là số lẻ hay số chẵn, chúng ta không thực sự cần phải thay thế IsOdd()hàm bằng một triển khai thay thế. Tương tự như vậy, tôi không thấy khi nào chúng ta cần cung cấp một Enumerable.Where()triển khai khác cho mục đích thử nghiệm.

Vì vậy, hãy kiểm tra tính dễ đọc của mã máy khách cho chức năng này:

Tùy chọn a (với chức năng được khai báo là phương thức mở rộng):

public void Execute(int number) {
    if (number.IsOdd())
        // Do something
}

Tùy chọn b:

public void Execute(int number) {
    var helper = new NumberHelper();
    if (helper.IsOdd(number))
        // Do something
}

Hàm tĩnh (mở rộng) làm cho đoạn mã đầu tiên dễ đọc hơn nhiều và khả năng đọc rất quan trọng, vì vậy hãy sử dụng các hàm tĩnh khi thích hợp.

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.