Hàm tĩnh so với các lớp


8

Giả sử tôi muốn xây dựng một số hàm tiện ích để thực hiện một số phép toán cơ bản với BigDecimals, ví dụ tôi muốn có một hàm tính trung bình của a List<BigDecimal>.

Đâu là cách tiếp cận lí tưởng nhất? Một hàm tĩnh hoặc một lớp tiện ích?

public static BigDecimal computeAverage(List<BigDecimal> numbers)

hoặc là

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)

Một điểm cần xem xét, là xác định lớp nào có trách nhiệm thực hiện phép tính này. Bạn có thể phát hiện ra rằng chức năng này không được yêu cầu trên toàn cầu bởi các lớp khác và do đó nó có thể là một phương thức riêng tư trong 1 lớp. Một điều khác cần xem xét là định nghĩa tĩnh và an toàn luồng.
NoChance

Câu trả lời:


9

Có lẽ tôi sẽ cho đi những phần C # của mình theo cách tồi tệ nhất có thể, nhưng tôi luôn đề xuất các phương thức tĩnh trong các lớp tiện ích. Điều này cung cấp cho bạn tốt nhất của cả hai thế giới. Các lợi ích tổ chức của lớp với khả năng sử dụng các phương thức được truy cập mà không cần khởi tạo một đối tượng để sử dụng chúng. Nếu lớp tiện ích của bạn có lý do để thỉnh thoảng thay đổi hành vi của nó, thì bạn đã có vị trí tốt để làm điều đó bằng cách giới thiệu kế thừa / giao diện sau này nếu bạn cần. Nếu bạn muốn thiết kế của mình dễ di chuyển hơn một chút, thì bạn sẽ thấy mô hình sẽ dễ dàng chuyển sang các ngôn ngữ OO khác.


"Nếu lớp tiện ích của bạn có lý do để thỉnh thoảng thay đổi hành vi của nó": vâng nhưng tôi đang ở trong một cửa hàng java và bạn không thể thay đổi một phương thức tĩnh bằng cách mở rộng lớp.
S4M

@ S4M Đương nhiên nếu hành vi phương thức của bạn cần thay đổi, thì bạn sẽ cần phải suy nghĩ lại về thiết kế. Như tôi đã nói trong câu trả lời của tôi, tuy nhiên, nếu bạn cần thay đổi thiết kế, bạn có vị trí tốt để làm điều đó. Bạn luôn có thể cấu trúc lại phương thức thành không tĩnh nếu bạn cần hành vi được mở rộng. Nếu bạn có tất cả các phương thức của mình rải rác khắp nơi thì bạn sẽ gặp khó khăn trong việc định vị chúng. Đương nhiên nếu phương thức chỉ cần một lớp duy nhất, thì tốt hơn hết bạn nên để nó trong lớp đó, trong trường hợp đó là vấn đề tĩnh hay không liên quan.
S.Robins

Thật ra tôi là một fan hâm mộ lớn của các phương thức tĩnh, nhưng trong công ty của tôi, tôi nhận thấy một nhà phát triển cấp cao sử dụng các phương thức không tĩnh, vì vậy điều này khiến tôi suy nghĩ. Về cơ bản, tôi sẽ liệt kê ListBigDecimalUtils.computeAlusive (x) trong khi anh ấy sẽ làm AverageListBigDecimal (). Tính toán (x). Tôi không thấy lợi ích của cách tiếp cận của anh ấy.
S4M

@ S4M Đơn giản là nó có thể cho phép anh ta cung cấp các phương thức của mình quyền truy cập vào các phương thức không tĩnh khác trong lớp. Tuy nhiên, đây có thể chỉ là các phương thức tĩnh riêng bổ sung. Nhiều khả năng có lẽ là anh ta đang đảm bảo anh ta để lại cho mình các lựa chọn để mở rộng lớp học nếu anh ta thấy cần thiết trong tương lai. Có lẽ tốt hơn để hỏi bạn nhà phát triển cấp cao về lý do của anh ta là gì nếu bạn muốn hiểu nơi anh ta thấy lợi ích.
S.Robins

@ s4m. Tôi nghĩ các lớp tĩnh đơn giản không linh tinh tốt và có thể là sự lựa chọn tốt nhất hiện nay (xấu hổ vì java thiếu phương thức mở rộng btw). Nhưng một "dịch vụ thống kê" cũng không ngớ ngẩn. Tôi có thể tưởng tượng rằng có nhu cầu thay đổi Trung bình từ trung bình sang trung bình thành trung bình cắt trong thời gian chạy. Tôi có thể sẵn sàng tưởng tượng các phép toán trong danh sách các int tạo ra entropy và sẽ yêu cầu một dịch vụ để kiểm tra.
Nathan Cooper

5

Nếu bạn muốn thực hiện các chức năng tiện ích, được kết nối bởi một số chủ đề, tạo một lớp cho chúng, làm cho chúng tĩnh trong lớp đó và tạo một hàm tạo của lớp đó thành riêng tư, để loại bỏ khả năng vô tình đưa ra một thể hiện. Vì vậy, sử dụng biến thể thứ hai, nhưng ẩn hàm tạo.

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)

private BigDecimalUtil(){super();}

Có một khả năng nữa - để không tạo constructor và làm cho lớp này trừu tượng. Nhưng theo cách này, bạn không thể sử dụng thể hiện của lớp không chỉ bên ngoài mà cả bên trong nó. Vì vậy, tôi nghĩ rằng, biến thể thứ hai này là quá nghiêm ngặt đối với bạn. Chọn chính mình.


4

Tôi đồng ý với @Gangnus rằng một lớp "bao bọc" tĩnh có ý nghĩa nếu các phương thức lớp riêng lẻ sẽ luôn được sử dụng một cách cô lập và điều này thường sẽ là một câu trả lời tốt.

Tuy nhiên, có thể có một số giá trị trong một lớp không tĩnh, với các phương thức không tĩnh, nếu bạn có các phép tính liên quan có khả năng được sử dụng cùng nhau. Ví dụ, xây dựng một ListStatsUtilđối tượng với List<BigDecimal>truyền vào constructor, nó có thể là thuận tiện để truy cập getStandardDeviation, getVariancegetAveragetheo thứ tự mà không đi lại trong đối tượng. Điều này có thể thanh lịch hơn, nhưng cũng có thể hoạt động tốt hơn, đặc biệt là nếu các hoạt động đắt tiền được tính toán có thể được tối ưu hóa trong các hoạt động bằng cách chia sẻ trạng thái riêng tư trong đối tượng.

public ListStatsUtil {
    private final List<BigDecimal> numbers;
    public ListStatsUtil(List<BigDecimal> numbers) {
        this.numbers = numbers;
    }
    public BigDecimal getAverage() {
        // return average of this.numbers
    }
    public BigDecimal getStandardDeviation() {
        // return std dev of this.numbers
    }
    public BigDecimal getVariance() {
        // return variance of this.numbers
    }
    public BigDecimal getMax() {
        // return max of this.numbers
    }
    public BigDecimal getMin() {
        // return min of this.numbers
    }
}

Tôi chưa có quyền trên trang web lập trình viên mới này để bình luận cho @Gangnus, vì vậy tôi sẽ để nó ở đây. Liệu super()trong hàm tạo riêng có ý nghĩa đối với một lớp tĩnh trong Java không?
mã hóa

Không còn nghi ngờ gì nữa, nó chỉ có ý nghĩa như một kỷ luật tự giác đối với trường hợp tái cấu trúc triệt để trong tương lai. Chúng tôi muốn nhà xây dựng này không bao giờ được chạy.
Gangnus

Tôi không hiểu Tất cả các hàm này, như getVariance và như vậy, lấy Danh sách <BigDecimal> và trả lại BigDecimal gấp đôi. Làm thế nào bạn có thể đặt chúng trong chuỗi? Ý tưởng về các tiện ích xích là tốt, nhưng a) không dành cho trường hợp do bạn cung cấp. b) không phải trong trường hợp đó là câu hỏi. Đó là cho trường hợp khi chúng ta cần một lớp mới với các tiện ích . Vì vậy, câu hỏi như vậy không bao giờ tăng - câu trả lời là rõ ràng.
Gangnus

Không phải trong một chuỗi, chỉ là bạn có thể muốn nhiều thao tác trong danh sách đơn. Câu hỏi không loại trừ khả năng này.
mã hóa

Lớp mà tôi đã mô tả, có thể dễ dàng định nghĩa các phương thức như vậy. Nó chỉ không đưa ra bất kỳ trường hợp ra , nhưng có thể sử dụng chúng trong OK. Trong trường hợp này, tất nhiên, các nhà xây dựng trở thành một công việc, nhưng chúng tôi đã chuẩn bị cho nó, phải không? :-)
Gangnus

1

Tôi đã bình chọn cho câu trả lời về tiền mã hóa và tôi muốn mở rộng thêm một chút.

Gói các bộ sưu tập trong các lớp học là điều tôi luôn cố gắng thực hiện - quy tắc của tôi là "Không bao giờ vượt qua bộ sưu tập trần trụi". Thất bại lớn nhất của OO tôi từng thấy là khi mọi người sợ thêm phương thức nơi họ thuộc về. Trong trường hợp của bạn, các phương thức thuộc về bộ sưu tập (Hoàn toàn rõ ràng nếu bạn nghĩ về nó), vì vậy hãy đặt chúng ở đó bằng cách gói bộ sưu tập và để đóng băng trên bánh, chỉ hiển thị chức năng bạn cần bên ngoài lớp mới của bạn (như mã hóa đã làm) để bạn có thể nhìn vào lớp học nhỏ, đơn lẻ của mình và dễ dàng hiểu mọi thứ thao túng bộ sưu tập đó.

Bạn không phải giải quyết mọi vấn đề có thể xảy ra vì đó là tất cả mã của bạn, bạn có thể chỉnh sửa trình bao bọc bộ sưu tập dễ dàng như bạn có thể chỉnh sửa các lớp khác tương tác với nó. Điều này cho phép bạn kéo mã khác vào lớp khi cần thiết - nếu bạn không bao giờ để lộ bộ sưu tập, nó sẽ nhanh chóng trở nên rõ ràng mã nào thuộc về trình bao bọc.

Điều này cũng cung cấp cho bạn toàn quyền kiểm soát bộ sưu tập để bạn có thể ngăn không cho nó vào trạng thái không phù hợp với chức năng của nó (Ví dụ: nếu không có mục nào trong bộ sưu tập có thể là null, chỉ cần ngăn không cho bất kỳ ai đưa null vào!)

Hầu hết các mã OO xấu mà tôi từng thấy đã được tạo bởi vì ai đó đã chọn viết các tiện ích tĩnh hoặc mã nội tuyến thuộc về các đối tượng mà họ không thể thêm mã vào thay vì bọc các đối tượng vi phạm bằng mã của riêng họ mà họ có thể sửa đổi .


Trong trường hợp cụ thể này, tôi muốn nói trung bình là một phương pháp hợp lý để gọi bất kỳ tập hợp số nào. Vì vậy, cuối cùng bạn có thể chỉ cung cấp cho tất cả các thùng chứa số tên mới của mình với chức năng nhiều hơn một chút. Âm thanh như trở lại xấu về nỗ lực. Đây là phương thức mở rộng dành cho c #, trong java bạn bị mắc kẹt với số liệu thống kê.
Nathan Cooper

Các tiện ích không có logic nghiệp vụ thường không có giải pháp OO tốt và điều tốt nhất bạn có thể làm là các lớp tĩnh và các lớp cố định như các bộ sưu tập. Nếu bạn thực sự phải làm một tiện ích mục đích chung, bạn cũng có thể từ bỏ oo như các bộ sưu tập, sử dụng toán học và tất cả các tiện ích khác làm. Trong trường hợp này, anh ta có lẽ chỉ đang sử dụng một bộ sưu tập cho mã doanh nghiệp của mình, vậy tại sao lại thực hiện một giải pháp chung?
Bill K

0

Nếu tôi không sai và các ngôn ngữ Java là ngôn ngữ OO thuần túy, bạn không thể có các hàm mà không bao bọc chúng trong một lớp.

Vì vậy, câu trả lời của bạn là: cả hai, một hàm tĩnh được bọc trong một lớp tiện ích tĩnh, không giống như lớp Toán.


-2

Làm thế nào về một giao diện CalcAlusiveStrargety được tiêm, và sau đó bạn có thể có các cài đặt trung bình / trung bình / chế độ được tiêm khi cần thiết.

Mỗi cách thực hiện đều dễ kiểm tra và cũng dễ giả để kiểm tra.


cái này đọc giống như một bình luận, xem Cách trả lời
gnat
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.