web-dev-qa-db-ja.com

OOPのデメリット?

通常、OOPの短所の詳細は知りたくありませんが、最近参加したインタビューで議論をしたときは、ちょっと奇妙に感じました。私に投稿された質問は、 オブジェクト指向プログラミング (OOP)の1つの欠点を教えてくれることでした。当時、私はOOPが手続き型モデルと機能モデルの次に最も成熟したプログラミングレベルであると感じていたので、ネガティブなものはまったく見られないと彼に答えました。

でも面接官は少ないと言っていたので、よろしければリストアップしてもらいました。彼は私がうまく消化できないという例を挙げました。彼は、OOPパターンは継承ルールを厳密に実装しておらず、ロケットの打ち上げ時に体の部分が定期的に崩壊して重量を取り除く衛星/ロケットの例を引用し、継承はこれをサポートしていないと述べました。

彼の例は私には非常に奇妙に感じました。その理由は、この例に継承を適用​​したためです。

彼の例はほとんど意味がないことは理解していますが、私はこの疑問を持っていました-

理想的なオブジェクト指向設計で、クラス階層を動的にアンプラグできますか(Javaそれは不可能です)にある程度自信があります)?

39
bragboy

私は彼の例を完全には理解していません。

ただし、OOPは、それを使用して自然にモデル化できるものには非常に効果的であり、他のもの(横断的関心事や側面など)には非常に効果がないことを理解することが重要です。これは1つの欠点です。もう1つは、動的ディスパッチのためにランタイムコストが発生することが多いことです。

さらに、OOPを悪用して無意味な抽象化を行うのは非常に簡単です。ロケットを体から継承させることはその一例です。私の経験では、初心者の開発者は信頼しないか、信頼しないかのどちらかです。他の動作(集約など)がより適切な場合は、継承を使用するか、過度に熱心で誤って使用します。時間の経過とともに、メカニズムの経験と理解が向上します。

OOPはパターンではないため、「OOPパターンは継承ルールを厳密に実装していません」の意味がわかりません。潜在的な問題の1つは、リスコフの原則に違反する可能性のあるサブタイプを記述できることです。置換の原則。これにより、オーバーライドされるメソッドは、オーバーライドされるメソッドのように「少なくとも」動作しなくなります。これを自動的にチェックする方法がないため、OOPの原則に違反するコードを記述できます。 。

最後の質問「理想的なオブジェクト指向設計でクラス階層を削除できますか?」については、ここで何を意味するのかわかりません。実行時に階層を変更し、実行のある時点からサブタイプ関係が存在しないようにすることについて質問している場合は、そうです。 Smalltalkなどの特定の言語で可能です。これは「MoreOOP」であると主張する人もいます。 Smalltalkでは、型によってサポートされる「メソッド」は、現在の階層と各クラスの現在の内容に基づいて、メソッド呼び出しの時点で決定されます。私はSmalltalkが大好きですが、実行時の驚きが少ないコンパイル時のチェックを好むので、これは私が夢中になっていない機能の1つです。

19
Uri

ハンマーを持っているからといって、すべてが釘であるとは限りません。

多くの人が、いつ適用するかを混乱させることがよくあります composition vs. inheritance when using OOP =プログラミングスタイルとして。インタビューの質問から引用した例は、まさにこの種の混乱の事例のようです。

私が経験から学んだことの1つは、継承はデザイン内のエンティティ間の表現「IS-A」関係の優れたパラダイムですが、継承よりも構成を優先する方がよい場合が多いことです

しかし、インタビュアーの質問の核心を調べてみましょう:いつOOPパラダイムの選択が不十分ですか?

OOPは、大規模なマルチ開発者、マルチモジュールプロジェクトで最適に機能します。 "小規模での開発 "-スクリプトや変換処理など、必ずしも付加価値がなくても、かなりのオーバーヘッドが必要になる場合があります。しかし、これらの場合でも、使い捨てのコードを書かない限り、大きなソリューションは小さなソリューションから進化することが多いので、早い段階で構造を確立し、関心の分離を確立することで、後で悲しみを救うことができます。

最終的に、OOPプログラミングには、ある程度の設計の厳密さと計画、および オブジェクト指向のコア原則 の理解も必要です。時間をかけてこれらの原則を研究して理解することを望まない場合は...まあ、おそらくOOPプログラミングはそうではありませんあなたのために。

それを超えて、特定の種類の問題は、代替のプログラミングスタイルにも役立ちます。たとえば、変換処理は、結果が計算され、連続する変換ステップに渡されて最終結果が生成される プログラミングの機能スタイル -に非常に適しています。特定の一致情報についてドメインを検索またはクエリする必要がある問題は、 クエリ言語 (SQLなど)に影響を受けやすく、命令型ではなく宣言型で意図を記述します。

解決している問題の種類を認識できることは、使用する言語またはツールの種類を選択するのに大いに役立ちます。適切なツールで仕事ははるかに簡単です...間違ったものはそれを難し​​くする可能性があります-または不可能です。

34
LBushkin

私はインタビュアーの結論に同意しますが(OOPには欠陥があります)、彼の論理はナンセンスに見えます。有能なOOプログラマーがロケットをスラスターから継承させることはないので、彼は理解できないことを批判しているようです。

しかし、人々はOOPに対して良い批判をしました。たとえば、Steve Yeggeの 名詞の王国での処刑

15
Chuck

彼の例は意味がありません。ロケットは体から受け継がれません。それは体を「持っている」。それは封じ込めです。したがって、ロケットが投棄されたときにロケットに取り付けられた部分を「削除」するポイントがあります。

11
jmucchiello

与えられた例を完全には理解していませんが、私には作曲のように聞こえるので、OOPの欠点を与えます

OOはテストが難しい

  • 読み取り/書き込みクラス変数/属性への直接アクセスはありません-カプセル化を破るゲッターとシーターを導入する必要があるかもしれません。

  • 継承により、サブクラスのメソッドの動作が変わる可能性があります

  • オブジェクトには状態があります。パブリックインターフェイスに依存しているため、これらの状態を生成するのは難しい場合があります。

  • 凝集度の低いクラスは、クラスが指定された以上のことを行うことを意味する可能性があるため、テストが難しい場合があります。

9

彼が運転していたポイントがちょっとわかります。議論は基本的に継承の不利な点に集中していると思います-注意しない場合、または継承が多すぎて機能を拡張し続けることができない場合は、冗長な機能がたくさんあり、不足しているクラスの肥大化した混乱に終わる可能性があります結束の。

彼のアナロジーは、ロケットがセグメント内の燃料を燃焼すると、セグメントが冗長になり、したがって自重になることを考慮すると機能します。ロケットはセグメントを投棄することができますが、継承したくないクラスのセクションを除外することはできないと思います(ただし、間違っている場合は、便利に聞こえるので訂正してください)。

3
Moonshield

ロケットの例は、多くのオブジェクト指向言語では、オブジェクトの型が不変であるという事実をほのめかしているのでしょうか。つまり、私がStudentオブジェクトを持っていて、それが表す人が勉強を終えて従業員になった場合、StudentオブジェクトをEmployeeオブジェクトに変えることはできません。新しいものを作成し、その過程でオブジェクトIDを失い、その結果、前の学生を指すすべての参照を更新する必要があります。

もちろん、そのような機能は静的型付け(オブジェクトの型がそれを指す参照の静的型のサブ型であるという型不変)を妨害します。しかし、状況によっては、柔軟性はそれだけの価値があるかもしれません。

2
meriton

これをOOPで使用すると言えますか?

RocketShip.BodyPart1 =なしまたは

RocketShip.BodyPart1.isDetached = false?

OOP自体が原因ではありませんが、実装方法で使用されるプログラミング言語の制限です。

1
paul

なぜExtendsが悪であるか に関するこの優れた記事を読んでください。とはいえ、それがオブジェクト指向プログラミングのconであるとは言えません。

宇宙船のインタビュアーの理想は 2001年の黒色オベリスク:宇宙の旅 だと思います。

他の人はすでにそれをカバーしていますが、あなたがそれについて考えるならば、すべてがオブジェクトであるわけではありません。コンピューティングでは、真のオブジェクトは何もありません。オブジェクトの概念を抽象化するだけです。

たとえば、岩は物質で構成されています。岩石はコンピューティング用語(OOP用語)のオブジェクトと考えることができますが、それは岩石の概念に関するプログラミングには役立ちません。

オブジェクト指向プログラミングの主な概念について考えると、カプセル化、ポリモーフィズム、クラス、継承..これらすべてのコアOOPの概念には問題があります。

1
Berlin Brown

OOPで考えられる最大の欠点は、高階関数がサポートされていないことです。メソッドを含むオブジェクトを渡すことはできますが、これは冗長で非効率的です。関数を別の関数に直接渡すことができます。

1
Robert

サブクラスを変更するオブジェクトをモデル化する1つの方法は、 状態パターン を使用することです。インタビューの質問と Wikipedia の助けを借りて、ロケットがさまざまな段階を通過するときの状態遷移をモデル化する方法を示すJavaコード)を次に示します。コマンドモジュールのステージは示されていませんが、月の降下、月の上昇、月の軌道のランデブー、地球への帰還を通じて同様のパターンに従います。ISpaceShipStateインターフェイスに追加するさらに興味深い動作を見つけることができるでしょう。

public class SpaceShip {
    private ISpaceShipState m_state = new SaturnFiveState();

    public void setState(ISpaceShipState state) {
        m_state = state;
    }

    public void jettison() {
        m_state = m_state.transition();
    }

    public int getStageNumber() {
        return m_stage.getStageNumber();
    }

    public int getNumberOfRocketMotors() {
        return m_stage.getNumberOfRocketMotors();
    }

    public String getRocketMotorTypeName() {
        return m_stage.getRocketMotorTypeName();
    }
}

interface ISpaceShipState {
    public ISpaceShipState transition();
    public int             getStageNumber();
    public int             getNumberOfRocketMotors();
    public String          getRocketMotorTypeName();
}

public class SaturnFiveState implements ISpaceShipState {
    public ISpaceShipState transition() {
        return new SaturnFiveSecondStageState();
    }

    public int getStageNumber() {
        return 1;
    }

    public int getNumberOfRocketMotors() {
        return 5;
    }

    public String getRocketMotorTypeName() {
        return "F-1";
    }
}

public class SaturnFiveSecondStageState implements ISpaceShipState {
    public ISpaceShipState transition() {
        return new SaturnFiveThirdStageState();
    }

    public int getStageNumber() {
        return 2;
    }

    public int getNumberOfRocketMotors() {
        return 5;
    }

    public String getRocketMotorTypeName() {
        return "J-2";
    }
}

public class SaturnFiveThirdStageState implements ISpaceShipState {
    public ISpaceShipState transition() {
        return new SaturnFiveCommandModuleState();
    }

    public int getStageNumber() {
        return 3;
    }

    public int getNumberOfRocketMotors() {
        return 1;
    }

    public String getRocketMotorTypeName() {
        return "J-2";
    }
}

理想的なオブジェクト指向設計で、クラス階層を動的に(Javaそれは不可能であると確信しています))プラグインできますか?

Javaでリフレクションを使用して動的型付けをシミュレートすることは可能ですが、動的型付け言語の同じものと比較すると、コードは非常に扱いにくいです。

1
richj

親クラスの不要なメソッドについて:それらは悪いアーキテクチャの兆候ではありませんか?つまり、各クラスに必要な機能を計画段階で分離してから、クラスのチェーンを互いに派生させるべきではないでしょうか。

たとえば、クラスA、B、C、およびDがあります。クラスAには、クラスB、C、およびDが必要とするメソッドがあります。クラスBには、クラスDが必要とするメソッドがあります。したがって、クラスDをBから、BをAから導出できます。一方、CはBが持っているものを必要としないため、CをAから導出します。そのため、基本的に、階層に1つのクラスを追加することで、不要なものがあるという問題を解決しました。もちろん、このようなものが計画段階で捕らえられず、クラスBが基本クラスとして使用された場合、クラスBを2つのクラスに分割することはより困難になりますが、余分な努力を払うことで、不要な荷物の問題は解決されます。

1
AndrejaKo

それの主な欠点は、その力に直接起因すると思います。 OOPで行われる最も一般的なことの1つは、コードの動作を特殊化するために、場所によっては親クラスを派生クラスに置き換えることです。注意深く行わないと、一部を無効にするのは簡単です。呼び出し元のコードで行われた仮定の一部です。宣言されているタイプは実際に使用されているタイプではないため、コードを確認するときにそれを見つけるのは非常に難しい場合があります。

簡単に言うと、ポリモーフィズムは明示的ではないため、派生クラスの奇妙な動作を見落としがちです。

1
fortran

これらの記事 を見てください。 OOPのいくつかの欠点、特にインタビュアーの例のように、物事を階層に強制することの危険性については、さまざまな説明があります。

1
finnw

Javaのように、オブジェクト参照をそのままにしてオブジェクトを別のオブジェクトにモーフィングできる静的に型付けされた言語について聞いたことがないと確信していますが、偽造できると確信しています。とはいえ、根本的に良い考えがなければ、それはおそらく混乱するでしょう。

インタビュアーがどのように質問を提示したか、またはあなたがどのように応答したかはわかりませんが、OOPの最大の欠点の1つは、実際にはOOP自体ではありません。これは、OOPそして人々が作成しようとする悪い「IS-A」関係。継承階層を作成するために思いつくことができる唯一の理由がコードの再利用である場合、それは悪い理由です。AOPはより良いアプローチであると主張することができます。そのためです(あなたが炎上したり反対票を投じたりする前に、私が「できる」と言ったことに注意してください)

OOPは、実装ではなく、インターフェースとタイプに関するものです。オブジェクトが求職者のリストを返すことができることを知っていることは、私が知る必要があるすべてです。それがRDBMS、列指向データベース、Webサービス、さらにはスプレッドシートのいずれからのものであるかは重要ではありません。そのオブジェクトをパラメーターとして他のメソッドに渡すことができ、他のメソッドが「FetchJobCandidates」を呼び出して、求職者を取得できることを知っています。途中で落とし穴がありますが、ほとんどの場合、クラスが内部で行うことではなく、他の世界に公開することに基づいてクラスを考えると、より良い基盤のIMOになります。

1
Jim L

古典は、継承よりも構成を好むことです。継承は、システムが変化する継ぎ目に沿って抽象化をキャプチャするためにあります。

その抽象化を明確に捉えていないと、そうです、デザインに奇妙な力がかかるでしょう。しかし、これは、抽象化が達成しようとしていることに対して完全に正しくない世界でのプログラミングの問題です。彼らが完璧になるというわけではありません。

1
Keith Nicholas

多分円-楕円問題のようなもの

1
sa_nyc