Làm thế nào tôi có thể ngăn chặn địa ngục tiêu đề?


45

Chúng tôi đang bắt đầu một dự án mới, từ đầu. Khoảng tám nhà phát triển, một tá các hệ thống con, mỗi hệ thống có bốn hoặc năm tệp nguồn.

Chúng ta có thể làm gì để ngăn chặn tiêu đề của Hell hell, tiêu đề mì spaghetti AKA?

  • Một tiêu đề cho mỗi tệp nguồn?
  • Cộng một hệ thống con?
  • Các typdefs, stucts & enums riêng biệt từ các nguyên mẫu hàm?
  • Tách hệ thống con nội bộ khỏi hệ thống bên ngoài thứ?
  • Khăng khăng rằng mỗi tệp duy nhất, cho dù tiêu đề hoặc nguồn phải có khả năng biên dịch độc lập?

Tôi không yêu cầu một cách tốt nhất của người Viking, chỉ cần xem những gì cần chú ý và những gì có thể gây ra đau buồn, để chúng ta có thể cố gắng tránh nó.

Đây sẽ là một dự án C ++, nhưng thông tin C sẽ giúp người đọc trong tương lai.


16
Nhận một bản sao của Thiết kế phần mềm C ++ quy mô lớn , nó sẽ không chỉ dạy để tránh các vấn đề với các tiêu đề, mà còn nhiều vấn đề khác liên quan đến sự phụ thuộc vật lý giữa các tệp nguồn và đối tượng trong dự án C ++.
Doc Brown

6
Tất cả các câu trả lời ở đây là tuyệt vời. Tôi muốn thêm rằng tài liệu để sử dụng các đối tượng, phương thức, hàm, nên có trong các tệp tiêu đề. Tôi vẫn thấy doc trong các tập tin nguồn. Đừng bắt tôi đọc nguồn. Đó là điểm của tệp tiêu đề. Tôi không cần phải đọc nguồn trừ khi tôi là người thực hiện.
Cửa Bill

1
Tôi chắc chắn rằng tôi đã làm việc với bạn trước đây. Thường :-(
Mawg

5
Những gì bạn mô tả không phải là một dự án lớn. Thiết kế tốt luôn được chào đón, nhưng bạn có thể không bao giờ phải đối mặt với các vấn đề "Hệ thống quy mô lớn".
Sam

2
Boost thực sự có một cách tiếp cận bao gồm tất cả. Mỗi tính năng độc lập có tệp tiêu đề riêng, nhưng mỗi mô-đun lớn hơn cũng có một tiêu đề bao gồm mọi thứ. Điều này hóa ra thực sự mạnh mẽ để giảm thiểu tiêu đề địa ngục mà không buộc bạn phải #inc loại trừ vài trăm tệp mỗi lần.
Cort Ammon

Câu trả lời:


39

Phương pháp đơn giản: Một tiêu đề cho mỗi tệp nguồn. Nếu bạn có một hệ thống con hoàn chỉnh mà người dùng không muốn biết về các tệp nguồn, hãy có một tiêu đề cho hệ thống con bao gồm tất cả các tệp tiêu đề bắt buộc.

Bất kỳ tệp tiêu đề nào cũng có thể tự biên dịch được (hoặc giả sử một tệp nguồn bao gồm mọi tiêu đề đơn lẻ sẽ biên dịch). Thật đau khổ nếu tôi tìm thấy tệp tiêu đề nào chứa những gì tôi muốn, và sau đó tôi phải tìm kiếm các tệp tiêu đề khác. Một cách đơn giản để thực thi điều này là có mọi tệp nguồn bao gồm tệp tiêu đề của nó trước tiên (cảm ơn doug65536, tôi nghĩ rằng tôi làm điều đó hầu hết thời gian mà không nhận ra).

Đảm bảo bạn sử dụng các công cụ có sẵn để giảm thời gian biên dịch - mỗi tiêu đề chỉ được đưa vào một lần, sử dụng các tiêu đề được biên dịch trước để giảm thời gian biên dịch, sử dụng các mô-đun được biên dịch trước nếu có thể để giảm thời gian biên dịch.


Trường hợp gặp khó khăn là các lệnh gọi hàm hệ thống con chéo, với các tham số của các kiểu được khai báo trong hệ thống con khác.
Mawg

6
Lừa đảo hay không, "#include <subsystem1.h>" sẽ biên dịch. Làm thế nào bạn đạt được điều đó, tùy thuộc vào bạn. @FrankPuffer: Tại sao?
gnasher729

13
@Mawg Điều đó cho thấy bạn cần một hệ thống con riêng, chia sẻ bao gồm các điểm chung của các hệ thống con riêng biệt hoặc bạn cần các tiêu đề "giao diện" đơn giản hóa cho mỗi hệ thống con (sau đó được sử dụng bởi các tiêu đề triển khai, cả hệ thống nội bộ và hệ thống chéo) . Nếu bạn không thể viết các tiêu đề giao diện mà không bao gồm chéo, thiết kế hệ thống con của bạn sẽ bị rối và bạn cần thiết kế lại mọi thứ để hệ thống con của bạn độc lập hơn. (Điều này có thể bao gồm việc rút ra một hệ thống con phổ biến như một mô-đun thứ ba.)
RM

8
Một kỹ thuật tốt để đảm bảo tiêu đề là độc lập là có một quy tắc rằng tệp nguồn luôn bao gồm tiêu đề của chính nó trước . Điều này sẽ bắt các trường hợp bạn cần di chuyển phụ thuộc bao gồm ra khỏi tệp thực hiện vào tệp tiêu đề.
doug65536

4
@FrankPuffer: Vui lòng không xóa bình luận của bạn, đặc biệt nếu người khác phản hồi, vì nó phản hồi không có ngữ cảnh. Bạn luôn có thể sửa câu nói của bạn trong một bình luận mới. Cảm ơn! Tôi muốn biết những gì bạn thực sự nói, nhưng bây giờ nó đã biến mất :(
MPW

18

Cho đến nay, yêu cầu quan trọng nhất là giảm sự phụ thuộc giữa các tệp nguồn của bạn. Trong C ++, thông thường sử dụng một tệp nguồn và một tiêu đề cho mỗi lớp. Do đó, nếu bạn có một thiết kế đẳng cấp tốt, bạn thậm chí sẽ không đến gần địa ngục tiêu đề.

Bạn cũng có thể xem điều này theo cách khác: Nếu bạn đã có tiêu đề địa ngục trong dự án của mình, bạn có thể khá chắc chắn rằng thiết kế phần mềm cần phải được cải thiện.

Để trả lời các câu hỏi cụ thể của bạn:

  • Một tiêu đề cho mỗi tệp nguồn? → Có, điều này hoạt động tốt trong hầu hết các trường hợp và giúp tìm đồ dễ dàng hơn. Nhưng đừng biến nó thành một tôn giáo.
  • Cộng một hệ thống con? → Không, tại sao bạn muốn làm điều này?
  • Các typdefs, stucts & enums riêng biệt từ các nguyên mẫu hàm? → Không, các chức năng và các loại liên quan thuộc về nhau.
  • Tách hệ thống con nội bộ khỏi hệ thống bên ngoài thứ? → Có, tất nhiên. Điều này sẽ làm giảm sự phụ thuộc.
  • Khăng khăng rằng mỗi tệp duy nhất, cho dù tiêu đề hoặc nguồn bởi standalones đều đáng trách? → Có, không bao giờ yêu cầu bất kỳ tiêu đề nào phải được đưa vào trước một tiêu đề khác.

12

Ngoài các khuyến nghị khác, dọc theo các dòng giảm phụ thuộc (chủ yếu áp dụng cho C ++):

  1. Chỉ bao gồm những gì bạn thực sự cần, nơi bạn cần nó (mức thấp nhất có thể). Ví dụ. không bao gồm trong tiêu đề nếu bạn chỉ cần các cuộc gọi trong nguồn.
  2. Sử dụng khai báo chuyển tiếp trong các tiêu đề bất cứ nơi nào có thể (tiêu đề chỉ chứa các con trỏ hoặc tham chiếu đến các lớp khác).
  3. Dọn dẹp bao gồm sau mỗi lần tái cấu trúc (nhận xét chúng, xem nơi biên dịch thất bại, di chuyển chúng đến đó, xóa các dòng bao gồm vẫn nhận xét).
  4. Đừng đóng gói quá nhiều phương tiện phổ biến vào cùng một tệp; phân chia chúng theo chức năng (ví dụ Logger là một lớp, do đó, một tiêu đề và một tệp nguồn; SystemHelper dito, v.v.).
  5. Bám sát các nguyên tắc OO, ngay cả khi tất cả những gì bạn nhận được là một lớp chỉ bao gồm các phương thức tĩnh (thay vì các hàm độc lập) - hoặc sử dụng một không gian tên thay thế .
  6. Đối với một số phương tiện phổ biến nhất định, mẫu singleton khá hữu ích, vì bạn không cần yêu cầu thể hiện từ một số đối tượng không liên quan khác.

5
Trên # 3, công cụ bao gồm những gì bạn đang sử dụng có thể giúp đỡ, tránh phương pháp biên dịch lại thủ công đoán và kiểm tra.
RM

1
Bạn có thể giải thích lợi ích của singletons là gì trong bối cảnh này? Tôi thực sự không có được nó.
Frank Puffer

@FrankPuffer Cơ sở lý luận của tôi là thế này: Không có singleton, một số thể hiện của một lớp thường sở hữu thể hiện của một lớp trợ giúp, như Logger. Nếu một lớp thứ ba muốn sử dụng nó, nó cần yêu cầu tham chiếu của lớp người trợ giúp từ chủ sở hữu, nghĩa là bạn sử dụng hai lớp và tất nhiên bao gồm các tiêu đề của họ - ngay cả khi người dùng không có kinh doanh với chủ sở hữu khác. Với một singleton, bạn chỉ cần bao gồm tiêu đề của lớp trình trợ giúp và có thể yêu cầu thể hiện trực tiếp từ nó. Bạn có thấy một lỗ hổng trong logic này không?
Murphy

1
# 2 (khai báo chuyển tiếp) có thể tạo ra sự khác biệt lớn trong thời gian biên dịch và kiểm tra phụ thuộc. Như câu trả lời này ( stackoverflow.com/a/9999752/509928 ) hiển thị, nó áp dụng cho cả C ++ và C
Dave Compton

3
Bạn cũng có thể sử dụng khai báo chuyển tiếp khi chuyển theo giá trị, miễn là hàm không được xác định nội tuyến. Một khai báo hàm không phải là một bối cảnh trong đó định nghĩa kiểu đầy đủ là cần thiết (một định nghĩa hàm hoặc gọi hàm là một bối cảnh như vậy).
Người kể chuyện - Unslander Monica

6

Một tiêu đề cho mỗi tệp nguồn, xác định những gì tệp nguồn của nó thực hiện / xuất.

Càng nhiều tệp tiêu đề càng cần thiết, được bao gồm trong mỗi tệp nguồn (bắt đầu bằng tiêu đề của chính nó).

Tránh bao gồm (tối thiểu hóa việc bao gồm) các tệp tiêu đề trong các tệp tiêu đề khác (để tránh phụ thuộc vòng tròn). Để biết chi tiết, hãy xem câu trả lời này để "hai lớp có thể nhìn thấy nhau bằng C ++ không?"

Có cả một cuốn sách về chủ đề này, Thiết kế phần mềm C ++ quy mô lớn của Lakos. Nó mô tả có "lớp" phần mềm: các lớp cấp cao sử dụng các lớp cấp thấp hơn chứ không phải ngược lại, điều này một lần nữa tránh được sự phụ thuộc vòng tròn.


4

Tôi sẽ khẳng định câu hỏi của bạn về cơ bản là không thể trả lời được, vì có hai loại địa ngục tiêu đề:

  • Loại mà bạn cần bao gồm một triệu tiêu đề khác nhau, và ai ở địa ngục thậm chí có thể nhớ tất cả chúng? Và duy trì những danh sách tiêu đề? Ừ
  • Loại mà bạn bao gồm một thứ, và tìm hiểu xem bạn đã bao gồm toàn bộ Tháp Babel (hay tôi nên nói là tháp Boost? ...)

điều này là, nếu bạn cố gắng tránh cái trước bạn kết thúc, ở một mức độ nào đó, với cái sau và ngược lại.

Ngoài ra còn có một loại địa ngục thứ ba, đó là phụ thuộc vòng tròn. Chúng có thể bật lên nếu bạn không cẩn thận ... tránh chúng không quá phức tạp, nhưng bạn cần dành thời gian để suy nghĩ về cách thực hiện. Xem John Lakos nói về Cấp độ trong CppCon 2016 (hoặc chỉ các slide ).


1
Bạn không thể luôn luôn tránh phụ thuộc vòng tròn. Một ví dụ là một mô hình trong đó các thực thể tham chiếu lẫn nhau. Ít nhất bạn có thể cố gắng giới hạn tính tuần hoàn trong hệ thống con, điều này có nghĩa là, nếu bạn bao gồm một tiêu đề của hệ thống con, bạn đã trừu tượng hóa tính tuần hoàn.
nalply

2
@nalply: Tôi có nghĩa là tránh phụ thuộc vòng tròn của các tiêu đề, không phải mã ... nếu bạn không tránh phụ thuộc tiêu đề tròn mà bạn có thể sẽ không thể xây dựng. Nhưng có, điểm lấy, +1.
einpoklum - phục hồi Monica

1

Tách

Cuối cùng, đó là về việc tách rời tôi vào cuối ngày ở mức thiết kế cơ bản nhất mà không có sắc thái đặc trưng của trình biên dịch và trình liên kết của chúng tôi. Ý tôi là bạn có thể làm những việc như làm cho mỗi tiêu đề chỉ xác định một lớp, sử dụng pimpls, khai báo chuyển tiếp thành các loại chỉ cần khai báo, không xác định, thậm chí có thể sử dụng các tiêu đề chỉ chứa khai báo chuyển tiếp (ví dụ <iosfwd>:), một tiêu đề cho mỗi tệp nguồn , tổ chức hệ thống một cách nhất quán dựa trên loại điều được khai báo / định nghĩa, v.v.

Kỹ thuật để giảm "phụ thuộc thời gian biên dịch"

Và một số kỹ thuật có thể giúp ích khá nhiều nhưng bạn có thể thấy mình cạn kiệt các thực tiễn này và vẫn thấy tệp nguồn trung bình trong hệ thống của bạn cần một đoạn mở đầu gồm hai trang #includeChỉ thị làm bất cứ điều gì có ý nghĩa với thời gian xây dựng tăng vọt nếu bạn tập trung quá nhiều vào việc giảm phụ thuộc thời gian biên dịch ở cấp tiêu đề mà không giảm phụ thuộc logic trong thiết kế giao diện của bạn, và trong khi đó có thể không được coi là "tiêu đề spaghetti" Vẫn nói rằng nó chuyển sang các vấn đề bất lợi tương tự đối với năng suất trong thực tế. Vào cuối ngày, nếu các đơn vị biên dịch của bạn vẫn yêu cầu một khối lượng thông tin có thể nhìn thấy để làm bất cứ điều gì, thì nó sẽ chuyển sang tăng thời gian xây dựng và nhân lên những lý do bạn phải quay lại và phải thay đổi mọi thứ trong khi tạo nhà phát triển cảm thấy như họ đang làm cho hệ thống chỉ cố gắng hoàn thành việc mã hóa hàng ngày của họ. Nó '

Ví dụ, bạn có thể làm cho mỗi hệ thống con cung cấp một tệp tiêu đề và giao diện rất trừu tượng. Nhưng nếu các hệ thống con không tách rời nhau, thì bạn sẽ nhận được một cái gì đó giống với spaghetti một lần nữa với các giao diện hệ thống con tùy thuộc vào các giao diện hệ thống con khác với biểu đồ phụ thuộc trông giống như một mớ hỗn độn để hoạt động.

Tuyên bố chuyển tiếp đến các loại bên ngoài

Trong tất cả các kỹ thuật tôi đã cạn kiệt để cố gắng có được một cơ sở mã cũ mất hai giờ để xây dựng trong khi các nhà phát triển đôi khi phải đợi 2 ngày để đến lượt CI trên các máy chủ xây dựng của chúng tôi (bạn gần như có thể tưởng tượng những cỗ máy xây dựng đó là những con thú kiệt sức đang cố gắng điên cuồng để theo kịp và thất bại trong khi các nhà phát triển thúc đẩy các thay đổi của họ), điều đáng nghi ngờ nhất với tôi là chuyển tiếp khai báo các loại được xác định trong các tiêu đề khác. Và tôi đã xoay sở để có được cơ sở mã hóa đó xuống còn 40 phút hoặc lâu hơn sau nhiều năm thực hiện việc này trong các bước tăng dần trong khi cố gắng giảm "spaghetti tiêu đề", cách thực hành đáng nghi ngờ nhất trong nhận thức (như khiến tôi mất đi bản chất cơ bản của thiết kế trong khi đường hầm có tầm nhìn về sự phụ thuộc lẫn nhau của tiêu đề) là các kiểu khai báo chuyển tiếp được định nghĩa trong các tiêu đề khác.

Nếu bạn tưởng tượng một Foo.hpptiêu đề có cái gì đó như:

#include "Bar.hpp"

Và nó chỉ sử dụng Bartrong tiêu đề một cách yêu cầu khai báo, không định nghĩa. sau đó nó có vẻ như không có trí tuệ để tuyên bố class Bar;để tránh làm cho định nghĩa Barhiển thị trong tiêu đề. Ngoại trừ trong thực tế thường bạn sẽ tìm thấy hầu hết các đơn vị biên dịch sử dụng Foo.hppcuối cùng vẫn cần Barphải được xác định với gánh nặng thêm là phải đặt Bar.hppchúng lên trên Foo.hpphoặc bạn gặp phải một kịch bản khác thực sự giúp ích và 99 % các đơn vị biên dịch của bạn có thể hoạt động mà không bao gồm Bar.hpp, ngoại trừ sau đó nó đặt ra câu hỏi thiết kế cơ bản hơn (hoặc ít nhất là tôi nghĩ nó nên hiện nay) về lý do tại sao họ cần phải xem tuyên bố Barvà tại saoFoo thậm chí cần phải bận tâm để biết về nó nếu nó không liên quan đến hầu hết các trường hợp sử dụng (tại sao lại gây gánh nặng cho một thiết kế với sự phụ thuộc vào một thiết bị khác hầu như chưa từng sử dụng?).

Bởi vì khái niệm, chúng tôi đã không thực sự tách rời Fookhỏi Bar. Chúng tôi đã làm cho nó để tiêu đề Fookhông cần nhiều thông tin về tiêu đề Barvà điều đó gần như không đáng kể như một thiết kế thực sự làm cho hai cái này hoàn toàn độc lập với nhau.

Scripting nhúng

Điều này thực sự dành cho các cơ sở mã quy mô lớn hơn nhưng một kỹ thuật khác tôi thấy vô cùng hữu ích là sử dụng ngôn ngữ kịch bản nhúng cho ít nhất là các phần cấp cao nhất trong hệ thống của bạn. Tôi thấy rằng tôi đã có thể nhúng Lua trong một ngày và nó có thể gọi tất cả các lệnh trong hệ thống của chúng tôi một cách thống nhất (các lệnh rất trừu tượng, rất may). Thật không may, tôi đã gặp phải một rào cản nơi các nhà phát triển không tin vào việc giới thiệu một ngôn ngữ khác và, có lẽ kỳ lạ nhất, với hiệu suất là sự nghi ngờ lớn nhất của họ. Tuy nhiên, trong khi tôi có thể hiểu các mối quan tâm khác, hiệu suất sẽ không thành vấn đề nếu chúng tôi chỉ sử dụng tập lệnh để gọi các lệnh khi người dùng nhấp vào nút, ví dụ, thực hiện không có vòng lặp quá lớn của riêng họ (chúng tôi đang cố gắng làm gì, lo lắng về sự khác biệt của nano giây trong thời gian phản hồi cho một lần bấm nút?).

Thí dụ

Trong khi đó, cách hiệu quả nhất mà tôi từng chứng kiến ​​sau khi sử dụng hết các kỹ thuật để giảm thời gian biên dịch trong các cơ sở mã lớn là các kiến ​​trúc thực sự làm giảm lượng thông tin cần thiết cho bất kỳ một thứ nào trong hệ thống để hoạt động, không chỉ tách một tiêu đề từ một trình biên dịch khác phối cảnh nhưng yêu cầu người dùng các giao diện này phải làm những gì họ cần làm trong khi biết (cả từ quan điểm của con người và trình biên dịch, việc tách rời thực sự vượt ra ngoài phụ thuộc trình biên dịch) ở mức tối thiểu.

ECS chỉ là một ví dụ (và tôi không khuyên bạn nên sử dụng một ví dụ), nhưng việc gặp nó cho tôi thấy rằng bạn có thể có một số bộ mã thực sự hoành tráng vẫn tạo ra một cách đáng ngạc nhiên trong khi vui vẻ sử dụng các mẫu và nhiều tính năng khác bởi vì ECS, về bản chất, tạo ra một kiến ​​trúc tách rời, trong đó các hệ thống chỉ cần biết về cơ sở dữ liệu ECS và thường chỉ có một số loại thành phần (đôi khi chỉ là một) để thực hiện công việc của chúng:

nhập mô tả hình ảnh ở đây

Thiết kế, thiết kế, thiết kế

Và những kiểu thiết kế kiến ​​trúc tách rời này ở cấp độ con người, ở mức độ khái niệm có hiệu quả hơn về mặt tối thiểu hóa thời gian biên dịch so với bất kỳ kỹ thuật nào tôi đã khám phá ở trên khi codebase của bạn phát triển và tăng trưởng, bởi vì sự tăng trưởng đó không đạt mức trung bình của bạn đơn vị biên dịch nhân số lượng thông tin cần thiết khi biên dịch và thời gian liên kết để hoạt động (bất kỳ hệ thống nào yêu cầu nhà phát triển trung bình của bạn bao gồm một khối lượng công cụ để làm bất cứ điều gì cũng cần chúng và không chỉ trình biên dịch biết về rất nhiều thông tin để làm bất cứ điều gì ). Nó cũng có nhiều lợi ích hơn giảm thời gian xây dựng và gỡ rối các tiêu đề, vì điều đó cũng có nghĩa là các nhà phát triển của bạn không cần biết nhiều về hệ thống ngoài những gì được yêu cầu ngay lập tức để làm điều gì đó với nó.

Ví dụ, nếu bạn có thể thuê một nhà phát triển vật lý chuyên gia để phát triển một công cụ vật lý cho trò chơi AAA của bạn trải rộng hàng triệu LỘC và anh ta có thể bắt đầu rất nhanh trong khi biết thông tin tối thiểu tuyệt đối về những thứ như các loại và giao diện có sẵn cũng như các khái niệm hệ thống của bạn, sau đó sẽ tự nhiên chuyển sang giảm lượng thông tin cho cả anh ấy và trình biên dịch để yêu cầu xây dựng công cụ vật lý của anh ấy, và tương tự như vậy để giảm thời gian xây dựng trong khi nói chung là không có gì giống với spaghetti bất cứ nơi nào trong hệ thống. Và đó là những gì tôi đề nghị ưu tiên hơn tất cả các kỹ thuật khác: cách bạn thiết kế hệ thống của mình. Làm cạn kiệt các kỹ thuật khác sẽ đóng băng trên đầu nếu bạn làm điều đó trong khi, nếu không,


1
Một câu trả lời tuyệt vời! Althoguh Tôi đã phải đào một chút để khám phá ra mụn là gì :-)
Mawg 18/12/18

0

Đó là một vấn đề của ý kiến. Xem này câu trả lời và rằng một. Và nó cũng phụ thuộc rất nhiều vào quy mô dự án (nếu bạn tin rằng bạn sẽ có hàng triệu dòng nguồn trong dự án của mình, thì nó không giống như có vài chục nghìn trong số chúng).

Ngược lại với các câu trả lời khác, tôi đề xuất một tiêu đề công khai (khá lớn) cho mỗi hệ thống phụ (có thể bao gồm các tiêu đề "riêng tư", có thể có các tệp riêng biệt để triển khai nhiều chức năng nội tuyến). Bạn thậm chí có thể xem xét một tiêu đề chỉ có một số #include chỉ thị.

Tôi không nghĩ rằng nhiều tệp tiêu đề được khuyến nghị. Cụ thể, tôi không đề xuất một tệp tiêu đề cho mỗi lớp hoặc nhiều tệp tiêu đề nhỏ của vài chục dòng mỗi dòng.

(Nếu bạn có rất nhiều tệp nhỏ, bạn sẽ cần bao gồm rất nhiều tệp đó trong mỗi đơn vị dịch thuật nhỏ và thời gian xây dựng tổng thể có thể bị ảnh hưởng)

Những gì bạn thực sự muốn là xác định, đối với mỗi hệ thống con và tệp, nhà phát triển chính chịu trách nhiệm về nó.

Cuối cùng, đối với một dự án nhỏ (ví dụ dưới một trăm nghìn dòng mã nguồn), điều đó không quan trọng lắm. Trong dự án, bạn sẽ dễ dàng có thể cấu trúc lại mã và sắp xếp lại nó trong các tệp khác nhau. Bạn sẽ chỉ sao chép và dán các đoạn mã vào các tệp (tiêu đề) mới, không phải là vấn đề lớn (điều khó khăn hơn là thiết kế một cách khôn ngoan cách bạn sắp xếp lại các tệp của mình và đó là dự án cụ thể).

(sở thích cá nhân của tôi là tránh các tệp quá lớn và quá nhỏ; tôi thường có các tệp nguồn gồm vài nghìn dòng mỗi dòng và tôi không sợ tệp tiêu đề - bao gồm các định nghĩa hàm được nội tuyến - của hàng trăm dòng hoặc thậm chí là một vài hàng ngàn người trong số họ)

Lưu ý rằng nếu bạn muốn sử dụng các tiêu đề được biên dịch sẵn với GCC ( đôi khi là một cách tiếp cận hợp lý để giảm thời gian biên dịch), bạn cần một tệp tiêu đề duy nhất (bao gồm tất cả các tiêu đề khác và cả các tiêu đề hệ thống).

Lưu ý rằng trong C ++, các tệp tiêu đề chuẩn đang lấy rất nhiều . Ví dụ: #include <vector>đang kéo hơn mười nghìn dòng trên GCC 6 của tôi trên Linux (18100 dòng). Và #include <map> mở rộng đến gần 40KLOC. Do đó, nếu bạn có nhiều tệp tiêu đề nhỏ bao gồm các tiêu đề tiêu chuẩn, cuối cùng bạn sẽ phân tích lại nhiều nghìn dòng trong quá trình xây dựng và thời gian biên dịch của bạn bị ảnh hưởng. Đây là lý do tại sao tôi không thích có nhiều dòng nguồn C ++ nhỏ (nhiều nhất là vài trăm dòng), nhưng ưu tiên có các tệp C ++ ít hơn nhưng lớn hơn (vài nghìn dòng).

(vì vậy việc có hàng trăm tệp C ++ nhỏ luôn bao gồm - cả gián tiếp - một số tệp tiêu đề tiêu chuẩn mang lại thời gian xây dựng lớn, gây khó chịu cho các nhà phát triển)

Trong mã C, các tệp tiêu đề khá thường xuyên mở rộng thành một cái gì đó nhỏ hơn, do đó sự đánh đổi là khác nhau.

Ngoài ra, hãy tìm cảm hứng, vào thực tiễn trước trong các dự án phần mềm miễn phí hiện có (ví dụ như trên github ).

Lưu ý rằng sự phụ thuộc có thể được xử lý với một hệ thống tự động hóa xây dựng tốt . Nghiên cứu tài liệu của GNU make . Hãy nhận biết các -Mcờ tiền xử lý khác nhau cho GCC (hữu ích để tạo phụ thuộc tự động).

Nói cách khác, dự án của bạn (có ít hơn một trăm tệp và một tá nhà phát triển) có thể không đủ lớn để thực sự quan tâm bởi "tiêu đề địa ngục", vì vậy mối quan tâm của bạn không được chứng minh . Bạn chỉ có thể có một tá tệp tiêu đề (hoặc thậm chí ít hơn nhiều), bạn có thể chọn để có một tệp tiêu đề cho mỗi đơn vị dịch, thậm chí bạn có thể chọn để có một tệp tiêu đề duy nhất và bất cứ điều gì bạn chọn sẽ không là "tiêu đề địa ngục" (và tái cấu trúc và sắp xếp lại các tệp của bạn sẽ dễ dàng hợp lý, vì vậy lựa chọn ban đầu không thực sự quan trọng ).

(Đừng tập trung nỗ lực của bạn vào "địa ngục tiêu đề" - đây không phải là vấn đề đối với bạn - nhưng hãy tập trung chúng để thiết kế một kiến ​​trúc tốt)


Các kỹ thuật bạn đề cập có thể đúng. Tuy nhiên, như tôi đã hiểu, OP đã yêu cầu gợi ý cách cải thiện khả năng duy trì và tổ chức mã, chứ không phải biên dịch thời gian. Và tôi thấy một cuộc xung đột trực tiếp giữa hai mục tiêu này.
Murphy

Nhưng nó vẫn là một vấn đề quan điểm. Và OP rõ ràng đang bắt đầu một dự án không quá lớn.
Basile Starynkevitch
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.