C ++およびオブジェクト指向プログラミングについて

こんにちは、Habr!



C ++言語を使用する場合、著者が純粋にオブジェクト指向のアプローチを承認していない記事に注意を向けたいと思います。可能であれば、作者の主張だけでなく、論理やスタイルも評価していただきたいと思います。



最近C ++について、そして言語がどこに向かっているのか、そして「現代のC ++」と呼ばれるもののどれだけがゲーム開発者にとって選択肢ではないかについて、多くの書き込みがあります。 私はこの見解を完全に共有していますが、ほとんどの開発者が指導する広範なアイデアを浸透させた結果として、C ++の進化を見る傾向があります。この記事では、私自身の考えと一緒にこれらのアイデアのいくつかを整理しようとします-そして多分私は何かスリムになるでしょう。







ツールとしてのオブジェクト指向プログラミング(OOP)



C ++はマルチパラダイムプログラミング言語として説明されていますが、実際には、ほとんどのプログラマーはC ++を純粋にオブジェクト指向言語として使用します(一般的なプログラミングはOOPを「補完」するために使用されます)。



OOPは、プログラマーがコードの問題を解決するために使用できる多くのパラダイムの1つであるツールであると想定されています。しかし、私の経験では、OOPはソフトウェア開発のゴールドスタンダードとしてほとんどの専門家に受け入れられています。基本的に、ソリューションの開発は、必要なオブジェクトを決定することから始まります。特定の問題の解決は、コードがオブジェクト間で配布された後に始まります。この種のオブジェクト指向の考え方への移行により、OOPはツールからツールボックス全体に変わります。



ソフトウェア開発を促進する秘密の力としてのエントロピーについて



私はOOPソリューションをコンステレーションと考えるのが好きです。それはランダムに線が引かれたオブジェクトのグループです。このような解決策は、オブジェクトがノードであり、それらの間の関係がエッジであるグラフと見なすこともできますが、星座の比喩によって伝えられるグループ/クラスターの現象は私に近いです(それに比べて、グラフは抽象的すぎます)。



しかし、私はそのような「オブジェクトの星座」が構成される方法が好きではありません。私の理解では、そのような各コンステレーションは、プログラマーの頭の中で形成された画像のスナップショットにすぎず、特定の瞬間のソリューション空間がどのように見えるかを反映しています。拡張性、再利用性、カプセル化など、オブジェクト指向の設計で与えられるすべての約束を考慮に入れても...将来は予測できないため、いずれの場合も、現在直面している問題の正確な解決策を提供できます。



目の前の問題を「ただ」解決していることを奨励する必要がありますが、私の経験では、OOPの精神で設計原則を使用するプログラマーは、問題自体は大幅に変化しないという前提で自分自身を制約しながら、解決策を作成します。したがって、ソリューションは永続的であると見なすことができます。つまり、これからは、データやアルゴリズムではなく、前述のコンステレーションを形成するオブジェクトの観点からソリューションについて話し始めるようになります。問題自体は抽象化されています。

それにもかかわらず、プログラムは他のシステムと同じようにエントロピーの影響を受けます。したがって、コードが変更されることは誰もが知っています。さらに、予測できない方法で。しかし、この場合の私にとって、意識的に戦わなければ、コードがいずれにせよ劣化し、混乱と混乱に陥ることは絶対に明らかです。



私はこのマニフェストをOOPソリューションでさまざまな方法で見てきました。



  • 新しい中間レベルが階層に表示されますが、元々は導入されることを意図していませんでした。
  • 新しい仮想関数は、ほとんどの階層に空の実装で追加されます。
  • コンステレーション内のオブジェクトの1つは、他のオブジェクト間の接続がスリップし始めるため、計画よりも多くの処理を必要とします。
  • , , , .
  • .…


これらはすべて、不適切に編成された拡張性の例です。さらに、結果は常に同じであり、数か月または数年で発生する可能性があります。彼らはリファクタリングの助けを借りて、新しいオブジェクトがコンステレーションに追加されたときに行われたOOP設計原則の違反を排除しようとしていますが、問題自体の再定式化のために追加されました。リファクタリングが役立つ場合があります。しばらくの間。エントロピーは安定しており、プログラマーはそれを克服するためにすべてのOOPコンステレーションをリファクタリングする時間がないため、どのプロジェクトも定期的に同じ状況に陥ります。その名前はカオスです。



OOPプロジェクトのライフサイクルでは、遅かれ早かれ、それを維持することが不可能になる瞬間が訪れます。通常、この時点で、次の2つのアクションのいずれかを実行する必要があります。



  • « »: - . , , , , , .
  • : -, , , .


注意:ブラックボックス付きのオプションは、新機能の開発を継続する必要がある場合やバグを排除する必要がある場合に備えて、引き続き書き直す必要があります。



ソリューションの書き換えの状況は、特定の瞬間に利用可能なソリューションスペースのスナップショットの現象に戻ります。では、OOPデザイン#1と現在の状況の間で何が変わったのでしょうか。基本的にはそれだけです。問題が変わったため、別の解決策が必要です。



OOP設計の原則に従ってソリューションを作成しているときに、問題を抽象化しました。問題が変わるとすぐに、ソリューションはカードの家のように崩壊しました。

何が悪かったのか疑問に思い始めたのはこの瞬間だと思いますが、逆に、事後(報告)の結果に基づいて問題解決の戦略を更新していきます。ただし、このような「書き換える時間」のシナリオに遭遇するたびに、何も変わりません。問題空間の現在の状態に対応する新しいスナップショットが実装される、OOP原則が再度使用されます。サイクル全体が繰り返されます。



設計原則としてのコード削除の容易さ



OOPの原則に基づいて構築されたシステムでは、主な注目を集めるのは「コンステレーション」内のオブジェクトです。しかし、オブジェクト間の関係は、オブジェクト自体と同じくらい重要であると私は信じています。



コードの依存関係グラフが最小数のノードとエッジで構成されている単純なソリューションを好みます。ソリューションが単純であるほど、変更するだけでなく、削除することも簡単になります。また、コードを削除するのが簡単であるほど、ソリューションの方向を変えて、変化する問題の状態にすばやく適応できることもわかりました。同時に、コードを整理し、混乱に陥るのを防ぐために必要な労力がはるかに少ないため、コードはエントロピーに対してより耐性があります。



定義によるパフォーマンスについて



ただし、OOP設計を回避するための主な考慮事項の1つは、パフォーマンスです。実行する必要のあるコードが多いほど、パフォーマンスは低下します。



また、OOP機能は、定義上、パフォーマンスに優れていないことに注意することもできません。コンパイラエクスプローラーの単一の純粋な仮想関数呼び出しをオーバーライドするインターフェイスと2つの派生クラスを備えた単純なOOP階層を実装しました



この例のコードは、プログラムに渡される引数の数に応じて、「Hello、World!」を出力するかどうかを示します。今説明したすべてを直接プログラミングする代わりに、標準のOOP設計パターンの1つである継承を使用して、コード内のこの問題を解決します。



この場合、最も印象的なのは、最適化した後でも、コードコンパイラが生成するコードの量です。引数の非ゼロの数がプログラムに渡され、まだコード割り当てメモリ(コール:その後、密接に見て、あなたはどのようにコストがかかると同時に無用なメンテナンスで見ることができますnew)、負荷のアドレスvtableの両方のオブジェクト、ロード関数のアドレスWork()については、ImplBそのため、その後すぐに、それにジャンプそこには何もすることがないので、戻ってください。最後に、delete割り当てられたメモリを解放するために呼び出されます。



これらの操作はまったく必要ありませんでしたが、プロセッサはそれらをすべて適切に実行しました。



したがって、製品の主な目標の1つが高性能の達成である場合(そうでない場合は奇妙です)、コードでは、不必要なコストのかかる操作を避け、判断しやすい単純な操作を優先し、この目標の達成に役立つ構造を使用する必要があります。Unity



を例にとってみましょう。彼らの最近の実践の一部として、この言語はすでにエンジン自体で使用されているためパフォーマンスはオブジェクト指向言語であるC#を使用した正確さです。ただし、C#のサブセット、さらにOOPに厳密に関連付けられていないサブセットに落ち着き、それに基づいて、高性能のためにシャープ化された構造を作成します。



プログラマーの仕事がコンピューターを使用して問題を解決することであることを考えると、私たちのビジネスが、プロセッサーが特に得意とする作業を実際に実行させるコードの記述にほとんど注意を払っていないことは考えられません。



ステレオタイプとの戦いについて



Angelo Pesce 記事過複雑化はすべての悪の根源である」で、著者はほとんどのソフトウェアの問題が実際には人的要因であることを認めることによって要点を理解しています(最後のセクション:人を参照)。



チームのメンバーは、全体的な目標とは何か、そしてそれを達成するための道は何かについて、相互作用し、共通の理解を深める必要があります。たとえば、目標への道についてチーム内で意見の相違がある場合、さらに前進するためには、コンセンサスを作成する必要があります。意見の相違が小さければ、これは通常難しいことではありませんが、オプションが根本的に異なる場合、たとえば「OOPかどうか」を許容することははるかに困難です。

考えを変えるのは簡単ではありません。あなたの見方を疑って、あなたがどれほど間違っていたかを理解し、あなたのコースを調整することは困難で苦痛です。しかし、他の誰かの心を変えることははるかに困難です!



私はOOPとその固有の問題についてさまざまな人々と多くの会話をしました。私はいつもこのように考える理由を説明できたと思いますが、そうでない場合はそうではありませんが、OOPから誰かを遠ざけることができたとは思いません。



確かに、何年にもわたる仕事の中で、私は自分自身の3つの主要な議論を特定しました。そのため、人々は反対側にチャンスを与える準備ができていません。



  • « ». « ». « » . , , ( , - ). « …».
  • « , , , ». «» , , . , « ».
  • 「誰もがOOPを知っています。一般的な知識を持っていて、共通の言語で人々と話すことは非常に便利です。」これは「人々への議論」と呼ばれる論理的な間違いです。つまり、ほとんどすべてのプログラマーがOOPの原則を使用している場合、この考えは不適切ではありません。


私は、議論の論理的誤りを明らかにするだけでは、それらを暴くのに十分ではないことをよく知っています。しかし、私はあなた自身の判断の欠陥を見て、あなたは真実の底に到達し、あなたが異常な考えを拒絶する深い理由を見つけることができると信じています。



All Articles