オブジェクト指向の落とし穴の回避、Cからの移行、何が効果的でしたか?


12

私はかなり長い間手続き言語でプログラミングしてきましたが、問題に対する私の最初の反応は、存在するさまざまなエンティティ(オブジェクト)とそれらの関係を考慮するのではなく、実行するタスクに分解することです。

私はOOPで大学のコースを受講し、カプセル化、データ抽象化、多態性、モジュール性、継承の基礎を理解しています。

私が読ん/programming/2688910/learning-to-think-in-the-object-oriented-way/programming/1157847/learning-object-oriented-thinking、そして、それらの答えで指摘されている本のいくつかを見ていきます。

中規模から大規模のプロジェクトのいくつかは、OOPの効果的な使用から恩恵を受けると思いますが、初心者として、時間のかかる一般的なエラーを避けたいと思います。

あなたの経験に基づいて、これらの落とし穴は何ですか、そしてそれらの周りの合理的な方法は何ですか?それらが落とし穴である理由、およびあなたの提案が問題に対処するのにどのように効果的であるかを説明できれば、それは高く評価されるでしょう。

「かなりの数のオブザーバーとモディファイヤメソッドを持ち、プライベート変数を使用するのは一般的ですか、それともそれらを統合/削減する技術はありますか?」

メソッドを混在させる正当な理由がある場合、純粋なオブジェクト指向言語としてC ++を使用することは心配していません。(控えめではありますが、GOTOを使用する理由を連想させます。)

ありがとうございました!


2
完全な答えに値するわけではありませんが、受け入れるのに長い時間がかかった重要なものです(つまり、私はそれについて多くの時間を読みましたが、ファンボーイトークとして扱いました)。これにより、クラスを最小限に抑えることができます。
stijn

@stijn基本的に、クラスに入れる必要がない場合は、そこに置かないでください。たとえば、これまでに読んだコードで簡単に無料の関数になる可能性のあるユーティリティメンバ関数がたくさんあります。
スティーブン

はい、それだけです。「非会員、非友人を好む」を検索すると、それに関する多くの情報が見つかります。最終的にはそれがシングルResponsability原則に付着したに降りてくる
stijn

回答:


10

私が学んだ大きなことの1つは、外部からクラスを設計することです。実装について考える前に、インターフェイスを設計します。これにより、基礎となるアルゴリズムを作成してクラスを作成し、必要に応じて新しいパブリックメンバー関数を作成するよりも、クラス(クラスを使用するユーザー)がはるかに直感的になります。


7
これに加えて、「意図的にプログラミングする」ことができます。つまり、新しいクラスを使用するサンプルコードを記述し、どのタイプのメソッドとポリシーを使用すると快適かを確認できます。次に、その情報を実装のベースラインとして使用します。

2
理論的には素晴らしい-インターフェイスを設計すれば、基本的には完了です。しかし、実際にはそれほど簡単には機能しません。多くの場合、インターフェイスの設計と実装は、最終的なインターフェイスが結晶化するまで連続して繰り返されます。その時点で、最終的な実装も行われる可能性があります。
ジーンブシュエフ

9

まあ最初は、あまりにも多くの情報を公開する落とし穴です。デフォルトはである必要がprivateありpublicます。

その後、ゲッター/セッターが多すぎます。データメンバーがいるとします。私はでください本当に、他のクラスでは、このデータを必要としますか?OK、ゲッターを作成します。私は本当に、オブジェクトの寿命の間にこのデータを本当に変更する必要がありますか?次に、セッターを作成します。

ほとんどの初心者プログラマーは、すべてのデータメンバーのゲッター/セッターを作成するデフォルトを持っています。これはインターフェイスを乱雑にし、多くの場合、設計上の不適切な選択です。


2
はい、表面的にカプセル化を理解し、データをプライベートにしてから、ゲッターとセッターでカプセル化を吹き飛ばすことを理解している人の間では非常に人気があります。
ジーンブシュエフ

Javaは、get / setメソッドを必要とする唯一の一般的な言語です。他のすべての言語はプロパティをサポートしているため、パブリックデータメンバとプロパティの間に構文上の違いはありません。Javaでさえ、クラスのすべてのユーザーを制御する場合、パブリックデータメンバーを持つことは罪になりません。
ケビンクライン

パブリックデータメンバーは維持が困難です。ゲッター/セッター(またはプロパティ)を使用すると、外部コードを変更することなく、内部データ表現を変更しながらインターフェイスを維持できます。プロパティが構文上、消費者の観点からメンバー変数と同一である言語では、この点は当てはまりません。
tdammers

これまでのところ、私はプライベートメンバーを関数のローカル変数として「ソート」していると思います...世界の残りはfnが動作するためにそれらについて何も知る必要はありません...そしてfnが変更、インターフェイスのみが一貫している必要があります。スパムゲッター/セッターを使用する傾向はないので、正しい方向に進むことができます。実際に書いたバッファークラスを見ると、パブリックデータメンバーはまったくありません。ありがとう!
スティーブン

2

その割れ目を越えたとき、私は次のアプローチに落ち着きました。

0)小規模/中規模の手続き型アプリで仕事を始めたが、ミッションクリティカルなものは仕事になかった。

1)OOスタイルで最初からプログラムを書く方法からの単純な1パスマッピング-その時点で私にとって最も重要であり、これは主観的である-は、すべての基本クラスを把握することでした。私の目的は、可能な限り基本クラスにカプセル化することでした。基本クラスで可能なすべての純粋な仮想メソッド。

2)次のステップは、派生の作成でした。

3)最後のステップは、元の手続きコードで、データ構造をオブザーバー/モディファイヤコードから分離することでした。次に、データの非表示を使用して、すべての共通データを基本クラスにマップします。サブクラスでは、プログラム全体で共通ではないデータが使用されました。そして、手続き型のオブザーバー/モディファイヤコードに対する同じ処理-すべての「どこでも使用される」ロジックが基本クラスに入りました。また、データのサブセットにのみ作用するビュー/変更ロジックは、派生クラスに入りました。

これは主観的ですが、手続き型のコードとデータ構造をよく知っていれば高速です。コードレビューでのFAILは、少しのデータまたはロジックが基本クラスに表示されず、どこでも使用される場合です。


2

OOPを使用して良いスタイルの感覚を得る他の成功したプロジェクトを見てください。Qtを参照することをお勧めします。これは、自分で設計を決定する際に常に尊敬しているプロジェクトです。


はい!人が何かのマスターになる前に、彼は彼の前に働いていた偉大なマスターを模倣することを学びます。他の人が実装した良いコードを勉強することは、学ぶ良い方法です。単純な設計から始めて、理解が深まるにつれて深くなるように、ブーストを検討することをお勧めします。
ジーンブシュエフ

1

誰と話すかに応じて、OOPのすべてのデータはプライベートまたは保護され、アクセサーとミューテーターを介してのみ利用可能になります。一般に、これは良い習慣だと思いますが、この規範から逸脱する場合があります。たとえば、一部のデータを論理ユニットにバインドすることだけを目的とするクラス(Javaの場合)がある場合、フィールドをパブリックのままにしておくのは理にかなっています。不変のカプセルの場合は、それらを最終的にマークし、コンストラクターで初期化します。これにより、クラスが(この場合)構造体よりも小さくなります(実際、C ++を使用すると、実際にこれを構造体と呼ぶ必要があります。クラスと同様に機能しますが、デフォルトの可視性はpublicであり、意図は明確です)。これらの場合、それを使用する方がはるかに快適であることがわかると思います。

絶対にしたくないことの1つは、ミューテーターの一貫性をチェックするフィールドを用意し、それを公開することです。


3
また、パブリックデータを保持するだけのクラスがある場合は、それらをstructs として宣言する必要があります-意味の違いはありませんが、意図を明確にし、特にCプログラマーにとってはより自然に使用できるようにします。

1
ええ、あなたがC ++を使用している場合(質問で言及しました)、ここで同意しますが、私はほとんどの作業をJavaで行い、残念ながら構造はありません。

集約クラス(まれなケース)と機能を備えたクラス(一般的なケース)を明確に区別する必要があります。後者は、カプセル化を実践し、パブリックインターフェイス経由でのみメンバーにアクセスする必要があります。データは公開されません。
ジーンブシュエフ


1

メソッドを混在させる正当な理由がある場合、純粋なオブジェクト指向言語としてC ++を使用することは心配していません。(控えめではありますが、GOTOを使用する理由を連想させます。)

私はこのビットを見るまで、会話を提供するために多くを持っているとは本当に思いませんでした。私は感情に反対しなければなりません。OOPは、C ++で使用できる、または使用すべきパラダイムの1つにすぎません。率直に言って、私の意見では、それはその最も強力な機能の1つではありません。

オブジェクト指向の観点から、C ++は実際には少し不足すると思います。たとえば、非仮想関数を使用するという考えは、この点でそれに対して反対です。私は自分と意見が合わない人たちと議論をしましたが、非仮想メンバーは私が考えている限りパラダイムに適合しません。ポリモーフィズムはオブジェクト指向の重要なコンポーネントであり、非仮想関数を持つクラスはオブジェクト指向の意味ではポリモーフィックではありません。オブジェクト指向言語として、C ++はJavaやObjective-Cのような言語と比較すると実際にはかなり弱いと思います。

一方、一般的なプログラミングでは、C ++にはこれがかなりあります。これにはより良い言語もあると言ったと聞きましたが、オブジェクトと汎用関数の組み合わせは非常に強力で表現力に富んでいます。さらに、プログラミング時間と処理時間の両方で非常に高速です。確かに、C ++の方が優れていると思いますが(たとえば、概念の言語サポート)、この領域で本当に優れていると思います。彼らがオブジェクト指向のパラダイムに固執し、不道徳のレベルでgotoステートメントの順序で他の人を扱うべきだと考える人は、このパラダイムを見ていないことによって本当に見逃されています。

テンプレートのメタプログラミング能力も非常に印象的です。たとえば、Boost.Unitsライブラリをご覧ください。このライブラリは、次元量の型サポートを提供します。現在働いているエンジニアリング会社でこのライブラリを広範囲に使用しました。それは、可能なプログラマーの1つの側面、または仕様エラーでさえ、はるかに迅速なフィードバックを提供するだけです。「=」演算子の両側が明示的なキャストなしで次元的に等価でない式を使用するプログラムをコンパイルすることは不可能です。私は個人的に、これが可能な他の言語の経験はなく、C ++のパワーと速度も備えた言語の経験はありません。

メタプログラミングは純粋に機能的なパラダイムです。

本当に、あなたはすでにいくつかの不幸な誤解でC ++に足を踏み入れていると思います。オブジェクト指向以外の他のパラダイムは回避されるべきではなく、レバレッジされるべきです。取り組んでいる問題の側面に自然なパラダイムを使用します。本質的にオブジェクトが発生しやすい問題ではないものにオブジェクトを強制しないでください。私の知る限り、オブジェクト指向はC ++の話の半分でもありません。


virtualキーワードは、C ++の仮想クラス機能を提供しませんか?後藤のコメントに関しては、私が推理していたのは、推論を理解している限り、経験則を破ることを心配していないということでした。しかし、私は、OOを支持して、必要であったかもしれないよりも命令的/手続きを避けることにさらに目を向けてきました。ありがとう。
スティーブン

仮想メソッドはポリモーフィズムの前提条件ではなく、それを行うための一般的な方法です。実際、他のすべてが等しい場合、クラスのapiのサイズを増やしているため、メソッドを仮想化するとカプセル化が実際に弱くなり、liskovなどに従うようにすることが難しくなります。継ぎ目が必要な場合にのみ仮想的なものを作成します。継承を介して新しい動作を注入する場所です(ただし、OOPでは継承も注意する必要があります)。仮想ために仮想的には、クラス「よりOOP」ことはありません
サラ

1

この質問に対する答えを受け入れたかったのですが、チェックマークを付けるための答えを1つ決めることができませんでした。そのため、元の著者を支持し、これを要約回答として作成しました。数分かかったすべての人のおかげで、あなたが提供した洞察が良い方向性と私が軌道に乗っていなかったという少しの安心感を与えてくれたことがわかりました。

しゅう

まあ最初は、あまりにも多くの情報を公開する落とし穴です。デフォルトはパブリックではなくプライベートにする必要があります。その後、ゲッター/セッターが多すぎます。

過去にこの問題を実際に観察していたと感じました。あなたのコメントは、基礎となる変数とその実装を隠すことで、それらに依存するものを破壊することなく実装を自由に変更できることも思い出させてくれました。

ドミニク・グルト

実装について考え始める前に、インターフェイスを設計します。Gene Bushuyevインターフェースの設計と実装は、最終的なインターフェースが結晶化するまで、連続して繰り返し行われることがよくあります。

ドミニクのコメントは理想的な理想だと思っていましたが、ジーンのコメントは実際に状況に影響を与えていると思います。これまでのところ、私はこれを実際に見てきました...そして、それが珍しくないことを少し良く感じました。プログラマーとして成熟するにつれて、より完全な設計に傾くと思いますが、今でも飛び込みに苦しみ、いくつかのコードが書かれています。

wantTheBest

私は、小さな/中規模の手続き型アプリを使用して、ゆっくりと始めました。仕事にミッションクリティカルなものはありませんでした。元の手続きコードでは、データ構造をオブザーバー/モディファイヤコードから分離します

これは理にかなっています...私は物事を仕事で働かせ続けるというアイデアが好きでしたが、クラスで重要でないもののいくつかをリファクタリングするために。

jpm

絶対にしたくないことの1つは、ミューテーターの一貫性を確認するフィールドを用意し、それを公開することです。

私はこれがデータをカプセル化する強みの1つであることをしばらく知っていました...一貫性を強制できること、さらに言えば条件/範囲/など。

クレイジーエディ

彼らがオブジェクト指向のパラダイムに固執し、不道徳のレベルでgotoステートメントの順序で他の人を扱うべきだと考える人は、このパラダイムを見ていないことによって本当に見逃されています。テンプレートのメタプログラミング能力も非常に印象的です。

もともとクレイジー・エディの答えをたくさん見逃していました。メタプログラミングのように、言及したトピックの一部を読んでいなかったからだと思います。CEの投稿での素晴らしいメッセージは、C ++は機能とスタイルが非常に混ざり合っているため、それぞれを最大限に活用すべきだということだと思います。

繰り返しになりますが、回答してくれた皆さんに感謝します!


0

最大の落とし穴は、OOPが銀の弾丸、または「1つの完璧なパラダイム」であるという信念です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.