Một lớp tiện ích có nên tĩnh không? [đóng cửa]


76

Nếu tôi phải thiết kế một lớp Tiện ích (chẳng hạn như ByteUtils hoặc StreamUtils hoặc StringUtils), lựa chọn thiết kế tốt nhất cho chúng là gì.

  • Nếu chúng là các lớp tĩnh (vì tôi sẽ không có bất kỳ trạng thái nào để lưu trữ)
  • Nếu chúng là các lớp không tĩnh (để nếu các đối tượng không được sử dụng, chúng sẽ được gc'd)

PS: Theo lớp tĩnh, ý tôi là một lớp có các phương thức tĩnh (và không phải lớp tĩnh bên trong)

Xin cho lời khuyên về lựa chọn thiết kế cho việc này?


5
Bạn cần phải viết lại câu hỏi này. Các lớp cấp ngoài không thể là tĩnh trong Java. Họ không thể có gì ngoài các phương thức tĩnh . Không giống nhau.
user207421,

Đúng. bạn nói đúng. Tôi có nghĩa là một lớp học với phương pháp là tĩnh
nibin012

Mặc dù các ví dụ của bạn (Byte, String, Stream) phải khá nhỏ, nhưng nếu bạn tình cờ có 1k LOC trong EJB sử dụng tiện ích ánh xạ có các phương thức tĩnh, việc kiểm tra EJB đó sẽ trở nên khá khó khăn.
LAFK nói Hãy phục hồi Monica vào

Câu trả lời:


46

Nếu đó là một tiện ích cho mục đích chung, thì IMO tĩnh sẽ tốt hơn. Bạn đã nói rằng bạn sẽ không có bất kỳ trạng thái nào để lưu trữ, vì vậy tôi không hiểu tại sao bạn nên đặt nó ở chế độ không tĩnh. Khai báo nó là tĩnh cũng sẽ tiết kiệm bộ nhớ.


10
Một máy vặn vít không thay đổi, nó luôn hoạt động giống như vậy. Nếu cũng có thể nói như vậy đối với lớp tiện ích của bạn, hãy chắc chắn làm cho nó tĩnh.
Gapton

10
Điều này đúng cho tất cả các phương thức không dựa vào trạng thái của đối tượng. Không chỉ các phương thức của lớp tiện ích.
Dorus

72

Các lớp tiện ích của tôi trông như thế này:

// final, because it's not supposed to be subclassed
public final class FooUtil {

    // private constructor to avoid unnecessary instantiation of the class
    private FooUtil() {
    }

    public static int doSomethingUseful() {
    }

    // ...
}

Lưu ý rằng, mặc dù điều này làm cho các phương thức tiện ích có thể dễ dàng kiểm tra và dễ dàng truy cập từ bên ngoài, nó cũng làm cho các lớp sử dụng chúng khó kiểm tra đơn vị, vì không dễ dàng để bắt chước các phương thức tiện ích này. Có quá nhiều lớp tiện ích như vậy có thể là dấu hiệu của việc thiếu thiết kế OO (lập trình thủ tục) và thực sự có thể làm cho mã khó kiểm tra.

Nếu bạn đang sử dụng một khung công tác tiêm phụ thuộc (Spring, Guice, bất cứ thứ gì), có thể là một ý tưởng hay nếu chỉ làm cho lớp tiện ích có thể ngay lập tức, với các phương thức không tĩnh và biến nó thành một singleton có thể tiêm. Bằng cách này, các lớp sử dụng các phương thức tiện ích này có thể được kiểm tra bằng cách giả lập đối tượng tiện ích.


2
Chúng ta có thể đơn phương pháp thử tĩnh nhờ sủ dụng powermock code.google.com/p/powermock
nibin012

14
@ nibin012 chỉ vì bạn có thể, không có nghĩa là bạn nên làm. Trong dự án của tôi, PowerMock là một công cụ gây phiền muộn liên tục - nó không tốt với các công cụ khác vì nó gây rối với bộ tải lớp quá nhiều.
LAFK nói rằng Hãy phục hồi Monica vào

@ jb-nizet, bạn có thể vui lòng cung cấp một ví dụ tối thiểu về:> có thể là một ý kiến ​​hay nếu bạn chỉ làm cho lớp tiện ích có thể chạy ngay được, với các phương thức không tĩnh và biến nó thành một singleton có thể tiêm được. Nó SGTM nhưng tôi đấu tranh để thực hiện.
alexsalo

1
"Nếu bạn đang sử dụng một khuôn khổ tiêm phụ thuộc" - và nếu không, thì đó là một ý tưởng tồi?
Dòng

bạn không phải là đơn vị kiểm tra chỉ vì bạn sử dụng powermocks. Nói chung, việc sử dụng một mô hình như powermock để kiểm tra một phương thức tĩnh mà bạn thiết kế có thể sai theo một cách nào đó, vì bạn đang ghi đè các phương thức "không trạng thái".
Wisienkas

24

Chỉ vì một cái gì đó có thể tĩnh, không có nghĩa là nó phải tĩnh.

Có một xem xét khác cho tất cả điều này: chế nhạo. Thật khó để bắt chước các phương thức tĩnh trong các bài kiểm tra của bạn hơn là mô phỏng hành vi của một thể hiện của một lớp.

Nói về phân bổ heap không cần thiết và GCing của các đối tượng giúp tôi tối ưu hóa quá sớm. JVM sẽ thực hiện một công việc khá tốt trong việc tối ưu hóa loại vấn đề này.


9

Cách đơn giản nhất để xác định một lớp Tiện ích là như một enum không có phiên bản

public enum Utility {;
     public static int utilityMethod(int x) { /* ... */ }
}

Một lớp tiện ích không nên có bất kỳ trạng thái nào hoặc có trạng thái tối thiểu, vì vậy bạn không nên lo lắng về GC.

Bạn có thể có các lớp trạng thái khác cho các mục đích cụ thể như Builder, Factory, bạn có thể tạo đối tượng như mong muốn và loại bỏ nó khi bạn đã hoàn thành.


1
Tôi cho rằng ý bạn không phải là một lớp lồng nhau tĩnh. Điều này tránh cần phải nhớ tạo phương thức khởi tạo riêng để tránh mọi người vô tình tạo một thể hiện của lớp tiện ích. Enums được finalmặc định.
Peter Lawrey

1
Trong Java, enumcó thể có các trường có thể thay đổi, các giao diện triển khai và các phương thức trừu tượng để chúng không chỉ là hằng số. ;)
Peter Lawrey

3
@PeterLawrey, trong khi điều này là đúng, đối với tôi, nó đánh lừa quá kỹ thuật. Kể từ năm 1996, tôi có thể đếm tổng cộng bằng 0 lần mà tôi đã thấy ai đó vô tình tạo ra một lớp có nghĩa là một tiện ích tĩnh đơn giản. Cung cấp các biện pháp bảo vệ như vậy là IMO ngớ ngẩn, bởi vì hậu quả của việc khởi tạo một cái gì đó như thế này là bạn nhận thấy ngay rằng nó sẽ không hoạt động. Đặt trong một enumvào trộn tuy nhiên thay đổi cách các kỹ sư mệt mỏi vào lúc 3h nhảy trên caffeine sẽ đọc điều và làm cho anh ta tự hỏi trong chốc lát những gì đang xảy ra. Nó không phải là một thành ngữ phổ biến. Sử dụng classvà hạnh phúc.

4
Điểm chính mà tôi thực sự đưa ra ở đây là khi bạn sử dụng một enum, bạn đang thông báo cho người đọc rằng có một lý do cho việc liệt kê ẩn trong thiết kế của nó. Không có. Bạn chỉ đang cố gắng làm cho nó dễ dàng hơn so với việc đưa vào một hàm tạo finalvà riêng, cả hai đều không phải là IMO có vấn đề. Đó là một thành ngữ bất thường. Và bạn đã thấy ai đó đang cố gắng khởi tạo một lớp các phương thức tĩnh tiện ích? Có vấn đề gì không? Nếu ai đó thấy lớp cung cấp các phương thức họ cần, họ biết chúng là gì ---- tại sao họ lại khởi tạo nó? Và điều gì sẽ xảy ra nếu họ làm vậy? Không nhiều.

3
Tôi nghĩ rằng chúng ta đã cần phải đặt câu hỏi về suy nghĩ quá bảo vệ của chúng ta trong một thời gian dài. Làm quen với gotos, tránh trả lại nhiều lần, bảo vệ khởi tạo, v.v., thực sự có rất ít mặt hỗ trợ cho tất cả những điều này. Tuy nhiên những gì đáng được bảo vệ là khả năng đọc của mã này. Luôn cố gắng tiếp cận với danh mục không cần trí tuệ nhất có thể.

4

Thật tốt khi làm cho lớp là không tĩnh với một phương thức khởi tạo riêng:

  • nếu chúng ta đặt lớp là tĩnh thì lớp sẽ được tải khi ứng dụng được triển khai, nếu nó là không tĩnh thì lớp sẽ được tải khi có lệnh gọi đến một trong các phương thức tĩnh của nó
  • tạo một phương thức khởi tạo riêng sẽ tránh việc khởi tạo lớp

Sau đó, làm thế nào để sử dụng lớp này? Không tĩnh với constrctr tin
Yousha Aleayoub

3

Thông thường các lớp tiện ích chứa các phương thức tĩnh và không có thuộc tính, cách tiếp cận này giúp sử dụng các phương thức của chúng dễ dàng hơn mà không cần khởi tạo lớp. Hi vọng điêu nay co ich.


2

Nếu bạn có thể làm cho chúng tĩnh, thì hãy chắc chắn làm như vậy!

Nói cách khác, nếu chúng không có trạng thái, chúng phải ở trạng thái tĩnh.


1
Tôi đã viết mã trong các dự án tuân theo nguyên tắc đó. "Nếu bạn có thể", đó là. Nhờ đó, thử nghiệm đã trở nên khó khăn. Và PowerMock còn khiến vấn đề trở nên tồi tệ hơn, do ma thuật của trình tải lớp đã va chạm với các công cụ khác theo những cách không bao giờ dễ dàng nhận ra. Bị phản đối.
LAFK nói Khôi phục Monica

1
@LIttleAncientForestKami, nhìn chung tôi đồng ý với câu "nếu bạn có thể". Tuy nhiên, tôi nghĩ những gì Petar đang nói là "nếu không có lý do gì để sử dụng bất cứ thứ gì khác ngoài tĩnh thì hãy sử dụng tĩnh" (hoặc những từ ít vụng về hơn để tạo ra hiệu ứng đó). Ít nhất đó là ý nghĩa mà tôi rút ra từ câu nói của anh ấy. IOW, "Nếu bạn không cần một phiên bản, thì đừng tạo một phiên bản chỉ để có một phiên bản." Một lần nữa, tôi tiếp nhận tuyên bố của anh ấy.

@tgm, Chính xác. Cảm ơn bạn!
Petar Ivanov

2

Chà, nếu bạn không có trạng thái nào để lưu trữ thì sẽ không có gì đối với GC, vì vậy tôi sẽ sử dụng static, theo cách đó bạn tránh mọi phân bổ heap và GC không cần thiết.


2

Các lớp tiện ích thuần túy thường phải là tĩnh. Khi bạn có một lớp với đầu vào và đầu ra được xác định rõ ràng, không có tác dụng phụ và không có trạng thái, thì theo định nghĩa, nó phải là một lớp tĩnh.

Nói chung, đừng thêm phức tạp (tiêm phụ thuộc trong trường hợp này) trước khi cần thiết và có lợi khi làm điều đó.


1

Không ai ở đây đề cập rằng các phương thức tiện ích tĩnh không thể mở rộng. Bạn không thể ghi đè chúng. Bạn không thể tận dụng OOP (đặc biệt là đa hình, là tính năng mạnh mẽ nhất của OOP). Điều đó dẫn đến sự trùng lặp mã.

Tái bút Tôi thấy bài viết này rất hữu ích. http://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html


Định nghĩa "có thể mở rộng" ... không có gì ngăn cản bạn tạo quá tải trong các lớp công cụ của riêng bạn gọi các công cụ hiện có (không phải là sự khác biệt lớn so với lớp con / quá tải IMO) và nếu bạn muốn ngăn trùng lặp mã, bạn luôn có thể sử dụng generics để cho phép chức năng công cụ của bạn xử lý nhiều loại hơn.
Nyerguds

Bài báo đó dường như đi theo tư duy "sử dụng các đối tượng vì lợi ích của nó là OOP", IMO chỉ là ô nhiễm bộ nhớ . Nó không chỉ cung cấp thêm công việc cho GC, mà vì các tham chiếu đối tượng có thể được lưu trữ, nên tất cả việc tạo đối tượng đều có nguy cơ tạo rò rỉ bộ nhớ. Không đáng.
Nyerguds

Không chắc tại sao điều này bị từ chối. Một bài viết tốt tóm tắt lý do tại sao đây là lựa chọn tốt hơn: vojtechruzicka.com/avoid-utility-classes
Pratik

@Nyerguds, bạn hoàn toàn sai về việc làm thêm cho GC. Cách thông thường để làm điều này là có một singleton (một phương thức getInstance () tĩnh) với một phương thức khởi tạo riêng.
Pratik

@Pratik Sau đó là ô nhiễm bộ nhớ, vì nó giữ các đối tượng đó vô thời hạn.
Nyerguds
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.