Tại sao tôi không nên sử dụng POJO bất biến thay vì JavaBeans?


79

Tôi đã triển khai một vài ứng dụng Java ngay bây giờ, chỉ có các ứng dụng máy tính để bàn cho đến nay. Tôi thích sử dụng các đối tượng không thể thay đổi để truyền dữ liệu xung quanh trong ứng dụng thay vì sử dụng các đối tượng có bộ đột biến (bộ định tuyến và bộ lấy ), còn được gọi là JavaBeans.

Nhưng trong thế giới Java, việc sử dụng JavaBeans dường như phổ biến hơn nhiều và tôi không thể hiểu tại sao mình nên sử dụng chúng thay thế. Theo cá nhân, mã sẽ tốt hơn nếu nó chỉ xử lý các đối tượng không thể thay đổi thay vì thay đổi trạng thái mọi lúc.

Các đối tượng bất biến cũng được khuyến nghị trong Mục 15: Giảm thiểu khả năng thay đổi , Java 2ed hiệu quả .

Nếu tôi có một đối tượng được Persontriển khai dưới dạng JavaBean, nó sẽ giống như sau:

public class Person {
    private String name;
    private Place birthPlace;

    public Person() {}

    public setName(String name) {
        this.name = name;
    }

    public setBirthPlace(Place birthPlace) {
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}

Personthực hiện tương tự như một đối tượng không thay đổi :

public class Person {
    private final String name;
    private final Place birthPlace;

    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}

Hoặc gần hơn với một structtrong C:

public class Person {
    public final String name;
    public final Place birthPlace;

    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }
}

Tôi cũng có thể có getters trong đối tượng bất biến để ẩn các chi tiết triển khai. Nhưng vì tôi chỉ sử dụng nó như một công cụ structnên tôi thích bỏ qua "getters" và giữ cho nó đơn giản.

Đơn giản, tôi không hiểu tại sao tốt hơn nên sử dụng JavaBeans, hoặc nếu tôi có thể và nên tiếp tục với các POJO bất biến của mình?

Nhiều thư viện Java dường như có hỗ trợ tốt hơn cho JavaBeans, nhưng có thể nhiều hỗ trợ hơn cho POJO bất biến trở nên phổ biến hơn theo thời gian?


1
Tôi nghĩ rằng bạn có nghĩa là mục 15: Giảm thiểu mutability , không * Giảm thiểu tính bất biến"
matt b

@matt: Cảm ơn =) Tôi đã cập nhật nó bây giờ.
Jonas

7
Cách tiếp cận không thay đổi của bạn rất tuyệt, giúp bạn tiết kiệm rất nhiều vấn đề về luồng. +1
whiskyierra

1
Bạn sẽ cần một constructor mặc định nếu sử dụng JPA
Neil McGuigan

Câu trả lời:


78

Ưu tiên JavaBeans khi

  • bạn phải tương tác với những môi trường mong đợi họ
  • bạn có rất nhiều thuộc tính mà sẽ rất bất tiện khi thực hiện tất cả các lần khởi tạo khi khởi tạo
  • bạn có trạng thái đắt tiền hoặc không thể sao chép vì một số lý do nhưng yêu cầu đột biến
  • bạn nghĩ rằng tại một số thời điểm, bạn có thể phải thay đổi cách các thuộc tính được truy cập (ví dụ: chuyển từ các giá trị được lưu trữ sang các giá trị được tính toán, ủy quyền truy cập, v.v.)
  • bạn muốn tuân theo các tiêu chuẩn mã hóa mà vô tâm nhấn mạnh rằng nó bằng cách nào đó "hướng đối tượng" hơn để sử dụng JavaBeans

Ưu tiên POJO bất biến khi

  • bạn có một số ít thuộc tính đơn giản
  • bạn không phải tương tác với các môi trường giả sử quy ước JavaBean
  • rất dễ dàng (hoặc ít nhất có thể) để sao chép trạng thái khi nhân bản đối tượng của bạn
  • bạn không bao giờ có kế hoạch nhân bản đối tượng
  • bạn khá chắc chắn rằng bạn không bao giờ phải sửa đổi cách truy cập các thuộc tính như trên
  • bạn không ngại lắng nghe những lời than vãn (hoặc chế nhạo) về cách mã của bạn không đủ "hướng đối tượng"

+1 cho danh sách và gần như -1 cho tên của bạn :) Sau tất cả, mọi người đều biết ý kiến của tôi là đúng. Tất nhiên, mọi người được định nghĩa theo nghĩa Discworld.
extraneon

12
Kiểm tra giới hạn và xác nhận đầu vào có thể được thực hiện trong hàm tạo. Nếu đầu vào không hợp lệ hoặc bị ràng buộc, tôi không muốn tạo một đối tượng.
Jonas

24
Điểm thứ hai của bạn về đậu không phải là đậu, mà là về mô hình Người xây dựng. Bạn vẫn có thể xây dựng một đối tượng bất biến.

4
Bạn không đề cập đến việc phân luồng với các POJO bất biến, đây là một trong những lý do lớn nhất để sử dụng chúng. Một mục quan trọng khác là sự đơn giản - khi một đối tượng không thay đổi, nó sẽ dễ làm việc hơn về lâu dài. Ngoài ra, với POJO bất biến, bạn không cần phải lo lắng về việc sao chép đối tượng nói chung, bạn có thể chuyển nó xung quanh.
defb

Làm cách nào để bạn xây dựng các biểu đồ đối tượng có thể có các tham chiếu hình tròn sao cho các đối tượng đó là bất biến? Điều này có khả thi không? Ví dụ, tôi muốn lớp A và B là bất biến nhưng A cần tham chiếu đến B và B cần tham chiếu đến A. Có cách nào để làm điều này không?
chakrit

39

Tôi rất ngạc nhiên là từ Thread không xuất hiện ở bất cứ đâu trong cuộc thảo luận này.

Một trong những lợi ích chính của các lớp không thay đổi là chúng vốn dĩ an toàn hơn cho luồng do không có trạng thái chia sẻ, có thể thay đổi.

Điều này không chỉ giúp việc viết mã của bạn dễ dàng hơn mà còn mang lại cho bạn hai lợi ích về hiệu suất như một tác dụng phụ:

  • Ít cần đồng bộ hơn.

  • Nhiều phạm vi hơn để sử dụng các biến cuối cùng, có thể tạo điều kiện cho việc tối ưu hóa trình biên dịch tiếp theo.

Tôi thực sự đang cố gắng hướng tới các đối tượng bất biến hơn là các lớp kiểu JavaBean. Phơi bày ruột của các đối tượng thông qua getters và setters có lẽ không phải là lựa chọn mặc định.


18

Nó phụ thuộc vào những gì bạn đang cố gắng làm. Nếu bạn làm việc với một lớp liên tục và bạn tìm nạp một số hàng từ cơ sở dữ liệu vào một POJO và bạn muốn thay đổi một thuộc tính và lưu nó trở lại, thì việc sử dụng kiểu JavaBean sẽ tốt hơn, đặc biệt nếu bạn có nhiều thuộc tính.

Hãy xem xét con người của bạn, có rất nhiều lĩnh vực như họ, tên đệm, họ, ngày sinh, thành viên gia đình, học vấn, công việc, mức lương, v.v.

Và Người đó tình cờ là một phụ nữ vừa kết hôn và được chấp nhận đổi họ, và bạn cần cập nhật cơ sở dữ liệu.

Nếu bạn đang sử dụng POJO không thay đổi, bạn tìm nạp một đối tượng Person đại diện cho cô ấy, sau đó bạn tạo một đối tượng Person mới mà bạn chuyển tất cả các thuộc tính mà bạn không thay đổi như hiện tại, và họ mới, và lưu nó.

Nếu đó là một Java bean, bạn chỉ có thể thực hiện setLastName () và lưu nó.

Đó là "Giảm thiểu khả năng thay đổi" chứ không phải "không bao giờ sử dụng các đối tượng có thể thay đổi". Một số tình huống hoạt động tốt hơn với các đối tượng có thể thay đổi, công việc của bạn thực sự là quyết định xem việc tạo một đối tượng có thể thay đổi sẽ phù hợp hơn với chương trình của bạn hay không. Bạn không nên luôn nói 'phải sử dụng các đối tượng bất biến', thay vào đó hãy xem bạn có thể tạo ra bao nhiêu lớp bất biến trước khi bắt đầu tự làm hại mình.


1
Nhưng ví dụ của bạn hầu như không bao giờ là những gì cần phải làm trong các ứng dụng "thực". Thông thường, đối tượng dữ liệu được cập nhật của bạn cần phải được chuyển đến một khung xác thực (Hibernate Validator, Spring Validator, v.v.) nơi đối tượng phải chịu các hoạt động khác nhau có thể được viết bởi các nhà phát triển khác nhau. Nếu đối tượng có thể thay đổi, bạn không biết liệu bạn có đang làm việc với cùng một dữ liệu đã được chuyển vào hay không (nếu một số xác thực được thực thi trước khi đối tượng của bạn đã thay đổi đối tượng tại chỗ). Nó làm cho quá trình xác nhận ít yếu tố quyết định hơn, phụ thuộc nhiều hơn vào trật tự nghiêm ngặt và không thể so sánh được.
thợ rèn

Ngoài ra, một ứng dụng "thực" có thể sẽ có một số loại bộ nhớ đệm trong bộ nhớ hoặc bộ đệm phân tán (EHCache, v.v.) chứa các đối tượng miền của bạn. Nếu bạn biết các đối tượng của mình là bất biến, bạn không cần phải chịu chi phí sao chép khi đọc. Bạn có thể truy xuất và sử dụng đối tượng trong bộ nhớ một cách an toàn mà không cần lo lắng về tính toàn vẹn của bộ nhớ cache. IMHO, đối với các đối tượng miền doanh nghiệp thuộc loại này, bạn luôn muốn các đối tượng bất biến. Các đối tượng có thể thay đổi được cho phạm vi cục bộ và một số trường hợp chuyên biệt khác mà tôi không có các ký tự để đưa vào đây.
thợ rèn

7

Tổng hợp các câu trả lời khác tôi nghĩ rằng:

  • Tính không thể thay đổi tạo điều kiện cho tính đúng đắn (cấu trúc có thể được chuyển qua tham chiếu và bạn biết rằng không có gì sẽ bị phá hủy bởi ứng dụng khách bị lỗi / độc hại) và mã đơn giản
  • Khả năng thay đổi tạo điều kiện cho sự đồng nhất : Spring và các khung công tác khác tạo ra một đối tượng không có đối số, thiết lập các thuộc tính đối tượng và với là . Đồng thời làm cho các giao diện dễ dàng hơn bằng cách sử dụng cùng một lớp để cung cấp dữ liệu và lưu sửa đổi (bạn không cần get(id): Clientsave(MutableClient)trở thành MutableClient một số hậu duệ của Client.

Nếu có một điểm trung gian (tạo, đặt thuộc tính, làm cho tính năng inmutable) có thể các khuôn khổ sẽ khuyến khích nhiều phương pháp tiếp cận inmutable hơn.

Dù sao thì tôi khuyên bạn nên suy nghĩ trong các đối tượng inmutable là "chỉ đọc Java Beans" nhấn mạnh điểm rằng nếu bạn là một cậu bé ngoan và không chạm vào phương pháp setProperty nguy hiểm đó thì tất cả sẽ ổn.


Tôi đồng ý rằng điều tốt nhất của cả hai thế giới sẽ là dành cho các thư viện và khuôn khổ chuyển sang một cách tiếp cận bất biến. Ưu điểm của bất biến là nội tại, trong khi lợi thế của có thể thay đổi là ngẫu nhiên.
thợ rèn

3

Từ Java 7, bạn có thể có các bean bất biến, tốt nhất của cả hai thế giới. Sử dụng chú thích @ConstructorProperties trên hàm tạo của bạn.

public class Person {
    private final String name;
    private final Place birthPlace;

    @ConstructorProperties({"name", "birthPlace"})
    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}


1

Thành thật mà nói, tôi không nghĩ rằng các đối tượng bất biến sẽ trở nên phổ biến như vậy.

Tôi thấy những lợi thế, nhưng các khung công tác như Hibernate và Spring hiện đang rất thịnh hành (và vì một lý do chính đáng nữa), và chúng thực sự hoạt động tốt nhất với bean.

Vì vậy, tôi không nghĩ rằng tính bất biến là xấu, nhưng nó chắc chắn sẽ hạn chế các tùy chọn tích hợp của bạn với các khuôn khổ hiện tại.

CHỈNH SỬA Các bình luận nhắc tôi làm rõ câu trả lời của mình một chút.

Chắc chắn có những lĩnh vực vấn đề mà tính bất biến rất hữu ích và thực sự được sử dụng. Nhưng tôi nghĩ rằng mặc định hiện tại dường như có thể thay đổi được vì đó là điều hầu hết được mong đợi và chỉ bất biến nếu điều đó có lợi thế rõ ràng.

Và mặc dù thực sự có thể sử dụng các hàm tạo với các đối số trong Spring, nó dường như được dự định như một cách để sử dụng mã kế thừa và / hoặc bên thứ ba với mã Spring hoàn toàn mới. Ít nhất đó là những gì tôi nhặt được từ tài liệu.


2
Ý tưởng của IoC / DI dựa trên trạng thái thiết lập / tiêm, đây là lý do tại sao tính bất biến không phổ biến trong mùa xuân. Tuy nhiên Joda-Time là một ví dụ điển hình về tính bất biến được thực hiện đúng.
lunohodov

Bạn cũng có thể sử dụng hàm tạo args để chèn các phụ thuộc với spring, có vẻ như không nhiều người làm điều đó (có thể vì khi bạn có nhiều phụ thuộc, việc có một hàm tạo tham số hơn 10 là điều đáng sợ).
Andrei Fierbinteanu

6
@lunohodov: Tôi đã sử dụng Google Guice cho DI / IoC và không có vấn đề gì khi thực hiện chèn phụ thuộc vào hàm tạo.
Jonas

Nói chung, sẽ dễ dàng nhất để làm việc với mọi thứ (đặc biệt là trong môi trường đa luồng) nếu dữ liệu được lưu trữ bằng cách sử dụng các tham chiếu không thể thay đổi được tạo một lần đến các đối tượng có thể thay đổi hoặc các tham chiếu có thể thay đổi đến các đối tượng bất biến. Việc có các tham chiếu có thể thay đổi tự do đến các đối tượng có thể thay đổi làm cho mọi thứ trở nên phức tạp hơn. Lưu ý rằng nếu XYtham chiếu đến cùng một đối tượng và một đối tượng muốn thay đổi X.something, người ta có thể giữ danh tính của X và thay đổi Y.something, hoặc giữ Y.somethingnguyên và thay đổi danh tính của X, nhưng người ta không thể giữ danh tính của X và trạng thái của Y trong khi thay đổi trạng thái của X.
supercat

Những người thúc đẩy việc sử dụng các đối tượng bất biến đôi khi không nhận ra tầm quan trọng của danh tính đối tượng và thực tế là các đối tượng có thể thay đổi có thể có danh tính bất biến theo cách mà các đối tượng bất biến không thể.
supercat

0

Bất biến về mặt lập trình trong Java: Thứ gì đó đã từng được tạo ra sẽ không bao giờ có sự thay đổi trạng thái, kể cả mong đợi hay bất ngờ!

Kỹ thuật này rất hữu ích trong lập trình phòng thủ khi một thực thể khác không thể tạo ra sự thay đổi trong trạng thái.

Ví dụ mà bạn không mong đợi sự thay đổi: Hệ thống bên ngoài (đơn hoặc đa luồng) lấy tham chiếu từ lớp của bạn và hoạt động trên đối tượng và cố ý hoặc vô tình thay đổi nó. Bây giờ nó có thể là một POJO hoặc một bộ sưu tập hoặc một tham chiếu đối tượng và bạn không mong đợi sự thay đổi trong đó hoặc bạn muốn bảo vệ dữ liệu. Bạn chắc chắn sẽ làm cho đối tượng trở nên bất biến như một kỹ thuật phòng thủ

Ví dụ mà bạn mong đợi sự thay đổi: Thực sự không cần tính bất biến vì nó sẽ cản trở các quy trình lập trình phù 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.