Tôi có một nhà máy class XFactory
tạo ra các đối tượng class X
. Các trường hợp X
rất lớn, vì vậy mục đích chính của nhà máy là lưu trữ chúng, càng rõ ràng với mã máy khách càng tốt. Đối tượng class X
là bất biến, vì vậy đoạn mã sau có vẻ hợp lý:
# module xfactory.py
import x
class XFactory:
_registry = {}
def get_x(self, arg1, arg2, use_cache = True):
if use_cache:
hash_id = hash((arg1, arg2))
if hash_id in _registry:
return _registry[hash_id]
obj = x.X(arg1, arg2)
_registry[hash_id] = obj
return obj
# module x.py
class X:
# ...
Đó có phải là một mô hình tốt? (Tôi biết đó không phải là Mô hình Nhà máy thực tế.) Có điều gì tôi nên thay đổi không?
Bây giờ, tôi thấy rằng đôi khi tôi muốn lưu trữ X
các đối tượng vào đĩa. Tôi sẽ sử dụng pickle
cho mục đích đó và lưu trữ dưới dạng các giá trị trong _registry
tên tệp của các đối tượng được chọn thay vì tham chiếu đến các đối tượng. Tất nhiên, _registry
bản thân nó sẽ phải được lưu trữ liên tục (có thể trong một tệp dưa của chính nó, trong tệp văn bản, trong cơ sở dữ liệu hoặc đơn giản bằng cách cung cấp cho các tệp dưa chua tên tệp có chứa hash_id
).
Ngoại trừ bây giờ tính hợp lệ của đối tượng được lưu trữ không chỉ phụ thuộc vào các tham số được truyền đến get_x()
mà còn phụ thuộc vào phiên bản mã đã tạo ra các đối tượng này.
Nói một cách chính xác, ngay cả một đối tượng được lưu trong bộ nhớ cũng có thể trở nên không hợp lệ nếu ai đó sửa đổi x.py
hoặc bất kỳ phụ thuộc nào của nó và tải lại nó trong khi chương trình đang chạy. Cho đến nay tôi đã bỏ qua mối nguy hiểm này vì nó dường như không thể cho ứng dụng của tôi. Nhưng tôi chắc chắn không thể bỏ qua nó khi các đối tượng của tôi được lưu trữ vào bộ nhớ lưu trữ liên tục.
Tôi có thể làm gì? Tôi cho rằng tôi có thể làm cho hash_id
mạnh mẽ hơn bằng cách tính băm của một tuple có chứa các đối số arg1
và arg2
, cũng như tên tệp và ngày sửa đổi cuối cùng cho x.py
và mọi mô-đun và tệp dữ liệu mà nó (đệ quy) phụ thuộc vào. Để giúp xóa các tệp bộ nhớ cache sẽ không bao giờ hữu ích nữa, tôi thêm vào phần _registry
đại diện chưa được chỉnh sửa của các ngày đã sửa đổi cho mỗi bản ghi.
Nhưng ngay cả giải pháp này không an toàn 100% vì về mặt lý thuyết, ai đó có thể tải mô-đun một cách linh hoạt và tôi sẽ không biết về nó từ việc phân tích tĩnh mã nguồn. Nếu tôi đi ra ngoài và giả sử mọi tệp trong dự án là một phụ thuộc, cơ chế vẫn sẽ bị hỏng nếu một số mô-đun lấy dữ liệu từ một trang web bên ngoài, v.v.).
Ngoài ra, tần suất thay đổi x.py
và phụ thuộc của nó khá cao, dẫn đến mất hiệu lực bộ đệm nặng.
Vì vậy, tôi cho rằng tôi cũng có thể từ bỏ một số an toàn và chỉ làm mất hiệu lực bộ đệm chỉ khi có sự không phù hợp rõ ràng. Điều này có nghĩa là class X
sẽ có một định danh xác thực bộ đệm cấp độ lớp nên được thay đổi bất cứ khi nào nhà phát triển tin rằng một thay đổi xảy ra sẽ làm mất hiệu lực bộ đệm. (Với nhiều nhà phát triển, một định huỷ bỏ hiệu riêng biệt là cần thiết cho mỗi người.) Định danh này được băm cùng với arg1
và arg2
và trở thành một phần trong những chìa khóa băm lưu trữ trong _registry
.
Vì các nhà phát triển có thể quên cập nhật mã nhận dạng xác thực hoặc không nhận ra rằng họ đã vô hiệu hóa bộ đệm hiện có, nên có thể tốt hơn để thêm một cơ chế xác thực khác: class X
có thể có một phương thức trả về tất cả các "đặc điểm" đã biết X
. Chẳng hạn, nếu X
là một bảng, tôi có thể thêm tên của tất cả các cột. Tính toán băm sẽ bao gồm các đặc điểm là tốt.
Tôi có thể viết mã này, nhưng tôi sợ rằng tôi đang thiếu một cái gì đó quan trọng; và tôi cũng tự hỏi liệu có lẽ đã có một khung hoặc gói có thể thực hiện tất cả những thứ này chưa. Lý tưởng nhất, tôi muốn kết hợp bộ nhớ đệm trong bộ nhớ và dựa trên đĩa.
BIÊN TẬP:
Dường như nhu cầu của tôi có thể được phục vụ tốt bởi một mô hình hồ bơi. Tuy nhiên, về điều tra thêm, nó không phải là trường hợp. Tôi nghĩ tôi sẽ liệt kê sự khác biệt:
Một đối tượng có thể được sử dụng bởi nhiều khách hàng?
- Bi-a: Không, mỗi đối tượng cần được kiểm tra và sau đó kiểm tra khi không còn cần thiết. Cơ chế chính xác có thể phức tạp.
- XFactory: Có. Đối tượng là bất biến, và có thể được sử dụng bởi vô số khách hàng cùng một lúc. Không bao giờ cần phải tạo một bản sao thứ hai của cùng một đối tượng.
Có kích thước hồ bơi cần phải được kiểm soát?
- Bể bơi: Thường, có. Nếu vậy, chiến lược để làm như vậy có thể khá phức tạp.
- XFactory: Không. Một đối tượng phải được cung cấp theo yêu cầu cho khách hàng và nếu một đối tượng hiện tại không phù hợp, một đối tượng mới cần được tạo.
Có phải tất cả các đối tượng tự do thay thế?
- Bi-a: Có, các đối tượng thường có thể thay thế tự do (hoặc nếu không, việc kiểm tra đối tượng khách hàng cần là không quan trọng).
- XFactory: Hoàn toàn không, và rất khó để tìm hiểu xem một đối tượng nhất định có thể phục vụ một yêu cầu khách hàng nhất định hay không. Nó phụ thuộc vào việc một đối tượng hiện có có được tạo ra với (a) cùng các đối số và (b) cùng một phiên bản của mã nguồn hay không. Phần (b) không thể được xác minh bởi XFactory, vì vậy nó yêu cầu khách hàng giúp đỡ. Khách hàng hoàn thành trách nhiệm này theo hai cách. Đầu tiên, khách hàng có thể tăng bất kỳ bộ đếm phiên bản nội bộ nào được chỉ định (một cho mỗi nhà phát triển). Điều này không thể xảy ra trong thời gian chạy, chỉ nhà phát triển mới có thể thay đổi các bộ đếm này khi anh ta tin rằng việc thay đổi mã nguồn làm cho các đối tượng hiện tại không thể sử dụng được. Thứ hai, một khách hàng sẽ trả về một số bất biến về các đối tượng mà nó cần và XFactory sẽ xác minh rằng các bất biến này không bị vi phạm trước khi phục vụ đối tượng cho khách hàng. Nếu bất kỳ kiểm tra nào thất bại,
Liệu tác động hiệu suất cần phân tích cẩn thận?
- Nhóm: Có, trong một số trường hợp, một nhóm thực sự làm tổn hại đến hiệu suất nếu tổng chi phí quản lý đối tượng lớn hơn tổng chi phí tạo / hủy đối tượng.
- XFactory: Không. Chi phí tính toán của các đối tượng trong câu hỏi được biết là rất cao và việc tải chúng từ bộ nhớ hoặc từ đĩa chắc chắn là vượt trội hơn so với việc tính toán lại chúng từ đầu.
Khi nào đồ vật bị phá hủy?
- Bể bơi: Khi bể bơi ngừng hoạt động. Có lẽ nó cũng có thể phá hủy các đối tượng nếu được yêu cầu (một phần) giải phóng tài nguyên hoặc nếu một số đối tượng nhất định không được sử dụng trong một thời gian.
- XFactory: Bất cứ khi nào một đối tượng được tạo ra với phiên bản của mã nguồn không còn hiện hành, bằng chứng là vi phạm bất biến hoặc không khớp. Quá trình định vị và tiêu diệt những vật thể như vậy vào đúng thời điểm khá phức tạp. Ngoài ra, việc vô hiệu hóa theo thời gian của tất cả các đối tượng có thể được thực hiện để giảm rủi ro tích lũy khi sử dụng các đối tượng không hợp lệ. Vì XFactory không bao giờ chắc chắn rằng nó là chủ sở hữu duy nhất của một đối tượng, nên tính hợp lệ đó đạt được tốt nhất bởi một phiên bản bổ sung đối với các đối tượng khách hàng, được tăng cường theo chương trình trên cơ sở định kỳ, thay vì nhà phát triển.
Những cân nhắc đặc biệt nào tồn tại cho môi trường đa luồng?
- Bi-a: Phải tránh va chạm trong việc kiểm tra / kiểm tra đối tượng (không muốn kiểm tra một đối tượng cho hai khách hàng)
- XFactory: Phải tránh xung đột khi tạo đối tượng (không muốn tạo hai đối tượng dựa trên hai yêu cầu giống nhau)
Những gì cần phải được thực hiện nếu khách hàng không phát hành một đối tượng?
- Bi-a: Nó có thể muốn làm cho đối tượng có sẵn cho người khác sau khi chờ đợi một thời gian.
- XFactory: Không áp dụng. Khách hàng không thông báo cho XFactory về thời điểm họ thực hiện với đối tượng.
Các đối tượng cần phải được sửa đổi?
- Bi-a: Có thể phải được đặt lại về trạng thái mặc định trước khi được sử dụng lại.
- XFactory: Không, các đối tượng là bất biến.
Có bất kỳ cân nhắc đặc biệt liên quan đến sự kiên trì của các đối tượng?
- Bể bơi: Điển hình là không. Một pool là về việc tiết kiệm chi phí tạo đối tượng, vì vậy tất cả các đối tượng được lưu trong bộ nhớ (đọc từ đĩa sẽ đánh bại mục đích).
- XFactory: Có, XFactory là về việc tiết kiệm chi phí thực hiện các phép tính phức tạp, vì vậy việc lưu trữ các đối tượng được tính toán trước trên đĩa có ý nghĩa. Kết quả là XFactory cần phải giải quyết các vấn đề điển hình của việc lưu trữ liên tục; ví dụ: khi khởi tạo, nó cần kết nối với bộ lưu trữ liên tục, lấy từ siêu dữ liệu về các đối tượng hiện có sẵn ở đó và sẵn sàng tải chúng vào bộ nhớ nếu được yêu cầu. Và đối tượng có thể ở một trong ba trạng thái: Không tồn tại, mà tồn tại trên đĩa, còn tồn tại trên bộ nhớ. Trong khi XFactory đang chạy, trạng thái chỉ có thể thay đổi theo một hướng (bên phải trong chuỗi này).
Tóm lại, độ phức tạp của nhóm nằm trong các mục 1, 2, 4, 6 và có thể là 5, 7, 8. Độ phức tạp XFactory nằm ở các mục 3, 6, 9. Sự chồng chéo duy nhất là mục 6 và thực sự không phải là cốt lõi chức năng của pool hoặc XFactory, nhưng thay vào đó là một ràng buộc đối với thiết kế phổ biến đối với bất kỳ mẫu nào cần hoạt động trong môi trường đa luồng.