2012-06-10 40 views
17

Tôi đang xử lý sự cố trong đó có một số triển khai Foo, kèm theo một số số FooBuilder. Trong khi Foo chia sẻ một số biến chung cần được đặt, chúng cũng có các biến riêng biệt yêu cầu FooBuilder tương ứng của chúng để triển khai một số chức năng cụ thể. Đối với tính cô đọng, tôi muốn có FooBuilder 's setters sử dụng phương pháp chaining, như:Java: trả về phân lớp trong chữ ký của phương thức siêu lớp

public abstract class FooBuilder { 
    ... 

    public FooBuilder setA(int A) { 
    this.A = A; 
    return this; 
    } 

    ... 
} 

public class FooImplBuilder extends FooBuilder{ 
    ... 
    public FooImplBuilder setB(int B) { 
    this.B = B; 
    return this; 
    } 
    public FooImplBuilder setC(int C) { 
    this.C = C; 
    return this; 
    } 
    ... 
} 

Và như vậy, với một số FooBuilder triển khai khác nhau. Tuy nhiên, kỹ thuật này làm mọi thứ tôi muốn, cách tiếp cận này nhạy cảm với thứ tự các phương thức gọi khi thực hiện chuỗi phương thức. Sau đây có lỗi biên dịch không xác định phương pháp:

someFoo.setA(a).setB(b)... 

Yêu cầu nhà phát triển nghĩ về thứ tự gọi phương thức trong chuỗi. Để tránh điều này, tôi muốn có các setters trong FooBuilder bằng cách nào đó trả về lớp con thực hiện thực tế. Tuy nhiên, tôi không chắc chắn làm thế nào để làm điều này. Đâu là cách tiếp cận lí tưởng nhất?

+0

Ngoài ra, nó buộc phải truyền ở khắp mọi nơi hoặc nó ngăn bạn thay đổi các thuộc tính của lớp bậc trên khi bạn muốn. –

Trả lời

13

Đây là câu hỏi hay và có vấn đề thực sự.

Cách dễ nhất để giải quyết vấn đề trong Java có thể liên quan đến việc sử dụng Generics, như đã đề cập trong câu trả lời của Jochen. Có một cuộc thảo luận tốt về vấn đề này và giải pháp hợp lý trong mục nhập blog này trên Using Inheritance with Fluent Interfaces, kết hợp generics với định nghĩa phương thức getThis() được ghi đè trong các lớp con của trình tạo để giải quyết vấn đề luôn trả về trình xây dựng của lớp chính xác.

+1

Tôi thích giải pháp này. Rất nhiều công phu hơn những gì tôi đề xuất, nhưng cũng là cách linh hoạt hơn và cuối cùng thanh lịch. – Jochen

+0

Chỉ để tóm tắt blog được liên kết và xóa các trình tạo riêng biệt: 'lớp trừu tượng công khai X > {public int a; công khai B setA (int foo) {this.a = foo; return getThis();} public abstract B getThis();} public class Y mở rộng X {public int b; public Y setB (int bar) {this.b = bar; return getThis();} public Y getThis() {return this;}} ' –

3

Generics có thể là cách để đi đến đây.

Nếu bạn khai báo Seta() một cái gì đó như thế này (pseudo-code)

<T> T setA(int a) 

trình biên dịch sẽ có thể tìm ra các loại động sản, và nếu nó không bạn có thể đưa ra gợi ý trong các mã như

obj.<RealFoo>setA(42) 
+3

+1: Có mô tả hay về chiến thuật này tại http://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html –

+0

Trình biên dịch * không * có thể tìm ra loại xích lời gọi phương thức và lặp lại kiểu cho mỗi lời gọi phương thức hầu như không phải là một tùy chọn. – meriton

+0

@DonRoby: Tại sao bạn không đăng câu trả lời đó? Nó sẽ xảy ra là một giải pháp tốt hơn so với của Jochen và chắc chắn sẽ xứng đáng một upvote ;-) – meriton

1

Đã tìm thấy this excellent answer Tôi hiện đang chia sẻ nó xung quanh.

public class SuperClass<I extends SuperClass> 
{ 
    @SuppressWarnings("unchecked") // If you're annoyed by Lint. 
    public I doStuff(Object withThings) 
    { 
     // Do stuff with things. 
     return (I)this ; // Will always cast to the subclass. Causes the Lint warning. 
    } 
} 

public class ImplementationOne 
extends SuperClass<ImplementationOne> 
{} // doStuff() will return an instance of ImplementationOne 

public class ImplementationTwo 
extends SuperClass<ImplementationTwo> 
{} // doStuff() will return an instance of ImplementationTwo 
Các vấn đề liên quan