私は本を読んでいます きれいなコード:アジャイルソフトウェアの職人のハンドブック そして、章6ページ95-98でオブジェクトとデータ構造の違いについて明確にします:
オブジェクトはデータを抽象化の背後に隠し、そのデータを操作する関数を公開します。データ構造はデータを公開し、意味のある機能を持ちません。
オブジェクトは動作を公開し、データを非表示にします。これにより、既存の動作を変更せずに新しい種類のオブジェクトを簡単に追加できます。また、既存のオブジェクトに新しい動作を追加するのが難しくなります。
データ構造はデータを公開し、重要な動作はありません。これにより、既存のデータ構造に新しい動作を簡単に追加できますが、既存の関数に新しいデータ構造を追加するのは難しくなります。
一部のクラスがオブジェクトであるかデータ構造であるかは少し混乱しています。たとえば、Java.utilのHashMapは、オブジェクトですか? (put()、get()などのメソッドのため、それらの内部の仕組みはわかりません)またはデータ構造ですか? (マップであるため、常にデータ構造と考えていました)。
文字列も、データ構造またはオブジェクトですか?
これまで私が書いてきたコードの大部分は、いわゆる「ハイブリッドクラス」であり、オブジェクトおよびデータ構造としても機能しようとしています。それらを避ける方法についてのヒントはありますか?
データ構造とクラス/オブジェクトの区別は、C++の場合よりもJavaで説明するのが難しくなります。Cには、クラスではなく、データ構造のみがあり、型付きフィールドと名前付きフィールド:C++はこれらの「構造」を継承しているため、「クラシック」データ構造と「実オブジェクト」の両方を使用できます。
Javaでは、メソッドを持たずパブリックフィールドのみを持つクラスを使用して、Cスタイルのデータ構造を「エミュレート」できます。
_public class VehicleStruct
{
public Engine engine;
public Wheel[] wheels;
}
_
VehicleStruct
のユーザーは、車両の部品について知っており、これらの部品と直接やり取りできます。動作、つまり関数は、クラスの外部で定義する必要があります。そのため、動作を簡単に変更できます。新しい関数を追加しても、既存のコードを変更する必要はありません。一方、データを変更するには、VehicleStruct
と対話するほぼすべての関数を変更する必要があります。カプセル化に違反しています!
OOPの背後にある考え方は、代わりにデータを非表示にし、動作を公開することです。エンジンやエンジンの数を知らなくても、車両でdoできることに焦点を当てますホイールがインストールされています:
_public class Vehicle
{
private Details hidden;
public void startEngine() { ... }
public void shiftInto(int gear) { ... }
public void accelerate(double amount) { ... }
public void brake(double amount) { ... }
}
_
Vehicle
がどのようにオートバイ、車、トラック、またはタンクになる可能性があるかに注意してください。詳細を知る必要はありません。データの変更は簡単です。クラスの外部の誰もデータを知らないため、クラスのユーザーを変更する必要はありません。動作の変更は困難です。新しい(抽象)関数がクラスに追加されると、すべてのサブクラスを調整する必要があります。
これで、「カプセル化の規則」に従って、フィールドをプライベートにし、アクセサーメソッドをVehicleStruct
に追加するだけで、データを非表示にすることができます。
_public class VehicleStruct
{
private Engine engine;
private Wheel[] wheels;
public Engine getEngine() { return engine; }
public Wheel[] getWheels() { return wheels; }
}
_
ボブおじさんの本では、これを行うことで、オブジェクトではなくデータ構造がまだあると主張しています。まだ車両をそのパーツの合計としてモデリングし、メソッドを使用してこれらのパーツを公開しています。これは、パブリックフィールドとプレーンな古いC struct
を持つバージョンと本質的に同じです。したがって、データ構造です。オブジェクトを作成するには、データを非表示にしてメソッドを公開するだけでは不十分です。メソッドが実際にbehaviorを公開するのか、データだけを公開するのかを考慮する必要があります。
2つのアプローチを組み合わせると、 getEngine()
とともにstartEngine()
を公開すると、「ハイブリッド」になります。私は手元にマーティンの本を持っていませんが、データと動作の両方を変更するのが難しいオブジェクトの両方の世界で最悪になるため、彼はハイブリッドをまったく推奨しなかったことを覚えています。
HashMapと文字列に関する質問は、かなり低レベルであり、アプリケーション用に記述するクラスの種類にはあまり適合しないため、少し注意が必要です。それでも、上記の定義を使用すると、それらに答えることができるはずです。
HashMap
はオブジェクトです。動作を公開し、厄介なハッシュの詳細をすべて隠します。 put
およびget
データに指示し、どのハッシュ関数が使用されるか、「バケット」がいくつあるか、衝突がどのように処理されるかは気にしません。実際には、HashMap
インターフェースのみを使用してMap
を使用しています。これは、抽象化と「実際の」オブジェクトを非常によく示しています。
Mapのインスタンスをデータ構造の代わりとして使用できることを混同しないでください!
_// A data structure
public class Point {
public int x;
public int y;
}
// A Map _instance_ used instead of a data structure!
Map<String, Integer> data = new HashMap<>();
data.put("x", 1);
data.put("y", 2);
_
一方、String
はほとんど文字の配列であり、これをあまり隠そうとしません。私はそれをデータ構造と呼ぶことができると思いますが、正直なところ、多くを獲得するかどうかはわかりません。
オブジェクトはクラスのインスタンスです。クラスは、実世界のさまざまなものをモデル化できます。それは何かの抽象化です(車、ソケット、地図、接続、学生、教師、あなたはそれを名付けます)。
データ構造は、特定のデータを特定の方法で編成する構造です。クラスを使用するのとは異なる方法で構造を実装できます(OOPをサポートしていない言語で行うことです。たとえば、Cでデータ構造を実装できます)。
HashMap in Javaは、ハッシュベースの実装を使用して地図データ構造をモデル化するクラスです。これがHashMapと呼ばれる理由です。
Javaのソケットは、データ構造ではなく他の何か(ソケット)をモデル化するクラスです。
これが、ロバートです。 C.マーティンは伝えようとしていた:
データ構造は、構造化データのコンテナとして単に機能するクラスです。例えば:
public class Point {
public double x;
public double y;
}
一方、オブジェクトは抽象化を作成するために使用されます。 abstractionは次のように解釈されます:
カバーの下で行われているもっと複雑なものの単純化 The Leawy Abstractionsの法則、Joel on Software
そのため、オブジェクトはすべての基盤を隠し、データのエッセンスを簡単な方法で操作するだけです。例えば:
public interface Point {
double getX();
double getY();
void setCartesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
Pointがどのように実装されているかがわからないが、それを消費する方法はわかります。
私が見るように、ロバート・マーティンが伝えようとしているのは、オブジェクトがその唯一の目的が単純なデータ・コンテナーとして機能することでない限り、ゲッターとセッターを介してデータを公開すべきではないということです。このようなコンテナの良い例は、Java Bean、エンティティオブジェクト(DBエンティティのオブジェクトマッピングから)など)です。
Java Collection Frameworkクラスは、内部データ(多くの場合、基本的な配列である)を実際に公開しないため、彼が参照しているものの良い例ではありません。含まれているオブジェクトを取得できる抽象化を提供するため、(私のPOVでは)「Objects」カテゴリに収まります。
理由は本から追加した引用文で述べられていますが、内部を公開することを控えるより良い理由があります。たとえば、ゲッターとセッターを提供するクラスは、デメテルの法則の違反を招きます。それに加えて、あるクラスの状態の構造を知る(どのゲッター/セッターがあるかを知る)と、そのクラスの実装を抽象化する能力が低下します。そのような理由は他にもたくさんあります。
データ構造は単なる抽象化であり、データを表す特別な方法です。それらは単なる人為的な構造であり、高レベルでの複雑さを軽減するのに役立ちます。つまり、低レベルでは機能しません。オブジェクトは同じことを意味しているように見えるかもしれませんが、オブジェクトとデータ構造の主な違いは、オブジェクトが何かを抽象化できることです。また、振る舞いも提供します。データ構造は単なるデータ保持メモリであるため、動作しません。
マップ、リストなどのライブラリクラス。 表すデータ構造のクラスです。それらは、データ構造を実装および設定し、プログラム内でインスタンス(オブジェクト)を作成することで簡単に作業できるようにします。
データ構造は、文字通りいくつかのデータの構造化された表現です。データの構造と命名以外の組み込みの「知識」はありません。 オブジェクトははるかに一般的であり、実際にはシステム内のほぼすべてのエンティティであり、相互作用のためのインターフェースを提供し、さまざまなコンテキストで使用できます。具体的には、たとえば、従来のclassオブジェクトには、データとデータにアクセスするための適切なメソッドが含まれる場合があり、そのデータはオブジェクトインスタンス固有(インスタンスごと)またはクラスレベルである可能性があります。
クラスにはデータが含まれることが多いため、オブジェクトまたはデータ構造として機能するという記述に基づいた「ハイブリッドクラス」という用語はわかりません。そのため、その用語は冗長なようです。
データ構造(DS)は、構造がデータを保持していると言う抽象的な方法です。いくつかのキーと値のペアを持つHashMapは、Javaのデータ構造です。関連付けられた配列はPHPなどでも同様です。オブジェクトはDSレベルよりも少し低いです。ハッシュマップはデータ構造です。ハッシュマップを使用するには、その「オブジェクト」を作成し、putメソッドを使用してそのオブジェクトにデータを追加します。データがあり、したがってDSである独自のクラスEmployeeを持つことができます。しかし、このDSを使用して、従業員が男性か女性かを確認するなどの操作を行うには、従業員のインスタンスが必要で、性別プロパティをテストします。
オブジェクトとデータ構造を混同しないでください。