2011-11-23 35 views
17

Tôi tìm thấy một article với một mảnh thú vị mã:Các lớp Java bất biến?

public class Employee { 

    private String firstName; 
    private String lastName; 

    //private default constructor 
    private Employee(String firstName, String lastName) { 
     this.firstName = firstName; 
     this.lastName = lastName; 
    } 

    public static Employee valueOf (String firstName, String lastName) { 
     return new Employee(firstName, lastName); 
    } 
} 

Tôi thực sự tò mò trong việc tìm hiểu các lợi thế của việc tạo ra loại lớp. Tôi hiểu rằng ở đây một đối tượng của lớp này sẽ không thay đổi, bởi vì không có cách nào thay đổi các giá trị biến của nó khi được khởi tạo. Tôi chưa bao giờ làm điều gì đó như thế này trước đây, và tôi không thực sự hiểu được lợi thế của nó.

  • Tại sao thực hành tốt?
  • Bạn có thể đặt tên cho trường hợp có thể sử dụng phương pháp này không?
  • Điều gì về các hằng số hoặc chỉ đọc các biến? Không phải là rất giống nhau?
  • Trong bài báo nói rằng điều này không tốt cho hiệu suất của ứng dụng. Nhưng tại sao?
+5

bang Mutable làm cho nó khó để suy luận về những gì đang làm. Nhìn vào lập trình hàm. –

+0

Lớp này không có phương thức truy cập. Đó là mục đích? –

+0

bài viết hữu ích nếu bạn đã không đọc nó chưa: http://www.javapractices.com/topic/TopicAction.do?Id=29 –

Trả lời

18

Ví dụ bạn đã đề cập là một số Immutable Objects. Các khái niệm được sử dụng rộng rãi của nó trong programming languages.

Trích dẫn từ liên kết ở trên.Những lợi thế là

  • rất đơn giản để xây dựng, kiểm tra, và sử dụng
  • sẽ được tự động thread-safe và không có đồng bộ vấn đề
  • không cần một constructor sao chép
  • không cần một thực hiện bản sao
  • cho phép hashCode sử dụng khởi tạo lười và lưu vào bộ nhớ cache giá trị trả về
  • không cần phải sao chép một cách bảo vệ khi được sử dụng làm trường
  • tạo các phím Bản đồ tốt và các phần tử Cài đặt (các đối tượng này không được thay đổi trạng thái khi đang trong bộ sưu tập)
  • có bất biến đẳng cấp của chúng được thiết lập một lần khi xây dựng và không bao giờ cần phải kiểm tra lại lần nữa
  • luôn có "atom atomicity" (một thuật ngữ được sử dụng bởi Joshua Bloch): nếu một đối tượng bất biến - ném một ngoại lệ, nó không bao giờ rơi vào trạng thái không mong muốn hoặc không xác định
+0

Công việc tốt liệt kê những lợi thế. Tôi chỉ muốn thêm rằng một lớp nhân viên thường không được thực hiện theo cách không thay đổi bởi vì không có điểm nào ở trên là cần thiết, nhưng trạng thái có thể thay đổi được chia sẻ giúp dễ dàng giữ tất cả các quan điểm của nhân viên nhất quán, tức là mọi sửa đổi đối với Nhân viên sẽ ngay lập tức được hiển thị cho tất cả mọi người có một tham chiếu đến nó.Nếu chúng ta phải tạo một đối tượng mới để biểu diễn trạng thái đã thay đổi, các tham chiếu cũ vẫn thấy trạng thái cũ. Đôi khi điều này là mong muốn, đôi khi không, và cập nhật tất cả các tài liệu tham khảo để trỏ đến đối tượng mới là không hiệu quả và cồng kềnh. – meriton

+2

Tôi không chắc chắn về "cho phép hashCode sử dụng khởi tạo lười biếng, và để cache giá trị trả về của nó". Bạn có nghĩa là mã băm của đối tượng được tính toán và lưu trữ trong một trường trong lần đầu tiên phương thức 'hashCode()' của nó được gọi, với trường đó được trả về sau đó? Bởi vì trong trường hợp đó, đối tượng thực sự là * không * không thay đổi, và nó mất thuộc tính "tự động an toàn chủ đề". Nhiều luồng không thể gọi một cách an toàn 'hashCode()' cùng một lúc trừ khi chúng có thể chắc chắn rằng trường này đã được khởi tạo trước đó (hoặc trừ khi bạn sử dụng 'sync' /' volatile'/whatnot). – ruakh

+0

@Ruakh: Có. Tôi tin rằng có nhiều cách để làm cho nó đa luồng an toàn ngay cả trong trường hợp đó - ví dụ, nếu bạn có thể xác định nguyên tử rằng mã băm chưa được tính toán, và sau đó tính toán và trả lại giá trị chính xác trong khi cũng khởi tạo mã băm, sau đó bạn biết rằng đối với chuỗi địa phương thực thi, giá trị chính xác luôn là giá trị được trả về. Bạn có thể tính toán giá trị hai lần, nhưng tôi nghĩ bạn vẫn sẽ luôn nhận được giá trị phù hợp. Tôi muốn tôi có "Java Concurrency in Practice" tiện dụng để tôi có thể tra cứu các chi tiết (và đảm bảo rằng tôi đúng). – jprete

4

lớp Immutable là:

  • thread-safe theo mặc định (đồng thời ghi không bao giờ xảy ra)
  • cachable

Bạn có thể đọc rất nhiều về chúng trong conext của ngôn ngữ Java trong Effective Java.

+9

'kiểm tra bình đẳng có thể được thực hiện với ==' ** Không đúng **. – Ingo

+0

Chỉ vì một lớp không thay đổi không có nghĩa là bạn có thể kiểm tra sự bình đẳng bằng "==". Chuỗi là bất biến chẳng hạn, và trong nhiều trường hợp, "==" sẽ không hoạt động, trong đó .equals() sẽ. Để làm việc này, nó cần phải được kết hợp với một nhà máy mở rộng để đảm bảo rằng không có nhiều hơn một cá thể tồn tại cho các giá trị "bằng nhau". – ziesemer

+0

Ok, tôi đã xóa nó, nhưng nếu tôi có toàn quyền kiểm soát đối với lớp, tôi có thể so sánh chúng với ==, tôi có nghĩa là những gì ziesemer đã viết. – zeller

2

Nếu bạn đang sử dụng hàm băm, có đối tượng không thay đổi là tốt vì bạn không cần phải tính toán lại hashCode khi trạng thái của đối tượng thay đổi (vì chúng không thể thay đổi).

3

Ưu điểm chính của các lớp không thay đổi là an toàn chỉ. Hầu hết các vấn đề với luồng đến từ việc chia sẻ, trạng thái có thể thay đổi. Bằng cách làm cho các đối tượng bất biến, nó dễ dàng hơn để lý luận về chúng đặc biệt là trong môi trường đa luồng.

Bài viết cho biết "tạo các đối tượng không thay đổi có thể đạt hiệu suất của ứng dụng". Tôi không chắc tại sao nó lại nói thế. Nó hoàn toàn sai. Không có gì vốn có về các đối tượng bất biến có thể ảnh hưởng đến hiệu suất của ứng dụng.

+0

Không có gì cả? Tạo một đối tượng mới và sao chép toàn bộ trạng thái của nó, nhanh như cập nhật một trường đơn lẻ? – meriton

3

-Tại sao nó là một thực hành tốt?

Bởi vì bạn có thể vượt qua lớp học xung quanh và chắc chắn nó sẽ không bao giờ bị sửa đổi bởi mã "lừa đảo". Tương tự cho Java Strings, chúng không thay đổi.

-Bạn có thể đặt tên cho trường hợp phương pháp này có thể được sử dụng không?

Nó rất hữu ích cho các dự án lớn nơi nhiều nhóm làm việc cùng nhau hoặc khi thiết kế khung hoặc API. Trong những trường hợp này, vì bạn không chịu trách nhiệm về các phần của mã, bạn không bao giờ có thể tin tưởng rằng một đối tượng bạn chuyển đến các phần khác của mã sẽ không bị thay đổi. Sử dụng tính bất biến nếu bạn cần đảm bảo đối tượng sẽ không bị sửa đổi.

-Câu về hằng số hoặc chỉ đọc các biến? Không phải là rất giống nhau?

Không có trong Java vì chúng tôi không có cả chữ và chỉ đọc. Tất cả những gì chúng tôi có là từ khóa cuối cùng đảm bảo một tham chiếu đối tượng sẽ không được sửa đổi ngoài lần gán đầu tiên. Nhưng đối tượng cơ bản vẫn có thể được sửa đổi ngay cả khi tham chiếu có thể không. Các lớp không thể thay đổi đảm bảo trạng thái đối tượng sẽ không bị thay đổi sau khi tạo.

-Trong bài báo nói rằng điều này không tốt cho hiệu suất của ứng dụng. Nhưng tại sao?

Bởi vì mỗi khi bạn cần sửa đổi đối tượng, bạn cần phải tạo các phiên bản mới. Tương tự cho Strings, bạn không thể làm myString.append("42"), bạn cần phải làm myString = myString+"42", tạo đối tượng String mới.

+2

Tôi gần như đã bỏ phiếu, ngoại trừ các nhận xét về hiệu suất, bởi vì họ sai. Có, sử dụng các đối tượng không thay đổi có nghĩa là bạn đang phân bổ nhiều đối tượng hơn, nhưng trong một triển khai JVM hiện đại, điều đó sẽ nhanh như chớp. –

+0

Có, phân bổ không phải là tốn kém, nhưng các nhà điều hành mới không chỉ phân bổ một đối tượng, nó cũng gọi các nhà xây dựng. Trong ví dụ trên, Chuỗi nối liên quan đến việc sao chép * nội dung * của toàn bộ chuỗi, có thể tốn kém cho các chuỗi lớn hoặc thường xuyên được sao chép. – meriton

+0

Chi phí phân bổ một bản sao mới của một đối tượng bất biến là một phần (hoặc hoàn toàn!) Được tạo ra bởi thực tế là bạn không cần phải tạo bản sao phòng thủ nữa. Kinh nghiệm của tôi là việc sao chép phòng thủ đối tượng giá trị thường xuyên hơn nhiều so với sửa đổi, vì vậy nếu đối tượng là bất biến, bạn thực sự tạo ra ít bản sao hơn. – jprete

1

Bài báo cho biết:

Để thực hiện một lớp học bất biến, bạn có thể xác định nó tất cả các nhà thầu tư nhân và sau đó tạo ra một phương pháp tĩnh nào để khởi tạo và đối tượng và gửi lại.

Thực ra, điều đó là sai.Hai khái niệm này không thực sự liên quan.

Ví dụ: bạn có thể khai báo hàm khởi tạo của lớp Employee của bạn và nó sẽ vẫn không thay đổi.

Hoặc bạn có thể vượt qua một đối tượng có thể thay đổi như một tham số cho phương thức nhà máy hoặc khai báo một phương pháp mutator

-> Nhân viên sẽ có thể thay đổi mặc dù bạn đang sử dụng một phương pháp nhà máy và xây dựng tư nhân.

0

Trong ví dụ cho bạn ông đang thực hiện xây dựng chế độ riêng tư, và do đó kiểm soát việc tạo đối tượng từ bên ngoài trực tiếp.

Ý nghĩa: Kể từ khi nhà xây dựng là tư nhân, bạn có thể không làm

Employee e = new Employee("steve","jobs"); 

từ bên ngoài của lớp này.

Bằng cách đó, các lập trình viên của lớp này, đang tiến hành tạo đối tượng cho lớp này vào kiểm soát của mình.

này là rất có lợi, khi bạn đang viết rất lớn lớp Server side, mà tạo ra một đối tượng có thể mất nhiều bộ nhớ do kích thước của nó. Bây giờ làm thế nào để bạn bảo vệ từ khách hàng của bạn, không tạo ra nhiều đối tượng cho lớp học của bạn?

trả lời cho câu hỏi trên là đơn giản, bằng cách làm cho nhà xây dựng riêng tư và bản thân bạn tạo đối tượng cho lớp học của bạn khi đã bao giờ bạn muốn trong phương pháp tĩnh. Lưu ý: Các phương thức tĩnh có thể được truy cập trực tiếp bằng cách sử dụng tên lớp.

Note: Đây là loại mẫu thiết kế sẽ được sử dụng nhiều trong các mẫu thiết kế Singleton, mà chỉ đòi hỏi một đối tượng cho một lớp nhất định.

Các vấn đề liên quan