分離
最終的には、コンパイラーとリンカーの特性の微妙な違いを欠いた最も基本的な設計レベルで、一日の終わりに私にデカップリングすることです。つまり、各ヘッダーでクラスを1つだけ定義する、ピンプルを使用する、宣言するだけで定義しないで宣言する必要がある型に宣言を転送する、または<iosfwd>
ソースファイルごとにヘッダーを1つだけ含む、前方宣言を含むヘッダー(例:、宣言/定義されているもののタイプに基づいて一貫してシステムを編成します。
「コンパイル時の依存関係」を減らす技術
また、いくつかの手法はかなり役立ちますが、これらのプラクティスを使い果たしても、システム内の平均ソースファイルを見つけるには2ページのプリアンブルが必要です。 #include
インターフェイスデザインの論理的な依存関係を減らすことなくヘッダーレベルでコンパイル時の依存関係を減らすことに重点を置きすぎると、厳密に言えば「スパゲッティヘッダー」とはみなされないかもしれませんが、それでも、実際の生産性と同様の有害な問題につながると言うでしょう。結局のところ、もしコンパイルユニットが何かをするために大量の情報を表示する必要がある場合、ビルド時間の増加につながり、開発者を作成している間に戻って物事を変更しなければならない理由を増やすことになります彼らは毎日のコーディングを終わらせようとしているだけで、システムに頭突きしているように感じます。それ'
たとえば、各サブシステムが非常に抽象的なヘッダーファイルとインターフェイスを提供するようにできます。しかし、サブシステムが相互に分離されていない場合、動作するために混乱のように見える依存関係グラフを持つ他のサブシステムインターフェイスに依存するサブシステムインターフェイスで、スパゲッティに似たものが得られます。
外部型への宣言の転送
開発者がビルドサーバーでCIを有効にするのに2日間待機する間に、ビルドに2時間かかった元のコードベースを取得しようと試みたすべてのテクニックのうち、開発者が変更をプッシュしている間、追いついて失敗するため)、私にとって最も疑わしいのは、他のヘッダーで定義された型を前方宣言することでした。そして、後から見た場合に最も疑わしいプラクティスである「ヘッダースパゲッティ」(私がトンネルの相互依存関係を念頭に置いた設計)は、他のヘッダーで定義されたタイプを前方宣言していました。
次のFoo.hpp
ようなヘッダーを想像すると:
#include "Bar.hpp"
またBar
、ヘッダーでは、定義ではなく宣言を必要とする方法のみを使用します。class Bar;
の定義がBar
ヘッダーに表示されるのを避けるために宣言するのは簡単なように思えるかもしれません。練習を除き、多くの場合、あなたはどちらかの使用があることコンパイル単位のほとんどを見つけることができますFoo.hpp
まだ必要終わるBar
含まれるようになるの追加負担でとにかく定義するBar.hpp
の上に自分自身をFoo.hpp
、またはあなたはそれが本当に助けと99をして別のシナリオに遭遇コンパイル単位の%は、を含めなくても機能しますBar.hpp
。ただし、宣言の確認が必要なBar
理由と、なぜFoo
ほとんどのユースケースに関係ない場合は、それについて知る必要があります(ほとんど使用されていない別の依存関係をデザインに負担させるのはなぜですか)。
なぜなら概念的にはFoo
から切り離されていないからBar
です。のヘッダーがのヘッダーFoo
に関する情報を必要としないように作成しました。これは、Bar
これら2つを互いに完全に独立させる設計ほど実質的ではありません。
埋め込みスクリプト
これは実際には大規模なコードベース用ですが、システムの少なくとも最も高レベルの部分に組み込みスクリプト言語を使用することは非常に便利です。Luaを1日で埋め込み、システム内のすべてのコマンドを一様に呼び出すことができました(コマンドは抽象的で、ありがたいことに)。残念ながら、開発者が別の言語の導入に不信感を抱き、おそらく最も奇妙なことに、パフォーマンスが最大の疑念であるという障害に遭遇しました。しかし、他の懸念を理解することはできましたが、ユーザーがボタンをクリックしたときにコマンドを呼び出すためにスクリプトを利用するだけであれば、パフォーマンスは問題ではないはずです。ボタンのクリックに対する応答時間のナノ秒の違いを心配しますか?)。
例
一方、大規模なコードベースでコンパイル時間を短縮する手法を徹底的に試した後、私がこれまでに見た中で最も効果的な方法は、システム内のあるものが動作するために必要な情報量を本当に減らすアーキテクチャであり、コンパイラからヘッダーを切り離すだけではありませんただし、これらのインターフェイスのユーザーは、最低限必要なこと(人間とコンパイラの両方の観点から、コンパイラの依存関係を超える真の分離)を行う必要があります。
ECSは一例に過ぎません(ただし、使用することはお勧めしません)が、遭遇したことにより、ECSによってテンプレートやその他の多くの便利な機能をうまく活用しながら、驚くほど迅速に構築できる非常に壮大なコードベースを作成できることがわかりました自然、非常に分離されたアーキテクチャを作成します。システムはECSデータベースについてのみ知る必要があり、通常は少数のコンポーネントタイプ(場合によっては1つ)だけで処理を行います。
デザイン、デザイン、デザイン
そして、人間の概念レベルでのこれらの種類の分離されたアーキテクチャ設計は、コードベースが成長し、成長し、成長するにつれて、私が上で検討したどの技術よりもコンパイル時間を最小化するという点でより効果的です。コンパイル単位とリンク時間で必要な情報量を乗算するコンパイル単位(平均的な開発者が何かを行うために大量の物を含める必要があるシステムでは、コンパイラーだけでなく、何でもするために大量の情報を知る必要があります)。また、ビルド時間の短縮とヘッダーのもつれの解消よりも多くの利点があります。これは、開発者が何かを行うためにすぐに必要なものを超えてシステムについて多くを知る必要がないことを意味するためです。
たとえば、数百万のLOCにまたがるAAAゲーム用の物理エンジンを開発するために専門の物理開発者を雇うことができ、利用可能なタイプやインターフェースなどの最低限の絶対情報を知っている間、彼は非常に迅速に始めることができますシステム概念だけでなく、物理エンジンを構築するために必要な情報量が減少することは当然のことであり、同様にスパゲッティに類似するものがないことを示唆しながら、ビルド時間の大幅な短縮につながります。システム内のどこでも。そして、それがこれらの他のすべてのテクニックより優先することを私が提案していることです:システムをどのように設計するか。他のテクニックを使い果たすと、それを行うと一番上に着氷します。