Đây là một phiên âm được hình thành tốt hơn của nhận xét ban đầu của tôi dưới câu hỏi của bạn. Câu trả lời cho các câu hỏi của OP có thể được tìm thấy ở dưới cùng của câu trả lời này. Ngoài ra xin vui lòng kiểm tra các lưu ý quan trọng nằm ở cùng một nơi.
Những gì bạn hiện đang mô tả, Sipo, là một mẫu thiết kế được gọi là Bản ghi hoạt động . Như với tất cả mọi thứ, ngay cả cái này đã tìm thấy vị trí của nó trong số các lập trình viên, nhưng đã bị loại bỏ để ủng hộ kho lưu trữ và các mẫu ánh xạ dữ liệu vì một lý do đơn giản, khả năng mở rộng.
Nói tóm lại, một bản ghi hoạt động là một đối tượng, trong đó:
- đại diện cho một đối tượng trong miền của bạn (bao gồm các quy tắc kinh doanh, biết cách xử lý các hoạt động nhất định trên đối tượng, chẳng hạn như nếu bạn có thể hoặc không thể thay đổi tên người dùng, v.v.),
- biết cách lấy, cập nhật, lưu và xóa thực thể.
Bạn giải quyết một số vấn đề với thiết kế hiện tại của bạn và vấn đề chính của thiết kế của bạn được giải quyết ở điểm cuối cùng, thứ 6, (cuối cùng nhưng không kém phần quan trọng, tôi đoán vậy). Khi bạn có một lớp mà bạn đang thiết kế một hàm tạo và bạn thậm chí không biết hàm tạo nên làm gì, thì lớp đó có thể đang làm gì đó sai. Điều đó đã xảy ra trong trường hợp của bạn.
Nhưng việc sửa thiết kế thực sự khá đơn giản bằng cách chia đại diện thực thể và logic CRUD thành hai (hoặc nhiều) lớp.
Đây là những gì thiết kế của bạn trông giống như bây giờ:
Employee
- chứa thông tin về cấu trúc nhân viên (thuộc tính của nó) và phương thức sửa đổi thực thể (nếu bạn quyết định đi theo cách có thể thay đổi), chứa logic CRUD cho Employee
thực thể, có thể trả về danh sách các Employee
đối tượng, chấp nhận một Employee
đối tượng khi bạn muốn cập nhật một nhân viên, có thể trả lại một Employee
thông qua một phương thức nhưgetSingleById(id : string) : Employee
Wow, lớp học có vẻ rất lớn.
Đây sẽ là giải pháp được đề xuất:
Employee
- chứa thông tin về cấu trúc nhân viên (thuộc tính của nó) và phương pháp làm thế nào để sửa đổi thực thể (nếu bạn quyết định đi theo cách có thể thay đổi)
EmployeeRepository
- chứa logic CRUD cho Employee
thực thể, có thể trả về danh sách các Employee
đối tượng, chấp nhận một Employee
đối tượng khi bạn muốn cập nhật một nhân viên, có thể trả về một đối tượng Employee
thông qua một phương thức nhưgetSingleById(id : string) : Employee
Bạn đã nghe nói về sự tách biệt của mối quan tâm ? Không, bạn sẽ bây giờ. Đây là phiên bản ít nghiêm ngặt hơn của Nguyên tắc Trách nhiệm duy nhất, nói rằng một lớp học thực sự chỉ có một trách nhiệm, hoặc như chú Bob nói:
Một mô-đun nên có một và chỉ một lý do để thay đổi.
Một điều khá rõ ràng là nếu tôi có thể phân chia rõ ràng lớp ban đầu của bạn thành hai lớp vẫn có giao diện được làm tròn tốt, thì lớp ban đầu có thể đã làm quá nhiều, và nó đã được.
Điều tuyệt vời về mẫu kho lưu trữ, nó không chỉ đóng vai trò là sự trừu tượng để cung cấp một lớp giữa giữa cơ sở dữ liệu (có thể là bất cứ thứ gì, tệp, noQuery, SQL, hướng đối tượng), mà thậm chí không cần phải cụ thể lớp học. Trong nhiều ngôn ngữ OO, bạn có thể định nghĩa giao diện là một thực tế interface
(hoặc một lớp với một phương thức ảo thuần túy nếu bạn ở trong C ++) và sau đó có nhiều triển khai.
Điều này hoàn toàn đưa ra quyết định cho dù một kho lưu trữ là một triển khai thực tế của bạn chỉ đơn giản là dựa vào giao diện bằng cách thực sự dựa vào cấu trúc với interface
từ khóa. Và kho lưu trữ chính xác là như vậy, nó là một thuật ngữ ưa thích cho sự trừu tượng hóa lớp dữ liệu, cụ thể là ánh xạ dữ liệu vào miền của bạn và ngược lại.
Một điều tuyệt vời khác về việc tách nó thành (ít nhất) hai lớp là bây giờ Employee
lớp có thể quản lý rõ ràng dữ liệu của chính nó và thực hiện nó rất tốt, bởi vì nó không cần phải quan tâm đến những điều khó khăn khác.
Câu 6: Vậy nhà xây dựng nên làm gì trong lớp vừa tạo Employee
? Nó đơn giản. Cần lấy các đối số, kiểm tra xem chúng có hợp lệ không (chẳng hạn như độ tuổi không nên âm hoặc tên không nên trống), đưa ra lỗi khi dữ liệu không hợp lệ và nếu xác thực được chuyển, hãy gán đối số cho các biến riêng tư của thực thể. Bây giờ nó không thể giao tiếp với cơ sở dữ liệu, vì đơn giản là nó không biết làm thế nào để làm điều đó.
Câu hỏi 4: Không thể trả lời được, nói chung, vì câu trả lời phụ thuộc rất nhiều vào chính xác những gì bạn cần.
Câu hỏi 5: Bây giờ bạn đã tách lớp cồng kềnh thành hai, bạn có thể có nhiều phương thức cập nhật trực tiếp trên Employee
lớp, như changeUsername
, markAsDeceased
sẽ thao tác dữ liệu của Employee
lớp chỉ trong RAM và sau đó bạn có thể giới thiệu một phương thức như registerDirty
từ Mẫu đơn vị công việc cho lớp kho lưu trữ, thông qua đó bạn sẽ cho kho lưu trữ biết rằng đối tượng này đã thay đổi thuộc tính và sẽ cần được cập nhật sau khi bạn gọi commit
phương thức.
Rõ ràng, đối với một bản cập nhật, một đối tượng yêu cầu phải có id và do đó đã được lưu và đó là khả năng đáp ứng của kho lưu trữ để phát hiện điều này và gây ra lỗi khi các tiêu chí không được đáp ứng.
Câu hỏi 3: Nếu bạn quyết định chọn mẫu Đơn vị công việc, create
phương thức sẽ là registerNew
. Nếu bạn không, tôi có thể gọi nó save
thay thế. Mục tiêu của kho lưu trữ là cung cấp một sự trừu tượng giữa miền và lớp dữ liệu, vì điều này tôi sẽ khuyên bạn rằng phương thức này (có thể registerNew
hoặc save
) chấp nhận Employee
đối tượng và tùy thuộc vào các lớp thực hiện giao diện kho lưu trữ, thuộc tính nào họ quyết định đưa ra khỏi thực thể. Vượt qua toàn bộ một đối tượng sẽ tốt hơn nên bạn không cần phải có nhiều tham số tùy chọn.
Câu hỏi 2: Cả hai phương thức bây giờ sẽ là một phần của giao diện kho lưu trữ và chúng không vi phạm nguyên tắc trách nhiệm duy nhất. Trách nhiệm của kho lưu trữ là cung cấp các hoạt động CRUD cho các Employee
đối tượng, đó là những gì nó làm (ngoài Đọc và Xóa, CRUD dịch sang cả Tạo và Cập nhật). Rõ ràng, bạn có thể phân chia kho lưu trữ hơn nữa bằng cách có EmployeeUpdateRepository
và không, nhưng điều đó hiếm khi cần thiết và một triển khai duy nhất thường có thể chứa tất cả các hoạt động CRUD.
Câu hỏi 1: Bạn đã kết thúc với một Employee
lớp đơn giản mà bây giờ (trong số các thuộc tính khác) có id. Việc id được điền hay trống (hoặc null
) tùy thuộc vào việc đối tượng đã được lưu hay chưa. Tuy nhiên, id vẫn là một thuộc tính mà thực thể sở hữu và trách nhiệm của Employee
thực thể là chăm sóc các thuộc tính của nó, do đó chăm sóc id của nó.
Cho dù một thực thể có hoặc không có id thường không quan trọng cho đến khi bạn cố gắng thực hiện một số logic-kiên trì trên nó. Như đã đề cập trong câu trả lời cho câu hỏi 5, trách nhiệm của kho lưu trữ là phát hiện bạn không cố lưu một thực thể đã được lưu hoặc cố cập nhật một thực thể mà không có id.
Lưu ý quan trọng
Xin lưu ý rằng mặc dù việc phân tách các mối quan tâm là rất tốt, nhưng thực sự thiết kế một lớp kho lưu trữ chức năng là một công việc khá tẻ nhạt và theo kinh nghiệm của tôi thì khó khăn hơn một chút so với cách tiếp cận hồ sơ hoạt động. Nhưng bạn sẽ kết thúc với một thiết kế linh hoạt hơn và có thể mở rộng, đó có thể là một điều tốt.
Employee
đối tượng để cung cấp sự trừu tượng hóa, các câu hỏi 4. và 5. nói chung là không thể trả lời được, tùy thuộc vào nhu cầu của bạn và nếu bạn tách cấu trúc và các hoạt động CRUD thành hai lớp, thì nó khá rõ ràng, hàm tạo củaEmployee
dữ liệu không thể tìm nạp dữ liệu từ db nữa, để câu trả lời 6.