Nguyên tắc trách nhiệm duy nhất - tôi có quá lạm dụng nó không?


13

Để tham khảo - http://en.wikipedia.org/wiki/Single_responsibility_principl

Tôi có một kịch bản thử nghiệm trong đó trong một mô-đun ứng dụng chịu trách nhiệm tạo các mục sổ cái. Có ba nhiệm vụ cơ bản có thể được thực hiện -

  • Xem các mục sổ cái hiện có trong định dạng bảng.
  • Tạo mục sổ cái mới bằng cách sử dụng nút tạo.
  • Nhấp vào một mục sổ cái trong bảng (được đề cập trong con trỏ đầu tiên) và xem chi tiết của nó trong trang tiếp theo. Bạn có thể vô hiệu hóa một mục sổ cái trong trang này.

(Có thêm vài thao tác / xác nhận trong mỗi trang nhưng vì lý do ngắn gọn nên tôi sẽ giới hạn ở những trang này)

Vì vậy, tôi quyết định tạo ra ba lớp khác nhau -

  • Sổ cái
  • Tạo NewLedgerEntryPage
  • ViewLedgerEntryPage

Các lớp này cung cấp các dịch vụ có thể được thực hiện trong các trang đó và các bài kiểm tra Selen sử dụng các lớp này để đưa ứng dụng đến trạng thái mà tôi có thể đưa ra khẳng định nhất định.

Khi tôi được nó xem xét lại với đồng nghiệp của mình thì anh ta đã quá khích và yêu cầu tôi làm một lớp duy nhất cho tất cả. Mặc dù tôi cảm thấy thiết kế của mình sạch sẽ nhưng tôi nghi ngờ nếu tôi lạm dụng nguyên tắc Trách nhiệm duy nhất


2
IMHO không có điểm nào cố gắng trả lời câu hỏi của bạn nói chung, mà không biết hai điều: Các lớp này lớn như thế nào (theo số phương thức và LỘC)? Và bao nhiêu lớn hơn / phức tạp hơn họ dự kiến ​​sẽ phát triển trong tương lai gần?
Péter Török

2
Mỗi phương thức 10 IMHO là một dấu hiệu rõ ràng không đưa mã vào một lớp với 30 phương thức.
Doc Brown

6
Đây là lãnh thổ biên giới: một lớp gồm 100 LỘC và 10 phương thức không quá nhỏ và một trong 300 LỘC không quá lớn. Tuy nhiên, 30 phương thức trong một lớp nghe có vẻ quá nhiều đối với tôi. Nhìn chung, tôi có xu hướng đồng ý với @Doc ở chỗ có ít rủi ro hơn khi có nhiều lớp nhỏ hơn là có một lớp thừa cân duy nhất. Đặc biệt có tính đến việc các lớp tự nhiên có xu hướng tăng cân theo thời gian ...
Péter Török

1
@ PéterTörök - Tôi đồng ý trừ khi mã có thể được tái cấu trúc thành một lớp có thể được sử dụng theo trực giác sử dụng lại mã thay vì sao chép chức năng như tôi mong đợi là OP có.
SoylentGray

1
Có lẽ áp dụng MVC sẽ xóa tất cả: ba khung nhìn, một mô hình (Ledger) và có thể là ba bộ điều khiển.
kevin cline

Câu trả lời:


19

Trích dẫn @YannisRizos đây :

Đó là một cách tiếp cận theo bản năng và có lẽ đúng, vì những gì có thể thay đổi hơn là logic kinh doanh của việc quản lý sổ cái. Nếu điều đó xảy ra, việc duy trì một lớp sẽ dễ dàng hơn rất nhiều so với ba lớp.

Tôi không đồng ý, ít nhất là bây giờ, sau khoảng 30 năm kinh nghiệm lập trình. Các lớp nhỏ hơn IMHO tốt hơn để duy trì trong hầu hết mọi trường hợp tôi có thể nghĩ đến trong các trường hợp như thế này. Càng nhiều chức năng bạn đặt vào một lớp, bạn sẽ càng có ít cấu trúc và chất lượng mã của bạn xuống cấp - mỗi ngày một chút. Khi tôi hiểu Tarun chính xác, mỗi trong số 3 thao tác này

LedgerLandingPage

CreateNewLedgerEntryPage

ViewLedgerEntryPage

là một trường hợp sử dụng, mỗi lớp này bao gồm nhiều hơn một hàm để thực hiện nhiệm vụ liên quan và mỗi lớp có thể được phát triển và kiểm tra riêng. Vì vậy, việc tạo các lớp cho mỗi người trong số họ nhóm các thứ lại với nhau, giúp xác định phần mã sẽ được thay đổi dễ dàng hơn nếu có gì đó thay đổi.

Nếu bạn có các chức năng chung giữa 3 lớp đó, thì chúng thuộc một lớp cơ sở chung, Ledgerchính lớp đó (tôi giả sử bạn có một, ít nhất là cho các hoạt động CRUD) hoặc trong một lớp trợ giúp riêng.

Nếu bạn cần thêm đối số để tạo nhiều lớp nhỏ hơn, tôi khuyên bạn nên xem cuốn sách "Clean Code" của Robert Martin .


đó là toàn bộ lý do tôi nghĩ ra ba lớp khác nhau thay vì đẩy mọi thứ vào một lớp. Vì không có chức năng chung trong số này nên tôi không có lớp chung. Cảm ơn đã liên kết.
Tarun

2
"Các lớp nhỏ hơn là tốt hơn để duy trì trong hầu hết mọi trường hợp tôi có thể nghĩ ra." Tôi không nghĩ ngay cả Clean Code cũng sẽ đi xa đến thế. Bạn có thực sự tranh luận rằng, trong một mẫu MVC, nên có một bộ điều khiển trên mỗi trang (hoặc trường hợp sử dụng, như bạn đặt nó), chẳng hạn? Trong Tái cấu trúc, Fowler có hai mùi mã cùng nhau: một lý do có nhiều lý do để thay đổi một lớp (SRP) và một đề cập đến việc phải chỉnh sửa nhiều lớp cho một thay đổi.
pdr

@pdr, OP tuyên bố không có chức năng chung giữa các lớp này, vì vậy (đối với tôi) thật khó để tưởng tượng một tình huống khi bạn cần chạm nhiều hơn một để thực hiện một thay đổi.
Péter Török

@pdr: Nếu bạn có quá nhiều lớp để chỉnh sửa cho một thay đổi, thì đó chủ yếu là một dấu hiệu bạn đã không cấu trúc lại chức năng chung ở một nơi riêng biệt. Nhưng trong ví dụ trên, điều này có thể sẽ dẫn đến một lớp thứ tư, và không chỉ một lớp.
Doc Brown

2
@pdr: BTW, bạn đã thực sự đọc "Clean Code" chưa? Bob Martin đi rất xa trong việc chia mã thành các đơn vị nhỏ hơn.
Doc Brown

11

Không có triển khai dứt khoát Nguyên tắc Trách nhiệm duy nhất, hoặc bất kỳ nguyên tắc nào khác. Bạn nên quyết định dựa trên những gì có thể xảy ra để thay đổi.

Như @Karpie viết trong một câu trả lời trước đó :

Bạn chỉ cần một lớp và trách nhiệm duy nhất của lớp đó là quản lý sổ cái.

Đó là một cách tiếp cận theo bản năng và có lẽ đúng, vì những gì có thể thay đổi hơn là logic kinh doanh của việc quản lý sổ cái. Nếu điều đó xảy ra, việc duy trì một lớp sẽ dễ dàng hơn rất nhiều so với ba lớp.

Nhưng điều đó nên được kiểm tra và quyết định cho mỗi trường hợp. Bạn không nên thực sự quan tâm đến những lo ngại với khả năng thay đổi rất nhỏ và tập trung áp dụng nguyên tắc vào những gì có nhiều khả năng thay đổi. Nếu logic quản lý sổ cái là một quá trình đa lớp và các lớp có xu hướng thay đổi độc lập hoặc là mối quan tâm cần tách biệt về nguyên tắc (logic và trình bày), thì bạn nên sử dụng các lớp riêng biệt. Đó là một trò chơi xác suất.

Một câu hỏi tương tự về chủ đề lạm dụng các nguyên tắc thiết kế, mà tôi nghĩ rằng bạn sẽ thấy thú vị: Các mẫu thiết kế: khi nào nên sử dụng và khi nào nên dừng mọi thứ bằng cách sử dụng các mẫu


1
Theo hiểu biết của tôi, SRP không thực sự là một mẫu thiết kế.
Doc Brown

@DocBrown Tất nhiên không phải là một mẫu thiết kế, nhưng cuộc thảo luận về câu hỏi này cực kỳ phù hợp ... Mặc dù vậy, tôi đã cập nhật câu trả lời
yannis

4

Hãy xem xét các lớp này:

  • LedgerLandingPage: Vai trò của lớp này là hiển thị danh sách các sổ cái. Khi ứng dụng phát triển, có lẽ bạn sẽ muốn thêm các phương thức để sắp xếp, lọc, tô đậm và hợp nhất trong dữ liệu từ các nguồn dữ liệu khác.

  • ViewLedgerEntryPage: Trang này hiển thị chi tiết sổ cái. Có vẻ khá thẳng về phía trước. Miễn là dữ liệu là thẳng về phía trước. Bạn có thể vô hiệu hóa sổ cái ở đây. Bạn cũng có thể muốn sửa nó. Hoặc bình luận, hoặc đính kèm một tập tin, biên lai hoặc số đặt phòng bên ngoài hoặc bất cứ điều gì. Và một khi bạn cho phép chỉnh sửa, bạn chắc chắn muốn hiển thị một lịch sử.

  • CreateLedgerEntryPage: Nếu ViewLedgerEntryPagecó khả năng chỉnh sửa đầy đủ, trách nhiệm của lớp này có thể được thực hiện bằng cách chỉnh sửa một "mục trống", có thể có hoặc không có ý nghĩa.

Các ví dụ về luận án có vẻ hơi xa vời, nhưng vấn đề là, từ một UX chúng ta đang nói đến các tính năng ở đây. Một tính năng duy nhất chắc chắn là một trách nhiệm riêng biệt. Bởi vì các yêu cầu đối với tính năng đó có thể thay đổi và các yêu cầu của hai tính năng khác nhau có thể thay đổi thành hai hướng đối lập, và sau đó bạn muốn bạn bị mắc kẹt với SRP ngay từ đầu.
Nhưng ngược lại, bạn luôn có thể đặt một mặt tiền lên ba lớp, nếu có một giao diện duy nhất thuận tiện hơn cho bất kỳ lý do nào bạn có thể nghĩ ra.


Có một thứ như lạm dụng SRP : Nếu bạn yêu cầu hàng tá lớp để đọc nội dung của tệp, sẽ khá an toàn khi cho rằng bạn đang làm như vậy.

Nếu bạn nhìn vào SRP từ phía bên kia, nó thực sự nói rằng, một trách nhiệm duy nhất nên được chăm sóc bởi một lớp duy nhất. Tuy nhiên, xin lưu ý rằng trách nhiệm chỉ tồn tại trong các mức độ trừu tượng. Vì vậy, nó hoàn toàn có ý nghĩa đối với một lớp từ mức độ trừu tượng cao hơn để thực hiện trách nhiệm của mình bằng cách ủy thác công việc cho thành phần cấp thấp hơn, điều này đạt được tốt nhất thông qua nghịch đảo phụ thuộc .


Tôi đoán thậm chí tôi không thể mô tả trách nhiệm của các lớp này tốt hơn bạn đã làm.
Tarun

2

Những lớp học chỉ có một lý do để thay đổi?

Nếu bạn chưa biết (thì), thì bạn có thể đã bước vào bẫy thiết kế đầu cơ / trên bẫy kỹ thuật (quá nhiều lớp, các mẫu thiết kế quá phức tạp được áp dụng). Bạn chưa hài lòng YAGNI . Trong giai đoạn đầu của vòng đời phần mềm (một số) yêu cầu có thể không rõ ràng. Do đó, hướng thay đổi hiện không thể nhìn thấy đối với bạn. Nếu bạn nhận ra điều đó, hãy giữ nó trong một hoặc một số lượng tối thiểu các lớp học. Tuy nhiên, điều này đi kèm với trách nhiệm pháp lý: Ngay khi các yêu cầu và hướng thay đổi rõ ràng hơn, bạn cần xem xét lại thiết kế hiện tại và thực hiện tái cấu trúc để đáp ứng lý do thay đổi.

Nếu bạn đã biết rằng có ba lý do khác nhau để thay đổi và chúng ánh xạ tới ba lớp đó, thì bạn chỉ cần áp dụng đúng liều SRP. Một lần nữa điều này đi kèm với một số trách nhiệm pháp lý: Nếu bạn sai hoặc yêu cầu trong tương lai thay đổi bất ngờ, bạn cần suy nghĩ lại về thiết kế hiện tại và thực hiện tái cấu trúc để đáp ứng lý do thay đổi.

Toàn bộ vấn đề là:

  • Giữ một mắt trên các trình điều khiển để thay đổi.
  • Chọn một thiết kế linh hoạt, có thể được tái cấu trúc.
  • Hãy sẵn sàng để tái cấu trúc mã.

Nếu bạn có suy nghĩ này, bạn sẽ định hình mã mà không sợ thiết kế đầu cơ / kỹ thuật quá mức.

Tuy nhiên, luôn có yếu tố thời gian: Đối với nhiều thay đổi, bạn sẽ không có thời gian bạn cần. Tái cấu trúc chi phí thời gian và công sức. Tôi thường gặp phải tình huống tôi biết rằng tôi phải thay đổi thiết kế, nhưng do hạn chế về thời gian, tôi đã phải hoãn lại. Điều này sẽ xảy ra và không thể tránh được. Tôi thấy rằng việc tái cấu trúc theo mã được thiết kế dễ dàng hơn so với mã được thiết kế. YAGNI cho tôi một hướng dẫn tốt: Nếu nghi ngờ, hãy giữ số lượng lớp học và việc áp dụng các mẫu thiết kế ở mức tối thiểu.


1

Có ba lý do để gọi SRP là lý do để tách một lớp:

  • để những thay đổi trong yêu cầu trong tương lai có thể khiến một số lớp không thay đổi
  • để một lỗi có thể được phân lập nhanh hơn
  • để làm cho lớp học nhỏ hơn

Có ba lý do để từ chối tách một lớp:

  • để các thay đổi trong yêu cầu trong tương lai sẽ không khiến bạn thực hiện cùng một thay đổi trong nhiều lớp
  • do đó, việc tìm kiếm lỗi sẽ không liên quan đến việc truy tìm các đường dẫn dài
  • để chống lại các kỹ thuật phá vỡ đóng gói (thuộc tính, bạn bè, bất cứ thứ gì) phơi bày thông tin hoặc phương thức cho mọi người khi chúng chỉ cần một lớp liên quan

Khi tôi xem xét trường hợp của bạn và tưởng tượng các thay đổi, một số trong số chúng sẽ gây ra thay đổi cho nhiều lớp (ví dụ: thêm một trường vào sổ cái, bạn sẽ cần thay đổi cả ba) nhưng nhiều người sẽ không - ví dụ như thêm việc sắp xếp vào danh sách các sổ cái hoặc thay đổi các quy tắc xác nhận khi thêm một mục sổ cái mới. Phản ứng của đồng nghiệp của bạn có lẽ là vì thật khó để biết nơi tìm lỗi. Nếu có gì đó không đúng trong danh sách sổ cái, có phải vì nó được thêm sai, hoặc có gì đó không đúng trong danh sách? Nếu có sự khác biệt giữa tóm tắt và chi tiết, điều đó có sai không?

Cũng có thể đồng nghiệp của bạn nghĩ rằng những thay đổi trong yêu cầu có nghĩa là bạn sẽ phải thay đổi cả ba lớp có khả năng cao hơn nhiều so với những thay đổi khi bạn chỉ thay đổi một. Đó có thể là một cuộc trò chuyện thực sự hữu ích để có.


thật tốt khi nhìn thấy một viễn cảnh khác
Tarun

0

Tôi nghĩ rằng nếu sổ cái là một bảng trong db, tôi khuyên bạn nên đặt các tác vụ này cho một lớp (ví dụ: DAO). Nếu sổ cái có nhiều logic hơn và không phải là một bảng trong db, tôi khuyên chúng ta nên tạo nhiều lớp hơn để thực hiện các tác vụ này (có thể là hai hoặc lớp thứ ba) và cung cấp một lớp mặt tiền để làm đơn giản cho khách hàng.


tốt, sổ cái là một tính năng trong UI và có nhiều hoạt động liên quan đến nó có thể được thực hiện từ các trang khác nhau.
Tarun

0

IMHO bạn không nên sử dụng các lớp học. Không có trừu tượng dữ liệu ở đây. Các sổ cái là một loại dữ liệu cụ thể. Các phương pháp để thao tác và hiển thị chúng là các chức năng và thủ tục. Chắc chắn sẽ có nhiều chức năng thường được sử dụng để chia sẻ, chẳng hạn như định dạng một ngày. Có rất ít chỗ cho bất kỳ dữ liệu nào được trừu tượng hóa và ẩn trong một lớp trong thói quen hiển thị và lựa chọn.

Có một số trường hợp để ẩn trạng thái trong một lớp để chỉnh sửa sổ cái.

Cho dù bạn có lạm dụng SRP hay không, bạn đang lạm dụng một thứ cơ bản hơn: bạn đang lạm dụng sự trừu tượng hóa. Đó là một vấn đề điển hình, gây ra bởi khái niệm thần thoại OO là một mô hình phát triển lành mạnh.

Lập trình là 90% cụ thể: việc sử dụng trừu tượng hóa dữ liệu nên hiếm và được thiết kế cẩn thận. Sự trừu tượng hóa chức năng, mặt khác nên phổ biến hơn, vì các chi tiết ngôn ngữ và sự lựa chọn thuật toán cần phải được tách ra khỏi ngữ nghĩa của hoạt động.


-1

Bạn chỉ cần một lớp và trách nhiệm duy nhất của lớp đó là quản lý sổ cái .


2
Đối với tôi một lớp '... người quản lý' thường cho thấy rằng bạn không thể bận tâm để phá vỡ nó thêm nữa. Lớp quản lý là gì? Cách trình bày, sự kiên trì?
sebastiangeiger

-1. Đặt 3 trường hợp sử dụng trở lên vào 1 lớp chính xác là điều mà SRP đang cố gắng tránh - và vì những lý do chính đáng.
Doc Brown

@sebastiangeiger Vậy ba lớp của OP đang làm gì? Trình bày hay kiên trì?
Sevenseacat

@Karpie Tôi thành thật hy vọng trình bày. Cấp, ví dụ này không thực sự tốt, nhưng tôi hy vọng rằng '... Trang' trong một trang web đang làm một cái gì đó tương tự như một chế độ xem.
sebastiangeiger

2
Trên thực tế, bạn chỉ cần một lớp cho toàn bộ ứng dụng, với trách nhiệm duy nhất là làm cho người dùng hài lòng ;-)
Péter Török
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.