2012-12-10 35 views
7

Tôi chỉ cần đọc một số mã được viết bởi một lập trình viên có kinh nghiệm hơn, và tôi đi qua như sau:Tại sao tạo danh sách cuối cùng riêng tư/Bộ/Bản đồ không thể sửa đổi?

public class ConsoleFormatter extends Formatter { 
    private static final Map<Level, String> PREFIXES; 

    static { 
     Map<Level, String> prefixes = new HashMap<Level, String>(); 
     prefixes.put(Level.CONFIG, "[config]"); 
     prefixes.put(Level.FINE, "[debug]"); 
     prefixes.put(Level.FINER, "[debug]"); 
     prefixes.put(Level.FINEST, "[trace]"); 
     prefixes.put(Level.INFO, "[info]"); 
     prefixes.put(Level.SEVERE, "[error]"); 
     prefixes.put(Level.WARNING, "[warning]"); 

     PREFIXES = Collections.unmodifiableMap(prefixes); 
    } 

    // ... 

} 

Như bạn có thể thấy, đây là một lớp học sử dụng cho đầu ra định dạng log. Tuy nhiên, điều khiến tôi chú ý là mã trong khối khởi tạo tĩnh: PREFIXES = Collections.unmodifiableMap(prefixes);.

Tại sao PREFIXES tạo bản đồ không thể sửa đổi? Đó là một hằng số riêng tư, do đó không có nguy cơ sửa đổi dữ liệu bên ngoài lớp đó. Nó đã được thực hiện để cung cấp cho bất biến của liên tục một cảm giác hoàn chỉnh?

Cá nhân, tôi đã trực tiếp khởi tạo PREFIXES làm HashMap và sau đó put các cặp khóa-giá trị trực tiếp, mà không tạo bản đồ giả, giữ chỗ hoặc làm cho trường trở thành bản đồ bất biến. Am i thiếu cái gì ở đây?

+0

Nó cũng có thể là _thought_ để ngăn chặn các vấn đề khởi tạo đồng thời khi khởi tạo lớp, vì việc gán một bước cho PREFIXES của toàn bộ dữ liệu. –

Trả lời

9

Bằng cách làm cho danh sách không thể sửa đổi tác giả đã ghi lại giả định của mình rằng các giá trị sẽ không bao giờ thay đổi. Bất cứ ai có thể chỉnh sửa lớp đó sau này có thể không chỉ thấy giả định đó, nhưng cũng sẽ được nhắc nhở trong trường hợp nó đã từng bị hỏng.

Điều này chỉ có ý nghĩa khi thực hiện chế độ xem dài hạn hơn. Nó làm giảm nguy cơ của các vấn đề mới phát sinh thông qua bảo trì. Tôi thích làm kiểu lập trình này, bởi vì tôi có xu hướng phá vỡ công cụ ngay cả trong các lớp học của riêng tôi. Một ngày nào đó bạn có thể đi sửa chữa nhanh và bạn quên mất một giả định được tạo ra ban đầu và có liên quan đến tính chính xác. Bạn càng có thể khóa mã, càng tốt.

+2

_ "Một ngày nào đó bạn có thể đi sửa chữa nhanh và quên mất một giả định đã được thực hiện ban đầu và có liên quan đến tính chính xác." _ Oh boy, tôi biết chính xác ý bạn là gì. – Konstantin

3

Thật đáng ngạc nhiên khi có một bản đồ, bộ sưu tập hoặc mảng có thể sửa đổi được từ bên ngoài lớp học. Bạn sẽ đánh dấu nó là final, tại sao cũng không nói ra rằng nó được cho là không thay đổi?

+0

Tôi thấy quan điểm của bạn, nhưng không có getters cho lĩnh vực được đề cập. Bạn đang nói rằng đó là thực hành tốt để làm cho họ bất biến anyway, chỉ trong trường hợp? – Konstantin

+1

@ KonstantinĐ. Không cần phải là một getter. Ví dụ yêu thích của tôi là trong Bộ luật làm việc với di sản của Michael Feather. Trong chương 13, ông đi vào chi tiết tẻ nhạt khó tin nhất về cách các vật thể có thể bị rò rỉ. Và trong chương 14, anh ta phơi bày một mảng 'private static'. Nếu anh ta làm điều đó, sau đó tôi không mong đợi một lập trình bình thường để làm cho nó đúng - trừ khi họ rất rõ ràng ẩn các đối tượng mutable đằng sau một wrapper unmodifiable. –

3

Giả sử bạn của bạn rời khỏi công việc của mình và lập trình viên ít kinh nghiệm hơn. Lập trình viên ít kinh nghiệm hơn cố gắng sửa đổi nội dung của PREFIXES ở đâu đó trong một phương thức khác trong cùng một lớp. Nó không thể sửa đổi được, nó sẽ không hoạt động. Đó là cách thích hợp để nói "đây là một hằng số, không bao giờ thay đổi nó."

9

Nếu bạn vô tình return PREFIXES từ một phương pháp, đột nhiên bất kỳ mã nào khác có thể sửa đổi nó. Làm cho hằng số thực sự bất biến bảo vệ chống lại sự ngu ngốc của riêng bạn khi bạn sửa đổi mã đó trong tương lai lúc 3 giờ sáng.

+0

chính xác ... Nếu không 'final' sẽ không đầy đủ cho bản đồ' Bản đồ PREFIXES'. – Chan

1

Giao diện bản đồ không giao tiếp mà bạn muốn thứ gì đó không thay đổi hoặc không thể sửa đổi được.

Các phương pháp sau đây sẽ hoạt động trong Eclipse Collections (trước đây là Bộ sưu tập GS).

private static final ImmutableMap<Level, String> PREFIXES = UnifiedMap.<Level, String>newMap() 
    .withKeyValue(Level.CONFIG, "[config]") 
    .withKeyValue(Level.FINE, "[debug]") 
    .withKeyValue(Level.FINER, "[debug]") 
    .withKeyValue(Level.FINEST, "[trace]") 
    .withKeyValue(Level.INFO, "[info]") 
    .withKeyValue(Level.SEVERE, "[error]") 
    .withKeyValue(Level.WARNING, "[warning]") 
    .toImmutable(); 

Điều này sẽ tạo một Bản đồ không thay đổi theo hợp đồng, vì ImmutableMap không có phương pháp đột biến trong API của nó.

Nếu bạn muốn giữ giao diện Bản đồ, cách tiếp cận này cũng sẽ hoạt động.

private static final Map<Level, String> PREFIXES = UnifiedMap.<Level, String>newMap() 
    .withKeyValue(Level.CONFIG, "[config]") 
    .withKeyValue(Level.FINE, "[debug]") 
    .withKeyValue(Level.FINER, "[debug]") 
    .withKeyValue(Level.FINEST, "[trace]") 
    .withKeyValue(Level.INFO, "[info]") 
    .withKeyValue(Level.SEVERE, "[error]") 
    .withKeyValue(Level.WARNING, "[warning]") 
    .asUnmodifiable(); 

Bạn nên lưu ý rằng không cần phải có khối tĩnh trong cả hai trường hợp.

Lưu ý: Tôi là người gửi thư cho Bộ sưu tập Eclipse.

0

Nếu bộ sưu tập được tạo thành cuối cùng, bạn không thể đặt đối tượng mới vào đó. Tuy nhiên, vẫn có thể thêm hoặc xóa các mục vào cùng một đối tượng.

Khi bạn làm cho nó không thể sửa đổi, bạn thậm chí không thể thêm hoặc xóa mục vào bộ sưu tập. Do đó, nó luôn luôn là nên làm cho bộ sưu tập Unmodifiable thay vì chỉ giữ nó như là cuối cùng.

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