Làm thế nào lớn là ok cho một lớp học?


29

Tôi là một nhà phát triển lâu năm (tôi 49 tuổi) nhưng khá mới đối với phát triển hướng đối tượng. Tôi đã đọc về OO kể từ Eiffel của Bertrand Meyer, nhưng đã thực hiện rất ít lập trình OO.

Vấn đề là mọi cuốn sách về thiết kế OO đều bắt đầu bằng một ví dụ về thuyền, ô tô hoặc bất kỳ đối tượng phổ biến nào chúng ta sử dụng rất thường xuyên, và họ bắt đầu thêm các thuộc tính và phương thức, và giải thích cách họ mô hình hóa trạng thái của đối tượng và những gì có thể được thực hiện với nó

Vì vậy, họ thường đi một cái gì đó như "mô hình càng tốt thì nó đại diện cho đối tượng trong ứng dụng càng tốt và tất cả đều xuất hiện tốt hơn".

Cho đến nay thì rất tốt, nhưng, mặt khác, tôi đã tìm thấy một số tác giả đưa ra các công thức nấu ăn như một lớp học chỉ phù hợp với một trang duy nhất (Tôi sẽ thêm vào trên kích thước màn hình nào? "Bây giờ chúng tôi không thử để in mã!).

Lấy ví dụ một PurchaseOrderlớp, có một máy trạng thái hữu hạn kiểm soát hành vi của nó và một tập hợp PurchaseOrderItem, một trong những đối số ở đây là chúng ta nên sử dụng một PurchaseOrderlớp đơn giản, với một số phương thức (ít hơn một lớp dữ liệu) và có một PurchaseOrderFSMlớp chuyên gia của người Viking, xử lý bộ máy trạng thái hữu hạn cho PurchaseOrder.

Tôi có thể nói rằng rơi vào Phân loại tính năng Envy Cảnh hoặc Phân loại thân mật không phù hợp của bài viết về mùi của Code của Jeff Atwood trên Mã hóa kinh dị. Tôi chỉ gọi đó là lẽ thường. Nếu tôi có thể phát hành, phê duyệt hoặc hủy bỏ đơn đặt hàng thật của tôi, sau đó các PurchaseOrderlớp nên có issuePO, approvePOcancelPOphương pháp.

Có phải điều đó không phù hợp với sự tối đa hóa sự gắn kết và tối thiểu hóa việc ghép nối các nguyên tắc cũ của tuổi mà tôi hiểu là nền tảng của OO?

Bên cạnh đó, điều đó có giúp gì cho khả năng duy trì của lớp không?


17
Không có lý do để mã hóa tên kiểu chữ thành tên phương thức. Tức là nếu bạn có PurchaseOrder, bạn chỉ có thể đặt tên cho phương pháp của bạn issue, approvecancel. Các POhậu tố cho biết thêm chỉ giá trị âm.
dash-tom-bang

2
Miễn là bạn tránh xa các phương pháp lỗ đen , bạn sẽ ổn thôi. :)
Đánh dấu C

1
Với độ phân giải màn hình hiện tại của tôi, tôi sẽ nói lớn 145 ký tự.
DavRob60

Cảm ơn mọi người vì ý kiến ​​của bạn, họ thực sự đã giúp tôi trong vấn đề này. Nếu trang web này cho phép tôi cũng đã chọn câu trả lời của TMN và lazarus, nhưng nó chỉ cho phép một, vì vậy nó đã được gửi tới Craig. Trân trọng.
Miguel Veloso

Câu trả lời:


27

Một lớp nên sử dụng Nguyên tắc Trách nhiệm duy nhất. Hầu hết các lớp rất lớn tôi đã thấy làm nhiều việc, đó là lý do tại sao chúng quá lớn. Nhìn vào từng phương thức và mã quyết định nó sẽ nằm trong lớp này hoặc riêng biệt, mã trùng lặp là một gợi ý. Bạn có thể có một phương thức vấn đề nhưng nó có chứa 10 dòng mã truy cập dữ liệu không? Mã đó có lẽ không nên ở đó.


1
Craig, tôi đã không nhận thức được Nguyên tắc Trách nhiệm duy nhất và chỉ đọc về nó. Điều đầu tiên tôi nghĩ đến, phạm vi của Trách nhiệm là gì? đối với một PO tôi sẽ nói là quản lý trạng thái của nó và đó là lý do cho các phương thức của vấn đềPO, phê duyệt và hủy, nhưng cũng bao gồm việc tương tác với lớp Purchasingderderem xử lý trạng thái của mục PO. Vì vậy, tôi không rõ liệu phương pháp này có phù hợp với SRP hay không. bạn muốn nói gì?
Miguel Veloso

1
+1 cho câu trả lời rất hay và súc tích. Trên thực tế, không thể có một biện pháp dứt khoát cho một lớp học nên lớn như thế nào. Áp dụng SRP đảm bảo rằng lớp chỉ lớn như nó cần phải có.
S.Robins

1
@MiguelVeloso - Phê duyệt PO có thể có logic và quy tắc khác nhau liên quan đến nó so với việc phát hành PO. Nếu vậy, việc phân tách các trách nhiệm thành POApprover và POIssuer là điều hợp lý. Ý tưởng là nếu các quy tắc thay đổi cho một, tại sao bạn lại muốn gây rối với lớp có chứa cái kia.
Matthew Flynn

12

Theo ý kiến ​​của tôi, kích thước lớp học không quan trọng miễn là các biến, hàm và phương thức có liên quan đến lớp được đề cập.


1
Mặt khác, kích thước lớn chỉ ra rằng các phương thức đó có thể không liên quan đến lớp được đề cập.
Billy ONeal

5
@Billy: Tôi muốn nói rằng các lớp lớn là một mùi mã, nhưng chúng không tự động xấu.
David Thornley

@David: Đó là lý do tại sao có từ "có lẽ" trong đó - nó quan trọng :)
Billy ONeal

Tôi phải không đồng ý ... Tôi làm việc trong một dự án có quy ước đặt tên khá nhất quán và mọi thứ được trình bày rõ ràng ... nhưng tôi vẫn có một lớp có 50 nghìn dòng mã. Nó quá lớn :)
Jay

2
Câu trả lời này cho thấy chính xác "con đường đến địa ngục" diễn ra như thế nào - nếu một người không hạn chế khả năng đáp ứng của một lớp, sẽ có nhiều biến, hàm và phương thức trở nên phù hợp với lớp - và điều này dễ dẫn đến việc có quá nhiều trong số chúng.
Doc Brown

7

Vấn đề lớn nhất với các lớp lớn là khi kiểm tra các lớp đó, hoặc sự tương tác của chúng với các lớp khác. Bản thân lớp học lớn không phải là một vấn đề, nhưng giống như tất cả các mùi, nó chỉ ra một vấn đề tiềm ẩn . Nói chung, khi lớp lớn, đó là một dấu hiệu cho thấy lớp đang làm nhiều hơn một lớp nên.

Đối với sự tương tự xe hơi của bạn, nếu lớp carquá lớn, đó có lẽ là một dấu hiệu cho thấy nó cần được chia thành lớp engine, lớp doorvà lớp windshield, v.v.


8
Và đừng quên một chiếc xe nên thực hiện giao diện Xe. :-)
Chris

7

Tôi không nghĩ có một định nghĩa cụ thể về một lớp học quá lớn. Tuy nhiên, tôi sẽ sử dụng các hướng dẫn sau để xác định xem tôi nên cấu trúc lại lớp của mình hay xác định lại phạm vi của lớp:

  1. Có nhiều biến độc lập với nhau không? (Hầu hết khả năng chịu đựng của tôi là khoảng 10 ~ 20 trong hầu hết các trường hợp)
  2. Có nhiều phương pháp được thiết kế cho các trường hợp góc? (Khả năng chịu đựng cá nhân của tôi là ... tốt, nó phụ thuộc nhiều vào tâm trạng của tôi, tôi đoán vậy)

Liên quan đến 1, hãy tưởng tượng rằng bạn xác định kích thước kính chắn gió, kích thước bánh xe, mô hình bóng đèn đuôi và 100 chi tiết khác trong lớp học của bạn car. Mặc dù tất cả chúng đều "có liên quan" đến chiếc xe, nhưng rất khó để theo dõi hành vi của lớp như vậy car. Và khi một ngày nào đó đồng nghiệp của bạn vô tình (hoặc, trong một số trường hợp, cố ý) thay đổi kích thước kính chắn gió dựa trên mô hình bóng đèn đuôi, bạn sẽ mãi mãi tìm ra lý do tại sao kính chắn gió không còn vừa với xe của bạn.

Liên quan đến 2, hãy tưởng tượng rằng bạn cố gắng xác định tất cả các hành vi mà một chiếc xe có thể có trong lớp car, nhưng car::open_roof()sẽ chỉ có sẵn cho xe mui trần và car::open_rear_door()không áp dụng cho những chiếc xe 2 cửa đó. Mặc dù định nghĩa xe mui trần và xe 2 cửa là "xe hơi" theo định nghĩa, việc thực hiện chúng trong lớp carlàm phức tạp lớp. Lớp học trở nên khó sử dụng hơn và khó bảo trì hơn.

Ngoài 2 hướng dẫn này, tôi sẽ đề xuất một định nghĩa rõ ràng về phạm vi lớp học. Khi bạn xác định phạm vi, thực hiện lớp dựa trên định nghĩa. Hầu hết thời gian, mọi người nhận được một lớp "quá lớn" do định nghĩa phạm vi kém hoặc do các tính năng tùy ý được thêm vào lớp


7

Nói những gì bạn cần trong 300 dòng

Lưu ý: quy tắc ngón tay cái này giả định rằng bạn đang theo phương pháp 'một lớp trên mỗi tệp', theo ý riêng của nó là một ý tưởng duy trì tốt

Đó không phải là một quy tắc tuyệt đối, nhưng nếu tôi thấy hơn 200 dòng mã trong một lớp thì tôi nghi ngờ. 300 dòng và chuông cảnh báo đi vào. Khi tôi mở mã của người khác và tìm 2000 dòng, tôi biết đó là thời gian tái cấu trúc mà không cần đọc trang đầu tiên.

Chủ yếu là điều này đi xuống khả năng bảo trì. Nếu đối tượng của bạn phức tạp đến mức bạn không thể diễn tả hành vi của nó trong 300 dòng, điều đó sẽ rất khó để người khác hiểu.

Tôi thấy rằng tôi đang bẻ cong quy tắc này trong Mô hình -> ViewModel -> Xem thế giới nơi tôi đang tạo một 'đối tượng hành vi' cho trang UI vì nó thường mất hơn 300 dòng để mô tả chuỗi hành vi hoàn chỉnh trên một trang. Tuy nhiên, tôi vẫn chưa quen với mô hình lập trình này và tìm ra các cách tốt để tái cấu trúc / tái sử dụng các hành vi giữa các trang / chế độ xem đang giảm dần kích thước của ViewModels của tôi.


Hướng dẫn cá nhân của tôi cũng khoảng 300 dòng. +1
Marcie

Tôi nghĩ rằng OP đang tìm kiếm một heuristic, và đây là một cái tốt.
neontapir

Cảm ơn, thật có giá trị khi sử dụng các con số chính xác làm hướng dẫn.
Will Sheppard

6

Hãy làm cho nó lạc hậu:

Có phải điều đó không phù hợp với sự tối đa hóa sự gắn kết và tối thiểu hóa việc ghép nối các nguyên tắc cũ của tuổi mà tôi hiểu là nền tảng của OO?

Trên thực tế, đó là nền tảng của lập trình, trong đó OO chỉ là một mô hình.

Tôi sẽ để Antoine de Saint-Exupéry trả lời câu hỏi của bạn (vì tôi lười biếng;)):

Sự hoàn hảo đạt được, không phải khi không còn gì để thêm, mà là khi không còn gì để lấy đi.

Lớp học càng đơn giản, bạn sẽ càng tự tin, điều đó càng dễ dàng để kiểm tra đầy đủ .


3

Tôi với OP về phân loại Envy Feature. Không có gì làm tôi khó chịu hơn là có một loạt các lớp học với một loạt các phương thức hoạt động trên các lớp bên trong của một số lớp khác. OO gợi ý rằng bạn mô hình hóa dữ liệu và các hoạt động trên dữ liệu đó. Điều đó không có nghĩa là bạn đặt các hoạt động ở một nơi khác với dữ liệu! Đó là cố gắng để bạn đặt dữ liệu và các phương thức đó lại với nhau .

Với các mô hình carpersonrất phổ biến, nó giống như đặt mã để thực hiện kết nối điện, hoặc thậm chí quay đầu khởi động, vào personlớp. Tôi hy vọng rằng điều này rõ ràng là sai đối với bất kỳ lập trình viên có kinh nghiệm.

Tôi thực sự không có một lớp lý tưởng hoặc kích thước phương thức, nhưng tôi thích giữ các phương thức và chức năng của mình phù hợp trên một màn hình để tôi có thể thấy toàn bộ chúng ở một nơi. (Tôi đã cấu hình Vim để mở cửa sổ 60 dòng, điều này xảy ra ngay xung quanh số lượng dòng trên một trang in.) Có những nơi điều này không có nhiều ý nghĩa, nhưng nó hoạt động với tôi. Tôi thích làm theo hướng dẫn tương tự cho các định nghĩa lớp và cố gắng giữ các tệp của mình dưới một vài "trang" dài. Bất cứ điều gì trên 300, 400 dòng bắt đầu cảm thấy khó sử dụng (chủ yếu là do lớp đã bắt đầu đảm nhận quá nhiều trách nhiệm trực tiếp).


+1 - quy tắc tốt nhưng không độc đoán về nó. Tuy nhiên, tôi sẽ nói rằng chỉ vì một lớp bị phá vỡ không có nghĩa là các lớp khác nhau sẽ chạm vào các thành viên riêng của nhau.
Billy ONeal

Tôi không nghĩ rằng các lớp khác nhau sẽ chạm vào các phần riêng tư của nhau. Mặc dù quyền truy cập của 'người bạn' thuận tiện cho việc khởi động và chạy, nhưng đối với tôi, đó là bước đệm để thiết kế tốt hơn. Nói chung, tôi cũng tránh người truy cập, vì phụ thuộc vào nội bộ của lớp khác là Mùi Mã khác .
dash-tom-bang

1

Khi một định nghĩa lớp có vài trăm phương thức thì nó quá lớn.


1

Tôi hoàn toàn đồng ý với việc sử dụng nguyên tắc trách nhiệm duy nhất cho các lớp nhưng nếu điều đó tuân theo và dẫn đến các lớp lớn thì cũng vậy. Trọng tâm thực sự là các phương thức và thuộc tính riêng lẻ không quá lớn, độ phức tạp theo chu kỳ nên là hướng dẫn ở đó và theo đó có thể dẫn đến nhiều phương thức riêng sẽ tăng kích thước lớp tổng thể nhưng sẽ khiến nó có thể đọc được và có thể kiểm tra ở mức độ chi tiết. Nếu mã vẫn có thể đọc được thì kích thước là không liên quan.


1

Phát hành một đơn đặt hàng thường là một quá trình phức tạp không chỉ liên quan đến đơn đặt hàng. Trong trường hợp này, giữ logic kinh doanh trong một lớp riêng biệt và làm cho đơn đặt hàng mua đơn giản hơn có lẽ là điều nên làm. Tuy nhiên, nói chung, bạn đúng - bạn không muốn các lớp "cấu trúc dữ liệu". Ví dụ: issue()phương thức lớp doanh nghiệp "POManager" của bạn có thể nên gọi issue()phương thức PO . Sau đó, PO có thể đặt trạng thái của nó thành "Đã phát hành" và ghi chú ngày phát hành, trong khi POManager sau đó có thể gửi thông báo tới các tài khoản phải trả để cho họ biết để mong đợi một hóa đơn và một thông báo khác để kiểm kê để họ biết có gì đó đang đến và ngày dự kiến.

Bất cứ ai đẩy "quy tắc" cho kích thước phương thức / lớp rõ ràng đã không dành đủ thời gian trong thế giới thực. Như những người khác đã đề cập, nó thường là một chỉ báo tái cấu trúc, nhưng đôi khi (đặc biệt là trong công việc "xử lý dữ liệu") bạn thực sự cần phải viết một lớp với một phương thức duy nhất kéo dài hơn 500 dòng. Đừng gác máy.


Tôi đoán POManager sẽ là "trình điều khiển" và Purchasingder sẽ là "mô hình" trong mẫu MVC, phải không?
Miguel Veloso

0

Tôi đã tìm thấy một số tác giả cung cấp các công thức nấu ăn như một lớp học chỉ phù hợp với một trang duy nhất

Xét về kích thước vật lý của một lớp, nhìn thấy một lớp lớn là dấu hiệu của mùi mã, nhưng không nhất thiết có nghĩa là luôn có mùi. (Thông thường họ là như vậy) Vì những tên cướp biển trong Cướp biển Caribbean sẽ đặt nó, đây là những gì bạn gọi là "hướng dẫn" hơn là các quy tắc thực tế. Tôi đã cố gắng tuân thủ nghiêm ngặt "không quá một trăm LỘC trong một lớp" và kết quả là RẤT NHIỀU kỹ thuật quá mức không cần thiết.

Vì vậy, họ thường đi một cái gì đó như "mô hình càng tốt thì nó đại diện cho đối tượng trong ứng dụng càng tốt và tất cả đều xuất hiện tốt hơn".

Tôi thường bắt đầu thiết kế của tôi với điều này. Lớp học này làm gì trong thế giới thực? Làm thế nào lớp của tôi phản ánh đối tượng này như được nêu trong các yêu cầu của nó? Chúng tôi thêm một chút trạng thái ở đây, một trường ở đó, một phương thức để làm việc trên các trường đó, thêm một vài trạng thái nữa và thì đấy! Chúng tôi đã có một lớp học làm việc. Bây giờ hãy điền vào các chữ ký với các triển khai thích hợp và chúng tôi tốt để đi, hoặc bạn nghĩ vậy. Bây giờ chúng tôi đã có lớp lớn đẹp với tất cả các chức năng chúng tôi cần. Nhưng vấn đề với điều này là, nó không tính đến cách thế giới thực hoạt động. Và điều đó có nghĩa là nó không tính đến "THAY ĐỔI".

Lý do tại sao các lớp tốt nhất là chia thành các phần nhỏ hơn là vì khó có thể thay đổi các lớp lớn sau này mà không ảnh hưởng đến logic hiện có hoặc đưa ra một lỗi mới hoặc hai lỗi vô tình hoặc chỉ khiến mọi thứ khó sử dụng lại. Đây là nơi tái cấu trúc, các mẫu thiết kế, RẮN, v.v. để chơi và kết quả cuối cùng thường là một lớp nhỏ làm việc trên các lớp con nhỏ hơn, chi tiết hơn.

Ngoài ra, trong ví dụ của bạn, việc thêm IssuePO, Phê duyệt và Hủy PO vào lớp Purchasingder có vẻ phi logic. Đơn đặt hàng không phát hành, phê duyệt hoặc tự hủy. Nếu PO nên có các phương thức, thì các phương thức sẽ hoạt động ở trạng thái không phải trên toàn bộ lớp. Chúng thực sự chỉ là các lớp dữ liệu / bản ghi mà các lớp khác làm việc.


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.