2012-07-07 36 views
21

Tôi có một lớp JAVA với nhiều trường. Chúng về cơ bản nên được đặt ở giai đoạn xây dựng và không bao giờ thay đổi. Về mặt ngữ nghĩa, lớp học là một điều bất biến.Java constructor của "class bất biến" với nhiều trường có giá trị mặc định?

public class A{ 
    final int a; 
    final short b; 
    final double e; 
    final String f; 
    final String g; 
    //and more 
} 

Vấn đề là thường những lĩnh vực có giá trị mặc định và do đó tôi không muốn lúc nào cũng gánh nặng cho người dùng một constructor với tất cả chúng. Hầu hết thời gian, họ chỉ cần thiết lập một vài trong số họ. Có một số cách để giải quyết vấn đề này:

  1. Tôi sẽ cần rất nhiều hàm tạo có chữ ký khác nhau.
  2. Tạo một nhóm phương thức thiết lập của trường này và chỉ đặt những giá trị không mặc định đó. Nhưng điều này bằng cách nào đó chỉ ra một ngữ nghĩa khác với bản chất bất biến.
  3. Tạo một lớp tham số mới có thể thay đổi và sử dụng lớp đó làm hàm tạo.

Không có điều nào là hoàn toàn thỏa đáng. Có cách tiếp cận nào khác không? Cảm ơn. Một cách

Trả lời

27

Tôi sẽ sử dụng một sự kết hợp của một lớp tham số và một người thợ xây API thông thạo cho việc tạo các tham số (mà bạn nên làm anyway!):

public class A { 
    private final int a; 
    private final short b; 
    private final double e; 
    private final String g; 

    public static class Aparam { 
     private int a = 1; 
     private short b = 2; 
     private double e = 3.141593; 
     private String g = "NONE"; 

     public Aparam a(int a) { 
      this.a = a; 
      return this; 
     } 

     public Aparam b(short b) { 
      this.b = b; 
      return this; 
     } 

     public Aparam e(double e) { 
      this.e = e; 
      return this; 
     } 

     public Aparam g(String g) { 
      this.g = g; 
      return this; 
     } 

     public A build() { 
      return new A(this); 
     } 
    } 

    public static Aparam a(int a) { 
     return new Aparam().a(a); 
    } 

    public static Aparam b(short b) { 
     return new Aparam().b(b); 
    } 

    public static Aparam e(double e) { 
     return new Aparam().e(e); 
    } 

    public static Aparam g(String g) { 
     return new Aparam().g(g); 
    } 

    public static A build() { 
     return new Aparam().build(); 
    } 

    private A(Aparam p) { 
     this.a = p.a; 
     this.b = p.b; 
     this.e = p.e; 
     this.g = p.g; 
    } 

    @Override public String toString() { 
     return "{a=" + a + ",b=" + b + ",e=" + e + ",g=" + g + "}"; 
    } 
} 

Sau đó tạo các trường hợp của A như thế này:

A a1 = A.build(); 
A a2 = A.a(7).e(17.5).build(); 
A a3 = A.b((short)42).e(2.218282).g("fluent").build(); 

Loại A là không thay đổi, thông số là tùy chọn và giao diện thông thạo.

+1

Bạn thậm chí không cần getters trong builder của bạn, và constructor của A có thể được tư nhân. Điều này cũng cho phép kiểm tra các tham số trong phương thức build() thay vì hàm tạo. –

+0

Đúng. Tôi coi việc làm ctor của A là riêng tư. Đó có lẽ là sạch hơn. –

+0

Một ưu điểm khác là người xây dựng có thể chọn trả về một thể hiện của A, ABis hoặc ATer (Abis và Ater là các lớp con của A), tùy thuộc vào các tham số. –

19

Hai điều bạn có thể làm:

  • Nhiều nhà xây dựng quá tải
  • Sử dụng một builder object
+4

+1 cho trình tạo. –

+0

Tôi không nghĩ rằng mô hình trình xây dựng phù hợp cho một lớp không thể thay đổi. – zggame

+0

@zggame: tất nhiên là không! Bạn sử dụng một người xây dựng để xây dựng một đối tượng khác. –

0

Một lựa chọn thú vị là để tạo ra một constructor mà phải mất một Map<String,Object> như là đầu vào, trong đó có các giá trị mà người dùng muốn chỉ định.

Nhà xây dựng có thể sử dụng giá trị được cung cấp trong bản đồ nếu có hoặc giá trị mặc định khác.

EDIT:

Tôi nghĩ rằng downvoters ngẫu nhiên đã hoàn toàn bị mất điểm - điều này không phải luôn luôn sẽ là sự lựa chọn tốt nhất nhưng nó là một kỹ thuật hữu ích mà có một số ưu điểm:

  • Ngắn gọn và tránh sự cần thiết phải tạo các lớp xây dựng/xây dựng riêng biệt
  • Nó cho phép dễ dàng lập trình các bộ tham số (ví dụ: nếu bạn đang xây dựng các đối tượng từ một DSL được phân tích cú pháp)
  • Đây là một kỹ thuật thường được sử dụng và được chứng minh để hoạt động trong các ngôn ngữ động. Bạn chỉ cần phải viết bài kiểm tra khá
+1

điều này dẫn đến nhiều lỗi, chủ yếu với khả năng tương thích kiểu, tôi sẽ nói rằng trong trường hợp này một cách tiếp cận xây dựng sẽ tốt hơn (như đề xuất của Jordão) –

+0

Vâng đó là một kỹ thuật từ ngôn ngữ động .... bạn đang giao dịch kiểm tra kiểu tĩnh tiện lợi/linh hoạt. Tùy thuộc vào bạn nếu bạn thích cách tiếp cận này, nhưng tôi thấy nó không phải là vấn đề nếu bạn viết các bài kiểm tra tốt. – mikera

+0

Đó là cách JavaScript và tốt bằng các ngôn ngữ có tính năng nhập động, nhưng tôi sẽ không làm điều đó trong Java. Có thể làm việc nếu tất cả các đối số là cùng loại có thể. Tuy nhiên, tôi nghĩ rằng Bob Martin có một cái gì đó để nói về "đi qua bản đồ băm xung quanh" không phải là một ý tưởng tốt.Không phải là -1 hoặc +1. –

0

Có nhiều trường có thể là dấu hiệu cho thấy một lớp học có quá nhiều.

Có thể bạn có thể phân chia lớp học trong một số lớp không thay đổi và chuyển trường hợp của các lớp này cho các hàm tạo của các lớp khác. Điều này sẽ hạn chế số lượng các nhà xây dựng.

1

Đây chỉ là đề xuất bán nghiêm túc, nhưng chúng tôi có thể sửa đổi mikera's answer thành an toàn.

Giả sử chúng ta có:

public class A { 
    private final String foo; 
    private final int bar; 
    private final Date baz; 
} 

Sau đó chúng ta viết:

public abstract class AProperty<T> { 
    public static final AProperty<String> FOO = new AProperty<String>(String.class) {}; 
    public static final AProperty<Integer> BAR = new AProperty<Integer>(Integer.class) {}; 
    public static final AProperty<Date> BAZ = new AProperty<Date>(Date.class) {}; 

    public final Class<T> propertyClass; 

    private AProperty(Class<T> propertyClass) { 
     this.propertyClass = propertyClass; 
    } 
} 

Và:

public class APropertyMap { 
    private final Map<AProperty<?>, Object> properties = new HashMap<AProperty<?>, Object>(); 

    public <T> void put(AProperty<T> property, T value) { 
     properties.put(property, value); 
    } 
    public <T> T get(AProperty<T> property) { 
     return property.propertyClass.cast(properties.get(property)); 
    } 
} 

Tín đồ của các mẫu thiết kế tiên tiến và/hoặc tối nghĩa thủ thuật Java sẽ nhận ra đây là một container không đồng nhất an toàn. Chỉ cần biết ơn tôi đã không sử dụng getGenericSuperclass() là tốt.

Sau đó, trở lại trong lớp mục tiêu:

public A(APropertyMap properties) { 
    foo = properties.get(AProperty.FOO); 
    bar = properties.get(AProperty.BAR); 
    baz = properties.get(AProperty.BAZ); 
} 

này đều sử dụng như thế này:

APropertyMap properties = new APropertyMap(); 
properties.put(AProperty.FOO, "skidoo"); 
properties.put(AProperty.BAR, 23); 
A a = new A(properties); 

Chỉ cần cho lulz, chúng tôi thậm chí có thể cung cấp bản đồ một giao diện thông thạo:

public <T> APropertyMap with(AProperty<T> property, T value) { 
    put(property, value); 
    return this; 
} 

Cho phép người gọi viết:

A a = new A(new APropertyMap() 
    .with(AProperty.FOO, "skidoo") 
    .with(AProperty.BAR, 23)); 

Có rất nhiều cải tiến nhỏ mà bạn có thể thực hiện cho việc này. Các loại trong AProperty có thể được xử lý một cách trang nhã hơn. APropertyMap có thể có một nhà máy tĩnh thay vì một nhà xây dựng, cho phép một phong cách mã thông thạo hơn, nếu bạn đang vào loại điều đó. APropertyMap có thể phát triển phương thức build gọi hàm tạo của A, về cơ bản biến nó thành trình tạo.

Bạn cũng có thể thực hiện một số đối tượng này thay vì chung chung hơn. APropertyAPropertyMap có thể có các lớp cơ sở chung đã thực hiện các bit chức năng, với các lớp con rất đơn giản A -specific.

Nếu bạn cảm thấy đặc biệt là doanh nghiệp và đối tượng miền của bạn là thực thể JPA2, thì bạn có thể sử dụng các thuộc tính siêu mô hình làm đối tượng thuộc tính. Điều này khiến map/builder làm việc nhiều hơn một chút, nhưng nó vẫn khá đơn giản; tôi có một trình xây dựng chung làm việc trong 45 dòng, với một phân lớp cho mỗi thực thể có chứa một phương thức một dòng.

+0

Đẹp. Tôi thích kiểu gõ tĩnh. Thỏa hiệp tốt, tôi có thể sử dụng nó cho một trường hợp khác, nơi chúng ta có một Map khổng lồ. Cảm ơn. – zggame

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