Là nguyên tắc trách nhiệm duy nhất áp dụng cho các chức năng?


17

Theo Robert C. Martin, SRP tuyên bố rằng:

Không bao giờ nên có nhiều hơn một lý do để một lớp thay đổi.

Tuy nhiên, trong cuốn sách Clean Code , chương 3: Hàm, ông cho thấy khối mã sau:

    public Money calculatePay(Employee e) throws InvalidEmployeeType {
        switch (e.type) {
            case COMMISSIONED:
                return calculateCommissionedPay(e);
            case HOURLY:
                return calculateHourlyPay(e);
            case SALARIED:
                return calculateSalariedPay(e);
            default:
                throw new InvalidEmployeeType(e.type);
        }
    }

Và sau đó tuyên bố:

Có một số vấn đề với chức năng này. Đầu tiên, nó lớn và khi các loại nhân viên mới được thêm vào, nó sẽ phát triển. Thứ hai, nó rất rõ ràng làm nhiều hơn một điều. Thứ ba, nó vi phạm Nguyên tắc Trách nhiệm Đơn lẻ (SRP) vì có nhiều hơn một lý do để nó thay đổi . [nhấn mạnh của tôi]

Đầu tiên, tôi nghĩ SRP được định nghĩa cho các lớp, nhưng hóa ra nó cũng có thể áp dụng cho các hàm. Thứ hai, làm thế nào mà chức năng này có nhiều hơn một lý do để thay đổi ? Tôi chỉ có thể thấy nó thay đổi vì sự thay đổi của Nhân viên.


5
Điều này có vẻ như một trường hợp sách giáo khoa cho đa hình.
wchargein

Đây là chủ đề rất xen kẽ. Có một cơ hội bạn thêm giải pháp sau đây cho vấn đề này? Tôi sẽ phân biệt rằng người ta đặt một tính toán tính toán trong mỗi lớp nhân viên nhưng đó sẽ là một người xấu bây giờ mỗi lớp nhân viên có thể được thay đổi bởi vì: 1. Các tính toán thanh toán. 2. thêm nhiều thuộc tính vào lớp, v.v.
Stav Alfi

Câu trả lời:


13

Một chi tiết thường bị bỏ qua của Nguyên tắc Trách nhiệm duy nhất là "lý do thay đổi" được nhóm theo các tác nhân trong trường hợp sử dụng (bạn có thể xem giải thích đầy đủ tại đây ).

Vì vậy, trong ví dụ của bạn, calculatePayphương thức sẽ cần được thay đổi bất cứ khi nào cần loại Nhân viên mới. Vì một loại nhân viên có thể không liên quan gì đến người khác, nên sẽ vi phạm nguyên tắc nếu bạn giữ họ cùng nhau, vì sự thay đổi sẽ ảnh hưởng đến các nhóm người dùng khác nhau (hoặc các tác nhân trong trường hợp sử dụng) trong hệ thống.

Bây giờ, về việc liệu nguyên tắc này có áp dụng cho các hàm hay không: Ngay cả khi bạn vi phạm chỉ trong một phương thức, bạn vẫn thay đổi một lớp vì nhiều lý do, vì vậy nó vẫn là vi phạm SRP.


1
Tôi đã cố gắng xem video youtube được liên kết, nhưng sau 10 phút vào nó mà không đề cập đến việc nhóm các diễn viên trong trường hợp sử dụng, tôi đã từ bỏ. 6 phút đầu tiên là tất cả lan man về entropy, không có lý do rõ ràng. Nếu bạn đưa ra một vị trí trong video nơi anh ấy bắt đầu thảo luận về điều này, nó sẽ hữu ích.
Michael Shaw

@MichaelShaw Hãy thử xem từ 10:40 trở đi. Chú Bob đề cập rằng mã sẽ "thay đổi vì những lý do khác nhau, vì những người khác nhau". Tôi nghĩ đó có thể là những gì MichelHenrich đang cố gắng chỉ cho chúng tôi.
Enrique

Xem xong toàn bộ video youtube dài 50 phút, phần lớn trong số đó không phải là về những gì nó được cho là cần làm rõ. Tôi nhận thấy ở mốc 16:00 rằng anh ấy đã chuyển từ chủ đề đó và anh ấy không bao giờ quay lại nó. "Giải thích" về cơ bản giải quyết vấn đề này: "trách nhiệm" trong SRP không có nghĩa là "lý do khác nhau để thay đổi", có nghĩa là "thay đổi theo yêu cầu của những người khác nhau", thực sự có nghĩa là "thay đổi tại yêu cầu vai trò khác nhau mà mọi người đóng ". "Giải thích" không làm rõ bất cứ điều gì, nó thay thế một từ mơ hồ bằng một từ khác.
Michael Shaw

2
@MichaelShaw giống như trong trích dẫn từ cuốn sách, nếu bạn cần giới thiệu các loại nhân viên khác nhau, bạn phải thay đổi lớp Nhân viên. Các vai trò khác nhau có thể chịu trách nhiệm thanh toán các loại nhân viên khác nhau, vì vậy trong trường hợp này, lớp Nhân viên phải được thay đổi nhiều hơn một vai trò, do đó vi phạm SRP.
MichelHenrich

1
@MichaelShaw có, bạn đúng - SRP không phụ thuộc vào cách tổ chức. Đó chính xác là lý do tại sao tôi thêm "may" hoặc "might" vào tất cả các bình luận của mình :). Tuy nhiên, ngay cả trong những trường hợp đó, trong khi mã có thể không vi phạm SRP, nó chắc chắn vi phạm OCP.
MichelHenrich

3

Trên trang 176, Chương 12: Sự xuất hiện, trong phần có tiêu đề Các lớp học và phương pháp tối thiểu, cuốn sách cung cấp phần nào sự điều chỉnh, bằng cách nêu:

Trong nỗ lực làm cho các lớp và phương thức của chúng ta nhỏ đi, chúng ta có thể tạo ra quá nhiều lớp và phương thức nhỏ. Vì vậy, quy tắc này cho thấy rằng chúng tôi cũng giữ chức năng và số lượng lớp của chúng tôi thấp

Số lượng cao và phương pháp đôi khi là kết quả của chủ nghĩa giáo điều vô nghĩa.

Rõ ràng, anh ta đang nói về chủ nghĩa giáo điều khi theo SRP để phá vỡ các phương pháp nhỏ vô tội hoàn toàn tốt như calculatePay()trên.


3

Khi ông Martin áp dụng SRP cho một chức năng, ông hoàn toàn mở rộng định nghĩa về SRP của mình. Vì SRP là một từ ngữ dành riêng cho OO của một nguyên tắc chung và vì đó là một ý tưởng tốt khi áp dụng cho các chức năng, tôi không thấy có vấn đề gì với điều đó (mặc dù nó có thể tốt nếu anh ấy đưa nó vào trong Định nghĩa).

Tôi cũng không thấy nhiều lý do để thay đổi và tôi không tin rằng việc nghĩ về SRP về "trách nhiệm" hay "lý do để thay đổi" là hữu ích. Về cơ bản những gì SRP đang nhận được là các thực thể phần mềm (chức năng, lớp, v.v.) nên làm một việc và làm tốt điều đó.

Nếu bạn nhìn vào định nghĩa của tôi, nó không hề mơ hồ so với cách diễn đạt thông thường của SRP. Vấn đề với các định nghĩa thông thường về SRP không phải là chúng quá mơ hồ, mà là chúng cố gắng quá cụ thể về một điều gì đó về cơ bản là mơ hồ.

Nếu bạn nhìn vào những gì calculatePaylàm, nó rõ ràng đang làm một điều duy nhất: công văn dựa trên loại. Do Java có các cách thức thực hiện công văn dựa trên kiểu, calculatePaykhông phù hợp và không thành ngữ, do đó, nó nên được viết lại, nhưng không phải vì các lý do đã nêu.


-2

Bạn nói đúng @Enrique. Bất kể đó là hàm hay phương thức của lớp, SRP có nghĩa là trong khối mã đó bạn chỉ làm một việc.

Tuyên bố 'lý do để thay đổi' đôi khi hơi sai lệch, nhưng nếu bạn thay đổi calculateSalariedPayhoặc calculateHourlyPayhoặc enum của Employee.typebạn phải thay đổi phương pháp này.

Trong ví dụ của bạn, hàm:

  • kiểm tra loại nhân viên
  • gọi một hàm khác tính tiền theo loại

Theo tôi, đó không phải là vi phạm SRP trực tiếp, vì các trường hợp chuyển đổi và các cuộc gọi không thể được viết ngắn hơn, nếu bạn nghĩ về Nhân viên và các phương thức đã tồn tại. Dù sao, đó là một vi phạm nguyên tắc đóng (OCP) rõ ràng vì bạn phải nối các câu lệnh 'trường hợp' nếu bạn thêm các loại nhân viên, do đó, đó là một cách thực hiện tồi: tái cấu trúc nó.

Chúng tôi không biết nên tính toán 'Tiền' như thế nào, nhưng cách dễ nhất là có Employeegiao diện và một số triển khai cụ thể bằng getMoneycác phương thức. Trong trường hợp đó, toàn bộ chức năng là không cần thiết.

Nếu tính toán phức tạp hơn, người ta có thể sử dụng mẫu khách truy cập không phải là 100% SRP nhưng đó là OCP nhiều hơn trường hợp chuyển đổi.


2
Không chắc chắn làm thế nào bạn có thể liệt kê 2 điều mà chức năng thực hiện, nhưng nói rằng đó không phải là vi phạm SRP.
JeffO

@JeffO: Đó không phải là 2 điều, đó là 2 phần của một điều: gọi hàm thích hợp dựa trên loại.
Michael Shaw
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.