Mã sạch: hậu quả của các phương thức ngắn với một vài tham số


15

Gần đây trong một lần xem xét mã, tôi đã xem qua mã, được viết bởi một đồng nghiệp mới, trong đó có một mẫu có mùi. Tôi nghi ngờ rằng các quyết định của đồng nghiệp của tôi dựa trên các quy tắc được đề xuất bởi cuốn sách Clean Code nổi tiếng (và có lẽ bởi các cuốn sách tương tự khác).

Theo hiểu biết của tôi, hàm tạo của lớp hoàn toàn chịu trách nhiệm cho việc tạo ra một đối tượng hợp lệ và nhiệm vụ chính của nó là gán các thuộc tính (riêng tư) của một đối tượng. Tất nhiên có thể xảy ra rằng các giá trị thuộc tính tùy chọn có thể được đặt bằng các phương thức khác ngoài hàm tạo của lớp, nhưng các tình huống như vậy khá hiếm (mặc dù không nhất thiết là sai, với điều kiện là phần còn lại của lớp sẽ tính đến tính tùy chọn của thuộc tính đó). Điều này rất quan trọng, vì nó cho phép đảm bảo rằng đối tượng luôn ở trạng thái hợp lệ.

Tuy nhiên, trong mã mà tôi gặp phải, hầu hết các giá trị thuộc tính thực sự được đặt bởi các phương thức khác ngoài hàm tạo. Các giá trị kết quả từ các tính toán được gán cho các thuộc tính được sử dụng bên trong một số phương thức riêng trong toàn lớp. Tác giả dường như sử dụng các thuộc tính lớp như thể chúng là các biến toàn cục có thể truy cập được trong toàn lớp, thay vì tham số hóa các giá trị này cho các hàm cần chúng. Ngoài ra, các phương thức của lớp nên được gọi theo một thứ tự cụ thể, vì lớp sẽ không làm gì khác.

Tôi nghi ngờ rằng mã này được lấy cảm hứng từ lời khuyên để giữ các phương thức ngắn (<= 5 dòng mã), để tránh danh sách tham số lớn (<3 tham số) và các nhà xây dựng không được thực hiện công việc (chẳng hạn như thực hiện một phép tính nào đó đó là điều cần thiết cho tính hợp lệ của đối tượng).

Bây giờ tất nhiên tôi có thể tạo ra một trường hợp chống lại mô hình này nếu tôi có thể chứng minh rằng tất cả các loại lỗi không xác định có khả năng phát sinh khi các phương thức không được gọi theo một thứ tự cụ thể. Tuy nhiên, tôi dự đoán rằng phản hồi cho điều này sẽ bổ sung các xác nhận hợp lệ để xác minh rằng các thuộc tính phải được đặt sau khi các phương thức được gọi cần các thuộc tính đó được đặt.

Tuy nhiên, tôi muốn đề xuất thay đổi hoàn toàn mã, để lớp trở thành một bản in màu xanh cho một đối tượng thực tế, thay vì một loạt các phương thức nên được gọi (theo thủ tục) theo một thứ tự cụ thể.

Tôi cảm thấy rằng mã mà tôi gặp phải có mùi. Trên thực tế, tôi tin rằng tồn tại một sự khác biệt khá rõ ràng là khi nào nên lưu một giá trị trong một thuộc tính lớp và khi nào nên đưa nó vào một tham số cho một phương thức khác để sử dụng - tôi thực sự tin rằng chúng có thể thay thế cho nhau . Tôi đang tìm kiếm các từ cho sự phân biệt này.


6
1. Chơi người ủng hộ của quỷ trong giây lát ... Liệu mã có thực sự hoạt động không? Bởi vì Đối tượng truyền dữ liệu là một kỹ thuật hoàn toàn hợp lệ và nếu đó là tất cả ...
Robert Harvey

7
2. Nếu bạn thiếu từ ngữ để mô tả vấn đề, thì bạn không có đủ kinh nghiệm để bác bỏ quan điểm của đồng nghiệp.
Robert Harvey

4
3. Nếu bạn có một số mã làm việc bạn có thể đăng, hãy đăng nó lên Code Review và để họ xem qua. Nếu không, đây chỉ là một vị tướng lang thang.
Robert Harvey

5
@RobertHarvey "Các giá trị do tính toán được gán cho các thuộc tính được sử dụng bên trong một số phương thức riêng tư trong lớp" không có vẻ như là một DTO tự trọng đối với tôi. Tôi đồng ý rằng một chút cụ thể hơn sẽ hữu ích.
topo Phục hồi Monica

4
Ngoài ra: Âm thanh như ai đó chưa thực sự đọc Clean Code trước khi bash nó. Tôi chỉ quét nó một lần nữa và không thể tìm thấy bất kỳ nơi nào có đề xuất "các nhà xây dựng không nên làm việc" (một số ví dụ trong thực tế thực hiện công việc) và giải pháp được đề xuất để tránh quá nhiều tham số là tạo một đối tượng tham số hợp nhất liên quan các nhóm tham số, không bastardize chức năng của bạn. Và cuốn sách không đề xuất mã tái cấu trúc để tránh sự phụ thuộc tạm thời giữa các phương thức. Tôi nghĩ rằng sự thiên vị của bạn đối với một vài kiểu mã ưa thích của anh ấy đã tô màu cho nhận thức của bạn về cuốn sách.
Eric King

Câu trả lời:


13

Là một người đã đọc Clean Code và xem loạt Clean Coders, nhiều lần, và thường dạy và huấn luyện người khác viết mã sạch hơn, tôi thực sự có thể khẳng định rằng những quan sát của bạn là chính xác - tất cả các số liệu bạn chỉ ra đều được đề cập trong cuốn sách .

Tuy nhiên, cuốn sách tiếp tục đưa ra những điểm khác cũng cần được áp dụng cùng với các hướng dẫn mà bạn đã chỉ ra. Chúng dường như bị bỏ qua trong mã bạn đang xử lý. Điều này có thể đã xảy ra bởi vì đồng nghiệp của bạn vẫn đang trong giai đoạn học tập, trong trường hợp đó, cần phải chỉ ra mùi của mã của họ, thật tốt khi nhớ rằng họ đang làm điều đó tốt, học và cố gắng để viết mã tốt hơn.

Clean Code không đề xuất rằng các phương thức nên ngắn gọn, với càng ít đối số càng tốt. Nhưng theo các hướng dẫn đó, nó đề xuất rằng chúng ta phải tuân theo các nguyên tắc OLID S , tăng sự gắn kết và giảm sự ghép nối .

Chữ S trong RẮN là viết tắt của Nguyên tắc Trách nhiệm Đơn, trong đó nêu rõ rằng một đối tượng chỉ chịu trách nhiệm cho một điều. "Điều" không phải là một thuật ngữ rất chính xác, vì vậy các mô tả của nguyên tắc này rất khác nhau. Tuy nhiên, chú Bob, tác giả của Clean Code, cũng là người đưa ra nguyên tắc này, mô tả nó như sau: "Tập hợp những thứ thay đổi vì cùng một lý do. Tách những thứ đó thay đổi vì những lý do khác nhau." Anh ấy tiếp tục nói những gì anh ấy nói với lý do để thay đổi ở đâyđây(một lời giải thích dài hơn ở đây sẽ là quá nhiều). Nếu nguyên tắc này được áp dụng cho lớp bạn đang xử lý, rất có thể các phần xử lý tính toán sẽ được tách ra khỏi các phần xử lý trạng thái giữ, bằng cách tách lớp thành hai hoặc nhiều hơn, tùy thuộc vào bao nhiêu lý do để thay đổi những tính toán có.

Ngoài ra, các lớp Clean phải được gắn kết , có nghĩa là hầu hết các phương thức của nó sử dụng hầu hết các thuộc tính của nó. Như vậy, một lớp gắn kết tối đa là một lớp trong đó tất cả các phương thức sử dụng tất cả các thuộc tính của nó; như một ví dụ, trong một ứng dụng đồ họa, bạn có thể có một Vectorlớp với các thuộc tính Point aPoint b, trong đó các phương thức duy nhất là scaleBy(double factor)printTo(Canvas canvas)cả hai đều hoạt động trên cả hai thuộc tính. Ngược lại, một lớp gắn kết tối thiểu là một lớp trong đó mỗi thuộc tính chỉ được sử dụng trong một phương thức và không bao giờ có nhiều hơn một thuộc tính được sử dụng bởi mỗi phương thức. Trung bình, một món quà lớp không gắn kết "nhóm" bộ phận gắn kết - tức là một vài phương pháp sử dụng các thuộc tính a, bc, trong khi việc sử dụng phần còn lại cd - có nghĩa là nếu chúng ta chia lớp thành hai, chúng ta sẽ có hai đối tượng gắn kết.

Cuối cùng, các lớp Clean nên giảm khớp nối càng nhiều càng tốt. Mặc dù có nhiều loại khớp nối đáng để thảo luận ở đây, có vẻ như mã trong tay chủ yếu bị khớp nối tạm thời , trong đó, như bạn đã chỉ ra, các phương thức của đối tượng sẽ chỉ hoạt động như mong đợi khi chúng được gọi theo đúng thứ tự. Và giống như hai hướng dẫn đã đề cập ở trên, các giải pháp cho vấn đề này thường liên quan đến việc chia lớp thành hai hoặc nhiều đối tượng gắn kết . Chiến lược phân tách trong trường hợp này thường liên quan đến các mẫu như Builder hoặc Factory, và trong các trường hợp rất phức tạp, State-Machines.

TL; DR: Hướng dẫn về Mã sạch mà đồng nghiệp của bạn tuân theo là tốt, nhưng chỉ khi tuân theo các nguyên tắc, thực tiễn và mô hình còn lại được đề cập trong sách. Các Sạch phiên bản của "lớp" bạn nhìn thấy sẽ chia thành nhiều lớp, mỗi với một trách nhiệm duy nhất, phương pháp gắn kết và không có khớp nối thời gian. Đây là bối cảnh nơi các phương thức nhỏ và các đối số ít có ý nghĩa.


1
Cả bạn và topo morto đã viết một câu trả lời hay, nhưng tôi chỉ có thể chấp nhận một. Tôi thích rằng bạn đã giải quyết SRP, tính cố kết và khớp nối. Đây là những thuật ngữ hữu ích mà tôi có thể sử dụng trong phần đánh giá mã. Chia đối tượng thành các đối tượng nhỏ hơn với trách nhiệm riêng của họ rõ ràng là cách để đi. Một phương thức (không phải hàm tạo) khởi tạo các giá trị vào một nhóm các thuộc tính lớp là một tặng cho đã chết mà một đối tượng mới phải được trả về. Tôi nên thấy điều đó.
dùng2180613

1
SRP là hướng dẫn quan trọng nhất; một vòng để cai trị tất cả và tất cả những thứ đó. Thực hiện tốt SRP tự nhiên dẫn đến các phương pháp ngắn hơn. Ví dụ: Tôi có một lớp mặt trước chỉ có 2 phương thức công khai và khoảng 8 phương thức không công khai. Không có nhiều hơn ~ 3 dòng; Cả lớp khoảng 35 LỘC. Nhưng tôi đã viết lớp này cuối cùng! Vào thời điểm tất cả các mã cơ bản được viết, lớp này về cơ bản đã tự viết và tôi thực sự không phải làm cho các phương thức lớn hơn. Tôi không bao giờ nói "Tôi sẽ viết các phương thức này thành 5 dòng nếu nó giết chết tôi." Mỗi khi bạn áp dụng SRP, nó chỉ xảy ra.
radarbob

11

Theo hiểu biết của tôi, hàm tạo của lớp hoàn toàn chịu trách nhiệm cho việc tạo ra một đối tượng hợp lệ và nhiệm vụ chính của nó là gán các thuộc tính (riêng tư) của một đối tượng.

Nó thường chịu trách nhiệm đưa đối tượng vào trạng thái hợp lệ ban đầu, vâng; các thuộc tính hoặc phương thức khác sau đó có thể thay đổi trạng thái sang trạng thái hợp lệ khác.

Tuy nhiên, trong mã mà tôi gặp phải, hầu hết các giá trị thuộc tính thực sự được đặt bởi các phương thức khác ngoài hàm tạo. Các giá trị kết quả từ các tính toán được gán cho các thuộc tính được sử dụng bên trong một số phương thức riêng trong toàn lớp. Tác giả dường như sử dụng các thuộc tính lớp như thể chúng là các biến toàn cục có thể truy cập được trong toàn lớp, thay vì tham số hóa các giá trị này cho các hàm cần chúng. Ngoài ra, các phương thức của lớp nên được gọi theo một thứ tự cụ thể, vì lớp sẽ không làm gì khác.

Cũng như các vấn đề về khả năng đọc và khả năng bảo trì mà bạn đề cập đến, có vẻ như có nhiều giai đoạn của luồng / biến đổi dữ liệu đang diễn ra trong chính lớp đó, điều này có thể cho thấy rằng lớp đang phạm lỗi của nguyên tắc trách nhiệm duy nhất.

nghi ngờ rằng mã này được lấy cảm hứng từ lời khuyên để giữ các phương thức ngắn (<= 5 dòng mã), để tránh danh sách tham số lớn (<3 tham số) và các nhà xây dựng không được thực hiện công việc (chẳng hạn như thực hiện một phép tính nào đó là điều cần thiết cho tính hợp lệ của đối tượng).

Thực hiện theo một số nguyên tắc mã hóa trong khi bỏ qua những người khác thường dẫn đến mã ngớ ngẩn. Ví dụ, nếu chúng ta muốn tránh một nhà xây dựng thực hiện công việc, cách hợp lý thường là thực hiện công việc trước khi xây dựng và chuyển kết quả của công việc đó cho nhà xây dựng. (Một lập luận cho cách tiếp cận đó có thể là bạn đang tránh giao cho hai lớp trách nhiệm của mình: công việc khởi tạo và 'công việc chính' của nó, bất kể đó là gì.)

Theo kinh nghiệm của tôi, làm cho các lớp học và phương thức trở nên nhỏ bé hiếm khi là điều tôi phải ghi nhớ như một sự xem xét riêng biệt - thay vào đó, nó đi theo một cách tự nhiên từ trách nhiệm duy nhất.

Tuy nhiên, tôi muốn đề xuất thay đổi hoàn toàn mã, để lớp trở thành một bản in màu xanh cho một đối tượng thực tế, thay vì một loạt các phương thức nên được gọi (theo thủ tục) theo một thứ tự cụ thể.

Bạn có thể sẽ đúng để làm như vậy. Không có gì sai khi viết mã thủ tục đơn giản; có một vấn đề trong việc lạm dụng mô hình OO để viết mã thủ tục bị xáo trộn.

Tôi tin rằng có một sự khác biệt khá rõ ràng là khi nào nên lưu một giá trị trong một thuộc tính lớp và khi nào nên đặt nó vào một tham số cho một phương thức khác để sử dụng - tôi thực sự không tin rằng chúng có thể thay thế cho nhau. Tôi đang tìm kiếm các từ cho sự phân biệt này.

Thông thường , bạn không nên đặt một giá trị trong một trường như một cách chuyển nó từ phương thức này sang phương thức khác; một giá trị trong một trường phải là một phần có ý nghĩa về trạng thái của một đối tượng tại một thời điểm nhất định. (Tôi có thể nghĩ ra một số ngoại lệ hợp lệ, nhưng không phải là ngoại lệ nơi các phương thức đó là công khai hoặc nơi có sự phụ thuộc đơn hàng mà người dùng của lớp nên biết)


2
upvote vì: 1. Nhấn mạnh SRP. "... phương pháp nhỏ ... tuân theo một cách tự nhiên" 2. Mục đích của nhà xây dựng - trạng thái hợp lệ. 3. "Thực hiện theo một số nguyên tắc mã hóa trong khi bỏ qua những người khác." Đây là Walking Dead của tiền mã hóa.
radarbob

6

Bạn rất có thể lan can chống lại mô hình sai ở đây. Các chức năng nhỏ và arity thấp hiếm khi có vấn đề về chính họ. Vấn đề thực sự ở đây là khớp nối gây ra sự phụ thuộc thứ tự giữa các hàm, vì vậy hãy tìm cách giải quyết mà không cần vứt bỏ lợi ích của các chức năng nhỏ.

Mã nói to hơn lời nói. Mọi người nhận được các loại chỉnh sửa tốt hơn rất nhiều nếu bạn thực sự có thể thực hiện một phần của tái cấu trúc và cho thấy sự cải thiện, có lẽ là một bài tập lập trình cặp. Khi tôi làm điều này, tôi thường thấy khó khăn hơn tôi nghĩ để có được thiết kế đúng, cân bằng tất cả các tiêu chí.

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.