Khi nào sử dụng Constructor và khi nào sử dụng phương thức getInstance () (các phương thức của nhà máy tĩnh)?


84
  1. Khi nào và như thế nào chúng ta nên sử dụng một hàm tạo

    Foo bar = new Foo();
    
  2. Và khi nào và làm thế nào chúng ta nên sử dụng getInstance () (phương thức nhà máy tĩnh)

    Foo bar = Foo.getInstance();
    

sự khác biệt giữa hai cái đó là gì? Tôi đã luôn sử dụng một hàm tạo, nhưng khi nào tôi nên sử dụng getInstance()thay thế?


Bạn có đang viết lớp cho mình không? Nếu không, bạn đang gọi cái gì mà cung cấp nó?
Chris B.

vì vậy, việc triển khai lớp chính nó có nghĩa là tôi đang thực hiện mô hình singleton, phải không?
zengr

Bạn đang gọi getInstance() hay bạn đang viết một phương thức được gọi getInstance()?
Chris B.

1
Nếu câu hỏi là về phương thức constructor và static factory , tôi khuyên bạn nên làm rõ và thay đổi tiêu đề.
Pascal Thivent

@zengr: liên quan đến update2 của bạn, điều này có thể là do bạn đã không đặt tên cho phương thức tĩnh của mình theo quy ước, điều này quy định rằng nó phải được đặt tên Foo.newInstance(). Foo.getInstance()là một quy ước để lấy thể hiện singleton của một lớp. Bạn nên sửa ví dụ của mình và sử dụng Foo.newInstance()thay thế.
JRL

Câu trả lời:


96

Mọi người dường như tập trung vào các singleton trong khi tôi nghĩ rằng câu hỏi thực sự là về phương thức constructor và static factory .

Đây thực sự là Mục 1: Hãy xem xét các phương thức nhà máy tĩnh thay vì các hàm tạo của Java hiệu quả của Joshua Bloch:

Mục 1: Xem xét các phương pháp nhà máy tĩnh thay vì các hàm tạo

Cách thông thường để một lớp cho phép máy khách lấy một thể hiện của chính nó là cung cấp một phương thức khởi tạo công khai. Có một kỹ thuật khác nên là một phần của bộ công cụ của mọi lập trình viên. Một lớp có thể cung cấp một phương thức nhà máy công cộng tĩnh , đơn giản là một phương thức tĩnh trả về một thể hiện của lớp. Đây là một ví dụ đơn giản từ Boolean(lớp nguyên thủy được đóng hộp cho kiểu nguyên thủy boolean). Phương thức này dịch một giá trị nguyên thủy boolean thành một Booleantham chiếu đối tượng:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

Lưu ý rằng một phương thức nhà máy tĩnh không giống với mẫu Phương pháp Nhà máy từ Mẫu thiết kế [Gamma95, tr. 107]. Phương pháp nhà máy tĩnh được mô tả trong mục này không có tương đương trực tiếp trong Mẫu thiết kế .

Một lớp có thể cung cấp cho khách hàng của nó các phương thức nhà máy tĩnh thay vì hoặc thêm vào các hàm tạo. Cung cấp một phương thức nhà máy tĩnh thay vì một phương thức khởi tạo công cộng có cả ưu điểm và nhược điểm.

Ưu điểm (trích sách):

  • Một ưu điểm của các phương thức static factory là, không giống như các constructor, chúng có tên.
  • Ưu điểm thứ hai của các phương thức static factory là, không giống như các hàm tạo, chúng không bắt buộc phải tạo một đối tượng mới mỗi khi chúng được gọi.
  • Ưu điểm thứ ba của các phương thức static factory là, không giống như các hàm tạo, chúng có thể trả về một đối tượng thuộc bất kỳ kiểu con nào thuộc kiểu trả về của chúng.
  • Ưu điểm thứ tư của các phương pháp nhà máy tĩnh là chúng giảm bớt tính chi tiết của việc tạo các thể hiện kiểu được tham số hóa.

Nhược điểm (vẫn trích sách):

  • Nhược điểm chính của việc chỉ cung cấp các phương thức nhà máy tĩnh là các lớp không có hàm tạo công khai hoặc được bảo vệ không thể được phân lớp.
  • Một nhược điểm thứ hai của các phương thức static factory là chúng không dễ phân biệt với các phương thức static khác.

8
Điều cuối cùng có thể được giảm nhẹ phần nào. Tôi có xu hướng có một lớp bên trong tĩnh được gọi là Factory và các phương thức newInstance khác nhau trong đó để làm rõ ràng hơn khi tôi tạo các phương thức factory. Nó đã giúp tôi săn lùng khoảng thời gian trong quá khứ. :)
Rich Schuler

@Qberticus lớp bên trong dành riêng cho các phương thức gốc là một ý tưởng hay. Tôi sẽ thử nó, thx.
Dave O.

Chắc chắn các hàm tạo của các lớp ít nhất protectedphải cho phép phân lớp, nhưng mã máy khách có nhận được bất kỳ lợi ích thực sự nào từ việc tạo một đối tượng mới và gọi hàm tạo của nó, so với việc gọi một phương thức nhà máy tĩnh không? Đối với tôi, có vẻ như một lệnh gọi hàm tạo chuỗi là một phương thức thể hiện về mặt ngữ nghĩa, trong khi chuỗi "tạo đối tượng đơn nguyên và gọi hàm tạo của nó" về mặt ngữ nghĩa tương đương với một lệnh gọi phương thức nhà máy tĩnh.
supercat

9

Bạn có hai câu hỏi: khi nào tôi nên gọi một getInstance()phương thức và khi nào tôi nên tạo một phương thức?

Nếu bạn đang quyết định có nên gọi một getInstance()phương thức hay không, thật dễ dàng. Bạn chỉ cần đọc tài liệu lớp học để biết khi nào bạn nên gọi nó. Ví dụ, NumberFormatcung cấp một phương thức khởi tạo một getInstance()phương thức; các getInstance()phương pháp sẽ cung cấp cho bạn một địa phương NumberFormat. Đối CalendarMặt khác, các nhà xây dựng được bảo vệ. Bạn phải gọi getInstance()để có được một.

Nếu bạn đang quyết định có nên tạo một getInstance()phương pháp hay không, bạn cần phải quyết định những gì bạn đang cố gắng hoàn thành. Hoặc bạn không muốn mọi người gọi hàm tạo của bạn (bạn đang tạo một singleton hoặc một nhà máy ), hoặc bạn không phiền (như ở NumberFormattrên, nơi họ khởi tạo một số đối tượng để thuận tiện cho người gọi).


Mẩu chuyện dài? Đừng lo lắng về việc tạo getInstance()các phương thức trong mã của riêng bạn. Nếu thời gian phát sinh khi chúng hữu ích, bạn sẽ biết. Và nói chung, nếu bạn có thể gọi phương thức khởi tạo của một lớp, bạn có thể đang làm điều đó, ngay cả khi lớp đó cung cấp một getInstance()phương thức.


7

Việc sử dụng các phương thức getInstance:

  • Nếu bạn muốn kiểm soát / hạn chế việc xây dựng, ví dụ như Singleton
  • triển khai Factory pattern, ví dụ DriverManager.getConnection
  • Khi bạn muốn cung cấp tên tốt hơn như thế nào trường hợp được xây dựng (nhà xây dựng phải có cùng tên với tên lớp), kiểm tra các NumberFormat phương pháp nhà máy getCurrencyInstance , getIntegerInstance và những người khác như ví dụ về điều này.

Nhưng hầu hết thời gian đối tượng của bạn sẽ là một POJO đơn giản và việc sử dụng các hàm tạo công khai là giải pháp thực tế và rõ ràng nhất.

U1: getInstance From Another Class

Để trả về một thể hiện của một lớp khác:

public class FooFactory {
    public static Foo getInstance() {
        return new Foo();
    }
}

NumberFormat.getInstancecác phương thức thực hiện điều này vì chúng thực sự trả về các trường hợp của DecimalFormat.

U2: Vấn đề về Singleton

Mô hình singleton hạn chế nhiều lợi ích của lập trình hướng đối tượng. Singleton thường có các hàm tạo riêng, do đó bạn không thể mở rộng chúng. Vì bạn sẽ truy cập nó thông qua phương thức getInstance của nó và không tham chiếu đến bất kỳ giao diện nào, bạn sẽ không thể hoán đổi nó cho một triển khai khác.


trong trường hợp của một factory pattern, một tên phương thức khác như 'createInstance' hoặc 'buildInstance' sẽ phù hợp hơn nhiều, nhưng vẫn có thể xảy ra trường hợp người hỏi có thể muốn (nghe có vẻ hơi khó hiểu và giống như một số bài tập về nhà đối với tôi)
jdehaan

6

Nếu bạn có thể sử dụng cả hai thì nó giống như một mô hình singleton được triển khai kém .

Sử dụng tùy chọn thứ hai nếu bạn dự định chỉ có một thể hiện duy nhất của lớp trong hệ thống của mình và đặt hàm tạo là riêng tư sau đó.

Sử dụng cái đầu tiên để cho phép xây dựng một số đối tượng của lớp.

NHƯNG đừng cung cấp cho lớp của bạn cả hai khả năng.

Chú ý không sử dụng quá nhiều các singleton, chỉ sử dụng chúng nếu thực sự chỉ có một thể hiện tồn tại trong hệ thống, nếu không bạn sẽ hạn chế khả năng sử dụng lại lớp của mình trong các dự án khác. Nghe có vẻ thú vị khi có thể gọi getInstance từ mọi nơi trong dự án của bạn nhưng điều đó làm cho không rõ ai thực sự sở hữu phiên bản đó: không ai và / hoặc tất cả. Nếu bạn có nhiều ổ đĩa đơn trong một dự án, bạn có thể đặt cược rằng hệ thống được thiết kế kém (thông thường). Singleton nên được sử dụng cẩn thận, áp dụng lời khuyên tương tự như đối với các biến toàn cục.


Đã cập nhật câu hỏi, hơi khó hiểu, xin lỗi
zengr

4
------> "Cẩn thận để không dùng quá độc thân" <------
Bart van Heukelom

1

Một trường hợp mà tôi luôn thích một nhà máy tĩnh hơn một nhà xây dựng thông thường là khi tôi biết việc xây dựng đối tượng sẽ chậm hơn. Tôi khởi tạo đơn giản trên phương thức khởi tạo, nhưng nếu tôi cần tạo thứ gì đó nặng, tôi sẽ sử dụng một phương thức tĩnh và ghi lại hành vi.


0

Singleton là ác. Các vấn đề tôi đã gặp xung quanh nó không phải là về việc tái sử dụng hoặc khả năng mở rộng của một hệ thống (mặc dù tôi có thể thấy điều đó có thể xảy ra như thế nào), hơn thế nữa tôi không thể đếm được số lần tôi đã thấy các lỗi khó hiểu trong hệ thống phát sinh từ các singleton.

Nếu bạn cần sử dụng một singleton, hãy đảm bảo rằng phạm vi của nó là cực kỳ hẹp, tức là giới hạn một cách hợp lý số lượng các đối tượng khác trong hệ thống của bạn biết về nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.