2012-03-05 22 views
12

Hãy nói rằng tôi có một lớp Foo trong Java có dữ liệu bất biến:Java: Phương pháp pseudo-setter cho các lớp học bất biến

class Foo { 
    final private int x; 
    public int getX() { return this.x; } 
    final private OtherStuff otherstuff; 
    public Foo(int x, OtherStuff otherstuff) { 
     this.x = x; 
     this.otherstuff = otherstuff; 
    } 
    // lots of other stuff... 
} 

Bây giờ tôi muốn thêm một phương pháp hữu ích mà sẽ tạo ra một "anh chị em" giá trị với trạng thái giống hệt nhau nhưng với một giá trị mới của x. Tôi có thể gọi nó là setX():

class Foo 
{ 
    ... 
    Foo setX(int newX) { return new Foo(newX, this.otherstuff); } 
    ... 
} 

nhưng ngữ nghĩa của setX() là khác nhau hơn so với ước setter tiêu chuẩn cho các đối tượng đậu có thể thay đổi, vì vậy bằng cách nào đó điều này không cảm thấy đúng.

Tên tốt nhất cho phương pháp này là gì?

Tôi có nên gọi nó là withX() hoặc newX() hoặc cái gì khác?


chỉnh sửa: ưu tiên bổ sung trong trường hợp của tôi: Tôi có khách hàng scripting (thông qua JSR-223 và một mô hình đối tượng tôi xuất khẩu) có thể dễ dàng có được một đối tượng Foo. Tuy nhiên, nó cồng kềnh, để gọi các nhà thầu hoặc tạo các nhà xây dựng hay bất cứ thứ gì. Vì vậy, nó là mong muốn cho tôi để cung cấp phương pháp này như là một tiện lợi cho khách hàng kịch bản.

+0

Một câu hỏi liên quan: http://stackoverflow.com/questions/521893/whats-the-best-name-for-a-non-mutating-add-method-on- một bộ sưu tập bất biến. (Tương tự như vậy, nhưng để 'add' thay vì' setX'.) – ruakh

Trả lời

12

withX() âm thanh OK vì nó là một quy ước sử dụng đối với một số mẫu Builder .

Đây là chi tiết của một "bản sao một phần" hay "người xây dựng" hơn là một "setter" ...

Nếu bạn nhìn vào java.lang.String (cũng không thay đổi) có tất cả các loại phương pháp mà trả về một String mới dựa trên một tuổi (chuỗi con, toLowerCase(), vv) ...

cập nhật: cũng xem câu trả lời từ aioobe [deriveFoo()] mà tôi thích - đó là lẽ rõ ràng hơn, đặc biệt là cho bất cứ ai không quen thuộc với các mẫu Builder .

1

Chắc chắn không phải là thiết lập, vì nó thực sự xây dựng và trả về đối tượng mới. Tôi nghĩ rằng ngữ nghĩa nhà máy sẽ là lựa chọn thích hợp hơn trong trường hợp này

public Foo newFooWith(int x) { 
    return new Foo(x, other); 
} 

Giải pháp thay thế có thể là một biến thể của các nhà xây dựng bản sao

class Foo { 
    public Foo(Foo foo, int x) { 
     return new Foo(x, foo.getOtherStuff()); 
    } 
} 
+0

thì sao chép "otherstuff" trong trường hợp này? – DNA

+0

Tôi đồng ý, nhưng một phần vấn đề của tôi xuất phát từ các trình khách kịch bản, ở đó dễ dàng có được một đối tượng 'Foo' nhưng rườm rà để có được một đối tượng' FooFactory'. –

+0

Điều này sẽ không sao chép dữ liệu cá thể khác –

12

Làm theo kiểu Font class trong API chuẩn mà bạn gọi là deriveFoo(int x).

Một tùy chọn khác là cung cấp lớp trình xây dựng có thể chấp nhận đối tượng Foo làm nguyên mẫu cho các đối tượng mới. Trong những trường hợp như vậy, bạn thường đặt tên setBar() là chỉ bar(). Điều này sẽ cung cấp cho bạn một cái gì đó như

Foo newFoo = new Foo.Builder(foo).x(123).build(); 
+2

Tôi thích phương pháp này, +1 –

+0

Trong phương pháp tiếp cận đầu tiên bạn đề xuất, làm thế nào bạn có thể lấy được 'Foo' nếu có cả hai trường' x' và 'y'' int'? –

+3

'java.awt.Font' là lớp JDK 1.0, nhiều quyết định đặt tên của bản phát hành đó không tốt. JDK 8 sử dụng quy ước 'withFoo' trong API' java.time' mới, ví dụ ['java.time.YearMonth.withMonth()'] (http://download.java.net/jdk8/docs/api/java /time/YearMonth.html#withMonth-int-). – leventov

2

Tôi sẽ gọi nó là withX(value). Nó nói rằng nó sẽ là một cái gì đó với x = value.

Nếu lớp đã có rất nhiều lĩnh vực, tôi sẽ phải sợ:

obj.withX(1).withY(2).withZ(3).withU(1)... 

Vì vậy, tôi có lẽ sẽ sử dụng xây dựng mô hình-giới thiệu một biến thể có thể thay đổi của lớp được chỉ với dữ liệu và phương pháp để tạo ra lớp gốc với trạng thái hiện tại của nó. Và ở đó tôi sẽ gọi những phương thức này x(), y(), z() và làm cho chúng trở về this. Vì vậy, nó sẽ trông giống như:

Immutable im2 = new Mutable(im1).x(1).y(2).z(3).build(); 
+0

Cách tiếp cận đôi khi có thể hữu ích để làm cho các phương thức kiểu 'với' trở nên hiệu quả ngay cả khi các mục dữ liệu cơ bản lớn và tốn kém để sao chép là thêm một mức không phụ thuộc vào kiểu đối tượng trừu tượng cơ bản và có kiểu trừu tượng đó bao gồm một phương pháp "flatten" ảo. Phương thức 'WithXX' sau đó có thể trả về một wrapper mới trỏ đến một đối tượng' WrapWithXX' thường giữ một tham chiếu đến đối tượng ban đầu và giá trị của thuộc tính mới, trừ khi nó phát hiện ra rằng "chuỗi" của các đối tượng đang nhận được quá mức trong trường hợp đó ... – supercat

+0

... nó sẽ gọi là 'Flatten()'. Phương thức 'Flatten' sẽ tạo ra một cá thể đối tượng cơ bản mới, có tính đến bất kỳ phương thức' WithXX' nào mà nó có thể phân tích cú pháp. Việc gọi 'Flatten' trên một đối tượng sẽ không thay đổi trạng thái * quan sát được của nó, nhưng sẽ thay thế việc thực hiện của nó bằng một dạng ít phức tạp hơn. Lưu ý rằng mức độ bổ sung của indirection sẽ không có chi phí, nhưng cũng sẽ không có lợi thế. Ví dụ, nếu hai trình bao bọc được tìm thấy có các đối tượng riêng biệt so sánh giống hệt nhau, một hoặc cả hai trình bao bọc có thể được thay đổi để trỏ đến một cá thể "kinh điển", thì hãy tiến hành so sánh. – supercat

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