2009-08-16 23 views
29

Tôi có rất nhiều nhà máy (trừu tượng) và chúng thường được triển khai dưới dạng đơn."Singleton" nhà máy, ok hay xấu?

Thông thường để thuận tiện cho việc không phải vượt qua chúng thông qua các lớp thực sự không có kinh doanh với việc sử dụng hoặc biết các nhà máy này.

Hầu hết các lần tôi chỉ cần đưa ra quyết định lúc khởi động trong đó nhà máy thực hiện phần còn lại của chương trình mã, có thể thông qua một số cấu hình

nó trông giống ví dụ giống như

abstract class ColumnCalculationFactory { 
    private static ColumnCalculationFactory factory; 

public static void SetFactory(ColumnCalculationFactory f) { 
     factory = f; 
    } 

    public static void Factory() { 
     return factory; 
    } 

public IPercentCalculation CreatePercentCalculation(); 
public IAverageCalculation CreateAverageCalculation(); 
    .... 

} 

Thỉnh thoảng có mùi về điều này, tôi chỉ không chắc chắn điều gì - nó có thể bị ảnh hưởng toàn cầu hơn một singleton. Nó không giống như thực sự thực sự phải là chỉ có một nhà máy bao giờ tạo ColumnCalculations - mặc dù chương trình của tôi không cần nhiều hơn nữa.

Điều này có được coi là thực tiễn tốt nhất không? Tôi có nên thay thế những thứ này trong một lớp AppContext toàn cầu (bán)? Một cái gì đó khác (Tôi không hoàn toàn sẵn sàng để chuyển sang một số container IoC lớn hơn, hoặc spring.net khá btw)?

Trả lời

11

Nó thực sự phụ thuộc vào những gì bạn đang làm và phạm vi ứng dụng của bạn. Nếu nó chỉ là một ứng dụng khá nhỏ và nó sẽ không bao giờ phát triển vượt ra ngoài điều này, thì cách tiếp cận hiện tại của bạn cũng có thể ổn. Không có thực hành "tốt nhất" phổ quát cho những thứ này. Mặc dù tôi không khuyên bạn nên sử dụng đơn cho bất kỳ thứ gì ngoài phương pháp lá không quốc tịch và/hoặc các cuộc gọi một chiều (ví dụ như đăng nhập), loại bỏ nó ra khỏi tay 'chỉ vì' đó là một singleton không nhất thiết phải là điều đúng đắn để làm.

Đối với bất kỳ điều gì khác ngoài mã mẫu hoặc mã nguyên mẫu, cá nhân tôi muốn sử dụng rõ ràng việc kiểm soát với hàm tạo constructor, vì nó có nghĩa là tất cả các phụ thuộc được tính và bạn không nhận được bất kỳ 'bất ngờ' nào. Trình biên dịch sẽ không cho phép bạn khởi tạo A mà không có B và B mà không có C. Singletons ngay lập tức chôn các mối quan hệ này - bạn có thể khởi tạo A mà không có B và B mà không có C. Khi cuộc gọi từ A đến B xảy ra, bạn sẽ nhận được tham chiếu null ngoại lệ.

Điều này đặc biệt khó chịu khi thử nghiệm, vì bạn phải lặp lại công việc ngược thông qua lỗi thời gian chạy. Khi bạn kiểm tra mã, bạn đang sử dụng API giống như một coder đồng nghiệp sẽ làm, do đó, nó là dấu hiệu của các vấn đề thiết kế với cách tiếp cận này. Việc xây dựng constructor đảm bảo điều này không bao giờ có thể xảy ra - tất cả các phụ thuộc được nêu lên phía trước. Nhược điểm với việc xây dựng tiêm là cấu hình của đồ thị đối tượng của bạn phức tạp hơn. Điều này được giảm thiểu thông qua việc sử dụng một container IoC.

Tôi đoán những gì tôi đang cố gắng nói rằng nếu bạn đã đến điểm mà bạn đang cân nhắc sử dụng một số loại đối tượng ngữ cảnh và mẫu đăng ký, bạn cũng có thể xem các thùng chứa IoC. Đi đến những nỗ lực của việc tung phiên bản mutt của riêng bạn có lẽ là một sự lãng phí thời gian khi bạn có thể sử dụng một sản phẩm được thành lập, miễn phí như Autofac.

3

Đây có phải là phương pháp hay nhất không?

Tôi thấy không có vấn đề gì: bạn nói "chương trình của tôi không cần nhiều hơn", do đó việc triển khai bất kỳ điều gì linh hoạt/trừu tượng hơn có thể là trường hợp You Ain't Gonna Need It.

6

Không, bởi vì những gì bạn đang làm ở đây đang tạo ra trạng thái toàn cầu. Có tất cả các loại vấn đề với nhà nước toàn cầu - trưởng trong số đó rằng một chức năng sau đó phụ thuộc vào cách khá vô hình về hành vi của các chức năng khác. Nếu một hàm gọi một hàm khác mà quên lưu trữ và khôi phục factory trước khi nó kết thúc, thì bạn có vấn đề bởi vì bạn thậm chí không thể lấy lại giá trị cũ trừ khi bạn đã lưu nó ở đâu đó. Và bạn phải thêm mã để làm điều đó (đánh giá bằng mã của bạn, tôi đoán bạn đang ở trong một ngôn ngữ với finally, để lại nhiều chỗ hơn cho những sai lầm). Hơn nữa, nếu bạn kết thúc với mã mà cần phải chuyển đổi giữa hai nhà máy một cách nhanh chóng cho hai subobjects, bạn phải viết một cuộc gọi phương thức tại mỗi điểm - bạn không thể lưu trữ trạng thái trong mỗi subobject (tốt, bạn có thể, nhưng sau đó bạn đánh bại mục đích của nhà nước toàn cầu [mà thừa nhận là không nhiều]).

Nó có thể có ý nghĩa nhất để lưu trữ loại nhà máy như một thành viên, và chuyển nó đến hàm tạo của đối tượng mới cần nó (hoặc tạo một đối tượng mới khi cần thiết, v.v.). Nó cũng cho phép bạn kiểm soát tốt hơn - bạn có thể đảm bảo rằng tất cả các đối tượng được xây dựng bởi đối tượng A đi qua cùng một nhà máy, hoặc bạn có thể đưa ra các phương pháp để hoán đổi các nhà máy.

+0

mặc dù được đặt trước công chúng SetFactory không đảm bảo ai đó không thể trao đổi thêm lộn xộn với nhà máy, tôi thường dường như chỉ đặt nhà máy khi khởi động dựa trên cấu hình ban đầu hoặc cấu hình. – leeeroy

+0

sau đó bạn rơi vào con mồi đối với nhược điểm của những người độc thân ... họ là những người độc thân. – coppro

4

Tôi khuyên bạn nên chống lại nó, bởi vì nó làm cho nó rất khó để viết bài kiểm tra đơn vị phong nha cho mã gọi các nhà máy này.

Misko Hevery có một số nice article về điều này trên blog của mình.

+0

Với mã trên, bạn có thể dễ dàng chế tạo một nhà máy. – nos

+0

Chắc chắn nhưng bạn sẽ ngay lập tức biết những người bạn phải giả lập, trong một ứng dụng không tầm thường mà bạn không biết các lớp phụ thuộc của trái tim? Một lần nữa, tôi đề cập đến bài viết của Misko. – jqno

+0

Misko cũng có một số video tuyệt vời trên youtube về chủ đề này, http://www.youtube.com/watch?v=acjvKJiOvXw – Despertar

11

Có một vài đơn là khá điển hình và thường không có vấn đề - rất nhiều người độc thân dẫn đến một số mã kích thích.

Chúng tôi vừa trải qua một tình huống mà chúng tôi phải kiểm tra các lớp học đơn giản của chúng tôi. Vấn đề là khi bạn đang kiểm tra lớp b, và nó được lớp c (một singleton), bạn không có cách nào để loại bỏ lớp c (Ít nhất EasyMock sẽ không cho phép chúng ta thay thế phương thức nhà máy tĩnh của một lớp singleton.

Một cách khắc phục đơn giản là có "Người định cư" cho tất cả các người độc thân của bạn vì mục đích thử nghiệm. Không thực sự khuyến khích.

Một điều khác mà chúng tôi đã cố gắng là có một lớp duy nhất chứa tất cả các đĩa đơn - đăng ký. Làm điều này là nhận được khá gần với tiêm phụ thuộc đó là những gì bạn gần như chắc chắn sẽ được sử dụng.

Ngoài thử nghiệm, tôi đã học được một thời gian dài trước đây rằng khi không bao giờ bao giờ có nhiều hơn một trường hợp của một đối tượng nhất định; trong vòng tiếp theo, họ thường muốn hai, làm cho các đơn MUCH tốt hơn các lớp tĩnh - ít nhất bạn có thể thêm một tham số vào getter của một singleton và trả về một tham số thứ hai mà không cần tái cấu trúc quá nhiều.).

Ở mọi mức độ, hãy nhìn vào DI, bạn có thể thực sự hạnh phúc.

+0

Đồng ý. Một hệ quả khác của singletons là "rò rỉ" các giá trị thiết lập trong các bài kiểm tra đơn vị. Chúng tôi đã phải tạo ra các JVM forking cho mỗi bài kiểm tra, vì một bài kiểm tra đơn lẻ hoặc chỉ được sử dụng bởi một bài kiểm tra ở trạng thái A và hạ lưu nó cần phải ở trạng thái B. – Trenton

+0

Vấn đề với TDD mở rộng, là bạn thường làm phức tạp mã và làm cho nó dễ bị lỗi hơn vì lợi ích của việc sử dụng các công cụ kiểm tra. Tôi mạnh mẽ tin rằng nếu bạn phải thay đổi kiến ​​trúc vì lợi ích của Mocking, bạn đang lạm dụng Mocking, và thay vào đó nên sử dụng một cấu hình giả định của Nhà máy/Singleton. Mức độ bao phủ 100% mã là mất trí và sẽ gây ra nhiều sự cố và phức tạp hơn mức độ sẽ giải quyết. – user3225313

+0

Tôi đồng ý về TDD, nhưng đó không phải là lý do duy nhất để sử dụng DI. Ngoài ra, mã TESTABLE nói chung sẽ tốt hơn mã không thể kiểm tra, bất kể sự tồn tại thực tế của các thử nghiệm. Có DI thay vì một singleton không bao giờ là xấu và có thể được thực sự tốt trong trường hợp bạn thực sự có thể muốn kiểm tra nó, ngay cả khi bạn không có 100% thử nghiệm bảo hiểm. –

1

Các hình thức xấu của singleton là một trong đó thực hiện tương đương với phương pháp máy của bạn với:

return new CalculationFactory(); 

Đó là khó khăn hơn nhiều để còn sơ khai, mô hình hoặc quấn.

Phiên bản trả về một đối tượng được tạo ở nơi khác tốt hơn nhiều, mặc dù giống như hầu hết mọi thứ, có thể bị lạm dụng hoặc lạm dụng.

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