Điều gì sẽ giúp khi tái cấu trúc một phương thức lớn để đảm bảo rằng tôi không phá vỡ bất cứ điều gì?


10

Tôi hiện đang tái cấu trúc một phần của một cơ sở mã lớn mà không có đơn vị nào kiểm tra bất cứ điều gì. Tôi đã cố gắng cấu trúc lại mã theo cách tàn bạo, tức là bằng cách đoán mã đang làm gì và những thay đổi nào sẽ không thay đổi ý nghĩa của nó, nhưng không thành công: nó phá vỡ ngẫu nhiên các tính năng xung quanh cơ sở mã.

Lưu ý rằng tái cấu trúc bao gồm di chuyển mã C # kế thừa sang kiểu chức năng hơn (mã kế thừa không sử dụng bất kỳ tính năng nào của .NET Framework 3 trở lên, bao gồm cả LINQ), thêm các tổng quát trong đó mã có thể được hưởng lợi từ chúng, v.v.

Tôi không thể sử dụng các phương pháp chính thức , với giá bao nhiêu.

Mặt khác, tôi cho rằng ít nhất "Bất kỳ mã kế thừa được tái cấu trúc nào cũng phải đi kèm với các thử nghiệm đơn vị" phải được tuân thủ nghiêm ngặt, bất kể nó có giá bao nhiêu. Vấn đề là khi tôi cấu trúc lại một phần nhỏ của phương thức riêng 500 LỘC, việc thêm các bài kiểm tra đơn vị dường như là một nhiệm vụ khó khăn.

Điều gì có thể giúp tôi biết các bài kiểm tra đơn vị nào có liên quan đến một đoạn mã nhất định? Tôi đoán rằng phân tích tĩnh của mã bằng cách nào đó sẽ hữu ích, nhưng các công cụ và kỹ thuật tôi có thể sử dụng để:

  • Biết chính xác những bài kiểm tra đơn vị tôi nên tạo,

  • Và / hoặc biết nếu thay đổi tôi đã thực hiện có ảnh hưởng đến mã gốc theo cách mà nó đang thực thi khác với bây giờ không?


Lý do của bạn là viết bài kiểm tra đơn vị sẽ tăng thời gian cho dự án này là gì? Nhiều người đề xướng sẽ không đồng ý, nhưng nó cũng phụ thuộc vào khả năng viết của bạn.
JeffO

Tôi không nói rằng nó sẽ tăng thời gian tổng thể cho dự án. Điều tôi muốn nói là nó sẽ tăng thời gian ngắn hạn (tức là thời gian ngay lập tức tôi dành ngay bây giờ khi tái cấu trúc mã).
Arseni Mourzenko

1
Dù sao bạn cũng sẽ không sử dụng formal methods in software developmentvì nó được sử dụng để chứng minh tính đúng đắn của chương trình sử dụng logic vị ngữ và sẽ không có khả năng tái cấu trúc một cơ sở mã lớn. Các phương thức chính thức thường được sử dụng để chứng minh mã hoạt động chính xác trong các lĩnh vực như ứng dụng y tế. Bạn đúng là rất tốn kém để làm điều đó là lý do tại sao nó không được sử dụng thường xuyên.
Nấm

Một công cụ tốt như các tùy chọn cấu trúc lại trong ReSharper giúp cho công việc như vậy dễ dàng hơn nhiều. Trong những tình huống như thế này cũng đáng đồng tiền bát gạo.
billy.bob

1
Không phải là một câu trả lời đầy đủ mà là một kỹ thuật ngu ngốc tôi thấy hiệu quả đáng ngạc nhiên khi tất cả các kỹ thuật tái cấu trúc khác đều làm tôi thất bại: Tạo một lớp mới, tách chức năng thành các hàm riêng biệt với chính xác mã đã có, chỉ cần ngắt 50 dòng hoặc hơn, thúc đẩy bất kỳ dòng nào các địa phương được chia sẻ giữa các chức năng cho các thành viên, sau đó các chức năng riêng lẻ phù hợp với đầu tôi hơn và cho tôi khả năng nhìn thấy trong các thành viên những phần nào được luồn qua toàn bộ logic. Đây không phải là mục tiêu cuối cùng, chỉ là một cách an toàn để có được một mớ hỗn độn di sản để sẵn sàng tái cấu trúc một cách an toàn.
Jimmy Hoffa

Câu trả lời:


12

Tôi đã có những thử thách tương tự. Cuốn sách Work with Legacy Code là một tài nguyên tuyệt vời, nhưng có một giả định rằng bạn có thể bấm còi trong các bài kiểm tra đơn vị để hỗ trợ công việc của bạn. Đôi khi điều đó là không thể.

Trong công việc khảo cổ học của tôi (thuật ngữ của tôi để duy trì mã kế thừa như thế này), tôi làm theo một cách tiếp cận tương tự như những gì bạn đã vạch ra.

  • Bắt đầu với một sự hiểu biết vững chắc về những gì các thói quen hiện đang làm.
  • Đồng thời, xác định những gì các thói quen được cho là đang làm. Nhiều người nghĩ viên đạn này và viên trước giống nhau, nhưng có một sự khác biệt tinh tế. Thông thường, nếu thói quen đang làm những gì đáng lẽ phải làm thì bạn sẽ không áp dụng thay đổi bảo trì.
  • Chạy một số mẫu thông qua thói quen và đảm bảo bạn nhấn các trường hợp ranh giới, các đường dẫn lỗi liên quan, cùng với đường dẫn chính. Kinh nghiệm của tôi là thiệt hại tài sản thế chấp (phá vỡ tính năng) xuất phát từ các điều kiện biên không được thực hiện theo cùng một cách chính xác.
  • Sau những trường hợp mẫu đó, hãy xác định những gì đang tồn tại mà không nhất thiết phải tồn tại. Một lần nữa, tôi đã thấy rằng các tác dụng phụ như thế này dẫn đến thiệt hại tài sản thế chấp ở nơi khác.

Tại thời điểm này, bạn nên có một danh sách ứng cử viên về những gì đã bị phơi bày và / hoặc bị thao túng bởi thói quen đó. Một số thao tác có khả năng là vô tình. Bây giờ tôi sử dụng findstrvà IDE để hiểu những lĩnh vực khác có thể tham chiếu các mục trong danh sách ứng cử viên. Tôi sẽ dành thời gian để hiểu những tài liệu tham khảo đó hoạt động như thế nào và bản chất của chúng là gì.

Cuối cùng, một khi tôi đã tự ảo tưởng rằng tôi hiểu tác động của thói quen ban đầu, tôi sẽ thực hiện các thay đổi của mình một lần và chạy lại các bước phân tích mà tôi đã nêu ở trên để xác minh rằng thay đổi đang hoạt động như tôi mong đợi nó để làm việc Tôi đặc biệt cố gắng tránh thay đổi nhiều thứ cùng một lúc vì tôi thấy điều này làm tôi thất vọng khi tôi thử và xác minh tác động. Đôi khi bạn có thể thoát khỏi nhiều thay đổi, nhưng nếu tôi có thể đi theo lộ trình một lần, đó là sở thích của tôi.

Nói tóm lại, cách tiếp cận của tôi tương tự như những gì bạn đặt ra. Đó là rất nhiều công việc chuẩn bị; sau đó thực hiện cắt bao quy đầu, thay đổi cá nhân; và sau đó xác minh, xác minh, xác minh.


2
+1 cho việc sử dụng "khảo cổ học" một mình. Đó là cùng một thuật ngữ tôi sử dụng để mô tả hoạt động này và tôi nghĩ đó là một cách tuyệt vời để đặt nó (cũng nghĩ rằng câu trả lời là tốt - tôi không thực sự nông cạn)
Erik Dietrich

10

Điều gì sẽ giúp khi tái cấu trúc một phương thức lớn để đảm bảo rằng tôi không phá vỡ bất cứ điều gì?

Câu trả lời ngắn: bước nhỏ.

Vấn đề là khi tôi cấu trúc lại một phần nhỏ của phương thức riêng 500 LỘC, việc thêm các bài kiểm tra đơn vị dường như là một nhiệm vụ khó khăn.

Xem xét các bước sau:

  1. Chuyển việc thực hiện sang một chức năng (riêng tư) khác và ủy thác cuộc gọi.

    // old:
    private int ugly500loc(int parameters) {
        // 500 LOC here
    }
    
    // new:    
    private int ugly500loc_old(int parameters) {
        // 500 LOC here
    }
    
    private void ugly500loc(int parameters) {
        return ugly500loc_old(parameters);
    }
    
  2. Thêm mã đăng nhập (đảm bảo đăng nhập không thất bại) trong chức năng ban đầu của bạn, cho tất cả các đầu vào và đầu ra.

    private void ugly500loc(int parameters) {
        static int call_count = 0;
        int current = ++call_count;
        save_to_file(current, parameters);
        int result = ugly500loc_old(parameters);
        save_to_file(current, result); // result, any exceptions, etc.
        return result;
    }
    

    Chạy ứng dụng của bạn và làm bất cứ điều gì bạn có thể với nó (sử dụng hợp lệ, sử dụng không hợp lệ, sử dụng điển hình, sử dụng không điển hình, v.v.).

  3. Bây giờ bạn có max(call_count)bộ đầu vào và đầu ra để viết bài kiểm tra của mình; Bạn có thể viết một đơn thử nghiệm lặp trên tất cả các thông số / bộ kết quả của bạn bạn có và thực thi chúng trong một vòng lặp. Bạn cũng có thể viết một bài kiểm tra có mục đích chạy một kết hợp cụ thể (sẽ được sử dụng để nhanh chóng kiểm tra chuyển qua một bộ i / o cụ thể).

  4. Di chuyển // 500 LOC heretrở lại ugly500locchức năng của bạn (và loại bỏ chức năng đăng nhập).

  5. Bắt đầu trích xuất các hàm từ hàm lớn (không làm gì khác, chỉ trích xuất các hàm) và chạy thử nghiệm. Sau này, bạn nên có nhiều hàm nhỏ hơn để cấu trúc lại, thay vì hàm 500LOC.

  6. Sống hạnh phúc mãi mãi về sau.


3

Thông thường các bài kiểm tra đơn vị là con đường để đi.

Thực hiện các thử nghiệm cần thiết chứng minh rằng hiện tại hoạt động như mong đợi. Hãy dành thời gian của bạn và bài kiểm tra cuối cùng phải khiến bạn tự tin về đầu ra.

Điều gì có thể giúp tôi biết các bài kiểm tra đơn vị nào có liên quan đến một đoạn mã nhất định?

Bạn đang trong quá trình tái cấu trúc một đoạn mã, bạn phải biết chính xác nó làm gì và tác động của nó. Vì vậy, về cơ bản, bạn cần phải kiểm tra tất cả các khu vực bị ảnh hưởng. Điều này sẽ khiến bạn mất rất nhiều thời gian ... nhưng đó là kết quả mong đợi của bất kỳ quá trình tái cấu trúc nào.

Sau đó, bạn có thể xé tất cả ra mà không có bất kỳ vấn đề.

AFAIK, không có kỹ thuật chống đạn cho việc này ... bạn chỉ cần có phương pháp (trên bất kỳ phương pháp nào bạn cảm thấy thoải mái), rất nhiều thời gian và rất nhiều kiên nhẫn! :)

Chúc mừng và chúc may mắn!

Alex


Công cụ bảo hiểm mã là rất cần thiết ở đây. Xác nhận rằng bạn đã bao quát mọi con đường thông qua một phương pháp phức tạp lớn thông qua kiểm tra là khó khăn. Một công cụ hiển thị rằng KitchenSinkMethodTest01 () ... KitchenSinkMethodTest17 () bao gồm các dòng 1-45, 48-220, 245-399 và 488-500 nhưng không chạm vào mã giữa; sẽ làm cho việc tìm ra những bài kiểm tra bổ sung mà bạn cần để viết đơn giản hơn nhiều.
Dan đang loay hoay bởi Firelight
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.