it-swarm-vi.tech

Lớp tiện ích có ác không?

Tôi thấy chủ đề này 

Nếu một lớp "Tiện ích" là xấu, tôi phải đặt mã chung ở đâu?

và nghĩ tại sao các lớp tiện ích là xấu? 

Hãy nói rằng tôi có một mô hình miền sâu hàng chục lớp. Tôi cần có thể xml-ify. Tôi có thực hiện phương thức toXml trên cha mẹ không? Tôi có tạo lớp trợ giúp MyDomainXmlUtility.toXml không? Đây là một trường hợp mà doanh nghiệp cần kéo dài toàn bộ mô hình miền - nó có thực sự thuộc về một phương thức ví dụ không? Còn nếu có một loạt các phương thức phụ trợ trên chức năng xml của ứng dụng thì sao?

77
hvgotcodes

Các lớp tiện ích không chính xác là xấu, nhưng chúng có thể vi phạm các nguyên tắc cấu thành một thiết kế hướng đối tượng tốt. Trong một thiết kế hướng đối tượng tốt, hầu hết các lớp nên đại diện cho một điều duy nhất và tất cả các thuộc tính và hoạt động của nó. Nếu bạn đang vận hành một thứ, phương pháp đó có lẽ nên là một thành viên của thứ đó.

Tuy nhiên, đôi khi bạn có thể sử dụng các lớp tiện ích để nhóm một số phương thức lại với nhau - một ví dụ là lớp Java.util.Collections cung cấp một số tiện ích có thể được sử dụng trên mọi Bộ sưu tập Java. Chúng không dành riêng cho một loại Bộ sưu tập cụ thể, mà thay vào đó triển khai các thuật toán có thể được sử dụng trên bất kỳ Bộ sưu tập nào.

Thực sự, những gì bạn cần làm là suy nghĩ về thiết kế của bạn và xác định nơi nào có ý nghĩa nhất để đặt các phương thức. Thông thường, nó là hoạt động bên trong một lớp. Tuy nhiên, đôi khi, nó thực sự là một lớp tiện ích. Tuy nhiên, khi bạn sử dụng một lớp tiện ích, đừng chỉ ném các phương thức ngẫu nhiên vào đó, thay vào đó, hãy tổ chức các phương thức theo mục đích và chức năng.

105
Thomas Owens

Tôi nghĩ rằng sự đồng thuận chung là các lớp tiện ích không phải là xấu mỗi se . Bạn chỉ cần sử dụng chúng một cách thận trọng:

  • Thiết kế các phương thức tiện ích tĩnh nói chung và có thể tái sử dụng. Hãy chắc chắn rằng chúng không trạng thái; tức là không có biến tĩnh.

  • Nếu bạn có nhiều phương thức tiện ích, hãy phân vùng chúng thành các lớp theo cách giúp các nhà phát triển dễ dàng tìm thấy chúng.

  • Không sử dụng các lớp tiện ích trong đó các phương thức tĩnh hoặc thể hiện trong một lớp miền sẽ là một giải pháp tốt hơn. Ví dụ, hãy xem xét nếu các phương thức trong một lớp cơ sở trừu tượng hoặc một lớp trình trợ giúp tức thời sẽ là một giải pháp tốt hơn.

  • Đối với Java 8 trở đi, "các phương thức mặc định" trong giao diện có thể là một tùy chọn tốt hơn so với các lớp tiện ích.


Cách khác để xem Câu hỏi này là quan sát rằng trong Câu hỏi được trích dẫn, "Nếu các lớp tiện ích là" xấu xa "" là một đối số rơm. Nó giống như tôi hỏi:

"Nếu lợn có thể bay, tôi có nên mang theo dù không?". 

Trong câu hỏi trên tôi không thực sự nói rằng lợn có thể bay ... hoặc tôi đồng ý với đề xuất rằng chúng có thể bay. 

Điển hình "xyz là ác" tuyên bố là những thiết bị tu từ nhằm mục đích khiến bạn phải suy nghĩ bằng cách đưa ra một quan điểm cực đoan. Chúng hiếm khi (nếu có) được dự định là tuyên bố của thực tế theo nghĩa đen. 

47
Stephen C

Các lớp tiện ích có vấn đề vì chúng không thể phân nhóm trách nhiệm với dữ liệu hỗ trợ chúng.

Tuy nhiên, chúng cực kỳ hữu ích và tôi xây dựng chúng mọi lúc dưới dạng cấu trúc vĩnh cửu hoặc là bước đệm trong quá trình tái cấu trúc kỹ lưỡng hơn.

Từ một Mã sạch các lớp tiện ích phối cảnh vi phạm Trách nhiệm đơn lẻ và Nguyên tắc đóng mở. Họ có rất nhiều lý do để thay đổi và theo thiết kế không thể mở rộng. Chúng thực sự chỉ nên tồn tại trong quá trình tái cấu trúc dưới dạng tàu trung gian.

12
Alain O'Dea

Tôi cho rằng nó bắt đầu trở nên xấu xa khi 

1) Nó trở nên quá lớn (chỉ nhóm chúng thành các loại có ý nghĩa trong trường hợp này).
2) Các phương thức không phải là phương thức tĩnh có mặt  

Nhưng miễn là những điều kiện này không được đáp ứng, tôi nghĩ chúng rất hữu ích.

7
Enno Shioji

Quy tắc của ngón tay cái

Bạn có thể xem xét vấn đề này từ hai quan điểm:

  • Các phương thức *Util tổng thể thường là một gợi ý về thiết kế mã xấu hoặc quy ước đặt tên lười biếng. 
  • Đây là giải pháp thiết kế hợp pháp cho các chức năng không trạng thái tên miền chéo có thể tái sử dụng. Xin lưu ý rằng đối với hầu hết các vấn đề phổ biến, có các giải pháp hiện có. 

Ví dụ 1. Sử dụng đúng các lớp/mô-đun util. Ví dụ thư viện bên ngoài

Giả sử bạn đang viết đơn đăng ký quản lý các khoản vay và thẻ tín dụng. Dữ liệu từ các mô-đun này được hiển thị thông qua các dịch vụ web ở định dạng json .. Về lý thuyết, bạn có thể tự chuyển đổi các đối tượng thành các chuỗi sẽ ở json, nhưng điều đó sẽ phát minh lại bánh xe. Giải pháp đúng sẽ là đưa vào cả hai mô-đun thư viện bên ngoài được sử dụng để chuyển đổi các đối tượng Java sang định dạng mong muốn. (trong ví dụ hình ảnh tôi đã hiển thị gson )

 enter image description here


Ví dụ 2. Sử dụng đúng các lớp/mô-đun util. Viết util của riêng bạn mà không bào chữa cho các thành viên khác trong nhóm

Như một trường hợp sử dụng giả định rằng chúng ta cần thực hiện một số tính toán trong hai mô-đun ứng dụng, nhưng cả hai đều cần biết khi nào có những ngày nghỉ lễ ở Ba Lan. Về lý thuyết, bạn có thể thực hiện các tính toán này bên trong các mô-đun, nhưng sẽ tốt hơn nếu trích xuất chức năng này để tách mô-đun. 

Đây là chi tiết nhỏ, nhưng quan trọng. Lớp/mô-đun bạn đã viết không được gọi là HolidayUtil, nhưng PolishHolidayCalculator. Về mặt chức năng, đây là lớp util, nhưng chúng tôi đã cố gắng tránh Word chung. 

 enter image description here

4
Marcin Szymczak

Tôi không hoàn toàn đồng ý rằng các lớp tiện ích là xấu xa.

Mặc dù một lớp tiện ích có thể vi phạm hiệu trưởng OO theo một số cách, nhưng chúng không phải lúc nào cũng xấu.

Ví dụ, hãy tưởng tượng bạn muốn một hàm sẽ dọn sạch một chuỗi tất cả các giá trị khớp với giá trị x.

stl c ++ (tính đến thời điểm hiện tại) không hỗ trợ trực tiếp điều này.

Bạn có thể tạo một phần mở rộng đa hình của std::string.

Nhưng vấn đề là, bạn có thực sự muốn MỌI chuỗi bạn sử dụng trong dự án của bạn là lớp chuỗi mở rộng của bạn không?

Đôi khi OO không thực sự có ý nghĩa và đây là một trong số đó. Chúng tôi muốn chương trình của chúng tôi tương thích với các chương trình khác, vì vậy chúng tôi sẽ gắn bó với std::string và tạo một lớp StringUtil_ (hoặc một cái gì đó).

Tôi muốn nói là tốt nhất nếu bạn gắn bó với một người sử dụng mỗi lớp. Tôi muốn nói rằng thật ngớ ngẩn khi sử dụng một lớp cho tất cả các lớp hoặc nhiều dụng cụ cho một lớp.

3
HumbleWebDev

Bây giờ nhìn lại câu hỏi này, tôi muốn nói rằng các phương thức mở rộng C # phá hủy hoàn toàn nhu cầu về các lớp tiện ích. Nhưng không phải tất cả các ngôn ngữ đều có cấu trúc thiên tài như vậy.

Bạn cũng có JavaScript, nơi bạn chỉ có thể thêm một chức năng mới vào đối tượng hiện có. 

Nhưng tôi không chắc có một cách thanh lịch để giải quyết vấn đề này bằng ngôn ngữ cũ hơn như C++ ...

Mã OO tốt rất khó viết và khó tìm vì việc viết Tốt OO đòi hỏi nhiều thời gian/kiến ​​thức hơn so với viết mã chức năng đàng hoàng.

Và khi bạn đang ở trong ngân sách, sếp của bạn sẽ không vui khi thấy bạn dành cả ngày để viết một loạt các lớp học ...

3
HumbleWebDev

Các lớp tiện ích rất tệ bởi vì chúng có nghĩa là bạn quá lười biếng để nghĩ ra một cái tên tốt hơn cho lớp :)

Điều đó đang được nói, tôi lười biếng. Đôi khi bạn chỉ cần hoàn thành công việc và đầu óc bạn trống rỗng .. đó là khi các lớp "Tiện ích" bắt đầu len vào.

3
Larry Watanabe

Thật dễ dàng để thương hiệu một cái gì đó một tiện ích đơn giản chỉ vì nhà thiết kế không thể nghĩ ra một nơi thích hợp để đặt mã. Thường có một vài "tiện ích" thực sự.

Theo nguyên tắc thông thường, tôi thường giữ mã trong gói nơi nó được sử dụng lần đầu tiên và sau đó chỉ tái cấu trúc đến một nơi chung chung hơn nếu tôi thấy rằng sau đó nó thực sự cần thiết ở nơi khác. Ngoại lệ duy nhất là nếu tôi đã có một gói thực hiện chức năng tương tự/liên quan và mã phù hợp nhất ở đó.

2
mdma

Các lớp tiện ích chứa các phương thức tĩnh không trạng thái có thể hữu ích. Đây thường là rất dễ dàng để kiểm tra đơn vị.

2
seand

Hầu hết các lớp Util đều xấu vì:

  1. Họ mở rộng phạm vi của các phương thức. Họ công khai mã mà nếu không sẽ là riêng tư. Nếu nhiều phương thức sử dụng là cần thiết cho nhiều người gọi trong các lớp riêng biệt và ổn định (nghĩa là không cần cập nhật), theo ý kiến ​​của tôi, sao chép và dán các phương thức trợ giúp riêng vào lớp gọi. Khi bạn hiển thị nó dưới dạng API, bạn sẽ khó hiểu điểm đầu vào công khai hơn đối với thành phần jar là gì (bạn duy trì một cây có cấu trúc được gọi là phân cấp với một cha mẹ cho mỗi phương thức. bạn có các phương thức được gọi từ nhiều phương thức cha). 
  2. Họ dẫn đến mã chết. Sử dụng các phương thức theo thời gian trở nên không được sử dụng khi ứng dụng của bạn phát triển và bạn kết thúc với mã không được sử dụng gây ô nhiễm cơ sở mã của bạn. Nếu nó vẫn ở chế độ riêng tư thì trình biên dịch của bạn sẽ cho bạn biết phương thức này không được sử dụng và bạn chỉ có thể xóa nó (mã tốt nhất là không có mã nào cả). Khi bạn thực hiện một phương thức như vậy không riêng tư, máy tính của bạn sẽ bất lực để giúp bạn loại bỏ mã không sử dụng. Nó có thể được gọi từ một tệp jar khác cho tất cả các máy tính biết.

Có một số tương tự với các thư viện tĩnh và động.

1
Sridhar-Sarnobat

Với Java 8, bạn có thể sử dụng các phương thức tĩnh trong các giao diện ... đã giải quyết được vấn đề.

1
Marc T

Các lớp tiện ích không phải lúc nào cũng xấu. Nhưng chúng chỉ nên chứa các phương thức phổ biến trên một loạt các chức năng. Nếu có các phương thức chỉ có thể sử dụng được trong số lượng lớp giới hạn, hãy xem xét việc tạo một lớp trừu tượng như là cha mẹ chung và đặt các phương thức trong đó.

0
Sid J