Mẫu C # để xử lý các chức năng miễn phí của Wikipedia một cách sạch sẽ, tránh túi tiện ích phong cách Helper


9

Gần đây tôi đã xem xét một vài lớp tĩnh "túi tiện ích" kiểu Helper nổi xung quanh một số cơ sở mã C # lớn mà tôi làm việc cùng, mọi thứ về cơ bản giống như đoạn trích rất cô đọng sau đây:

// Helpers.cs

public static class Helpers
{
    public static void DoSomething() {}
    public static void DoSomethingElse() {}
}

Các phương pháp cụ thể tôi đã xem xét là

  • hầu như không liên quan đến nhau,
  • không có trạng thái rõ ràng vẫn tồn tại trên các yêu cầu,
  • nhỏ, và
  • mỗi loại được tiêu thụ bởi một loạt các loại không liên quan.

Chỉnh sửa: Trên đây không có ý định là một danh sách các vấn đề bị cáo buộc. Đây là danh sách các đặc điểm chung của các phương pháp cụ thể mà tôi đang xem xét. Đó là bối cảnh để giúp câu trả lời cung cấp các giải pháp phù hợp hơn.

Chỉ với câu hỏi này, tôi sẽ gọi loại phương pháp này là GLUM (phương pháp tiện ích nhẹ nói chung). Ý nghĩa tiêu cực của "glum" là một phần dự định. Tôi xin lỗi nếu điều này đi qua như một sự chơi chữ ngu ngốc.

Ngay cả việc bỏ qua sự hoài nghi mặc định của riêng tôi về GLUM, tôi không thích những điều sau đây về điều này:

  • Một lớp tĩnh đang được sử dụng chỉ như một không gian tên.
  • Định danh lớp tĩnh về cơ bản là vô nghĩa.
  • Khi một GLUM mới được thêm vào, (a) lớp "túi" này sẽ bị chạm mà không có lý do chính đáng hoặc (b) một lớp "túi" mới được tạo ra (mà bản thân nó thường không phải là vấn đề; các lớp tĩnh thường chỉ lặp lại vấn đề không liên quan, nhưng với ít phương thức hơn).
  • Meta đặt tên là inescapably khủng khiếp, phi tiêu chuẩn, và thường trong nội bộ mâu thuẫn, cho dù đó là Helpers, Utilitieshoặc bất cứ điều gì.

Một mô hình hợp lý và đơn giản để tái cấu trúc điều này là gì, tốt nhất là giải quyết các mối quan tâm trên, và tốt nhất là với một cú chạm càng nhẹ càng tốt?

Tôi có lẽ nên nhấn mạnh: Tất cả các phương pháp tôi đang xử lý đều không liên quan đến nhau. Dường như không có một cách hợp lý nào để chia chúng thành các túi phương thức lớp tĩnh hơn nhưng vẫn có nhiều thành viên.


1
Giới thiệu về GLUMs ™: Tôi nghĩ rằng viết tắt cho "nhẹ" có thể được loại bỏ, vì các phương pháp nên gọn nhẹ bằng cách thực hiện theo các thực tiễn tốt nhất;)
Fabio

@Fabio Tôi đồng ý. Tôi đã bao gồm thuật ngữ này như một trò đùa (có lẽ không quá buồn cười và có thể khó hiểu) đóng vai trò viết tắt cho câu hỏi này. Tôi nghĩ "nhẹ" có vẻ phù hợp ở đây vì các cơ sở mã được đề cập là tồn tại lâu dài, di sản và hơi lộn xộn, tức là có nhiều phương pháp khác (thường không phải là các GUM này ;-) là "nặng". Nếu tôi có nhiều ngân sách hơn (ehem, any) vào lúc này, tôi chắc chắn sẽ có một cách tiếp cận tích cực hơn và giải quyết vấn đề sau.
William

Câu trả lời:


17

Tôi nghĩ rằng bạn có hai vấn đề liên quan chặt chẽ với nhau.

  • Các lớp tĩnh chứa các hàm không liên quan đến logic
  • Tên của các lớp tĩnh không cung cấp thông tin hữu ích về ý nghĩa / ngữ cảnh của các hàm được chứa.

Những vấn đề này có thể được khắc phục bằng cách chỉ kết hợp các phương thức liên quan logic vào một lớp tĩnh và di chuyển các lớp khác vào lớp tĩnh của riêng chúng. Dựa trên miền vấn đề của bạn, bạn và nhóm của bạn sẽ quyết định tiêu chí nào bạn sẽ sử dụng để tách các phương thức thành các lớp tĩnh có liên quan.

Mối quan tâm và giải pháp có thể

Các phương thức hầu hết không liên quan đến nhau - vấn đề. Kết hợp các phương thức liên quan dưới một lớp tĩnh và di chuyển các phương thức không liên quan đến các lớp tĩnh của riêng chúng.

Các phương thức không có trạng thái rõ ràng vẫn tồn tại trên các yêu cầu - không phải là vấn đề. Phương pháp phi trạng thái là một tính năng, không phải là một lỗi. Dù sao, trạng thái tĩnh rất khó kiểm tra và chỉ nên được sử dụng nếu bạn cần duy trì trạng thái trên các yêu cầu phương thức, nhưng có nhiều cách tốt hơn để làm điều đó như máy trạng thái và yieldtừ khóa.

Phương pháp là nhỏ - không phải là một vấn đề. - Phương pháp nên nhỏ.

Các phương thức được sử dụng bởi nhiều loại không liên quan - không phải là vấn đề. Bạn đã tạo một phương thức chung có thể được sử dụng lại ở nhiều nơi không liên quan.

Một lớp tĩnh đang được sử dụng chỉ như một không gian tên - không phải là vấn đề. Đây là cách các phương thức tĩnh được sử dụng. Nếu kiểu phương thức gọi này làm phiền bạn, hãy thử sử dụng các phương thức mở rộng hoặc using statickhai báo.

Định danh lớp tĩnh về cơ bản là vô nghĩa - vấn đề . Nó là vô nghĩa vì các nhà phát triển đang đặt các phương thức không liên quan vào nó. Đặt các phương thức không liên quan vào các lớp tĩnh của riêng chúng và đặt cho chúng các tên cụ thể và dễ hiểu.

Khi một GLUM mới được thêm vào, (a) lớp "túi" này sẽ bị chạm (nỗi buồn SRP) - không thành vấn đề nếu bạn làm theo ý tưởng rằng chỉ các phương thức liên quan đến logic nên ở trong một lớp tĩnh. SRPkhông bị vi phạm ở đây, vì nguyên tắc nên được áp dụng cho các lớp hoặc cho các phương thức. Trách nhiệm duy nhất của các lớp tĩnh có nghĩa là chứa các phương thức khác nhau cho một "ý tưởng" chung. Ý tưởng đó có thể là một tính năng hoặc chuyển đổi hoặc lặp lại (LINQ) hoặc chuẩn hóa dữ liệu hoặc xác thực ...

hoặc ít thường xuyên hơn (b) một lớp "túi" mới được tạo ra (nhiều túi hơn) - không thành vấn đề. Các lớp / lớp tĩnh / hàm - là các công cụ (nhà phát triển) của chúng tôi, chúng tôi sử dụng để thiết kế phần mềm. Đội của bạn có một số giới hạn cho việc sử dụng các lớp học? Giới hạn tối đa cho "nhiều túi" là gì? Lập trình là tất cả về bối cảnh, nếu giải pháp trong ngữ cảnh cụ thể của bạn, bạn kết thúc với 200 lớp tĩnh được đặt tên dễ hiểu, được đặt một cách hợp lý trong không gian tên / thư mục với hệ thống phân cấp logic - đó không phải là vấn đề.

Việc đặt tên meta không thể tránh khỏi khủng khiếp, không chuẩn và thường không nhất quán trong nội bộ, cho dù đó là Người trợ giúp, Tiện ích hay bất cứ điều gì - có vấn đề. Cung cấp tên tốt hơn mô tả hành vi dự kiến. Chỉ giữ các phương thức liên quan trong một lớp, cung cấp trợ giúp trong việc đặt tên tốt hơn.


Đây không phải là vi phạm SRP, nhưng rõ ràng là vi phạm Nguyên tắc Mở / Đóng. Điều đó nói rằng, tôi thường thấy Ocp gặp nhiều rắc rối hơn giá trị của nó
Paul

2
@Paul, Không thể áp dụng nguyên tắc Mở / Đóng cho các lớp tĩnh. Đó là quan điểm của tôi, rằng các nguyên tắc SRP hoặc OCP không thể được áp dụng cho các lớp tĩnh. Bạn có thể áp dụng nó cho các phương thức tĩnh.
Fabio

Được rồi, có một số nhầm lẫn ở đây. Danh sách các mục đầu tiên của câu hỏi không phải là danh sách các vấn đề bị cáo buộc. Đây là danh sách các đặc điểm chung của các phương pháp cụ thể mà tôi đang xem xét. Đó là bối cảnh để giúp câu trả lời cung cấp nhiều giải pháp áp dụng hơn. Tôi sẽ chỉnh sửa để làm rõ.
William

1
Re "lớp tĩnh như không gian tên", thực tế đó là một mẫu chung không có nghĩa nó không phải là mẫu chống. Phương thức (về cơ bản là "hàm miễn phí" để mượn một thuật ngữ từ C / C ++) trong lớp tĩnh liên kết thấp đặc biệt này là thực thể có ý nghĩa trong trường hợp này. Trong bối cảnh cụ thể này, có vẻ tốt hơn về mặt cấu trúc khi có một không gian tên phụ Avới 100 lớp tĩnh phương thức đơn (trong 100 tệp) so với một lớp tĩnh Acó 100 phương thức trong một tệp.
William

1
Tôi đã làm một số dọn dẹp khá lớn trên câu trả lời của bạn, chủ yếu là mỹ phẩm. Tôi không chắc đoạn cuối của bạn nói về cái gì; không ai lo lắng về cách họ đang kiểm tra mã gọi Mathlớp tĩnh.
Robert Harvey

5

Chỉ cần nắm lấy quy ước rằng các lớp tĩnh được sử dụng như không gian tên trong các tình huống như thế này trong C #. Không gian tên có được tên tốt, các lớp này cũng vậy. Các using statictính năng của C # 6 làm cho cuộc sống dễ dàng hơn một chút, quá.

Các tên lớp có mùi như Helpersbáo hiệu rằng các phương thức nên được chia thành các lớp tĩnh được đặt tên tốt hơn, nếu có thể, nhưng một trong những giả định được nhấn mạnh của bạn là các phương thức bạn xử lý là "không liên quan theo cặp", nghĩa là chia tách thành một lớp tĩnh mới cho mỗi phương thức hiện có. Điều đó có thể tốt, nếu

  • có những cái tên hay cho các lớp tĩnh mới và
  • bạn đánh giá nó thực sự có lợi cho khả năng bảo trì của dự án của bạn.

Như một ví dụ giả định, Helpers.LoadImagecó thể trở thành một cái gì đó như thế FileSystemInteractions.LoadImage.

Tuy nhiên, bạn có thể kết thúc với các túi phương thức lớp tĩnh. Một số cách điều này có thể xảy ra:

  • Có lẽ chỉ một số (hoặc không có) các phương thức ban đầu có tên tốt cho các lớp mới của chúng.
  • Có thể các lớp mới sau này phát triển để bao gồm các phương thức liên quan khác.
  • Có lẽ tên lớp ban đầu không có mùi và thực sự ổn.

Điều quan trọng cần nhớ là các túi phương thức lớp tĩnh này không phải là quá phổ biến trong các cơ sở mã C # thực. Có lẽ không phải là bệnh hoạn khi có một vài cái nhỏ .


Nếu bạn thực sự thấy một lợi thế trong việc có từng phương thức giống như "hàm miễn phí" trong tệp riêng của mình (điều này tốt và đáng giá nếu bạn đánh giá trung thực rằng nó thực sự có lợi cho khả năng bảo trì dự án của bạn), bạn có thể xem xét tạo một lớp tĩnh như vậy thay vì tĩnh lớp một phần, sử dụng tên lớp tĩnh tương tự như cách bạn sẽ tạo một không gian tên và sau đó sử dụng nó thông qua using static. Ví dụ:

cấu trúc của dự án giả sử dụng mô hình này

Chương trình.cs

namespace ConsoleApp1
{
    using static Foo;
    using static Flob;

    class Program
    {
        static void Main(string[] args)
        {
            Bar();
            Baz();
            Wibble();
            Wobble();
        }
    }
}

Foo / Bar.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Bar() { }
    }
}

Foo / Baz.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Baz() { }
    }
}

Lũ / Wibble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wibble() { }
    }
}

Lũ / Wobble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wobble() { }
    }
}

-6

Nói chung, bạn không nên có hoặc yêu cầu bất kỳ "phương pháp tiện ích chung" nào. Trong logic kinh doanh của bạn. Hãy xem xét kỹ hơn và đặt chúng ở một nơi thích hợp.

Nếu bạn đang viết một thư viện toán học hoặc một cái gì đó thì tôi sẽ đề nghị, mặc dù tôi thường ghét chúng, Phương pháp mở rộng.

Bạn có thể sử dụng Phương thức mở rộng để thêm chức năng vào các kiểu dữ liệu tiêu chuẩn.

Ví dụ: Hãy nói rằng tôi có một hàm chung MySort () thay vì Helpers.MySort hoặc thêm ListOfMyThings.MySort mới Tôi có thể thêm nó vào IEnumerable


Vấn đề là không có một "cái nhìn khó khăn" nào khác. Vấn đề là có một mẫu có thể tái sử dụng có thể dễ dàng dọn sạch một "túi" tiện ích. Tôi biết rằng các tiện ích là mùi. Câu hỏi nêu rõ điều này. Mục đích là để hợp lý cô lập những độc lập (nhưng quá chặt chẽ đồng nằm) đơn vị miền cho bây giờ và đi dễ dàng như có thể về ngân sách dự án.
William

1
Nếu bạn không có "phương thức tiện ích chung", có lẽ bạn không tách biệt các phần logic với phần còn lại của logic. Ví dụ, theo như tôi biết, không có chức năng nối đường dẫn URL mục đích chung trong .NET, vì vậy tôi thường kết thúc việc viết một. Loại cruft đó sẽ tốt hơn khi là một phần của thư viện tiêu chuẩn, nhưng mọi lib tiêu chuẩn đều thiếu một cái gì đó . Sự thay thế, để lại logic được lặp lại trực tiếp bên trong mã khác của bạn, làm cho nó dễ đọc hơn rất nhiều .
jpmc26


1
@Ewan không phải là một chức năng, đó là một chữ ký đại biểu cho mục đích chung ...
TheCatWhisperer

1
@Ewan Đó là toàn bộ quan điểm của TheCatWhisperer: các lớp tiện ích là một khoảng trống xung quanh việc phải đặt các phương thức trong một lớp. Vui mừng bạn đồng ý.
jpmc26
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.