Phải làm gì với tệp nguồn C ++ 11000 dòng?


229

Vì vậy, chúng tôi có tệp nguồn mainmodule.cpp khổng lồ này (là 11000 dòng?) Trong dự án của chúng tôi và mỗi khi tôi phải chạm vào nó, tôi co rúm lại.

Vì tệp này rất trung tâm và lớn, nó tiếp tục tích lũy ngày càng nhiều mã và tôi không thể nghĩ ra một cách hay để làm cho nó thực sự bắt đầu co lại.

Tệp được sử dụng và thay đổi tích cực trong một số (> 10) phiên bản bảo trì của sản phẩm của chúng tôi và do đó rất khó để cấu trúc lại nó. Nếu tôi "đơn giản" tách nó ra, hãy nói bắt đầu, thành 3 tệp, sau đó hợp nhất các thay đổi từ các phiên bản bảo trì sẽ trở thành một cơn ác mộng. Và nếu bạn chia ra một tập tin có lịch sử lâu dài và phong phú như vậy, việc theo dõi và kiểm tra các thay đổi cũ trong SCClịch sử đột nhiên trở nên khó khăn hơn rất nhiều.

Về cơ bản, tệp chứa "lớp chính" (điều phối và điều phối công việc nội bộ chính) của chương trình của chúng tôi, vì vậy mỗi khi một tính năng được thêm vào, nó cũng ảnh hưởng đến tệp này và mỗi khi nó phát triển. :-(

bạn sẽ làm gì trong tình huống này? Bạn có ý tưởng nào về cách di chuyển các tính năng mới sang một tệp nguồn riêng biệt mà không làm rối quá SCCtrình làm việc không?

(Lưu ý về các công cụ: Chúng tôi sử dụng C ++ với Visual Studio; Chúng tôi sử dụng AccuRevnhư SCCnhưng tôi nghĩ loại SCCkhông thực sự quan trọng ở đây; Chúng tôi sử dụng Araxis Mergeđể làm so sánh thực tế và sáp nhập các tập tin)


15
@BoltClock: Thật ra Vim sẽ mở nó khá nhanh.
vào

58
69305 dòng và đếm. Một tệp trong ứng dụng của chúng tôi mà đồng nghiệp của tôi đã bỏ hầu hết mã của anh ấy vào. Không thể cưỡng lại việc đăng bài này ở đây. Tôi không có ai trong công ty của tôi báo cáo điều này.
Agnel Kurian

204
Tôi không hiểu Làm thế nào có thể "bỏ công việc đó" nhận được rất nhiều upvote? Một số người dường như sống trong một thế giới thần tiên, nơi tất cả các dự án được viết từ đầu và / hoặc sử dụng 100% nhanh nhẹn, TDD, ... (đặt bất kỳ từ thông dụng nào của bạn ở đây).
Stefan

39
@Stefan: Khi phải đối mặt với một cơ sở mã tương tự tôi đã làm chính xác điều đó. Tôi không thích dành 95% thời gian của mình để làm việc xung quanh cơ sở mã 10 năm tuổi và 5% thực sự viết mã. Thực sự không thể kiểm tra một số khía cạnh của hệ thống (và ý tôi không phải là kiểm tra đơn vị, ý tôi là thực sự chạy mã để xem nó có hoạt động không). Tôi đã không kéo dài thời gian dùng thử 6 tháng, tôi cảm thấy mệt mỏi khi phải chiến đấu thua trận và viết mã mà tôi không thể đứng yên.
Nhị phân nhị phân

50
liên quan đến khía cạnh theo dõi lịch sử của việc chia tách tệp: sử dụng lệnh sao chép của hệ thống kiểm soát phiên bản của bạn để sao chép toàn bộ tệp theo nhiều lần bạn muốn tách tệp và sau đó xóa tất cả mã khỏi từng bản sao mà bạn không muốn trong tập tin đó Điều này bảo tồn lịch sử tổng thể, vì mỗi một trong số các tệp được phân tách có thể theo dõi lịch sử của nó thông qua việc phân tách (trông sẽ giống như một sự xóa bỏ khổng lồ của hầu hết các nội dung của tệp).
rmeador

Câu trả lời:


86
  1. Tìm một số mã trong tệp tương đối ổn định (không thay đổi nhanh và không thay đổi nhiều giữa các nhánh) và có thể đứng như một đơn vị độc lập. Di chuyển cái này vào tập tin riêng của nó, và cho vấn đề đó vào lớp riêng của nó, trong tất cả các nhánh. Bởi vì nó ổn định, điều này sẽ không gây ra (nhiều) sự hợp nhất "vụng về" phải được áp dụng cho một tệp khác với tệp mà chúng được tạo ban đầu, khi bạn hợp nhất thay đổi từ nhánh này sang nhánh khác. Nói lại.

  2. Tìm một số mã trong tệp về cơ bản chỉ áp dụng cho một số ít chi nhánh và có thể độc lập. Không quan trọng việc nó thay đổi nhanh hay không, vì số lượng chi nhánh ít. Di chuyển cái này vào các lớp và tập tin riêng của nó. Nói lại.

Vì vậy, chúng tôi đã loại bỏ mã giống nhau ở mọi nơi và mã dành riêng cho một số chi nhánh nhất định.

Điều này khiến bạn có một hạt nhân của mã được quản lý kém - nó cần ở mọi nơi, nhưng nó khác nhau ở mỗi nhánh (và / hoặc nó thay đổi liên tục để một số nhánh chạy phía sau các nhánh khác), và nó nằm trong một tệp duy nhất mà bạn là không thành công khi cố gắng hợp nhất giữa các chi nhánh. Đừng làm thế nữa. Chi nhánh tập tin vĩnh viễn , có lẽ bằng cách đổi tên nó trong mỗi chi nhánh. Nó không còn "chính" nữa, đó là "chính cho cấu hình X". OK, do đó, bạn mất khả năng áp dụng cùng một thay đổi cho nhiều chi nhánh bằng cách hợp nhất, nhưng trong bất kỳ trường hợp nào, cốt lõi của mã nơi hợp nhất không hoạt động tốt. Nếu bạn vẫn phải tự quản lý các kết hợp để xử lý các xung đột, thì việc áp dụng chúng một cách độc lập trên mỗi nhánh sẽ không mất gì.

Tôi nghĩ bạn đã sai khi nói rằng loại SCC không thành vấn đề, vì ví dụ như khả năng hợp nhất của git có lẽ tốt hơn công cụ hợp nhất bạn đang sử dụng. Vì vậy, vấn đề cốt lõi, "hợp nhất là khó khăn" xảy ra ở những thời điểm khác nhau đối với các SCC khác nhau. Tuy nhiên, bạn không có khả năng thay đổi SCC, vì vậy vấn đề có thể không liên quan.


Về việc sáp nhập: Tôi đã xem GIT và tôi đã xem SVN và tôi đã xem Perforce và để tôi nói với bạn rằng không có gì tôi thấy ở bất cứ nơi nào đánh bại AccuRev + Araxis cho những gì chúng tôi làm. :-) (Mặc dù GIT có thể làm điều này [ stackoverflow.com/questions/1728922/ triệt] và AccuRev không thể - mọi người phải tự quyết định nếu đây là một phần của việc hợp nhất hoặc phân tích lịch sử.)
Martin Ba

Đủ công bằng - có thể bạn đã có sẵn công cụ tốt nhất. Khả năng của Git để hợp nhất một thay đổi xảy ra trong Tệp A trên nhánh X, thành Tệp B trên nhánh Y, để giúp phân chia các tệp phân nhánh dễ dàng hơn, nhưng có lẽ hệ thống bạn sử dụng có những ưu điểm bạn thích. Dù sao, tôi không đề nghị bạn chuyển sang git, chỉ nói rằng SCC sẽ tạo ra sự khác biệt ở đây, nhưng ngay cả vậy tôi cũng đồng ý với bạn rằng điều này có thể được giảm giá :-)
Steve Jessop

129

Hợp nhất sẽ không phải là một cơn ác mộng lớn như vậy khi bạn nhận được 30000 tệp LỘC trong tương lai. Vì thế:

  1. Dừng thêm mã vào tập tin đó.
  2. Tách nó đi.

Nếu bạn không thể ngừng mã hóa trong quá trình tái cấu trúc, bạn có thể rời khỏi tệp lớn này trong một thời gian ít nhất mà không cần thêm mã vào nó: vì nó chứa một "lớp chính" mà bạn có thể kế thừa từ nó và giữ lớp kế thừa ( es) với các chức năng quá tải trong một số tệp mới được thiết kế tốt.


@Martin: may mắn là bạn chưa dán tập tin của bạn ở đây, vì vậy tôi không biết gì về cấu trúc của nó. Nhưng ý tưởng chung là chia nó thành các phần hợp lý. Các phần logic như vậy có thể chứa các nhóm hàm từ "lớp chính" của bạn hoặc bạn có thể chia nó thành nhiều lớp phụ trợ.
Kirill V. Lyadvinsky

3
Với 10 phiên bản bảo trì và nhiều nhà phát triển hoạt động, không chắc tệp có thể bị đóng băng trong thời gian đủ dài.
Kobi

9
@Martin, bạn có một vài mẫu GOF sẽ thực hiện thủ thuật, một Mặt tiền duy nhất ánh xạ các chức năng của mainmodule.cpp, thay vào đó (tôi đã đề xuất bên dưới) tạo một bộ các lớp Lệnh mà mỗi ánh xạ thành một hàm / tính năng của mainmodule.app. (Tôi đã mở rộng về điều này trên câu trả lời của mình.)
ocodo

2
Có hoàn toàn đồng ý, đến một lúc nào đó bạn phải dừng thêm mã vào nó hoặc cuối cùng nó sẽ là 30k, 40k, 50k, mô-đun chính kaboom chỉ bị lỗi. :-)
Chris

67

Tôi nghe có vẻ như bạn đang đối mặt với một số mã có mùi ở đây. Trước hết, lớp chính dường như vi phạm nguyên tắc mở / đóng . Có vẻ như nó đang xử lý quá nhiều trách nhiệm . Do đó, tôi cho rằng mã này dễ vỡ hơn mức cần thiết.

Mặc dù tôi có thể hiểu mối quan tâm của bạn về khả năng truy tìm nguồn gốc sau khi tái cấu trúc, tôi hy vọng rằng lớp này khá khó để duy trì và nâng cao và bất kỳ thay đổi nào bạn thực hiện có thể gây ra tác dụng phụ. Tôi sẽ cho rằng chi phí của những khoản này lớn hơn chi phí tái cấu trúc lớp.

Trong mọi trường hợp, vì mã có mùi sẽ chỉ trở nên tồi tệ hơn theo thời gian, ít nhất tại một số điểm, chi phí của chúng sẽ vượt xa chi phí tái cấu trúc. Từ mô tả của bạn, tôi sẽ cho rằng bạn đã qua điểm tới hạn.

Tái cấu trúc điều này nên được thực hiện trong các bước nhỏ. Nếu có thể hãy thêm các kiểm tra tự động để xác minh hành vi hiện tại trước khi tái cấu trúc bất cứ điều gì. Sau đó chọn ra các khu vực nhỏ của chức năng biệt lập và trích xuất chúng dưới dạng các loại để giao phó trách nhiệm.

Trong mọi trường hợp, nó có vẻ như là một dự án lớn, thật may mắn :)


18
Nó có mùi rất nhiều: nó có mùi giống như mô hình chống Blob ở trong nhà ... en.wikipedia.org/wiki/God_object . Bữa ăn yêu thích của anh ấy là mã spaghetti: en.wikipedia.org/wiki/Spaghetti_code :-)
jdehaan

@jdehaan: Tôi đã cố gắng ngoại giao về nó :)
Brian Rasmussen

+1 Từ tôi cũng vậy, tôi không dám chạm vào ngay cả mã phức tạp mà tôi đã viết mà không cần kiểm tra để che nó.
Daniel Thomas

49

Giải pháp duy nhất tôi từng tưởng tượng cho những vấn đề như vậy sau đây. Lợi ích thực tế của phương pháp được mô tả là sự tiến bộ của các diễn biến. Không có cuộc cách mạng nào ở đây, nếu không bạn sẽ gặp rắc rối rất nhanh.

Chèn một lớp cpp mới phía trên lớp chính ban đầu. Hiện tại, về cơ bản, nó sẽ chuyển hướng tất cả các cuộc gọi đến lớp chính hiện tại, nhưng nhằm mục đích làm cho API của lớp mới này rõ ràng và cô đọng nhất có thể.

Một khi điều này đã được thực hiện, bạn có khả năng thêm các chức năng mới trong các lớp mới.

Đối với các chức năng hiện có, bạn phải dần dần di chuyển chúng trong các lớp mới khi chúng trở nên đủ ổn định. Bạn sẽ mất trợ giúp SCC cho đoạn mã này, nhưng không có nhiều điều có thể được thực hiện về điều đó. Chỉ cần chọn đúng thời điểm.

Tôi biết điều này không hoàn hảo, mặc dù tôi hy vọng nó có thể giúp ích, và quy trình phải phù hợp với nhu cầu của bạn!

Thông tin thêm

Lưu ý rằng Git là một SCC có thể theo các đoạn mã từ tệp này sang tệp khác. Tôi đã nghe những điều tốt về nó, vì vậy nó có thể giúp đỡ trong khi bạn đang dần dần di chuyển công việc của bạn.

Git được xây dựng xung quanh khái niệm các đốm màu, nếu tôi hiểu chính xác, đại diện cho các phần của tệp mã. Di chuyển các phần này xung quanh trong các tệp khác nhau và Git sẽ tìm thấy chúng, ngay cả khi bạn sửa đổi chúng. Ngoài video từ Linus Torvalds được đề cập trong các bình luận bên dưới, tôi không thể tìm thấy điều gì rõ ràng về điều này.


Một tài liệu tham khảo về cách GIT làm điều đó / cách bạn làm điều đó với GIT sẽ được chào đón nhất.
Martin Ba

@Martin Git tự động làm điều đó.
Matthew

4
@Martin: Git thực hiện tự động - vì nó không theo dõi tệp, nó theo dõi nội dung. Thực sự khó khăn hơn khi chỉ "lấy lịch sử của một tập tin".
Arafangion

1
@Martin youtube.com/watch?v=4XpnKHJAok8 là một cuộc trò chuyện nơi Torvalds nói về git. Ông đề cập đến nó sau này trong cuộc nói chuyện.
Matthew

6
@ Martin, nhìn vào câu hỏi này: stackoverflow.com/questions/1728922/...
Benjol

30

Khổng Tử nói: "bước đầu tiên để thoát khỏi lỗ là ngừng đào hố".


25

Hãy để tôi đoán: Mười khách hàng với các bộ tính năng khác nhau và một người quản lý bán hàng khuyến khích "tùy biến"? Tôi đã từng làm việc trên các sản phẩm như thế trước đây. Về cơ bản chúng tôi có cùng một vấn đề.

Bạn nhận ra rằng có một tệp khổng lồ là rắc rối, nhưng rắc rối hơn nữa là mười phiên bản mà bạn phải giữ "hiện tại". Đó là nhiều bảo trì. SCC có thể làm cho điều đó dễ dàng hơn, nhưng nó không thể làm cho nó đúng.

Trước khi bạn cố gắng chia tệp thành nhiều phần, bạn cần đưa mười nhánh trở lại đồng bộ với nhau để bạn có thể xem và định hình tất cả mã cùng một lúc. Bạn có thể thực hiện một nhánh này tại một thời điểm, kiểm tra cả hai nhánh dựa trên cùng một tệp mã chính. Để thực thi hành vi tùy chỉnh, bạn có thể sử dụng #ifdef và bạn bè, nhưng tốt hơn hết là sử dụng if / other đối với các hằng số đã xác định. Bằng cách này, trình biên dịch của bạn sẽ xác minh tất cả các loại và hầu hết có thể loại bỏ mã đối tượng "chết". (Tuy nhiên, bạn có thể muốn tắt cảnh báo về mã chết.)

Khi chỉ có một phiên bản của tệp đó được chia sẻ ngầm bởi tất cả các chi nhánh, thì việc bắt đầu các phương thức tái cấu trúc truyền thống sẽ dễ dàng hơn.

Các #ifdefs chủ yếu tốt hơn cho các phần mà mã bị ảnh hưởng chỉ có ý nghĩa trong bối cảnh của các tùy chỉnh khác trên mỗi nhánh. Người ta có thể lập luận rằng những điều này cũng thể hiện một cơ hội cho cùng một kế hoạch sáp nhập chi nhánh, nhưng đừng đi hoang dã. Một dự án khổng lồ tại một thời điểm, xin vui lòng.

Trong ngắn hạn, tập tin sẽ xuất hiện để phát triển. Không sao đâu Những gì bạn đang làm là mang mọi thứ lại gần nhau. Sau đó, bạn sẽ bắt đầu thấy các khu vực rõ ràng giống nhau bất kể phiên bản nào; chúng có thể được để lại một mình hoặc tái cấu trúc theo ý muốn. Các khu vực khác sẽ rõ ràng khác nhau tùy thuộc vào phiên bản. Bạn có một số tùy chọn trong trường hợp này. Một phương pháp là ủy thác sự khác biệt cho các đối tượng chiến lược trên mỗi phiên bản. Một cách khác là lấy các phiên bản máy khách từ một lớp trừu tượng chung. Nhưng không có biến đổi nào trong số này có thể thực hiện được miễn là bạn có mười "mẹo" phát triển trong các nhánh khác nhau.


2
Tôi đồng ý rằng mục tiêu nên có một phiên bản phần mềm, nhưng sẽ không tốt hơn nếu sử dụng các tệp cấu hình (thời gian chạy) và không biên dịch lưu giữ thời gian
Esben Skov Pedersen

Hoặc thậm chí "lớp cấu hình" cho mỗi bản dựng của khách hàng.
tc.

Tôi cho rằng cấu hình thời gian biên dịch hoặc thời gian chạy là không liên quan về mặt chức năng, nhưng tôi không muốn giới hạn các khả năng. Cấu hình thời gian biên dịch có lợi thế là máy khách không thể hack xung quanh với tệp cấu hình để kích hoạt các tính năng bổ sung, vì nó đặt tất cả cấu hình trong cây nguồn thay vì mã "đối tượng văn bản" có thể triển khai. Mặt trái là bạn có xu hướng về AlternateHardAndSoftLayers nếu đến lúc chạy.
Ian

22

Tôi không biết điều này có giải quyết được vấn đề của bạn không, nhưng điều tôi đoán bạn muốn làm là di chuyển nội dung của tệp sang các tệp nhỏ hơn độc lập với nhau (tóm tắt). Điều tôi cũng nhận được là bạn có khoảng 10 phiên bản phần mềm khác nhau trôi nổi và bạn cần hỗ trợ tất cả chúng mà không làm mọi thứ rối tung lên.

Trước hết, không có cách nào dễ dàng và sẽ tự giải quyết trong vài phút động não. Các chức năng được liên kết trong tệp của bạn đều quan trọng đối với ứng dụng của bạn và chỉ cần cắt chúng và di chuyển chúng sang các tệp khác sẽ không cứu được vấn đề của bạn.

Tôi nghĩ bạn chỉ có những lựa chọn sau:

  1. Đừng di chuyển và ở lại với những gì bạn có. Có thể bỏ công việc của bạn và bắt đầu làm việc trên phần mềm nghiêm túc với thiết kế tốt ngoài ra. Lập trình cực đoan không phải lúc nào cũng là giải pháp tốt nhất nếu bạn đang làm việc trong một dự án dài hạn với đủ tiền để sống sót sau một hoặc hai vụ tai nạn.

  2. Xây dựng bố cục về cách bạn muốn tệp của mình trông như thế nào khi nó được chia ra. Tạo các tệp cần thiết và tích hợp chúng trong ứng dụng của bạn. Đổi tên các hàm hoặc quá tải chúng để lấy một tham số bổ sung (có thể chỉ là một boolean đơn giản?). Khi bạn phải làm việc với mã của mình, hãy di chuyển các hàm bạn cần làm việc sang tệp mới và ánh xạ các lệnh gọi hàm của các hàm cũ sang các hàm mới. Bạn vẫn nên có tệp chính của mình theo cách này và vẫn có thể thấy các thay đổi đã được thực hiện đối với nó, một khi nó đến một chức năng cụ thể mà bạn biết chính xác khi nào nó được thuê ngoài, v.v.

  3. Cố gắng thuyết phục đồng nghiệp của bạn với một số bánh tốt mà quy trình làm việc được đánh giá cao và bạn cần phải viết lại một số phần của ứng dụng để làm kinh doanh nghiêm túc.


19

Chính xác thì vấn đề này được xử lý trong một trong các chương của cuốn sách "Làm việc hiệu quả với mã kế thừa" ( http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 ).


notifyit.com/store/product.aspx?vdn=0131177052 cho phép xem TOC của cuốn sách này (và 2 chương mẫu). Chương 20 dài bao nhiêu? (Chỉ để cảm nhận mức độ hữu ích của nó.)
Martin Ba

17
chương 20 dài 10.000 dòng, nhưng tác giả đã tìm ra cách chia nó thành các phần dễ tiêu hóa ... 8)
Tony Delroy

1
Nó khoảng 23 trang, nhưng với 14 hình ảnh. Tôi nghĩ bạn nên có được nó, bạn sẽ cảm thấy tự tin hơn rất nhiều khi cố gắng quyết định nên làm gì.
Emile Vrijdags

Một cuốn sách tuyệt vời cho vấn đề này, nhưng các khuyến nghị mà nó đưa ra (và các khuyến nghị khác trong chủ đề này) đều có chung một yêu cầu: nếu bạn muốn cấu trúc lại tệp này cho tất cả các chi nhánh của mình, thì cách duy nhất bạn có thể làm là đóng băng tập tin cho tất cả các chi nhánh và thực hiện các thay đổi cấu trúc ban đầu. Không có cách nào khác. Cuốn sách phác thảo một cách tiếp cận lặp đi lặp lại để trích xuất các lớp con một cách an toàn mà không cần hỗ trợ tái cấu trúc tự động, bằng cách tạo các phương thức trùng lặp và ủy quyền các cuộc gọi, nhưng tất cả điều này là không cần thiết nếu bạn không thể sửa đổi các tệp.
Dan Bryant

2
@Martin, cuốn sách rất xuất sắc nhưng nó phụ thuộc khá nhiều vào bài kiểm tra, cấu trúc lại, chu trình kiểm tra có thể khá khó khăn từ nơi bạn đang ở. Tôi đã ở trong một tình huống tương tự và cuốn sách này là cuốn sách hữu ích nhất mà tôi tìm thấy. Đó là những gợi ý tốt cho vấn đề xấu xí mà bạn có. Nhưng nếu bạn không thể có được một khai thác thử nghiệm nào đó vào bức tranh, tất cả các đề xuất tái cấu trúc trên thế giới sẽ không giúp bạn.

14

Tôi nghĩ rằng tốt nhất bạn nên tạo một tập hợp các lớp lệnh ánh xạ tới các điểm API của mainmodule.cpp.

Khi chúng đã sẵn sàng, bạn sẽ cần cấu trúc lại cơ sở mã hiện có để truy cập các điểm API này thông qua các lớp lệnh, khi đã xong, bạn có thể cấu trúc lại mỗi lần thực hiện của lệnh thành một cấu trúc lớp mới.

Tất nhiên, với một lớp duy nhất là 11 KLOC, mã trong đó có thể được ghép rất cao và dễ gãy, nhưng việc tạo các lớp lệnh riêng lẻ sẽ giúp ích nhiều hơn bất kỳ chiến lược proxy / mặt tiền nào khác.

Tôi không ghen tị với nhiệm vụ, nhưng khi thời gian trôi qua, vấn đề này sẽ chỉ trở nên tồi tệ hơn nếu nó không được giải quyết.

Cập nhật

Tôi muốn đề xuất rằng mẫu Lệnh thích hợp hơn với Mặt tiền.

Duy trì / tổ chức nhiều lớp Lệnh khác nhau trên Mặt tiền nguyên khối (tương đối) là tốt hơn. Ánh xạ một Mặt tiền vào một tệp 11 KLOC có thể sẽ cần được chia thành một vài nhóm khác nhau.

Tại sao phải cố gắng để tìm ra các nhóm mặt tiền? Với mẫu Lệnh, bạn sẽ có thể nhóm và sắp xếp các lớp nhỏ này một cách hữu cơ, do đó bạn có thể linh hoạt hơn rất nhiều.

Tất nhiên, cả hai tùy chọn đều tốt hơn so với tập tin 11 KLOC và đang phát triển.


+1 một giải pháp thay thế cho giải pháp tôi đề xuất, với cùng một ý tưởng: thay đổi API để tách vấn đề lớn thành vấn đề nhỏ.
Benoît

13

Một lời khuyên quan trọng: Không trộn lẫn cấu trúc lại và sửa lỗi. Những gì bạn muốn là một Phiên bản của chương trình của bạn giống hệt với phiên bản trước, ngoại trừ mã nguồn là khác nhau.

Một cách có thể là bắt đầu phân tách chức năng / phần nhỏ nhất thành tệp riêng của nó và sau đó bao gồm một tiêu đề (do đó biến main.cpp thành một danh sách #includes, âm thanh có mùi mã trong chính nó * Tôi không một C ++ Guru mặc dù), nhưng ít nhất bây giờ nó được chia thành các tệp).

Sau đó, bạn có thể thử chuyển tất cả các bản phát hành bảo trì sang main.cpp "mới" hoặc bất kỳ cấu trúc nào của bạn. Xin nhắc lại: Không có thay đổi hoặc Lỗi nào khác vì việc theo dõi những điều đó gây nhầm lẫn như địa ngục.

Một điều nữa: Nhiều như bạn có thể mong muốn thực hiện một bước lớn trong việc tái cấu trúc toàn bộ mọi thứ trong một lần, bạn có thể cắn nhiều hơn những gì bạn có thể nhai. Có thể chỉ cần chọn một hoặc hai "phần", đưa chúng vào tất cả các bản phát hành, sau đó thêm một số giá trị khác cho khách hàng của bạn (sau tất cả, Tái cấu trúc không thêm giá trị trực tiếp nên đó là một chi phí phải được chứng minh) và sau đó chọn một phần khác một hoặc hai phần.

Rõ ràng điều đó đòi hỏi một số kỷ luật trong nhóm để thực sự sử dụng các tệp được phân tách và không chỉ thêm công cụ mới vào main.cpp mọi lúc, mà một lần nữa, cố gắng thực hiện một công cụ tái cấu trúc lớn có thể không phải là cách hành động tốt nhất.


1
+1 để bao thanh toán và #inc loại trừ trở lại. Nếu bạn đã làm điều này cho tất cả 10 chi nhánh (một chút công việc ở đó, nhưng có thể quản lý được), bạn vẫn sẽ gặp vấn đề khác, đó là đăng các thay đổi trên tất cả các chi nhánh của bạn, nhưng vấn đề đó sẽ không xảy ra ' t đã mở rộng (nhất thiết). Nó có xấu không? Vâng, nó vẫn còn, nhưng nó có thể mang lại một chút hợp lý cho vấn đề. Đã dành vài năm để bảo trì và phục vụ cho một sản phẩm thực sự, thực sự lớn, tôi biết rằng bảo trì liên quan đến rất nhiều nỗi đau. Ít nhất là học hỏi từ nó, và phục vụ như một câu chuyện cảnh báo cho người khác.
Jay

10

Rofl, điều này nhắc nhở tôi về công việc cũ của tôi. Có vẻ như, trước khi tôi tham gia, mọi thứ đều nằm trong một tệp lớn (cũng là C ++). Sau đó, họ đã chia nó (tại các điểm hoàn toàn ngẫu nhiên bằng cách sử dụng bao gồm) thành khoảng ba (vẫn là các tệp lớn). Chất lượng của phần mềm này, như bạn có thể mong đợi, thật kinh khủng. Dự án có tổng cộng khoảng 40k LỘC. (hầu như không có bình luận nào ngoài RẤT NHIỀU mã trùng lặp)

Cuối cùng tôi đã viết lại dự án. Tôi bắt đầu bằng cách làm lại phần tồi tệ nhất của dự án từ đầu. Tất nhiên tôi đã nghĩ đến một giao diện có thể (nhỏ) giữa phần mới này và phần còn lại. Sau đó, tôi đã chèn phần này vào dự án cũ. Tôi đã không cấu trúc lại mã cũ để tạo giao diện cần thiết, nhưng chỉ thay thế nó. Sau đó tôi thực hiện các bước nhỏ từ đó, viết lại mã cũ.

Tôi phải nói rằng việc này mất khoảng nửa năm và không có sự phát triển của cơ sở mã cũ bên cạnh các lỗi trong thời gian đó.


biên tập:

Kích thước ở mức khoảng 40 nghìn LỘC nhưng ứng dụng mới chứa nhiều tính năng hơn và có lẽ ít lỗi hơn trong phiên bản ban đầu so với phần mềm 8 năm tuổi. Một lý do của việc viết lại cũng là chúng tôi cần các tính năng mới và giới thiệu chúng bên trong mã cũ là gần như không thể.

Phần mềm dành cho một hệ thống nhúng, một máy in nhãn.

Một điểm khác mà tôi nên thêm là về lý thuyết, dự án là C ++. Nhưng đó hoàn toàn không phải là OO, đó có thể là C. Phiên bản mới hướng đến đối tượng.


9
Mỗi khi tôi nghe "từ đầu" trong chủ đề về tái cấu trúc, tôi giết một con mèo con!
Kugel

Tôi đã ở trong một tình huống âm thanh rất giống nhau, mặc dù vòng lặp chương trình chính mà tôi phải nắm bắt chỉ là ~ 9000 LỘC. Và thế là đủ tệ.
AndyUK

8

OK vì vậy đối với hầu hết các API viết lại mã sản xuất là một ý tưởng tồi khi bắt đầu. Hai điều cần phải xảy ra.

Thứ nhất, bạn cần phải thực sự yêu cầu nhóm của mình quyết định đóng băng mã trên phiên bản sản xuất hiện tại của tệp này.

Hai, bạn cần lấy phiên bản sản xuất này và tạo một nhánh quản lý các bản dựng bằng cách sử dụng các chỉ thị tiền xử lý để phân tách tệp lớn. Việc phân tách quá trình biên dịch bằng các chỉ thị tiền xử lý JUST (#ifdefs, #includes, #endifs) dễ dàng hơn so với mã hóa lại API. Nó chắc chắn dễ dàng hơn cho SLA của bạn và hỗ trợ liên tục.

Ở đây bạn có thể chỉ cần cắt bỏ các hàm liên quan đến một hệ thống con cụ thể trong lớp và đặt chúng vào một tệp có tên mainloop_foo ware.cpp và đưa nó vào mainloop.cpp ở đúng vị trí.

HOẶC LÀ

Một cách tốn thời gian hơn nhưng mạnh mẽ hơn sẽ là đưa ra một cấu trúc phụ thuộc nội bộ với sự gián tiếp kép trong cách mọi thứ được đưa vào. Điều này sẽ cho phép bạn phân chia mọi thứ và vẫn quan tâm đến các đồng phụ thuộc. Lưu ý rằng phương pháp này yêu cầu mã hóa theo vị trí và do đó nên được kết hợp với các nhận xét phù hợp.

Cách tiếp cận này sẽ bao gồm các thành phần được sử dụng dựa trên biến thể bạn đang biên dịch.

Cấu trúc cơ bản là main class.cpp của bạn sẽ bao gồm một tệp mới có tên MainClassComponents.cpp sau một khối các câu lệnh như sau:

#if VARIANT == 1
#  define Uses_Component_1
#  define Uses_Component_2
#elif VARIANT == 2
#  define Uses_Component_1
#  define Uses_Component_3
#  define Uses_Component_6
...

#endif

#include "MainClassComponents.cpp"

Cấu trúc chính của tệp MainClassComponents.cpp sẽ ở đó để xử lý các phụ thuộc bên trong các thành phần phụ như thế này:

#ifndef _MainClassComponents_cpp
#define _MainClassComponents_cpp

/* dependencies declarations */

#if defined(Activate_Component_1) 
#define _REQUIRES_COMPONENT_1
#define _REQUIRES_COMPONENT_3 /* you also need component 3 for component 1 */
#endif

#if defined(Activate_Component_2)
#define _REQUIRES_COMPONENT_2
#define _REQUIRES_COMPONENT_15 /* you also need component 15 for this component  */
#endif

/* later on in the header */

#ifdef _REQUIRES_COMPONENT_1
#include "component_1.cpp"
#endif

#ifdef _REQUIRES_COMPONENT_2
#include "component_2.cpp"
#endif

#ifdef _REQUIRES_COMPONENT_3
#include "component_3.cpp"
#endif


#endif /* _MainClassComponents_h  */

Và bây giờ, đối với mỗi thành phần, bạn tạo một tệp thành phần_xx.cpp.

Tất nhiên tôi đang sử dụng số nhưng bạn nên sử dụng một cái gì đó hợp lý hơn dựa trên mã của bạn.

Sử dụng bộ tiền xử lý cho phép bạn phân chia mọi thứ mà không phải lo lắng về các thay đổi API vốn là một cơn ác mộng trong sản xuất.

Khi bạn đã giải quyết xong việc sản xuất, bạn có thể thực sự thiết kế lại.


Điều này trông giống như kết quả kinh nghiệm làm việc nhưng ban đầu-đau đớn.
JBRWilkinson

Trên thực tế, đó là một kỹ thuật đã được sử dụng trong trình biên dịch Borland C ++ để mô phỏng Sử dụng kiểu Pascal để quản lý các tệp tiêu đề. Đặc biệt là khi họ đã thực hiện cổng ban đầu của hệ thống Windowing dựa trên văn bản của họ.
Vua Elf

8

Vâng, tôi hiểu nỗi đau của bạn :) Tôi cũng đã tham gia một vài dự án như vậy và nó không đẹp. Không có câu trả lời dễ dàng cho việc này.

Một cách tiếp cận có thể phù hợp với bạn là bắt đầu thêm các bộ bảo vệ an toàn trong tất cả các chức năng, đó là kiểm tra các đối số, các điều kiện trước / sau trong các phương thức, sau đó thêm các kiểm tra đơn vị để nắm bắt chức năng hiện tại của các nguồn. Khi bạn có thứ này, bạn được trang bị tốt hơn để xác định lại mã bởi vì bạn sẽ có các xác nhận và lỗi xuất hiện cảnh báo bạn nếu bạn quên một cái gì đó.

Đôi khi, mặc dù có những lúc tái cấu trúc chỉ có thể mang lại nhiều nỗi đau hơn là lợi ích. Sau đó, có thể tốt hơn là chỉ cần rời khỏi dự án ban đầu và trong trạng thái bảo trì giả và bắt đầu lại từ đầu, sau đó tăng dần chức năng từ con thú.


4

Bạn không nên quan tâm đến việc giảm kích thước tệp, mà là giảm kích thước lớp. Nó gần giống nhau, nhưng khiến bạn nhìn vấn đề từ một góc độ khác (như @Brian Rasmussen gợi ý , lớp của bạn dường như phải chịu nhiều trách nhiệm).


Như mọi khi, tôi muốn nhận được một lời giải thích cho downvote.
Bjorn Pollex

4

Những gì bạn có là một ví dụ cổ điển về một antipotype thiết kế được gọi là blob . Hãy dành chút thời gian để đọc bài viết tôi chỉ ở đây, và có lẽ bạn có thể tìm thấy một cái gì đó hữu ích. Ngoài ra, nếu dự án này lớn như vẻ ngoài của nó, bạn nên xem xét một số thiết kế để ngăn chặn việc phát triển thành mã mà bạn không thể kiểm soát.


4

Đây không phải là một câu trả lời cho vấn đề lớn, mà là một giải pháp lý thuyết cho một phần cụ thể của nó:

  • Chỉ ra nơi bạn muốn chia tệp lớn thành các tệp con. Đặt ý kiến ​​trong một số định dạng đặc biệt tại mỗi điểm.

  • Viết một kịch bản khá tầm thường sẽ chia tệp thành các tệp con tại các điểm đó. (Có lẽ các bình luận đặc biệt đã nhúng các tên tệp mà tập lệnh có thể sử dụng làm hướng dẫn về cách phân tách nó.) Nó nên giữ các bình luận như là một phần của việc phân tách.

  • Chạy kịch bản. Xóa tập tin gốc.

  • Khi bạn cần hợp nhất từ ​​một nhánh, trước tiên hãy tạo lại tệp lớn bằng cách ghép các mảnh lại với nhau, thực hiện hợp nhất và sau đó phân tách lại.

Ngoài ra, nếu bạn muốn lưu giữ lịch sử tệp SCC, tôi hy vọng cách tốt nhất để làm điều đó là báo cho hệ thống kiểm soát nguồn của bạn rằng các tệp riêng lẻ là bản sao của bản gốc. Sau đó, nó sẽ lưu giữ lịch sử của các phần được lưu trong tệp đó, mặc dù tất nhiên nó cũng sẽ ghi lại rằng các phần lớn đã bị "xóa".


4

Một cách để phân chia nó mà không có quá nhiều nguy hiểm sẽ là xem xét lịch sử về tất cả các thay đổi dòng. Có những chức năng nhất định ổn định hơn những chức năng khác? Điểm nóng của sự thay đổi nếu bạn sẽ.

Nếu một dòng không được thay đổi trong một vài năm, bạn có thể chuyển nó sang tập tin khác mà không phải lo lắng quá nhiều. Tôi sẽ xem nguồn được chú thích với phiên bản cuối cùng đã chạm vào một dòng nhất định và xem liệu có bất kỳ chức năng nào bạn có thể rút ra không.


Tôi nghĩ những người khác đề xuất những điều tương tự. Điều này là ngắn và đến thời điểm và tôi nghĩ rằng đây có thể là một điểm khởi đầu hợp lệ cho vấn đề ban đầu.
Martin Ba

3

Wow, âm thanh tuyệt vời. Tôi nghĩ rằng giải thích với sếp của bạn, rằng bạn cần rất nhiều thời gian để cấu trúc lại con thú rất đáng để thử. Nếu anh ta không đồng ý, bỏ việc là một lựa chọn.

Dù sao, những gì tôi đề nghị về cơ bản là loại bỏ tất cả việc thực hiện và tập hợp lại thành các mô-đun mới, hãy gọi đó là "các dịch vụ toàn cầu". "Mô-đun chính" sẽ chỉ chuyển tiếp đến các dịch vụ đó và BẤT K code mã mới nào bạn viết sẽ sử dụng chúng thay vì "mô-đun chính". Điều này sẽ khả thi trong một khoảng thời gian hợp lý (vì chủ yếu là sao chép và dán), bạn không phá vỡ mã hiện có và bạn có thể thực hiện một phiên bản bảo trì tại một thời điểm. Và nếu bạn vẫn còn thời gian, bạn có thể sử dụng nó để tái cấu trúc tất cả các mô-đun phụ thuộc cũ để sử dụng các dịch vụ toàn cầu.


3

Tôi đồng cảm - trong công việc trước đây của tôi, tôi đã gặp một tình huống tương tự với một tệp lớn hơn nhiều lần so với tệp bạn phải xử lý. Giải pháp là:

  1. Viết mã để kiểm tra toàn diện chức năng trong chương trình đang đề cập. Có vẻ như bạn sẽ không có thứ này trong tay ...
  2. Xác định một số mã có thể được trừu tượng hóa thành một lớp trợ giúp / tiện ích. Không cần phải lớn, chỉ cần một cái gì đó không thực sự là một phần của lớp 'chính' của bạn.
  3. Tái cấu trúc mã được xác định trong 2. thành một lớp riêng biệt.
  4. Chạy lại các bài kiểm tra của bạn để đảm bảo không có gì bị hỏng.
  5. Khi bạn có thời gian, goto 2. và lặp lại theo yêu cầu để làm cho mã có thể quản lý được.

Các lớp bạn xây dựng trong bước 3. Lặp lại có thể sẽ phát triển để hấp thụ nhiều mã hơn phù hợp với chức năng mới rõ ràng của chúng.

Tôi cũng có thể thêm:

0: mua sách của Michael Feathers về làm việc với mã kế thừa

Thật không may loại công việc này là quá phổ biến, nhưng kinh nghiệm của tôi là có giá trị lớn trong việc có thể làm cho mã làm việc nhưng mã khủng khiếp tăng dần ít kinh khủng hơn trong khi giữ cho nó hoạt động.


2

Xem xét các cách để viết lại toàn bộ ứng dụng theo cách hợp lý hơn. Có thể viết lại một phần nhỏ của nó như một nguyên mẫu để xem ý tưởng của bạn có khả thi hay không.

Nếu bạn đã xác định một giải pháp khả thi, hãy cấu trúc lại ứng dụng cho phù hợp.

Nếu tất cả các nỗ lực để tạo ra một kiến ​​trúc hợp lý hơn đều thất bại, thì ít nhất bạn cũng biết giải pháp có thể là xác định lại chức năng của chương trình.


+1 - viết lại nó trong thời gian của bạn mặc dù nếu không thì ai đó có thể nhổ cái hình nộm của họ.
Jon Black

2

0,05 euro của tôi:

Thiết kế lại toàn bộ mớ hỗn độn, chia nó thành các hệ thống con có tính đến các yêu cầu kỹ thuật và kinh doanh (= nhiều rãnh bảo trì song song với cơ sở mã hóa khác nhau cho mỗi loại, rõ ràng cần có khả năng sửa đổi cao, v.v.).

Khi tách thành các hệ thống con, hãy phân tích những nơi có nhiều thay đổi nhất và tách chúng ra khỏi các phần không thay đổi. Điều này sẽ cho bạn thấy những điểm rắc rối. Tách các phần thay đổi nhiều nhất cho các mô-đun của riêng chúng (ví dụ dll) theo cách để API mô-đun có thể được giữ nguyên và bạn không cần phải ngắt BC mọi lúc. Bằng cách này, bạn có thể triển khai các phiên bản khác nhau của mô-đun cho các nhánh bảo trì khác nhau, nếu cần, trong khi có lõi không thay đổi.

Thiết kế lại có thể sẽ cần phải là một dự án riêng biệt, cố gắng thực hiện nó cho một mục tiêu di động sẽ không hoạt động.

Đối với lịch sử mã nguồn, ý kiến ​​của tôi: hãy quên nó cho mã mới. Nhưng hãy giữ lịch sử ở đâu đó để bạn có thể kiểm tra nó, nếu cần. Tôi cá là bạn sẽ không cần nó nhiều như vậy sau khi bắt đầu.

Bạn rất có thể cần phải mua quản lý cho dự án này. Bạn có thể tranh luận có lẽ với thời gian phát triển nhanh hơn, ít lỗi hơn, duy trì dễ dàng hơn và ít hỗn loạn hơn. Một cái gì đó cùng dòng "Chủ động kích hoạt tính khả thi và khả năng bảo trì trong tương lai của các tài sản phần mềm quan trọng của chúng tôi" :)

Đây là cách tôi bắt đầu giải quyết vấn đề ít nhất.


2

Bắt đầu bằng cách thêm ý kiến ​​cho nó. Với tham chiếu đến nơi các chức năng được gọi và nếu bạn có thể di chuyển mọi thứ xung quanh. Điều này có thể khiến mọi thứ chuyển động. Bạn thực sự cần phải đánh giá mức độ dễ vỡ của mã cơ sở. Sau đó di chuyển các bit phổ biến của chức năng với nhau. Thay đổi nhỏ tại một thời điểm.



2

Một cái gì đó tôi thấy hữu ích để làm (và tôi đang làm nó bây giờ mặc dù không phải ở quy mô mà bạn phải đối mặt), là trích xuất các phương thức dưới dạng các lớp (tái cấu trúc đối tượng phương thức). Các phương thức khác nhau trên các phiên bản khác nhau của bạn sẽ trở thành các lớp khác nhau có thể được đưa vào một cơ sở chung để cung cấp các hành vi khác nhau mà bạn cần.


2

Tôi thấy câu này là phần thú vị nhất trong bài viết của bạn:

> Tệp được sử dụng và thay đổi tích cực trong một số (> 10) phiên bản bảo trì của sản phẩm của chúng tôi và do đó rất khó để cấu trúc lại nó

Trước tiên, tôi khuyên bạn nên sử dụng hệ thống kiểm soát nguồn để phát triển các phiên bản bảo trì 10+ này hỗ trợ phân nhánh.

Thứ hai, tôi sẽ tạo mười chi nhánh (một cho mỗi phiên bản bảo trì của bạn).

Tôi có thể cảm thấy bạn co rúm lại! Nhưng hoặc kiểm soát nguồn của bạn không hoạt động cho tình huống của bạn do thiếu tính năng hoặc nó không được sử dụng đúng cách.

Bây giờ đến chi nhánh bạn làm việc - tái cấu trúc nó khi bạn thấy phù hợp, an toàn với kiến ​​thức mà bạn sẽ không làm phiền chín chi nhánh khác của sản phẩm.

Tôi sẽ có một chút lo ngại rằng bạn có rất nhiều trong hàm main () của bạn.

Trong bất kỳ dự án nào tôi viết, tôi sẽ sử dụng hàm main () chỉ thực hiện khởi tạo các đối tượng cốt lõi - như đối tượng mô phỏng hoặc ứng dụng - các lớp này là nơi mà công việc thực sự sẽ diễn ra.

Tôi cũng sẽ khởi tạo một đối tượng ghi nhật ký ứng dụng chính để sử dụng trên toàn cầu trong suốt chương trình.

Cuối cùng, trong chính tôi cũng thêm mã phát hiện rò rỉ trong các khối tiền xử lý để đảm bảo nó chỉ được kích hoạt trong các bản dựng DEBUG. Đây là tất cả tôi sẽ thêm vào main (). Chính () nên ngắn!

Bạn nói thế

> Về cơ bản, tệp chứa "lớp chính" (điều phối và điều phối công việc nội bộ chính) của chương trình của chúng tôi

Nghe có vẻ như hai nhiệm vụ này có thể được chia thành hai đối tượng riêng biệt - một điều phối viên và một người điều phối công việc.

Khi bạn tách chúng ra, bạn có thể làm rối tung "quy trình làm việc SCC" của mình, nhưng có vẻ như việc tuân thủ nghiêm ngặt quy trình làm việc SCC của bạn đang gây ra sự cố bảo trì phần mềm. Bỏ nó ngay bây giờ và đừng nhìn lại, vì ngay khi bạn sửa nó, bạn sẽ bắt đầu dễ ngủ.

Nếu bạn không thể đưa ra quyết định, hãy chiến đấu với người quản lý của bạn cho nó - ứng dụng của bạn cần được tái cấu trúc - và rất tệ bởi âm thanh của nó! Đừng mất không một câu trả lời!


Theo tôi hiểu, vấn đề là ở đây: nếu bạn cắn viên đạn và tái cấu trúc, bạn không thể mang các bản vá giữa các phiên bản nữa. SCC có thể được thiết lập hoàn hảo.
peterchen

@peterchen - chính xác là vấn đề. SCC thực hiện hợp nhất ở cấp độ tập tin. (Hợp nhất 3 chiều) Nếu bạn di chuyển mã xung quanh giữa các tệp, bạn sẽ phải bắt đầu thủ công các khối mã được sửa đổi từ tệp này sang tệp khác. (Tính năng GIT mà người khác đề cập trong một nhận xét khác chỉ tốt cho lịch sử, không phải để hợp nhất theo như tôi có thể nói)
Martin Ba

2

Như bạn đã mô tả, vấn đề chính là phân tách trước khi phân tách so với sau chia tách, hợp nhất trong sửa lỗi, v.v. Công cụ xung quanh nó. Sẽ không mất nhiều thời gian để mã hóa một tập lệnh trong Perl, Ruby, v.v. để loại bỏ phần lớn tiếng ồn từ việc phân tách trước tách ra so với ghép nối sau chia tách. Làm bất cứ điều gì dễ nhất về mặt xử lý tiếng ồn:

  • xóa một số dòng nhất định trước / trong quá trình nối (ví dụ bao gồm các bộ bảo vệ)
  • loại bỏ những thứ khác từ đầu ra khác nếu cần thiết

Bạn thậm chí có thể làm cho nó bất cứ khi nào có đăng ký, phần kết nối sẽ chạy và bạn đã chuẩn bị một cái gì đó khác với các phiên bản tệp đơn.


2
  1. Đừng bao giờ chạm vào tập tin này và mã một lần nữa!
  2. Đối xử giống như một cái gì đó bạn đang bị mắc kẹt. Bắt đầu viết bộ điều hợp cho các chức năng được mã hóa ở đó.
  3. Viết mã mới trong các đơn vị khác nhau và chỉ nói chuyện với các bộ điều hợp gói gọn chức năng của quái vật.
  4. ... Nếu chỉ một trong những điều trên là không thể, hãy bỏ công việc và lấy cho bạn một cái mới.

2
+/- 0 - nghiêm túc, bạn đang sống ở đâu mà bạn khuyên bạn nên bỏ việc dựa trên một chi tiết kỹ thuật như thế này?
Martin Ba

1

"Về cơ bản, tệp chứa" lớp chính "(điều phối và điều phối công việc nội bộ chính) của chương trình của chúng tôi, vì vậy mỗi khi một tính năng được thêm vào, nó cũng ảnh hưởng đến tệp này và mỗi khi nó phát triển."

Nếu SWITCH lớn đó (mà tôi nghĩ là có) trở thành vấn đề bảo trì chính, bạn có thể cấu trúc lại nó để sử dụng từ điển và mẫu Lệnh và xóa tất cả logic chuyển đổi khỏi mã hiện có sang trình tải, tức là:

    // declaration
    std::map<ID, ICommand*> dispatchTable;
    ...

    // populating using some loader
    dispatchTable[id] = concreteCommand;

    ...
    // using
    dispatchTable[id]->Execute();

2
Không, không có chuyển đổi lớn thực sự. Câu này chỉ là câu gần nhất mà tôi có thể đến để mô tả về mớ hỗn độn này :)
Martin Ba

1

Tôi nghĩ cách dễ nhất để theo dõi lịch sử nguồn khi chia tệp sẽ giống như thế này:

  1. Tạo các bản sao của mã nguồn gốc, sử dụng bất kỳ lệnh sao chép bảo tồn lịch sử nào mà hệ thống SCM của bạn cung cấp. Có thể bạn sẽ cần gửi vào thời điểm này, nhưng chưa cần nói với hệ thống xây dựng của bạn về các tệp mới, vì vậy điều đó sẽ ổn.
  2. Xóa mã từ các bản sao này. Điều đó không nên phá vỡ lịch sử cho các dòng bạn giữ.

"Sử dụng bất kỳ lệnh sao chép bảo tồn lịch sử nào mà hệ thống SCM của bạn cung cấp" ... điều tồi tệ là nó không cung cấp bất kỳ
Martin Ba

Quá tệ. Điều đó một mình nghe có vẻ là một lý do tốt để chuyển sang một cái gì đó hiện đại hơn. :-)
Christopher Creutzig

1

Tôi nghĩ rằng những gì tôi sẽ làm trong tình huống này là bit đạn và:

  1. Tìm hiểu làm thế nào tôi muốn tách tệp lên (dựa trên phiên bản phát triển hiện tại)
  2. Đặt khóa quản trị trên tệp ("Không ai chạm vào mainmodule.cpp sau 5 giờ chiều Thứ Sáu !!!"
  3. Dành cuối tuần dài của bạn để áp dụng thay đổi đó cho> 10 phiên bản bảo trì (từ cũ nhất đến mới nhất), cho đến và bao gồm cả phiên bản hiện tại.
  4. Xóa mainmodule.cpp khỏi tất cả các phiên bản phần mềm được hỗ trợ. Đó là một Thời đại mới - không còn mainmodule.cpp nữa.
  5. Quản lý thuyết phục rằng bạn không nên hỗ trợ nhiều hơn một phiên bản bảo trì của phần mềm (ít nhất là không có hợp đồng hỗ trợ $$$ lớn). Nếu mỗi khách hàng của bạn có phiên bản độc đáo của riêng họ .... yeeeeeshhhh. Tôi sẽ thêm các chỉ thị của trình biên dịch thay vì cố gắng duy trì hơn 10 nhánh.

Theo dõi các thay đổi cũ đối với tệp được giải quyết đơn giản bằng nhận xét đăng ký đầu tiên của bạn với nội dung như "tách từ mainmodule.cpp". Nếu bạn cần quay lại một cái gì đó gần đây, hầu hết mọi người sẽ nhớ đến sự thay đổi, nếu sau 2 năm kể từ bây giờ, bình luận sẽ cho họ biết nơi cần tìm. Tất nhiên, sẽ có giá trị như thế nào khi quay trở lại hơn 2 năm để xem ai đã thay đổi mã và tại sao?

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.