Tái cấu trúc là gì và chỉ sửa đổi mã là gì?


79

Tôi biết rằng tái cấu trúc là "thay đổi cấu trúc của một chương trình để chức năng không bị thay đổi". Tôi đã nói chuyện với một số người mà tôi đang làm việc cùng trong dự án năm cuối của tôi ở trường đại học và tôi đã rất ngạc nhiên khi họ có quan điểm mở rộng hơn nhiều (vì muốn có một từ hay hơn) về tái cấu trúc.

Tôi coi việc tái cấu trúc là những thứ như giải nén các phương thức và đổi tên các lớp. Họ cũng đề xuất những thứ như thay đổi cấu trúc dữ liệu (như Java LinkedListthành một ArrayList), thay đổi thuật toán (sử dụng sắp xếp hợp nhất thay vì sắp xếp bong bóng) và thậm chí viết lại các đoạn mã lớn dưới dạng tái cấu trúc.

Tôi khá chắc chắn rằng họ đã sai, nhưng tôi không thể đưa ra lý do chính đáng vì những gì họ đề xuất đã thay đổi chương trình (và có lẽ là làm cho nó tốt hơn) mà không thay đổi hành vi của nó. Tôi nói đúng, và quan trọng hơn, tại sao?

Câu trả lời:


75

Martin Fowler của "Refactoring: Cải thiện thiết kế của mã hiện tại" có lẽ là tài liệu tham khảo:

Tái cấu trúc là một kỹ thuật được kiểm soát để cải thiện thiết kế của một cơ sở mã hiện có. Bản chất của nó là áp dụng một loạt các phép biến đổi duy trì hành vi nhỏ, mỗi phép biến đổi đều "quá nhỏ để đáng làm". Tuy nhiên, tác động tích lũy của mỗi phép biến đổi này là khá đáng kể. Bằng cách thực hiện chúng theo từng bước nhỏ, bạn sẽ giảm nguy cơ mắc lỗi. Bạn cũng tránh để hệ thống bị hỏng trong khi tiến hành tái cấu trúc - điều này cho phép bạn dần dần cấu trúc lại hệ thống trong một khoảng thời gian dài.

Tái cấu trúc đi đôi với kiểm thử đơn vị. Viết các bài kiểm tra trước khi bạn tái cấu trúc và sau đó bạn có mức độ tin cậy trong việc tái cấu trúc (tỷ lệ với mức độ phù hợp của các bài kiểm tra).

Một tài liệu tham khảo tốt là: Thông tin về Refactoring


16
Trích dẫn của Fowler tất nhiên là có liên quan, và vâng, nó đi đôi với các bài kiểm tra đơn vị ... Nhưng, điều này có thực sự trả lời được các câu hỏi được đặt ra: Các ví dụ được đề cập là cấu trúc lại hay chỉ sửa đổi mã? Ai đúng, OP hoặc đồng nghiệp của anh ấy, và tại sao?
Jonik

35

Fowler vẽ ra một ranh giới rõ ràng giữa những thay đổi đối với mã có và những thay đổi không ảnh hưởng đến hành vi của nó. Ông gọi những điều đó không phải là "tái cấu trúc". Đây một điểm khác biệt quan trọng, bởi vì nếu chúng ta chia công việc của mình thành các hoạt động sửa đổi mã tái cấu trúc và không tái cấu trúc (Fowler gọi nó là "đội những chiếc mũ khác nhau"), chúng ta có thể áp dụng các kỹ thuật khác nhau, phù hợp với mục tiêu.

Nếu chúng tôi đang thực hiện tái cấu trúc hoặc sửa đổi mã duy trì hành vi:

  • tất cả các bài kiểm tra đơn vị của chúng tôi phải vượt qua trước và sau khi sửa đổi
  • chúng ta không cần phải sửa đổi bất kỳ bài kiểm tra nào hoặc viết bất kỳ bài kiểm tra mới nào
  • chúng tôi mong đợi mã sạch hơn khi chúng tôi hoàn thành
  • chúng tôi không mong đợi hành vi mới

Nếu chúng tôi đang thực hiện sửa đổi mã thay đổi hành vi:

  • chúng tôi mong đợi hành vi mới
  • chúng ta nên viết các bài kiểm tra mới
  • chúng tôi có thể nhận được mã bẩn hơn khi chúng tôi hoàn thành (và sau đó nên cấu trúc lại nó)

Nếu chúng ta không nhận ra sự khác biệt này, thì kỳ vọng của chúng ta đối với bất kỳ tác vụ sửa đổi mã cụ thể nào sẽ bị xáo trộn và phức tạp, hoặc ở bất kỳ mức độ nào cũng lộn xộn và phức tạp hơn nếu chúng ta lưu tâm đến nó. Đó là lý do tại sao từ và ý nghĩa của nó rất quan trọng.


3
+1, chính xác. Đặc biệt là cơ sở lý luận bạn đưa ra; những kỳ vọng bị xáo trộn. Khi viết câu trả lời của riêng mình, tôi đã nghĩ đến điều này, ngay cả khi tôi không viết nó ra một cách gọn gàng :)
Jonik

18

Để đưa ra quan điểm của tôi:

Những thay đổi nhỏ, gia tăng khiến mã ở trạng thái tốt hơn những gì nó được tìm thấy

Chắc chắn là Có: Các thay đổi "thẩm mỹ" không liên quan trực tiếp đến các tính năng (tức là nó không thể lập hóa đơn như một yêu cầu thay đổi).

Chắc chắn là Không: Viết lại các phần lớn rõ ràng vi phạm phần "nhỏ, tăng dần". Tái cấu trúc thường được sử dụng ngược lại với viết lại: thay vì thực hiện lại, hãy cải tiến hiện có.

Chắc chắn Có thể: Việc thay thế cấu trúc dữ liệu và thuật toán là một phần của trường hợp biên giới. Sự khác biệt quyết định ở đây IMO là các bước nhỏ: sẵn sàng giao hàng, sẵn sàng làm việc với trường hợp khác.


Ví dụ: Hãy tưởng tượng bạn có Mô-đun công cụ ngẫu nhiên báo cáo bị chậm lại do sử dụng vectơ. Bạn đã xác định rằng chèn vectơ là nút thắt cổ chai, nhưng thật không may, mô-đun dựa vào bộ nhớ liên tục ở nhiều nơi để khi sử dụng danh sách, mọi thứ sẽ bị phá vỡ một cách im lặng.

Viết lại có nghĩa là vứt bỏ Mô-đun để xây dựng một tòa nhà tốt hơn và nhanh hơn từ đầu, chỉ cần chọn một số mảnh từ cái cũ. Hoặc viết một lõi mới, sau đó lắp nó vào hộp thoại hiện có.

Tái cấu trúc có nghĩa là thực hiện các bước nhỏ để loại bỏ số học con trỏ, để chuyển đổi. Có thể bạn thậm chí tạo một hàm tiện ích bao gồm số học con trỏ, thay thế thao tác con trỏ trực tiếp bằng các lệnh gọi đến hàm đó, sau đó chuyển sang trình vòng lặp để trình biên dịch phàn nàn về những nơi mà số học con trỏ vẫn được sử dụng, sau đó chuyển sang a list, rồi xóa chức năng loét.


Ý tưởng đằng sau là mã tự trở nên tồi tệ hơn. Khi sửa lỗi và thêm tính năng, chất lượng sẽ giảm theo từng bước nhỏ - ý nghĩa của một biến thay đổi một cách tinh vi, một hàm nhận được một tham số bổ sung để phá vỡ sự cô lập, một vòng lặp hơi phức tạp, v.v. Không có lỗi nào trong số này là lỗi thực sự, bạn có thể 't nói với một số dòng làm cho vòng lặp trở nên phức tạp, nhưng bạn làm tổn hại đến khả năng đọc và bảo trì.

Tương tự, thay đổi tên biến hoặc trích xuất một hàm, không phải là những cải tiến hữu hình của riêng chúng. Nhưng nhìn chung, chúng chống lại sự xói mòn chậm.

Giống như một bức tường đá cuội mà hàng ngày người ta rơi xuống đất. Và hàng ngày, một người qua đường nhặt nó lên và đặt nó lại.


12

Với định nghĩa của Martin Fowler,

Tái cấu trúc là một kỹ thuật có kỷ luật để tái cấu trúc một phần mã hiện có, thay đổi cấu trúc bên trong của nó mà không thay đổi hành vi bên ngoài của nó.

... Tôi nghĩ rằng bạn rõ ràng đúng.

Họ cũng đề xuất những thứ như thay đổi cấu trúc dữ liệu (như Java LinkedList thành ArrayList), thay đổi thuật toán (sử dụng sắp xếp hợp nhất thay vì sắp xếp bong bóng) và thậm chí viết lại các đoạn mã lớn dưới dạng tái cấu trúc.

Thay đổi một thuật toán thành một thứ gì đó nhanh hơn nhiều rõ ràng không phải là tái cấu trúc, bởi vì hành vi bên ngoài bị thay đổi! (Sau đó, một lần nữa, nếu hiệu ứng không bao giờ đáng chú ý, có lẽ bạn có thể gọi nó là tái cấu trúc - và cũng là tối ưu hóa sớm. :-)

Đây là một con thú cưng của tôi; thật khó chịu khi mọi người sử dụng thuật ngữ một cách cẩu thả - tôi thậm chí đã bắt gặp một số người có thể tình cờ sử dụng cấu trúc lại cho bất kỳ loại thay đổi hoặc sửa chữa cơ bản nào. Vâng, đó là một từ thông dụng sành điệu và thú vị và tất cả, nhưng không có gì sai với các thuật ngữ cũ đơn giản như thay đổi , viết lại hoặc cải thiện hiệu suất . Chúng ta nên sử dụng chúng khi thích hợp và dự phòng tái cấu trúc cho các trường hợp khi bạn thực sự chỉ cải thiện cấu trúc bên trong phần mềm của mình. Đặc biệt, trong một nhóm phát triển, có một ngôn ngữ chung để thảo luận chính xác về công việc của bạn là điều quan trọng.


3
Tôi không chắc rằng việc tạo mã nhanh hơn có đủ điều kiện để thay đổi hành vi bên ngoài ...
GalacticCowboy

2
Vâng, tôi thấy quan điểm của bạn, tôi đoán nó phụ thuộc vào góc độ bạn nhìn vào nó. :) Trong mọi trường hợp, IMO, khi lập trình, bạn nên chú ý đến chiếc "mũ" bạn đang đội tại mỗi thời điểm, tức là chính xác bạn đang cố gắng đạt được điều gì. Nói cách khác, bạn nên tách biệt một cách có ý thức khi bạn thêm / sửa một tính năng, khi cấu trúc lại và khi tối ưu hóa (cải thiện hiệu suất). IIRC, Fowler cũng nói về điều này trong cuốn sách dứt khoát của mình về tái cấu trúc.
Jonik

1
Đối với phần thay đổi bên ngoài, chúng ta có thể viết lại thành: [...] mà không thay đổi hành vi bên ngoài của nó nếu hành vi đó là quan trọng. Nếu hiệu suất là quan trọng theo cách nào đó (nhanh hơn hoặc chậm hơn), thì không thực hiện thay đổi có thể ảnh hưởng đến nó như một phần của giai đoạn "tái cấu trúc" của bạn.
Loki

10

Nếu giao diện của một đoạn mã thay đổi, thì tôi coi đó hơn là tái cấu trúc.

Trường hợp điển hình để tái cấu trúc là

  • "Ồ, tất cả các bài kiểm tra đơn vị của tôi đều chạy, nhưng tôi nghĩ mã của tôi có thể được làm sạch hơn"
  • Thay đổi mã để dễ đọc / rõ ràng / hiệu quả hơn
  • Chạy lại các bài kiểm tra đơn vị (mà không thay đổi các bài kiểm tra) và kiểm tra chúng vẫn hoạt động

Điều này có nghĩa là thuật ngữ tái cấu trúc có liên quan đến giao diện mà bạn đang thảo luận. tức là bạn có thể đang cấu trúc lại mã đằng sau một giao diện, trong khi thay đổi rộng rãi hơn mã của một giao diện khác ở cấp thấp hơn (có thể sự khác biệt đó là điều gây ra sự nhầm lẫn giữa bạn và đồng nghiệp của bạn ở đây?)


7

Tôi nghĩ bạn đúng, nhưng tranh luận về ý nghĩa của một từ không đặc biệt thú vị hoặc hiệu quả.


6
Tôi thường đồng ý với điều này, nhưng cuộc thảo luận đã diễn ra khi chúng tôi xem xét các tài liệu mà chúng tôi đã viết, và tôi nghĩ sẽ tốt khi chúng ta nói về cùng một điều khi chúng ta sử dụng cùng một từ.
David Johnstone

4

http://en.wikipedia.org/wiki/Code_refactoring

Tái cấu trúc mã là quá trình thay đổi cấu trúc bên trong của chương trình máy tính mà không sửa đổi hành vi chức năng bên ngoài hoặc chức năng hiện có của nó, nhằm cải thiện các thuộc tính phi chức năng bên trong của phần mềm, chẳng hạn như để cải thiện khả năng đọc mã, đơn giản hóa cấu trúc mã, để thay đổi mã để tuân thủ một mô hình lập trình nhất định, để cải thiện khả năng bảo trì, cải thiện hiệu suất hoặc cải thiện khả năng mở rộng.

Tôi đồng ý rằng mã tái cấu trúc bao gồm việc phá vỡ mã hiện có. Chỉ cần đảm bảo rằng bạn có các bài kiểm tra đơn vị để bạn không tạo ra bất kỳ lỗi nào và phần còn lại của mã sẽ biên dịch. Sử dụng các công cụ tái cấu trúc như Resharper cho C # làm cho việc này trở nên thật dễ dàng!

  • Làm cho mã dễ hiểu hơn
  • Làm sạch mã và làm cho nó gọn gàng hơn
  • Đang xóa mã! Mã dự phòng, không sử dụng và nhận xét nên bị xóa
  • Cải thiện hiệu suất
  • Làm một cái gì đó chung chung hơn. Bắt đầu với điều đơn giản nhất có thể, sau đó cấu trúc lại nó để giúp dễ dàng kiểm tra / cô lập hoặc chung chung để nó có thể hoạt động theo nhiều cách khác nhau thông qua tính đa hình
  • Giữ mã KHÔ - Đừng lặp lại chính bạn, vì vậy một phiên tái cấu trúc có thể liên quan đến việc lấy một số mã lặp lại và cấu trúc lại nó thành một thành phần / lớp / mô-đun duy nhất.

3

Tôi không đồng ý :

Trong kỹ thuật phần mềm, "tái cấu trúc" mã nguồn có nghĩa là cải thiện nó mà không làm thay đổi kết quả tổng thể của nó [...]

Bạn đã biết các thuật ngữ chính xác hơn được sử dụng cho các tập con của việc tái cấu trúc và vâng, đó là một thuật ngữ rất chung chung.


1

Tôi nghĩ rằng không ai có thể hưởng lợi từ một định nghĩa quá mạnh mẽ về thuật ngữ 'tái cấu trúc'. Ranh giới giữa cách bạn nhìn nhận nó và đồng nghiệp của bạn rất mờ và có thể gần với quan điểm của họ hoặc của bạn hơn tùy thuộc vào nhiều sự kiện. Vì nó động, hãy cố gắng xác định nó. Trước hết, xác định ranh giới của hệ thống hoặc hệ thống con mà bạn đang cố gắng tái cấu trúc.

Nếu nó là một phương thức, hãy giữ tên, các đối số đầu vào, kiểu của giá trị trả về và có thể cố định các câu lệnh. Áp dụng tất cả các thay đổi bên trong phương thức mà không thay đổi cách nó được xem bên ngoài.

Nếu bạn cấu trúc lại một lớp, hãy sửa API công khai của nó và sử dụng các biến đổi tên, các phương thức trích xuất và tất cả các kỹ thuật có sẵn khác sẽ thay đổi lớp đó để dễ đọc hơn và / hoặc hiệu suất hơn.

Nếu phần mã mà bạn đang cấu trúc lại là một gói hoặc một mô-đun, thì việc tái cấu trúc bên trong nó có thể đổi tên các lớp, xóa, giới thiệu giao diện, đẩy / kéo mã vào các lớp siêu / con.


0

Tái cấu trúc = cải thiện các yêu cầu phi chức năng trong khi vẫn giữ nguyên các yêu cầu chức năng.

Yêu cầu phi chức năng = tính mô-đun, khả năng kiểm tra, khả năng bảo trì, khả năng đọc, tách biệt các mối quan tâm, nguyên tắc liskov, v.v.

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.