Cách tốt nhất để phân tích một lớp lớn trước khi tái cấu trúc nó thành các lớp nhỏ hơn?


8

Lời tựa

Tôi không tìm cách để cấu trúc lại một lớp mã spaghetti lớn, chủ đề đó đã được đề cập trong các câu hỏi khác.

Câu hỏi

Tôi đang tìm kiếm các kỹ thuật để bắt đầu hiểu một tệp lớp được viết bởi một đồng nghiệp khác trải dài trên 4000 dòng và có một phương pháp cập nhật lớn duy nhất là hơn 2000 dòng.

Cuối cùng, tôi hy vọng sẽ xây dựng các bài kiểm tra đơn vị cho lớp này và tái cấu trúc nó thành nhiều lớp nhỏ hơn tuân theo DRY và Nguyên tắc Trách nhiệm Đơn lẻ.

Làm thế nào tôi có thể quản lý và tiếp cận nhiệm vụ này? Cuối cùng tôi muốn có thể vẽ sơ đồ các sự kiện xảy ra trong lớp và sau đó chuyển sang chức năng trừu tượng hóa, nhưng tôi đang đấu tranh để có được cái nhìn từ trên xuống về trách nhiệm và sự phụ thuộc của lớp.


Chỉnh sửa: Trong các câu trả lời, mọi người đã đề cập đến các chủ đề liên quan đến tham số đầu vào, thông tin này dành cho người mới:

Trong trường hợp này, phương thức chính của lớp không có tham số đầu vào và chạy một vòng lặp while cho đến khi được lệnh dừng lại. Các constructor cũng không có tham số.

Điều này làm tăng thêm sự nhầm lẫn của lớp, các yêu cầu và phụ thuộc của nó. Lớp này được tham chiếu đến các lớp khác thông qua các phương thức tĩnh, singletons và tiếp cận thông qua các lớp mà nó đã tham chiếu đến.


1
Điều này khác với hầu hết các nhiệm vụ tái cấu trúc như thế nào? Bạn không định phá vỡ giao diện phải không?
JeffO 18/03/2015

@JeffO Tôi nghĩ rằng, anh ấy muốn có một cái nhìn 30000 ft, làm thế nào để hiểu một cách có phương pháp về những gì lớp đang làm và những gì cần phải làm; không phải "phương pháp trích xuất", "biến trích xuất", v.v.
Thomas Junk

1
Cuối cùng không xây dựng các bài kiểm tra đơn vị. Bắt đầu xây dựng các bài kiểm tra đơn vị ngay bây giờ và trong các bài kiểm tra mã hóa từng bit thông tin mà bạn tìm hiểu về lớp học khi bạn đi cùng. (Thật không may, các bài kiểm tra thể khám phá ra lỗi trong lớp, mà sẽ kết thúc việc thêm rắc rối cho toàn bộ hình ảnh, nhưng nó vẫn là tốt hơn so với cố gắng hiểu những gì đang xảy ra mà không cần bất kỳ thử nghiệm tại chỗ.)
Mike Nakis


1
bản sao có thể có của Giải mã mã nước ngoài
gnat

Câu trả lời:


10

Thật thú vị, tái cấu trúc là cách hiệu quả nhất mà tôi đã tìm thấy để hiểu mã như thế này. Bạn không cần phải làm điều đó sạch sẽ lúc đầu. Bạn có thể thực hiện một phép tái cấu trúc phân tích nhanh và bẩn, sau đó hoàn nguyên và thực hiện cẩn thận hơn với các bài kiểm tra đơn vị.

Lý do điều này hoạt động là vì tái cấu trúc được thực hiện đúng là một loạt các thay đổi nhỏ, gần như cơ học, mà bạn có thể làm mà không thực sự hiểu mã. Ví dụ, tôi có thể thấy một đốm mã được lặp đi lặp lại ở mọi nơi và đưa yếu tố đó vào một phương thức, mà không thực sự cần biết nó hoạt động như thế nào. Tôi có thể đặt cho nó một số tên khập khiễng như thế nào step1đó hoặc một cái gì đó. Sau đó, tôi nhận thấy điều đó step1step7dường như thường xuyên xuất hiện cùng nhau, và yếu tố đó. Sau đó, đột nhiên khói đã xóa đủ để tôi thực sự có thể nhìn thấy bức tranh lớn và tạo ra một số tên có ý nghĩa.

Để tìm cấu trúc tổng thể, tôi đã tìm thấy việc tạo một biểu đồ phụ thuộc thường giúp ích. Tôi làm điều này bằng tay bằng cách sử dụng graphviz vì tôi đã tìm thấy công việc thủ công giúp tôi tìm hiểu nó, nhưng có lẽ có các công cụ tự động có sẵn. Đây là một ví dụ gần đây từ một tính năng tôi đang làm việc. Tôi thấy đây cũng là một cách hiệu quả để truyền đạt thông tin này đến các đồng nghiệp của tôi.

biểu đồ phụ thuộc


2
Chết tiệt, tôi ước tôi đã đọc được điều này trước cuộc phỏng vấn hôm nay .... Tôi được giao nhiệm vụ tái cấu trúc một lớp học, điều này sẽ giúp tôi rất nhiều.
Grim

8

Đầu tiên, một lời cảnh báo (mặc dù đó không phải là điều bạn đang hỏi) trước khi trả lời câu hỏi của bạn: Một câu hỏi cực kỳ quan trọng cần hỏi là tại saobạn có muốn cấu trúc lại cái gì không? Việc bạn đề cập đến "đồng nghiệp" đặt ra câu hỏi: Có lý do kinh doanh nào cho sự thay đổi không? Đặc biệt là trong môi trường doanh nghiệp, sẽ luôn có cả mã kế thừa và thậm chí cả mã mới được cam kết mà bạn sẽ không hài lòng. Thực tế của vấn đề là không có hai kỹ sư sẽ giải quyết một vấn đề nhất định với cùng một giải pháp. Nếu nó hoạt động và không có ý nghĩa hiệu suất khủng khiếp, thì tại sao không để nó lại? Tính năng công việc thường có lợi hơn nhiều so với tái cấu trúc vì bạn không đặc biệt thích triển khai. Điều đó nói rằng, có một cái gì đó để nói về nợ kỹ thuật và dọn sạch mã không thể nhận ra. Đề nghị duy nhất của tôi ở đây là bạn đã tạo ra sự khác biệt và đã mua để thực hiện các thay đổi, nếu không bạn có thể sẽ được hỏi "sau khi chìm đắm trong rất nhiều nỗ lực và có khả năng sẽ nhận được nhiều hơn (và có thể hiểu được) đẩy lùi.


Trước khi giải quyết một công cụ tái cấu trúc, tôi luôn muốn đảm bảo rằng tôi có một sự hiểu biết vững chắc về những gì tôi đang thay đổi. Có hai phương pháp tôi thường sử dụng khi giải quyết loại điều này: ghi nhật ký và gỡ lỗi. Cái sau được tạo điều kiện bởi sự tồn tại của các bài kiểm tra đơn vị (và kiểm tra hệ thống nếu chúng có ý nghĩa). Vì bạn không có bất kỳ điều gì, tôi rất mong bạn viết bài kiểm tra với phạm vi bảo hiểm toàn chi nhánh để đảm bảo rằng những thay đổi của bạn không tạo ra những thay đổi hành vi bất ngờ. Các bài kiểm tra đơn vị và bước qua mã giúp bạn có được sự hiểu biết ở mức độ thấp về hành vi trong một môi trường được kiểm soát. Ghi nhật ký giúp bạn hiểu rõ hơn về hành vi trong thực tế (điều này đặc biệt hữu ích trong các tình huống đa luồng).

Khi tôi hiểu rõ hơn về mọi thứ đang diễn ra thông qua gỡ lỗi và ghi nhật ký, thì tôi sẽ bắt đầu đọc mã. Tại thời điểm này, tôi thường nắm bắt tốt hơn nhiều về những gì đang diễn ra và ngồi xuống và đọc 2k LoC sẽ có ý nghĩa hơn nhiều vào thời điểm đó. Ý định của tôi ở đây là nhiều hơn để có được một cái nhìn từ đầu đến cuối và đảm bảo rằng không có bất kỳ trường hợp cạnh kỳ quặc nào mà đơn vị kiểm tra mà tôi đã viết không bao gồm.

Sau đó, tôi sẽ suy nghĩ về thiết kế và đưa ra một cái mới. Nếu khả năng tương thích ngược là quan trọng, tôi sẽ đảm bảo rằng giao diện chung không thay đổi.

Với sự hiểu biết vững chắc về những gì đang diễn ra, các bài kiểm tra và một thiết kế mới trong tay, tôi sẽ đưa nó lên để xem xét. Khi thiết kế đã được ít nhất hai người xem xét (hy vọng quen thuộc với mã), tôi sẽ bắt đầu thực hiện các thay đổi.

Hy vọng rằng điều đó không quá đau đớn rõ ràng và giúp đỡ.


1
Câu trả lời của bạn thực sự hữu ích với tôi. Tôi thực sự đã không nghĩ đến việc tạo ra kết quả nhật ký từ lớp hiện tại trước tiên tuy nhiên có một số vấn đề: Tôi không tin rằng tôi có thể đơn vị kiểm tra lớp này. Tất cả các chức năng chính của nó xảy ra trong một phương thức không có tham số đầu vào, phương thức này lấy hầu hết trạng thái của nó từ các lớp khác không được truyền vào, hàm tạo của nó cũng không có đối số, phụ thuộc của nó được truy cập thông qua singletons. Đề xuất của bạn là thử nghiệm đơn vị trước sau đó bắt đầu thiết kế lại, làm thế nào để tôi thích ứng nếu nó chỉ ra rằng thiết kế lại có khả năng cần thiết để thử nghiệm đơn vị?
sydan 18/03/2015

Một lưu ý bổ sung về lời cảnh báo của bạn. Tôi hiểu điều này, tôi đã thấy mọi người nói về việc thường xuyên để những thứ này một mình, tuy nhiên lớp hiện vẫn đang được phát triển và các nhà phát triển mới đang bắt đầu làm việc với nó. Với các sửa đổi mới và không có bài kiểm tra đơn vị, không có cách nào để hiểu sự thay đổi ảnh hưởng đến lớp như thế nào. Tôi đã được giao nhiệm vụ trừu tượng hóa một tính năng của lớp và tôi đã hoàn thành nhiệm vụ này, nhưng điều này đã tạo ra một chút khác biệt đối với kích thước voi ma mút của vấn đề và tôi tin rằng cần phải thực hiện nhiều hơn nữa.
sydan 18/03/2015

1
@sydan: Bạn có thể chế giễu sự tương tác với các lớp khác không? Tôi biết điều này dễ dàng hơn trong một số ngôn ngữ so với những ngôn ngữ khác. Nếu đó là điều không nên, có lẽ hệ thống sẽ bắt đầu thử nghiệm?
Demian Brecht

Thú vị, tại sao các downvote?
Demian Brecht

Tôi cũng quan tâm.
sydan

4

Không có công thức chung, nhưng một số quy tắc ( giả sử một ngôn ngữ được gõ tĩnh nhưng điều đó không thực sự quan trọng):

1) Hãy xem một chữ ký phương thức. Nó cho bạn biết những gì đi vào và hy vọng đi ra . Từ trạng thái chung của lớp thần này , tôi cho rằng đây là điểm đau đầu tiên . Tôi cho rằng, nhiều hơn một tham số được sử dụng.

2) Sử dụng chức năng tìm kiếm từ Editor / EDI để xác định Thoát-điểm (thường là một trở statement_ được sử dụng)

Từ đó bạn đã biết, những gì các chức năng cần cho thử nghiệm và những gì bạn mong đợi trong trở lại .

Vì vậy, một thử nghiệm đầu tiên đơn giản sẽ gọi hàm với các tham số cần thiết và mong đợi rằng kết quả là không rỗng . Đó không phải là nhiều, nhưng một điểm khởi đầu.

Từ đó bạn có thể bước vào một vòng tròn ẩn dật (một thuật ngữ được đặt ra bởi HG Gadamer - một triết gia người Đức). Vấn đề là: bây giờ bạn đã có một sự hiểu biết thô sơ về lớp học và cập nhật sự hiểu biết này với kiến ​​thức chi tiết mới và có một sự hiểu biết mới về cả lớp.

Điều này kết hợp với phương pháp khoa học : đưa ra các giả định và xem xét nếu chúng giữ.

3) Lấy một tham số và xem, trong đó trong lớp nó được chuyển đổi bằng cách nào đó:

Ví dụ, bạn đang làm Java như tôi, thường có gettersetter mà bạn có thể nhìn. Tìm kiếm mô hình $objectname. (hoặc $objectname\.(get|set)nếu bạn đang làm Java)

Bây giờ bạn có thể đưa ra các giả định thêm về những gì phương pháp làm.

Chỉ theo dõi các tham số đầu vào ( đầu tiên ) thông qua phương thức. Nếu cần, hãy tạo một số sơ đồ hoặc bảng , trong đó bạn ghi lại mọi thay đổi cho từng biến.

Từ đó, bạn có thể viết các bài kiểm tra thêm, mô tả hành vi của phương pháp. Nếu bạn có hiểu biết sơ bộ về cách từng tham số đầu vào được chuyển đổi theo phương thức, hãy bắt đầu thử nghiệm : truyền vào null cho một tham số hoặc đầu vào lạ . Đưa ra các giả định, kiểm tra kết quả và thay đổi đầu vào và giả định.

Nếu bạn làm điều này một lần, bạn có "tấn" các bài kiểm tra mô tả hành vi của phương pháp của bạn.

4) Trong bước tiếp theo tôi sẽ tìm kiếm các phụ thuộc : phương thức cần gì ngoài đầu vào của nó để hoạt động đúng ? Có khả năng để giảm hoặc cơ cấu lại những người? Bạn càng ít phụ thuộc, bạn càng thấy rõ các điểm, nơi thực hiện các phân tách đầu tiên.

5) Từ đó bạn có thể đi xuống toàn bộ con đường tái cấu trúc với các mẫu tái cấu trúctái cấu trúc cho các mẫu.

Đây là một Vid hay: GoGaRuCo 2014- Phương pháp khắc phục sự cố khoa học Đó là về khắc phục sự cố nhưng vẫn hữu ích cho một phương pháp chung để hiểu cách thức hoạt động của một cái gì đó .

Bạn đề cập rằng hàm được gọi không có tham số đầu vào : Trong trường hợp đặc biệt này, trước tiên tôi sẽ cố gắng xác định các phụ thuộc và cấu trúc lại chúng thành các tham số, vì vậy bạn có thể trao đổi chúng vào và ra theo ý muốn.


Xin chào Thomas, cảm ơn bạn đã trả lời, giống như Demian, nó rất hữu ích và là một phương pháp tốt. Tôi tin rằng tôi sẽ bắt đầu di chuyển xuống tuyến đường này bằng cách kết hợp việc ghi nhật ký mà Demian đề xuất với các đầu vào khác nhau mà bạn đã đề xuất. Đáng buồn là phương thức mà lớp này chạy không có tham số đầu vào. Đó là một nhiệm vụ đồng hồ chạy một vòng lặp vĩnh cửu, có vẻ như các phụ thuộc được lấy thông qua các singletons, các lớp tĩnh và sau đó bằng cách tiếp cận qua các lớp để lấy các lớp khác.
sydan 18/03/2015

Giai đoạn đầu tiên của việc thiết lập các phương thức đầu vào có thể sẽ khó nhất đối với tôi vì không có cách nào để biết biến nào mà lớp sử dụng là đầu vào, hằng, đầu ra, biến tạm thời hoặc biến gỡ lỗi ngẫu nhiên.
sydan 18/03/2015

Ồ, nghe có vẻ vui! Có lẽ, bạn nhảy ngay vào nó và theo dõi đối tượng đầu tiên bạn tìm thấy. Vòng tròn sau đó bắt đầu như được mô tả;)
Thomas Junk

Đó có thể là con đường tốt nhất ... liệu 'ctrl-f' cho biến được sử dụng nhiều nhất có vẻ như là một điểm khởi đầu mạnh mẽ? Tôi có nên chỉnh sửa câu hỏi ban đầu của mình để đề cập đến việc thiếu tham số đầu vào và cấu trúc phương thức không?
sydan 18/03/2015

1
Tôi thích những gì bạn đề cập trong chỉnh sửa của bạn.
sydan 19/03/2015

4

Bạn đề cập đến một updatephương pháp 2000 dòng .

Tôi bắt đầu đọc phương pháp này từ trên xuống dưới. Khi bạn tìm thấy một tập hợp các câu lệnh thuộc về nhau, nghĩa là chia sẻ một trách nhiệm (ví dụ: tạo người dùng), trích xuất các câu lệnh này thành một hàm.

Bằng cách này, bạn không thay đổi cách mã hoạt động nhưng bạn

  • rút ngắn phương pháp khổng lồ này
  • đạt được sự hiểu biết tốt hơn, ở cấp độ cao hơn về phương pháp này
  • chuẩn bị phương pháp cho tái cấu trúc

Lặp lại các bước này cho các phương pháp khác.


1
Quá trình này khi được thực hiện không phá hủy (chỉ cần đọc, thông báo, kế hoạch) đôi khi được gọi là xem xét mã . Cuốn sách của Steven C. McConnell - Code Complete - có một số mẫu danh sách kiểm tra đẹp cho dịp này
xmojmr 19/03/2015
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.