Tôi phải thỏa hiệp: DRY, hoặc tách-truy vấn lệnh?


10

Gần đây tôi đã cấu trúc lại một phương thức vừa là lệnh vừa là phương thức truy vấn.

Sau khi tách nó thành một phương thức một lệnh và một phương thức truy vấn, tôi thấy rằng hiện tại có nhiều vị trí trong mã mà tôi đang gọi lệnh sau đó nhận giá trị từ truy vấn, có vẻ như vi phạm nguyên tắc DRY.

Nhưng nếu tôi gói mã chung đó vào một phương thức, phương thức đó sẽ là cả lệnh và truy vấn. điều này có chấp nhận được không?


được rồi, tôi không biết liệu cộng đồng có đồng thuận hay không và tôi không thể tìm thấy bất kỳ cuộc thảo luận nào về chủ đề này.
Kris Welsh

Nó thường được gọi là CQRS google.com /
Daniel Little

@DanielLittle - không, không phải vậy. CQS và CQRS là các đối tượng khác nhau rõ ràng. CQRS là một mẫu kiến ​​trúc liên quan nhiều hơn trong khi CQS có nhiều mẫu thiết kế hơn và dễ nắm bắt và thực hiện hơn nhiều. Xem codebetter.com/gregyoung/2009/08/13/command-query-separation
Erik Funkenbusch

@Erik Funkenbusch Bạn nói đúng
Daniel Little

Câu trả lời:


11

Luôn có sự đánh đổi để xem xét giữa các nguyên tắc thiết kế mâu thuẫn. Cách để giải quyết nó là xem xét các lý do cơ bản đằng sau các nguyên tắc. Trong trường hợp này, việc không thể chạy truy vấn mà không chạy lệnh là vấn đề, nhưng không thể chạy lệnh mà không chạy truy vấn nói chung là vô hại. Miễn là có cách để chạy độc lập truy vấn, tôi thấy không có lý do gì để không thêm kết quả truy vấn vào lệnh, đặc biệt là nếu thực hiện một cái gì đó như thế này:

QueryResult command()
{
   // do command stuff
   return query();
}

4

Trước đây tôi chưa từng nghe về Phân tách truy vấn lệnh (CQS), nhưng có vẻ như nó sẽ liên quan đến Nguyên tắc trách nhiệm duy nhất (SRP), trong đó nêu rõ rằng một chức năng / lớp lý tưởng chỉ chịu trách nhiệm thực hiện một việc và một việc duy nhất .

Nếu mã lệnh của bạn là 20 dòng mã và mã truy vấn là 30 dòng khác và tất cả chúng nằm trong một thân hàm, rõ ràng bạn đang vi phạm SRP và tôi cũng giả sử CQS và hai đoạn logic đó phải được tách biệt với nhau .

Tuy nhiên, đi với ví dụ giả thuyết của bạn, rất có thể tôi sẽ tạo một phương thức trình bao bọc kết hợp lệnh và truy vấn của bạn để DRY không bị vi phạm ở nhiều vị trí trong mã. Tôi cũng sẽ không coi đây là vi phạm SRP (và có thể là CQS), vì trình bao bọc vẫn chỉ có một trách nhiệm: kết hợp lệnh với truy vấn và tạo mức độ trừu tượng cao hơn, dễ tiêu thụ hơn.

Tôi nghĩ rằng phương pháp trình bao bọc là giải pháp hoàn toàn chấp nhận được và để minh họa điều đó, hãy đưa ví dụ của bạn tiến thêm một bước. Điều gì sẽ xảy ra nếu bạn phải chạy 2 truy vấn thay vì 1 và sau đó thực hiện một hành động lệnh dựa trên đó. Vì vậy, 2 dòng mã của bạn sẽ là 6 hoặc 8. Điều gì xảy ra nếu có một số xác thực / kiểm tra dữ liệu giữa cái này và cái kia, vì vậy bây giờ bạn có 15 dòng mã. Bạn có nghĩ hai lần về việc tạo một trình bao bọc thực hiện tất cả điều đó, thay vì rắc 15 dòng đó vào nhiều tệp không?


Tôi nghĩ rằng "nguyên tắc duy nhất" của trình bao bọc nên giữ các phương thức khác cần lệnh và truy vấn cùng DRY.
Dropogans


Trong khi giải pháp của Karl cho vấn đề này là tốt hơn, tôi thấy công phu của bạn về các hàm bao bọc dài hơn là một điểm rất tốt.
Kris Welsh

-3

DRY quan trọng hơn, vì nó giải quyết được một nhu cầu cơ bản hơn nhiều - tránh những nỗ lực lãng phí, lãng phí một cách hiệu quả. Đây là một điều cơ bản - người ta không cần phải là một lập trình viên để hiểu nó.

CQS là một phản ứng với khó khăn, trong các ngôn ngữ không hỗ trợ theo dõi hiệu ứng, hiểu mã được thực thi cả về kết quả và hiệu ứng của nó. Tuy nhiên:

  1. Không thể tránh khỏi việc thực thi mã cho kết quả của nó, bởi vì đây là cơ sở để soạn các chương trình lớn từ các đơn vị nhỏ.

  2. Sự cần thiết phải thực thi mã cho các hiệu ứng của nó cũng không thể tránh được, bởi vì, ngoài toán học và khoa học máy tính lý thuyết, giá trị của việc chạy một chương trình nằm ở những gì nó có thể làm được cho chúng ta.

  3. Sự cần thiết phải gây ra hiệu ứng và tạo ra kết quả trong cùng một mã không thể tránh được, bởi vì, trong thực tế, chúng ta cần cả hiệu ứng và thành phần, không chỉ một hoặc khác.

Tất nhiên, giải pháp thực tế cho vấn đề theo dõi hiệu ứng quá khó đối với con người không được trả tiền là, tất nhiên, có máy tính hỗ trợ con người chúng ta ! Một điều tương tự có thể được nói về việc theo dõi các mối quan hệ phức tạp giữa các giá trị thời gian chạy (chẳng hạn như tính hợp lệ của các chỉ số mảng), trong đó các trường hợp ngoại lệ và hợp đồng được thi hành theo thời gian tạo thành các giải pháp (không).

Tóm lại, "các giải pháp" như CQS chỉ đơn thuần là cách thiết kế các chương trình theo nguyên tắc hợp lý dựa trên thực tế. Đi cho DRY.


Đôi khi bạn cần tránh ghép nối để giảm độ phức tạp. Bạn nên xem CQRS.
Daniel Little

@Lavinski: Công cụ tốt nhất để tránh sự phức tạp (không làm giảm nó, đó là vô ích) là sự trừu tượng - tách rời bản chất chung của các vấn đề chúng ta đang giải quyết từ các chi tiết cụ thể của các vấn đề chung nói trên. Công thức ma thuật (hay "mẫu thiết kế" như tôi nghe thấy chúng được gọi) tốt nhất có thể ngăn bạn gây ra quá nhiều thiệt hại khi bạn thiết kế sai, nhưng chúng không thể biến một thiết kế sai thành đúng.
pyon

@Lavinski: Đối với CQRS cụ thể, giải pháp thay thế đúng về mặt khái niệm là 1. hiểu mô hình dữ liệu (không có số lượng lớp đối tượng nào có thể loại bỏ sự cần thiết cho việc này), 2. mã hóa càng nhiều thuộc tính chính xác như bạn có thể trong lược đồ cơ sở dữ liệu. (Đáng buồn thay, hầu hết các RDBMS phổ biến đều cung cấp hỗ trợ khá hạn chế cho cái sau, chưa kể đến các NoQuery, điều này thậm chí còn sai hơn. Nghiên cứu hiện tại của tôi đang cung cấp một giải pháp tốt hơn cho việc này.)
pyon

CQRS hoạt động hoàn toàn phù hợp với Thiết kế hướng tên miền Tôi khuyên bạn nên nghiên cứu một chút. Tên miền bên trong ứng dụng sẽ thực thi tính chính xác chứ không phải lưu trữ dữ liệu của bạn.
Daniel Little

1
@ EduardoLeón: Nếu bạn muốn chứng minh thiết kế của mình là chính xác, thì hãy thử viết bài kiểm tra cho chương trình của bạn. Tôi có thể đảm bảo với bạn rằng việc loại bỏ CQS sẽ chỉ cản trở những nỗ lực của bạn trong đó.
Stefan Billiet
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.