2008-11-20 37 views
5

Tôi rất tò mò về khả năng cung cấp bất biến cho đậu java (bằng đậu ở đây tôi có nghĩa là các lớp học với một hàm tạo rỗng cung cấp getters và setters cho các thành viên). Rõ ràng các lớp này không phải là bất biến và nơi chúng được sử dụng để vận chuyển các giá trị từ lớp dữ liệu, điều này có vẻ như là một vấn đề thực sự.Đậu bất biến trong Java

Một cách tiếp cận cho vấn đề này đã được đề cập ở đây trong StackOverflow được gọi là "Mẫu đối tượng không thể thay đổi trong C#", nơi đối tượng bị đóng băng khi được xây dựng hoàn chỉnh. Tôi có một cách tiếp cận khác và thực sự muốn nghe ý kiến ​​của mọi người về nó.

Mẫu liên quan đến hai lớp không thay đổi và có thể thay đổi khi cả hai có thể thay đổi và không thay đổi cả hai đều triển khai một giao diện cung cấp các phương thức đậu không biến đổi.

Ví dụ

public interface DateBean { 
    public Date getDate(); 
    public DateBean getImmutableInstance(); 
    public DateBean getMutableInstance(); 
} 

public class ImmutableDate implements DateBean { 
    private Date date; 

ImmutableDate(Date date) { 
    this.date = new Date(date.getTime()); 
} 

public Date getDate() { 
    return new Date(date.getTime()); 
} 

    public DateBean getImmutableInstance() { 
     return this; 
    } 

    public DateBean getMutableInstance() { 
     MutableDate dateBean = new MutableDate(); 
     dateBean.setDate(getDate()); 
     return dateBean; 
    } 
} 

public class MutableDate implements DateBean { 
    private Date date; 

public Date getDate() { 
    return date; 
} 

public void setDate(Date date) { 
    this.date = date; 
} 

public DateBean getImmutableInstance() { 
    return new ImmutableDate(this.date); 
} 

    public DateBean getMutableInstance() { 
     MutableDate dateBean = new MutableDate(); 
     dateBean.setDate(getDate()); 
     return dateBean; 
    } 
} 

Cách tiếp cận này cho phép đậu được xây dựng sử dụng phản chiếu (bằng các công ước thông thường) và cũng cho phép chúng ta chuyển đổi sang một phiên bản bất biến vào dịp gần nhất. Thật không may là rõ ràng là một số lượng lớn boilerplate cho mỗi đậu.

Tôi rất muốn nghe cách tiếp cận của người khác đối với vấn đề này. (Lời xin lỗi của tôi vì đã không cung cấp một câu hỏi hay, có thể được trả lời chứ không phải là thảo luận :)

Trả lời

2

tôi nghĩ rằng tôi muốn sử dụng các mô hình đoàn - làm cho một lớp ImmutableDate với một thành viên DateBean duy nhất phải được xác định trong các nhà xây dựng:

public class ImmutableDate implements DateBean 
{ 
    private DateBean delegate; 

    public ImmutableDate(DateBean d) 
    { 
     this.delegate = d; 
    } 

    public Date getDate() 
    { 
     return delegate.getDate(); 
    } 
} 

Nếu bao giờ tôi cần phải buộc bất biến trên một DateBean d, tôi chỉ mới ImmutableDate (d) trên đó. Tôi có thể đã được thông minh và chắc chắn rằng tôi đã không ủy nhiệm các đại biểu, nhưng bạn có được ý tưởng. Điều đó tránh được vấn đề của một khách hàng đang cố gắng đưa nó vào thứ gì đó có thể thay đổi được. Điều này giống như JDK thực hiện với Collections.unmodifiableMap(), vv (trong những trường hợp đó, tuy nhiên, các hàm đột biến vẫn phải được thực hiện và được mã hóa để ném một ngoại lệ thời gian chạy. Dễ dàng hơn nhiều nếu bạn có một giao diện cơ bản không có mutators).

Tuy nhiên, một lần nữa nó là mã soạn sẵn tẻ nhạt nhưng nó là loại điều mà một IDE tốt như Eclipse có thể tự động tạo ra cho bạn chỉ với một vài cú click chuột.

Nếu đó là thứ bạn làm cho nhiều đối tượng miền, bạn có thể muốn xem xét sử dụng proxy động hoặc thậm chí có thể AOP. Nó sẽ là tương đối dễ dàng sau đó để xây dựng một proxy cho bất kỳ đối tượng, ủy thác tất cả các phương pháp có được, và bẫy hoặc bỏ qua các phương pháp thiết lập khi thích hợp.

+0

Đó là một ý tưởng rất hay. Điều đó phù hợp với vấn đề rất thanh lịch. Chúc mừng Chris. –

+0

Nếu đó là một đối tượng ngày được tổ chức trong DateBean, bạn sẽ phải tạo một ngày mới trên get. Nếu không, bạn có thể sửa đổi ngày qua ImmutableDate.getDate(). Set .... – monksy

3

Một số bình luận (không nhất thiết là vấn đề):

  1. Lớp ngày là chính nó có thể thay đổi vì vậy bạn đang sao chép một cách chính xác nó để bảo vệ bất biến, nhưng cá nhân tôi thích chuyển đổi thành dài trong hàm tạo và trả về một Date (longValue) mới trong getter.
  2. Cả hai phương thức getWhateverInstance() của bạn trả về DateBean sẽ cần phải đúc, có thể là một ý tưởng để thay đổi giao diện để trả về loại cụ thể thay thế.
  3. Có nói rằng tất cả những gì tôi có xu hướng chỉ có hai lớp học có thể thay đổi và một không thay đổi, chia sẻ một giao diện chung (tức là chỉ nhận) nếu thích hợp. Nếu bạn nghĩ rằng sẽ có rất nhiều chuyển đổi qua lại, sau đó thêm một hàm tạo bản sao cho cả hai lớp.
  4. Tôi thích các lớp không thay đổi để khai báo các trường như final để làm cho trình biên dịch cũng thực thi bất biến.

ví dụ:

public interface DateBean { 
    public Date getDate(); 
} 

public class ImmutableDate implements DateBean { 
    private final long date; 

    ImmutableDate(long date) { 
     this.date = date; 
    } 

    ImmutableDate(Date date) { 
     this(date.getTime()); 
    } 

    ImmutableDate(DateBean bean) { 
     this(bean.getDate()); 
    } 

    public Date getDate() { 
     return new Date(date); 
    } 
} 


public class MutableDate implements DateBean { 
    private long date; 

    MutableDate() {} 

    MutableDate(long date) { 
     this.date = date; 
    } 

    MutableDate(Date date) { 
     this(date.getTime()); 
    } 

    MutableDate(DateBean bean) { 
     this(bean.getDate()); 
    } 

    public Date getDate() { 
     return new Date(date); 
    } 

    public void setDate(Date date) { 
     this.date = date.getTime(); 
    } 

} 
+0

Cảm ơn nhận xét của bạn. Tôi đồng ý rằng các nhà xây dựng bản sao là một cải tiến đáng kể so với các phương pháp nhà máy giao diện. Các lưu ý về các thành viên cuối cùng cũng được thực hiện. Chúc mừng. –

1

Tôi sử dụng giao diện và truyền để kiểm soát khả năng thay đổi của đậu. Tôi không thấy một lý do chính đáng để làm phức tạp các đối tượng miền của tôi với các phương thức như getImmutableInstance()getMutableInstance().

Tại sao không chỉ sử dụng thừa kế và trừu tượng? ví dụ.

public interface User{ 

    long getId(); 

    String getName(); 

    int getAge(); 

} 

public interface MutableUser extends User{ 

    void setName(String name); 

    void setAge(int age); 

} 

Dưới đây là những gì khách hàng của mã này sẽ được thực hiện:

public void validateUser(User user){ 
    if(user.getName() == null) ... 
} 

public void updateUserAge(MutableUser user, int age){ 
    user.setAge(age); 
} 

Liệu nó trả lời câu hỏi của bạn?

yc

+0

Tôi giả sử bạn có nghĩa là cho giao diện MutableUser để mở rộng giao diện người dùng? Không cần phải nói điều này vẫn đòi hỏi một lớp cụ thể. Phía xuống là nó sẽ mở bạn "không thay đổi" được thay đổi có thể thay đổi bởi một dàn diễn viên đơn giản mà có thể không phải là những gì bạn muốn. –

+0

Mặc dù giao diện người dùng không cung cấp các phương pháp để thay đổi đối tượng người dùng không phải trên khuôn mặt của nó như một sự bảo đảm chắc chắn về tính bất biến như tôi sẽ yêu cầu từ mẫu này. –

+0

Mặc dù tôi nghĩ rằng cách tiếp cận của bạn cung cấp một giải pháp làm việc tốt, trong đó loại (Người dùng) ngầm gợi ý rằng Người dùng không nên bị đột biến. Trong nhiều ngữ cảnh, điều này có thể là đủ. Cảm ơn. –

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