OOはコードベースの管理に役立つ機能ですが、大規模な非OOコードベースはどのように管理されますか?それとも単に " Big Ball of Mud "最終的には?
更新:
「抽象化」は単なるモジュール化またはデータ非表示であると誰もが考えているようです。しかし、私見、それはまた、依存性注入とテストに必要な「抽象クラス」または「インターフェース」の使用を意味します。非OOコードベースはこれをどのように管理しますか?また、抽象化以外に、カプセル化は、データと関数の間の関係を定義および制限するため、大規模なコードベースの管理にも役立ちます。
Cを使用すると、疑似OOコードを書くことが非常に可能になります。他の非オブジェクト指向言語についてはあまり知りません。それで、それは大きなCコードベースを管理する方法ですか?
OOPが抽象化を達成する唯一の手段であると考えているようです。
OOPは確かにそれを行うのに非常に優れていますが、それが唯一の方法というわけではありません。大きなプロジェクトは、妥協のないモジュール化によって管理可能に保つこともできます(PerlまたはPythonを見てください。どちらも優れています)。そのとき、MLやHaskellなどの関数型言語もそうです)、テンプレートなどのメカニズム(C++)を使用します。
モジュール、(外部/内部)関数、サブルーチン...
konradが言ったように、OOPは大きなコードベースを管理する唯一の方法ではありません。実際、かなり多くのソフトウェアがその前に(C++ *より前に)書かれていました。
モジュール性の原則は、オブジェクト指向言語に限定されません。
現実的には、頻繁ではない変更(社会保障の退職金の計算を考える)や、システムなどを保守している人々がしばらくの間そうしているために深く根付いた知識(周期的な見方は仕事の安全)です。
より優れたソリューションは、繰り返し可能な検証です。つまり、自動テスト(ユニットテストなど)と、「クリックアラウンドして何が壊れているかを確認するのではなく」、禁止された手順(たとえば、リグレッションテスト)に従う人間のテストです。
既存のコードベースである種の自動テストに移行するには、 Michael Feather's Working With Working with Legacy Code を読むことをお勧めします。これは、ある種の反復可能なテストフレームワークまで既存のコードベースを導入するためのアプローチの詳細ですOOかどうか。これは、モジュール化など、他の人が回答したようなアイデアにつながりますが、この本では、物事を壊すことなくそうするための正しいアプローチについて説明しています。
インターフェースまたは抽象クラスに基づく依存関係の注入は、テストを行う非常に優れた方法ですが、必須ではありません。ほとんどすべての言語には、関数ポインタまたはevalのいずれかがあり、インターフェイスまたは抽象クラスで実行できるすべてのことを実行できることを忘れないでください(問題は、それらが実行できるということですmore物、およびそれら自体はメタデータを提供しない)。そのようなプログラムは、実際にこれらのメカニズムで依存性注入を実現できます。
メタデータが厳密であることは非常に役立つことがわかりました。 OO言語では、コードのビット間の関係は、リフレクションAPIのように十分に標準化された方法で、クラス構造によって(ある程度)定義されます。手続き型言語では、それらを自分で発明してください。
また、手続き型言語では(オブジェクト指向言語と比較して)コード生成の方がはるかに便利です。これは、メタデータがコードと同期していることを保証し(コードの生成に使用されるため)、アスペクト指向プログラミングのカットポイントのようなものを提供します-必要なときにコードを挿入できる場所です。 DRY私が理解できるような環境でプログラミングする唯一の方法である場合があります。
実際、 最近発見した のように、依存関係の反転に必要なのは一次関数だけです。
Cは一次関数と ある程度のクロージャでも をサポートしています。また、Cマクロは、必要な注意を払って処理する場合、汎用プログラミングにとって強力な機能です。
それだけです。 [〜#〜] sglib [〜#〜] は、Cを使用して非常に再利用可能なコードを作成する方法の非常に良い例です。そして、私はそこにもっとたくさんあると信じています。
抽象化がなくても、ほとんどのプログラムはある種のセクションに分割されます。これらのセクションは通常、特定のタスクまたはアクティビティに関連しており、抽象化されたプログラムの最も具体的なビットで作業するのと同じ方法で作業します。
小規模から中規模のプロジェクトでは、純粋なOO実装を使用すると、これが実際に簡単になります。
抽象化、抽象クラス、依存性注入、カプセル化、インターフェースなどは、大規模なコードベースを制御する唯一の方法ではありません。これは正しいオブジェクト指向の方法です。
主な秘密は、非OOPをコーディングするときにOOPと考えることを避けることです。
モジュール性は非OO言語の鍵です。 Cでは、これはDavid Thornleyがコメントで述べたように達成されます。
インターフェイスは.hファイルに含まれ、パブリックに利用可能な関数は.cファイルに含まれ、プライベート変数と関数には静的アクセス修飾子が付加されます。
コードを管理する1つの方法は、MVC(model-view-controller)アーキテクチャーに沿って、コードを次のタイプのコードに分解することです。
このコード編成方法は、OOまたは非OO言語で記述されたソフトウェアに適しています。これは、共通の設計パターンが各領域に共通していることが多いためです。また、これらの種類のコード境界は、アルゴリズムを除いて、最も疎結合です。入力からモデル、次に出力へとデータ形式をリンクするためです。
システムの進化は、多くの場合、ソフトウェアでより多くの種類の入力またはより多くの種類の出力を処理するという形を取りますが、モデルとビューは同じであり、コントローラーは非常に似た動作をします。または、入力、モデル、アルゴリズムが同じで、コントローラーとビューが類似している場合でも、システムは時間の経過とともに、ますます多くの種類の出力をサポートする必要があります。または、システムを拡張して、同じ入力セット、類似の出力、および類似のビューの新しいモデルとアルゴリズムを追加することもできます。
1つの方法OOプログラミングでは、コードの編成が難しくなります。これは、一部のクラスが永続データ構造に深く結び付いている場合とそうでない場合があります。永続データ構造がカスケード1のようなものと密接に関連している場合: N関係またはm:n関係では、正しく理解する前にシステムの重要で意味のある部分をコーディングするまで、クラス境界を決定することは非常に困難です。永続データ構造に関連付けられているクラスは、次の場合に進化しにくくなります。永続データ変更のスキーマ。アルゴリズム、フォーマット、および解析を処理するクラスは、永続データ構造のスキーマの変更に対して脆弱である可能性が低くなります。MVCの種類のコード編成を使用すると、最も厄介なコード変更をモデルに分離することができます。コード。
処理方法は、使用する要素の境界を見つけることです。たとえば、C++の次の要素には明確な境界があり、境界外の依存関係は慎重に検討する必要があります。
これらの要素を組み合わせて境界を再認識させることで、c ++内でほぼすべてのプログラミングスタイルを作成できます。
これの例は、関数が依存関係を引き起こすため、関数から他の関数を呼び出すのが悪いことを認識することです。代わりに、元の関数のパラメーターのメンバー関数のみを呼び出す必要があります。
大規模なコードベースの管理について質問している場合は、コードベースを比較的粗いレベルで適切に構造化する方法を求めています(ライブラリ/モジュール/サブシステムの構築/名前空間の使用/適切な場所に適切なドキュメントを配置する)等。)。 OO原則、特に「抽象クラス」または「インターフェース」は、コードを内部的に非常に詳細なレベルでクリーンに保つための原則です。したがって、大規模なコードベースを管理しやすい状態に保つためのテクニックOOまたは非OOコードの場合は異なります。
組み込みの構造と組織機能がない言語で作業する場合(たとえば、名前空間、パッケージ、アセンブリなどがない場合)、またはこれらのサイズでは、そのサイズのコードベースを制御するのに不十分な場合、自然な対応は、コードを編成するための独自の戦略。
この編成戦略には、さまざまなファイルを保存する場所に関する基準、特定のタイプの操作の前後に発生する必要のある事柄、命名規則やその他のコーディング基準、および多くの「これが設定方法です。 -それを台無しにしないでください!」タイプコメント-理由を説明する限り有効です!
戦略は、プロジェクトの特定のニーズ(人、テクノロジー、環境など)に合わせて調整されることになる可能性が高いため、大規模なコードベースの管理に万能のソリューションを提供することは困難です。
したがって、私は最良のアドバイスはプロジェクト固有の戦略を採用し、それを管理することを最優先事項とすることだと思います:構造を文書化し、なぜそれがそうであるか、変更を加えるプロセス、それを監査してそれが遵守されていることを確認します、そして決定的に重要なのは、変更が必要なときに変更することです。
私たちはほとんどクラスとメソッドのリファクタリングに精通していますが、そのような言語の大規模なコードベースでは、必要に応じてリファクタリングする必要があるのは組織化戦略自体(ドキュメントを完備)です。
推論はリファクタリングの場合と同じです。システムの全体的な構成が混乱していると感じた場合は、システムの小さな部分での作業に向けて精神的なブロックを発達させ、最終的にそれを悪化させます(少なくともそれは私の考えです)それ)。
警告も同じです:リグレッションテストを使用し、リファクタリングがうまくいかない場合は簡単に元に戻せることを確認し、最初からリファクタリングを容易にするように設計します(または、そうしないでください!)。
これは直接コードをリファクタリングするよりもはるかにトリッキーであり、なぜそれが行われる必要があるのか理解できないかもしれないマネージャ/クライアントから時間を検証/隠すのは難しいですが、これらはソフトウェアの腐敗に最もかかりやすいプロジェクトのタイプでもあります柔軟性のないトップレベルの設計によって引き起こされる...