web-dev-qa-db-ja.com

ソフトウェアの複雑さを管理するためにOO言語が本当に必要ですか?

これは非常に技術的ではない質問であり、これが適切なプラットフォームかどうかはわかりません。しかし、私はCSの初心者なので、皆さんがそれを許容できることを願っています。

最初の学期では、OOPカプセル化、データの非表示、モジュール性、継承などの概念をJavaとUMLを通じて紹介しました。(Javaが私の最初のプログラミング言語)

私が理解しているように、OOPはソフトウェアの複雑さを管理する方法です。しかし、その原則は新しいものやユニークなものではなく、ある意味ですべてのエンジニアリング分野に共通です。

たとえば、自動車は非常に複雑な構造であり、その複雑さは、明確に定義された動作とインターフェイスを備えた、モジュール化されカプセル化されたコンポーネントの階層によって管理されます。

しかし、私は新しいプログラミングパラダイムを導入する背後にある理由を理解していません。複雑さの管理に使用されるすべての原則は、手続き型プログラミング言語で実現できると思います。たとえば、モジュール化するために、プログラムを、コードが個別のファイルに含まれている明確に定義されたタスクを実行する多くの小さなプログラムに分割することができます。これらのプログラムは、明確に定義された入出力を介して相互に対話します。カプセル化を実現するために、ファイルを保護(暗号化?)することができます。コードを再利用するために、新しいプログラムで必要なときにいつでもそれらのファイルを呼び出すことができます。これは、OOPが何であるかをすべてキャプチャしているのではないか、それとも非常に明白なものがない

OOPが複雑さを管理するという証明を求めているのではありません。私の意見では確かにそうです。しかし、モジュール性、カプセル化、データ隠蔽などの複雑さを管理するために使用されるすべての原則は、手続き型言語で非常に簡単に実装できるので、なぜOOPなしで複雑さを管理できるのでしょうか?

212
steakexchange

本当に低い理論の答えを試してみましょう:)

手続き型言語を使用してOOコードを設計および記述できるのに、なぜオブジェクト指向(OO)のサポートを言語に直接含めるのですか?

そして答えは:OOがソースコードでどのように表現されるかについての標準を持っているので、同じ抽象化のために22の異なる実装になることはありません。

たとえば、ユーザーインターフェイスシステムで使用できるMagicButtonMagicSliderを作成するとします。 MagicButtonで使用できるメソッド、MagicSliderでのみ使用できるメソッド、および両方で使用できるメソッドをグループ化する方法が必要です。これらのオブジェクトは両方ともMagic guiオブジェクトであるため、いくつかのメソッドを共有しています。

特別な方法で関数に名前を付けることでグループ化を行うことができますMagicSlider_DoSomething ...、特別な方法で名前が付けられた特定のファイルにメソッドを含めることでMagicSliderMethods.XXX、または同じことを行う他の特別な方法を見つけることができます。言語に標準的な方法がない場合、私はあなたとは異なり、他の人とは異なります。これにより、コードの共有がはるかに困難になります。

はい、遅延ディスパッチ(OO言語の仮想メソッド)は手続き型言語で実装できますが、実装にはさまざまな方法があります。誰がコードを書いたかによって、同じプログラム内でのOOの実装が異なります。

貧しいメンテナンス開発者について考えてください。この担当者は、元のコードの作成者に応じて、さまざまなオブジェクトの抽象化と仮想メソッドを呼び出すさまざまな方法を管理する必要があります。

また、言語に抽象化があると、Eclipseなどの高度なコードエディターでコードに対して多くの静的分析を行うことができます。たとえば、Eclipseは、オブジェクトで使用できるすべてのメソッドのリストと、空の「TODOメソッド」の自動実装を提供できます。 Eclispeは、拡張するクラスと実装するインターフェースに基づいて、クラスが実装する必要のあるメソッドを正確に把握しています。オブジェクト指向を行うための言語標準がなければ、これはほとんど不可能です。

176
MTilsted

最初の学期では、OOPおよびUMLを通じて、カプセル化、データの非表示、モジュール性、継承などのJavaの概念を紹介されました。 (Javaは私の最初のプログラミング言語です)

これらはどれもOOPの概念ではありません。それらはすべてOOから独立してOOの外部に存在し、多くはOOの前に発明されました。

したがって、 that がOOのすべてであると考える場合、結論は正しいです。これらはすべて手続き型言語で実行できますOOとは関係がないためです。

たとえば、モジュール性に関する重要な論文の1つは On the Criteria to be Used in Decomposing Systems to Modules です。そこにはOOについての言及はありません。 (それは1972年に書かれ、それまでにOOは10年以上前のものであったにもかかわらず、まだあいまいなニッチでした。)

Data Abstraction はオブジェクト指向では重要ですが、OO(メッセージング)の主な機能の結果です。それは定義的な機能よりも。また、異なる種類のデータ抽象化があることを覚えておくことは非常に重要です。現在使用されている2つの最も一般的な種類のデータ抽象化(「抽象化をまったく行わない」を無視した場合、おそらく他の2つの組み合わせよりも多く使用されます)は、 Abstract Data Types およびオブジェクト。したがって、「情報の非表示」、「カプセル化」、および「データの抽象化」と言うだけで、OOは one の形式にすぎないため、OOについては何も述べていません。データ抽象化の2つであり、実際には2つは根本的に異なります。

  • 抽象データ型では、抽象化のメカニズムは type system ;です。実装を隠すのは型システムです。 (型システムは必ずしも静的である必要はありません。)オブジェクトを使用すると、実装は手続き型インターフェースの背後に隠され、型を必要としません。 (たとえば、ECMAScriptで行われるように、クロージャーを使用して実装できます。)
  • 抽象データ型では、異なるADTのインスタンスは相互にカプセル化されますが、 same ADTのインスタンスは、互いの表現とプライベート実装を検査してアクセスできます。オブジェクトは always からカプセル化され、 everything からカプセル化されます。オブジェクト itself のみが独自の表現を検査し、独自のプライベート実装にアクセスできます。 他のオブジェクトなし、同じタイプの他のオブジェクト、同じクラスの他のインスタンス、同じプロトタイプを持つ他のオブジェクト、オブジェクトのクローン、またはそれを行うことができるもの。 なし

ところで、これが意味することは、Javaではクラスはオブジェクト指向ではないということです。同じクラスの2つのインスタンス can は、互いの表現とプライベート実装にアクセスできます。したがって、クラスのインスタンスはオブジェクトではなく、実際にはADTインスタンスです。 Java interfacesただし、 do はオブジェクト指向のデータ抽象化を提供します。つまり、言い換えると、インターフェースのインスタンスのみがJavaのオブジェクトであり、クラスのインスタンスはそうではありません。

基本的に、型については、インターフェースのみを使用できます。つまり、メソッドとコンストラクターのパラメーターの型、メソッドの戻り値の型、インスタンスフィールドの型、静的フィールド、およびローカルフィールド、instanceof演算子またはキャスト演算子の引数、およびジェネリック型コンストラクターの型引数常にインターフェースである必要があります。クラスは、new演算子の直後にのみ使用でき、それ以外の場所では使用できません。

たとえば、モジュール化するために、プログラムを、コードが個別のファイルに含まれている明確に定義されたタスクを実行する多くの小さなプログラムに分割することができます。これらのプログラムは、明確に定義された入出力を介して相互に対話します。カプセル化を実現するために、ファイルを保護(暗号化?)することができます。コードを再利用するために、新しいプログラムで必要なときにいつでもそれらのファイルを呼び出すことができます。これはOOPのすべてをキャプチャしたものではないのですか、それとも非常に明白なものがないのですか?

あなたが説明するものOOです。

それは確かにOOについて考える良い方法です。実際、それはOOの最初の発明者が念頭に置いていたものとほぼ同じです。 (Alan Kayはさらに一歩進んだ:彼はネットワークを介して互いにメッセージを送信する多くの小さなコンピューターを想定していた。)「プログラム」と呼ばれるものは通常「オブジェクト」と呼ばれ、「呼び出し」の代わりに「メッセージを送信する」と通常言う」.

オブジェクト指向は、 Messaging (別名 dynamic dispatch )がすべてです。 「オブジェクト指向」という用語は、Smalltalkの主な設計者であるAlan Kay博士によって造られました。彼は これを次のように定義します

私にとってのOOPは、メッセージング、ローカルでの保持と状態プロセスの保護と非表示、およびすべてのものの極端な遅延バインディングのみを意味します。

それを分解してみましょう:

  • メッセージング(Smalltalkに慣れていない場合は、「仮想メソッドディスパッチ」)
  • 状態プロセスはである必要があります
    • ローカルに保持
    • 保護された
    • 隠された
  • すべてのものの極端な遅延バインディング

実装に関しては、メッセージングは​​遅延バインドされたプロシージャコールであり、プロシージャコールが遅延バインドされている場合、設計時にを呼び出すかがわからないため、状態の具体的な表現についての仮定を行います。つまり、実際にはメッセージングに関するものであり、遅延バインディングはメッセージングの実装であり、カプセル化はその結果です。

彼は後に「 ビッグアイデアは 'メッセージング' "であることを明確にし、"オブジェクト指向 "という用語は、"メッセージ指向 "ではなく"オブジェクト指向 "と呼んだことを後悔している。重要ではないもの(オブジェクト)に焦点を当て、本当に重要なもの(メッセージ)から注意をそらします。

前回のOOPSLAで少し苦労して、Smalltalkはその構文やクラスライブラリだけでなく、クラスについてでもないことをすべての人に思い出させようと心がけました。多くの人があまり重要ではないアイデアに集中できるようになるため、このトピックの「オブジェクト」という用語をかなり以前に作り出したのは残念です。

大きなアイデアは「メッセージング」です。それがSmalltalk/Squeakの核心です(そして、Xerox PARCフェーズでは完全には完了していません)。日本人は小さな単語を持っています-ma-「間にあるもの」-おそらく最も近い英語の同等語は「間質」です。優れた拡張可能なシステムを作成するための鍵は、モジュールの内部プロパティや動作ではなく、モジュールの通信方法を設計することです。インターネットについて考えてみましょう。それは、(a)単一の標準を超えたさまざまな種類のアイデアと実現を可能にし、(b)これらのアイデア間のさまざまな程度の安全な相互運用を可能にする必要があります。

(もちろん、今日、ほとんどの人はオブジェクトに焦点を当てるのではなく、クラスに焦点を当てていますが、これはさらに間違っています。)

メッセージングは​​、メタファーとしてもメカニズムとしても fundamental to OOです。

あなたが誰かにメッセージを送った場合、あなたは彼らがそれをどうするかわかりません。 only で確認できるのは、その応答です。彼らがメッセージを自分で処理したかどうか(つまり、オブジェクトにメソッドがあるかどうか)、メッセージを他の誰か(委任/プロキシ)に転送したかどうか、彼らがそれを理解したかどうかはわかりません。それがカプセル化のすべてです。それがOOのすべてです。期待どおりに応答する限り、プロキシを本物と区別することもできません。

「メッセージング」のより「現代的な」用語は「動的メソッドディスパッチ」または「仮想メソッド呼び出し」ですが、メタファーを失い、メカニズムに焦点を当てています。

したがって、Alan Kayの定義を見るには2つの方法があります。それだけで立っていると、メッセージングは​​基本的に遅延バインディングプロシージャコールであり、遅延バインディングはカプセル化を意味するため、#1と結論付けることができます。および#2は実際には冗長であり、OOはすべて遅延バインディングに関するものです。

しかし、彼は後で重要なことはメッセージングであることを明らかにしたので、それを別の角度から見ることができます。メッセージングは​​遅延型です。ここで、メッセージングが only で可能な場合、#3は当然のことです。1つしか存在せず、そのものが遅延バインドされている場合、 all 物事は遅れてバインドされています。そして再び、カプセル化はメッセージングから続きます。

同様の点が On Data Abstraction、Revisited by William R. Cook および彼の 「オブジェクト」と「オブジェクト指向」の簡略化された現代的な定義の提案

操作の動的ディスパッチは、オブジェクトの本質的な特性です。これは、呼び出される操作がオブジェクト自体の動的プロパティであることを意味します。操作を静的に特定することはできません。一般に、実行することを除いて、特定の要求に応じてどの操作が実行されるかを正確に[知る]方法はありません。これは、常に動的にディスパッチされるファーストクラス関数の場合とまったく同じです。

Smalltalk-72では、オブジェクトさえありませんでした! only メッセージストリームが解析、書き換え、再ルーティングされました。最初に来たメソッド(メッセージストリームを解析および再ルーティングする標準的な方法)、後にオブジェクト(いくつかのプライベート状態を共有するメソッドのグループ)が登場しました。継承はずっと後で行われ、クラスは継承をサポートする方法としてのみ導入されました。ケイの研究グループがプロトタイプについてすでに知っていたら、おそらく最初からクラスを紹介したことはなかっただろう。

Types and Programming Languages のBenjamin Pierceは、オブジェクト指向の定義機能は Open Recursion であると主張しています。

つまり、Alan Kay氏によると、OOはすべてメッセージングに関するものです。ウィリアムクックによれば、OOは動的メソッドディスパッチに関するものです(これはまったく同じことです)。 Benjamin Pierce氏によれば、OOはすべてOpen Recursionに関するものです。つまり、基本的には、自己参照が動的に解決される(または少なくともそれを考える方法です)、つまりメッセージングです。

ご覧のとおり、「OO」という用語を作成した人はオブジェクトに対してかなり形而上学的な見方をしており、クックはかなり実用的な見方をしており、ピアスは非常に厳密な数学的見方をしています。しかし重要なことは、哲学者、実用主義者、理論家はすべて同意することです!メッセージングは​​OOの1つの柱です。限目。

ここでは継承についての言及がないことに注意してください!継承はオブジェクト指向には必須ではありません。一般に、ほとんどのOO言語には実装の再利用の方法がありますが、必ずしも継承である必要はありません。たとえば、ある種の委任であることもあります。実際、 The Treaty of Orlando は、継承の代替としての delegation と、さまざまな形式の委任と継承は、オブジェクト指向言語の設計空間内のさまざまな設計ポイントにつながります。 (Javaのような継承をサポートする言語でも、実際にはそれを回避するように教えられていることに注意してください。これもOOには必要ないことを示しています。)

211
Jörg W Mittag

しかし、モジュール性、カプセル化、データ隠蔽などの複雑さを管理するために使用されるすべての原則は、手続き型言語で非常に簡単に実装できると思います。

「非常に簡単に」と言うと、非常に大胆な発言をしていることになります。私がそれを読む方法は次のとおりです:「私は難しさを見ないので、それはそれほど大きくてはいけません。」そのように言うと、「なぜOOが必要なのか」と尋ねているのではなく、「OOすぐにわかりますか?」

その質問に対する答えは、これらの困難の多くは、取り組んでいるプログラムの種類には存在しないということです。あなたは40年前のスパゲッティコードを更新するように求められていません。オペレーティングシステム用の新しいディスプレイマネージャーを作成しようとしていません。マルチスレッドの分散アプリケーションをデバッグしていません。

私たちのCS学生が書くことを任務とするおもちゃプログラムの多くの種類では、BASICまたはアセンブリでJavaまたはPythonとして)書くこともできます。これは、タスクは非常に少なく、開発者は1人だけで、従来の相互運用性の問題はありません。パフォーマンスは重要ではありません。また、コードは1台のマシンで数回しか実行されない可能性があります。

学生の運転手を連れて、ラッシュアワーのにぎやかな通りに合流するように求め、シンクロメッシュのない手動トランスミッションで急な坂を上っているところを想像してみてください。災害。どうして?彼らは、タスクが必要とするすべてのルールに同時に従うために必要な複雑さのレベルを管理することができません。

次に、同じ学生、同じ車両が、空の駐車場でウォーキングペースで運転しているところを想像してください。彼らのスキルのレベルはタスクに適切であるため、彼らは大丈夫です。プレッシャーはなく、リスクはほとんどありません。開始、クラッチ、シフト、加速、ステアリングの各サブタスクを一度に1つずつ実行できます。

熟練したドライバーがこれらすべてを同時に実行できる場合、その学生はなぜ自動変速機を持っているのかと尋ねるかもしれません。答えは、熟練した十分なドライバーは、最適な条件では、自動を必要としないということです。しかし、私たちはすべてが最高の状態のプロのドライバーというわけではなく、通常、自動車の設計者にそのすべての複雑さの面倒を見てもらうという便利さを求めています。

熟練した、規律のとれた十分なプログラマは、C、またはアセンブリで機能する高複雑度システムを実際に作成できます。しかし、私たちはすべてのLinus Torvaldsとは限りません。また、有用なソフトウェアを作成するために、そうである必要はありません。

私は、目前の問題に取り組む前に、現代の言語のすべての機能を再発明しなければならないことに個人的に興味がありません。すでに解決された問題の解決策を含む言語を利用できるのであれば、なぜ私はそうしないのですか?

だから私はあなたの質問を逆にしてあなたに尋ねます、言語がカプセル化やポリモーフィズムのような便利な機能を提供するなら、なぜそれらを使うべきではないのですか?

66
Clement Cherlin

あなたが説明しているのはOOPではなく、抽象化です。抽象化は、OOPでないものも含め、すべての現代のデザインモデルに存在します。そしてOOPは非常に特殊な種類の抽象化です。

まず、OOPの定義が1つではないことは注目に値します。そのため、私がOOPとして特徴付けていることに同意できない人がいるかもしれません。

次に、OOPは従来の設計モデルに触発されたものであり、自動車設計との類似点は偶然ではないことを覚えておくことが重要です。

しかし、ここにいくつかの方法がありますOOPはあなたが言ったことよりも微妙です:

  • カプセル化:これは、単にモジュールのインターフェースを設定すること(つまり、抽象化)ではなく、このインターフェースを超えてアクセスを禁止することです。 Javaでは、プライベート変数へのアクセスはコンパイルエラーですが、車の設計では、(場合によっては)意図したインターフェイスとは異なる方法で物事を使用できます。

  • 継承:これは実際にOOPを一意にするものです。インターフェイスを定義すると、複数のものがそのインターフェースを実装しており、これを階層的な方法で行うことができ、実装の特定の部分を変更しながら、前の部分をすべて継承して、コードの重複を大幅に削減します。

    車のカプセル化されたコンポーネントの観点から考えると、これに相当するものは実際にはありません。別のギアを使用して、その実装の特定の部分を変更してギアを作成する方法はありません。 (少なくとも私はそうは思わない、私は車についてあまり知らない)。

  • ポリモーフィズム:インターフェースを定義すると、そのインターフェースを使用するものは、どの操作が利用可能であるかという観点から見分けがつかなくなります。インターフェイスを使用するために使用されている実装を知る必要はありません。ここでサブタイピングと Liskov Substitution Principle が重要になります。

  • カップリング:OOPの重要な点は、同じ操作に密接に関連し、異なるものを広げることですフォームに含めることができます。データはそのデータに対する操作にバンドルされています。これは、新しい形式のデータ(新しい実装)を追加するのは非常に簡単ですが、新しい操作をインターフェースに追加するのは非常に難しいためです( dは、インターフェイスを実装する各クラスを更新する必要があります。これは、新しい操作を追加するのが非常に簡単である(すべてのケースを処理する関数を記述するだけ)関数言語の代数的データ型とは対照的ですが、新しいバリアントを追加します(すべての関数に新しいケースを追加する必要があるため)。

22
jmite

ソフトウェアの複雑さを管理するためにOO言語が本当に必要ですか?

これは、「必要」という言葉の意味に依存します。

「必要」が必要とする場合、必要ありません。

「必要」が「強い利益をもたらす」ことを意味するなら、私は「はい」と言うでしょう、私たちはそれを望みます。

大局

オブジェクト指向言語は、機能をデータにバインドします。

このバインドを回避して、データ値を渡す関数を作成できます。

しかし、そうなると、データのコンステレーションがまとまって、データのタプル、レコード、またはディクショナリの周りを移動し始めるでしょう。

そして、実際には、すべてのメソッド呼び出しは次のとおりです。バインドされたデータのセットに対する部分関数。

機能ごとの機能

OOPの機能:

  • Inheritanceは、コード(mixins)とコンセプト(Abstract Base Classes/interfaces)の再利用を可能にします-しかし、サブクラスの関数と変数を再装飾することでこれを得ることができます範囲。
  • Encapsulationを使用すると、情報を非表示にして、より高い抽象化レベルで作業できます。ただし、ヘッダーファイル、関数、モジュールを使用してこれを行うことができます。
  • Polymorphismは、それらの引数が同じインターフェイスをサポートしている限り、異なる型の引数を使用することを許可しますが、関数を使用してそれを行うこともできます。

ただし、これらの機能は、これらの機能をファーストクラスでサポートするオブジェクト指向言語ほど簡単には起こりません。

参考文献

OOPの批評家 はたくさんいます。

ただし、OOPによるコードの再利用により、プログラマーの生産性が向上することが研究により示されているようです。これは物議を醸している調査結果であり、特定の制約を前提として、これらの生産性の向上を再現できないと言う研究者もいます。 (ソース)

結論

OOPは必要ありません。しかし、場合によっては、ユーザーがOOPを必要とします

私の理解は、成熟したプログラマーはオブジェクト指向のスタイルで非常に生産的になることができるということです。また、パッケージに簡単に理解できるシンプルなインターフェースを持つコアオブジェクトがある場合、新しいプログラマーでもすぐに生産性を高めることができます。

11
Aaron Hall

簡潔に説明します。

OOの中核となる原則は、単一の組織単位(オブジェクト)におけるデータと動作の組み合わせです。

これが複雑さを制御することを可能にするものであり、それが出現したときそれはかなり革新的なコンセプトでした。これを一方でファイル(純粋なデータ)と比較し、他方でそれらのファイルを読み取って処理し(純粋なロジック)、出力する(純粋なデータを再度)プログラム。

データとロジックのパッケージをまとめて、実際のエンティティをモデリングしたら、メッセージの交換、子クラスの作成、プライベートデータとビヘイビアの分離、ポリモーフィックビヘイビアの実装、OO固有の魔法のすべてを実行できます。

だから、はい、OOは大したことです。いや、それは単なる派手な名前の古いものの束ではありません。

それをすべて分解し、要素を調べてから、「まあ、ここには今まで見たことがないものは何もない」と言っても、イノベーションを保持する議会を認めていません。結果は、その部分の合計よりも多くなります。

10
Martin Maat

オブジェクト指向プログラミングの「公式な」定義はなく、合理的な人々は実際にオブジェクト指向の品質を定義しているものについて意見が分かれています。メッセージングと言う人もいれば、サブタイプを言う人もいれば、継承を言う人もいれば、データと動作のバンドルを言う人もいます。 本物の OOが何であるかについての言い争いに巻き込まれすぎてはならないということだけを意味します。

カプセル化とモジュール性は設計のより基本的な原則であり、すべてのプログラミングパラダイムに適用する必要があります。 OOの支持者は、これらのプロパティがOOでのみ達成できることを主張していません-OOもちろん、関数型プログラミングのような他のパラダイムの支持者は、彼らのパラダイムについても同じことを主張しています。実際、成功している言語の多くはマルチパラダイムであり、オブジェクト指向、関数型などは「真の方法」ではなくツールとして見なされるべきです」.

複雑さの管理に使用されるすべての原則は、手続き型プログラミング言語で実現できると思います。

結局のところ、最終的にはどのプログラミング言語でも何でもできるからです。すべての言語には異なる長所と短所があるため、一部の言語では他の言語よりも簡単な場合があります。

8
JacquesB

他の答えが言及していない何か:状態。

あなたはOOを管理するためのツールとしてcomplexityについて話します)複雑さは何ですか?それはあいまいな用語です。意味のゲシュタルト感覚ですが、特定するのは困難です。循環的複雑度、つまりコードを介したランタイムパスの数を測定することはできますが、これを使用しているとき、それが何について話しているかわかりません= OO複雑さを管理するため。

私たちが話していることは考えている状態関連の複雑さです。

encapsulationの背後には2つの主なアイデアがあります。それらの1つである実装の詳細の非表示は、他の回答でかなり十分にカバーされています。しかし、もう1つはそのランタイム状態を隠しています。オブジェクトの内部データをいじる必要はありません。メッセージを渡します(または、JörgMittagが指摘したように、コンセプトよりも実装の詳細を好む場合はメソッドを呼び出します)。どうして?

アクセスするコードを変更せずにデータの内部構造を変更することはできず、300か所ではなく1か所(アクセサーメソッド)で変更したいためです。

しかし、それはコードが推論するのを難しくするからでもあります:手続き型コード(本質的に手続き型の言語であるか、単にそのスタイルで記述されているか) )状態の変化に制限を課すことについてはほとんど助けを提供しません。いつでもどこからでも何でも変わる可能性があります。関数/メソッドの呼び出しには、不気味な距離のアクションがある場合があります。テストの成功は、広くアクセス/アクセス可能な非ローカル変数の値によって決まるため、自動テストはより困難です。

他の2つの大きなプログラミングパラダイム(OOと関数型)は、状態に関連する複雑さの問題に対して、興味深いがほぼ正反対のソリューションを提供します。関数型プログラミングでは、それを完全に回避しようとします。関数は一般的に純粋であり、データ構造に対する操作は、元の場所を更新するのではなくコピーを返します。

一方OOは、managing状態を処理するためのツールを提供します(それを回避するためのツールではなく)。アクセス修飾子(保護された/パブリック/プライベート)、ゲッター、セッターなどの言語レベルのツールに加えて、他のオブジェクトデータにアクセスするためにオブジェクトを通過しないようにアドバイスする、デメテルの法則などの関連する規則もいくつかあります。 。

実際にはこれを行うためにオブジェクトをneedしないことに注意してください:アクセスできないデータを持ち、関数のデータ構造を返すクロージャーを持つことができますそれを操作します。しかし、それはオブジェクトではありませんか?それは、オブジェクトが何であるかという私たちの概念に直観的に適合しませんか?そして、もし私たちがこの概念を持っているなら、(他の答えが言っているように)競合するアドホック実装の組み合わせの爆発に頼るよりも、言語でそれを再具体化する方が良いのではないですか?

7
Jared Smith

ソフトウェアの複雑さを管理するために本当にOO言語が必要なのでしょうか?

いいえしかし、それらは多くの状況で役立ちます。

私は何十年もの間、主に単一のOO言語を使用してきましたが、私のコードのほとんどは、実際には完全にOOスタイル以前の手続きです。ただし、GUIに関連するものについては、組み込みのメソッドとオブジェクトの言語の膨大なOOライブラリを使用します。これにより、コードが大幅に簡素化されます。

たとえば、元の低レベルのWindows APIを使用してフォーム、ボタン、編集フィールドを表示するWindowsアプリケーションは、多くのコードを必要としますが、Visual BasicまたはC#またはDelphiに付属するオブジェクトのライブラリを使用すると、同じことができますプログラムは小さくて些細なものです。したがって、私のOOコードは通常比較的小さく、GUI向けですが、これらのオブジェクトが呼び出す私のコードは通常はるかに大きく、通常はOOであることに関係ありません(ただし、問題によって異なります)解決しようとしています)。

OOプログラムは、過度に複雑で、オブジェクトの実装方法に関する複雑な難解なルールに依存しており、OOの概念なしで記述した場合ははるかに単純になる可能性があります。私は反対も見ました。複雑なシステムは、オブジェクトを使用することで再実装され、単純化されることを求めています。

経験を積むにつれて、さまざまな状況でさまざまなツールやソリューションが必要になり、1つのサイズですべてに対応できるわけではありません。

5
joe snyder

完全にCで書かれた非常に大規模なプロジェクトに携わっている人として、私は間違いなく「いいえ」と答えることができます。

モジュール性は重要です。ただし、モジュール性は、実際には適切な言語で実装できます。たとえば、Cはモジュラーコンパイル、ヘッダーファイル、構造タイプをサポートしています。これは、99%の場合には十分です。必要な新しい抽象データ型ごとにモジュールを定義し、データ型を操作するための関数を定義します。場合によっては、パフォーマンスが必要で、これらの関数がインライン関数としてヘッダーファイルにある場合や、標準関数を使用する場合があります。どちらの方法を選択してもユーザーには見えません。

構造は構成をサポートします。たとえば、mutexロックと通常のハッシュテーブルで構成されるロックされたハッシュテーブルを持つことができます。これはオブジェクト指向プログラミングではありません。サブクラス化は行われません。コンポジションは、オブジェクト指向プログラミングのアイデアよりもはるかに古いツールです。

コンパイルレベルのモジュール性では不十分で、ランタイムモジュール性が必要な場合の1%には、関数ポインターと呼ばれるものがあります。それらは明確に定義されたインターフェースの個々の実装を持つことを可能にします。これは、非オブジェクト指向言語でのオブジェクト指向プログラミングではないことに注意してください。これはインターフェースを定義し、それを実装することです。たとえば、サブクラス化はここでは使用されません。

おそらく最も複雑なオープンソースプロジェクトがあるとしましょう。つまり、Linuxカーネルです。完全にC言語で書かれています。これは主に、構成を含む標準のコンパイルレベルのモジュール性ツールを使用して行われます。実行時のモジュール性が必要な場合は、関数ポインターを使用してインターフェイスを定義および実装します。

Linuxカーネルでオブジェクト指向プログラミングの例を見つけようとする場合、オブジェクト指向プログラミングを拡張して「インターフェイスの定義と実装」などの標準的なタスクを含めるようにしない限り、そのような例を見つけるのは非常に難しいと思います。

Cプログラミング言語でさえ、本当に必要な場合はオブジェクト指向プログラミングをサポートしていることに注意してください。たとえば、GTKグラフィカルユーザーインターフェイスツールキットを考えてみます。非オブジェクト指向言語で書かれていますが、実際にはオブジェクト指向です。したがって、これは、「オブジェクト指向言語」が必要であるという考えには深い欠陥があることを示しています。オブジェクト指向言語ができることは、別の種類の言語ではできないことです。さらに、あなたがエキスパートプログラマであれば、オブジェクト指向コードを任意の言語で非常に簡単に作成する方法を知っています。たとえば、Cを使用するのは負担ではありません。

したがって、結論は、オブジェクト指向言語は、概念が実際にどのように実装されているかを理解できない初心者プログラマーにのみおそらく役立つであろうということです。ただし、プログラマーがそのような初心者プログラマーであるようなプロジェクトの近くになりたくはありません。

3
juhist

オブジェクト指向メソッドを含むプログラミングパラダイムを導入する理由は、より洗練された強力なプログラムを簡単に作成できるようにするためです。バイトマガジンの1981年8月号で、Smalltalkの主要な作成者の1人である Daniel Ingalls は、「オブジェクト指向」を次の機能を含むものとして定義しました。

  • 自動ストレージ管理
  • メッセージを交換する機能
  • 言語のすべての操作に適用される統一メタファー
  • 他のコンポーネントの内部に依存するコンポーネントはありません(モジュール性)
  • プログラムはオブジェクトの動作のみを定義し、オブジェクトの表現は定義しません(多態性)
  • 各コンポーネントは1つの場所にのみ表示されます(ファクタリング)
  • ハードウェアに依存しない仮想マシンの使用
  • ユーザーがアクセスできるすべてのコンポーネントを観察および制御できるようにする必要があります(事後対応)
  • 全体的なコントローラーがないこと(オペレーティングシステムがないこと)

これらは、Xerox Parc Researchによって開発されたSmalltalk-80の設計上の考慮事項であるとインガルスが確認した原則です。前述の雑誌記事では、これらの各原則の詳細な説明と、それらがIngallsによるオブジェクト指向パラダイムにどのように貢献するかを読むことができます。

これらの原則はすべて、手続き型、アセンブリ言語など、チューリング完全言語を使用して適用できます。これらは言語仕様ではなく、設計原則です。オブジェクト指向言語は、ソフトウェアを作成するときにこれらの原則を簡単に使用できるようにすることを目的としています。

たとえば、Ingallの最初の原則(自動ストレージ管理)を取るために、誰でも独自の自動ストレージ管理システムを手続き型言語で記述できますが、そのようにするのは大変な作業になります。 SmalltalkやJavaが組み込まれたJava $ ===などの言語を使用する場合、プログラマーはメモリの管理にそれほど多くの作業を行う必要がありません。トレードオフは、プログラマーがオブジェクト指向プログラミングのような設計パラダイムのアイデアは、パラダイムの利点が少なくとも一部のプログラマーの欠点を上回っていることです。

2
Tyler Durden

これは非常に良い質問であり、ここで与えられた回答は正義を成し遂げていないと感じます。そのため、先に進んで私の考えを追加します。

目的は-ソフトウェアの複雑さを管理するです。目的は「OO言語の使用)」ではありません。

新しいパラダイムを導入することには「理由」はありません。これは、コーディングが成熟するにつれて自然に発生したものです。リンクリストの最後に新しいノードを追加するのではなく、列車(リンクリストを使用してモデル化されている列車)の最後にコーチを追加するコードを記述する方が理にかなっています。


現実世界のエンティティに関するコーディングは、現実世界のエンティティに関するコーディングである場合のコーディングの明白で正しい方法です。


コンピューターは、リンクされたリストの最後にノードを追加するのと同じように、列車の最後にコーチを追加するのと同じくらい簡単に作業できます。しかし、人間にとっては、リンクリストとノードを使用するよりも、列車とコーチを使用する方が簡単です。レベルを深くすると、リンクリストを使用して列車がモデル化されていることがわかります。

ファイルを保護または暗号化しても、カプセル化は実現できません。暗号化の反対は復号化です。カプセル化の反対は カプセル化解除 です。これは、より優れたパフォーマンスを実現するための、プログラミング言語での構造とクラスの分解を意味します。メモリトラフィックを減らし、OOPルールチェックを回避することで得られるパフォーマンス。

したがって、これらの2つは異なる概念であるため、暗号化され、適切にカプセル化されたコードを記述できます。

カプセル化は、現実に近いため、複雑さの管理に役立ちます。

したがって、オブジェクトでプログラミングするのは、コーディングが容易であり、あなたや他のすべての人が理解するのが速いためです。

0
displayName

覚えておくべきことはこれです:
OOPは言語機能に関するものではありません。コードを構造化する方法に関するものです

OOPは考え方であり、コードのアーキテクチャーを設計する方法であり、ほぼすべての言語で実行できます。これには特に、アセンブラーおよびCと呼ばれる低レベルの非OO言語が含まれます。アセンブラーで完全にオブジェクト指向プログラミングを実行でき、Cで記述されたLinuxカーネルは、多くの点で非常にオブジェクト指向です。 。

そうは言っても、-言語のOO機能は、望ましい結果を達成するために記述する必要があるボイラープレートコードの量を大幅に減らします仮想関数テーブルを明示的に定義し、Cで適切な関数ポインターを入力する必要がある場合、Javaでnothingを実行するだけで完了です。 OO言語は、ソースコードから有効な残骸をすべて削除し、ニースの言語レベルの抽象化(クラス、メソッド、メンバー、基本クラス、暗黙的なコンストラクター/デストラクター呼び出しなど)の背後に隠します。 )。

したがって、いいえ、必要はありませんOO OOPを実行するための言語です。OOPは、まともなOO言語で行うには簡単)です。

ソフトウェアの複雑さを管理する1つの方法は、ドメイン固有言語を使用して、フレームワークを目的のアクションから完全に分離することです。つまり、プログラミングコードのレベルは、目的の結果が構成されるレベルとは異なり、完全に異なる言語またはシステムです。これが適切に行われると、従来のコードは本質的にライブラリになり、ユーザーまたは他の人が目的の結果を作成すると、スクリプトジェネレーターやレポートジェネレーターなどのビジュアルデザインツールを使用してプラグインできます。

これを機能させるには、可能な操作とそれらがリンクする方法(フォーム構築ツールのようなスクリプト言語またはビジュアルデザイン)の周りに厳密な境界を描く必要があります。メタデータは、コード化の詳細からランタイム構成を抽象化する重要な方法であり、これにより、システムが幅広い望ましい結果をサポートできるようになります。境界が定められて守られている場合(それに伴う拡張のすべての要求を受け入れるわけではありません)、プログラマが必要なことを実行する必要なく、人々のために機能する長続きする堅牢なシステムを使用できます。

Martin Fowlerがこれについて本を書いていて、そのテクニックはプログラミングそのものとほとんど同じです。すべてのプログラミング言語はドメイン固有言語であると言っても過言ではありません。そのため、このアイデアは固有のものであり、非常に明白であるため見落とされています。ただし、独自のスクリプトツールやビジュアルデザインツールを作成して、作業を簡単にすることができます。問題を一般化すると、解決がはるかに簡単になることがあります。

0
user251748