以下のコードを例にとると:
Shape p1 = new Square();
Square c1;
if(p1 instanceof Square) {
c1 = (Square) p1;
}
ポリモーフィズムよりもポリモーフィズムを好むとはどういう意味instanceof
ですか?
編集: ポリモーフィズムとは何かを理解しています。私が見逃しているのは、ではなくどのように使用するかですinstanceof
。
以下のコードを例にとると:
Shape p1 = new Square();
Square c1;
if(p1 instanceof Square) {
c1 = (Square) p1;
}
ポリモーフィズムよりもポリモーフィズムを好むとはどういう意味instanceof
ですか?
編集: ポリモーフィズムとは何かを理解しています。私が見逃しているのは、ではなくどのように使用するかですinstanceof
。
if...else... (または switch または Visitor) とポリモーフィズムの主な違いはモジュール性です。いわゆるオープンクローズの原則があります。これは基本的に、既存のプログラムに新しい機能を追加する場合、既存のコードに加える変更が少ないほど良いということです (すべての変更には何らかの作業が必要であり、バグが発生する可能性があるため)。それでは、変化量を比較してみましょう。
新しいメソッドを追加する (たとえば、paint() と getArea() がある場合は、getCircumference() を追加しましょう): if-else ソリューションを使用すると、1 つのファイル (新しいメソッドを含むファイル) を変更するだけで済みます。ポリモーフィズムでは、Shape クラスのすべての実装を変更する必要があります。
新しい種類の形状を追加します (正方形、円があります - 三角形を追加しましょう): if-else ソリューションでは、既存のすべてのクラスを if-else で確認し、三角形の新しい if ブランチを追加する必要があります。ポリモフィズムでは、新しいクラスを追加して、必要なすべてのメソッドをその中に実装するだけです。
したがって、if...else... またはポリモーフィズム: モジュール性に依存します。後で多くの新しいサブクラスが追加されることが予想される場合は、ポリモーフィズムを使用してください。後で多くの新しいメソッドが追加されることが予想される場合は、if...else... を使用し、クラスにはアクセサーなどの最も「基本的な」メソッドのみを配置します。言い換えれば、多くの if...else... 分岐があると予想される場合は、多態性を使用する必要があります。
さらに: if...else... ブランチが少ないと予想されるが、多くの場所で、この if...else... を Visitor パターンでカプセル化するか、ブランチごとに個別のケースで列挙型を作成することを検討する必要があります。 .
アイデアは、扱っている形状の種類を気にする必要がないということです。たとえば、Shape が抽象 draw() メソッドを定義する場合、Triangles、Square、および Shape を拡張する他のすべてのものも同じメソッドを持ちます。
ポリモーフィズムの単純な定義は、「異なる型を同じであるかのように扱う」、つまり同じインターフェースを使用することです。
理想的な世界では、扱っているオブジェクトの特定のタイプについて心配する必要はなく、そのインターフェースですべての使用シナリオをカバーする、より一般的なタイプのインターフェースのみを心配する必要があります。
Shape p1 = new Square();
Shape p2 = new Triangle();
p1.draw();
p2.draw();
このコードでは、p1 と p2 で Shape.draw() を直接呼び出しています。実装クラスが何をするかは気にせず、インターフェイス (または抽象親クラス) によって定義されるものだけを気にします。
編集:質問の例に関しては、可能な場合は動作をカプセル化することにより、そのようなコードパターンを回避することが一般的に推奨されます。新しいクラスを追加するたびにすべての条件を更新する必要があるため、instanceof の使用はコードの臭いと見なすことができます。
次のことを考慮してください
abstract class Shape {
public abstract int getEdgesNumber();
}
class Square extends Shape {
public int getEdgesNumber(){
return 4;
}
}
class Circle extends Shape {
public int getEdgesNumber(){
return 1; //not so sure it counts as one but for the example is fine ^^'
}
}
Shape square = new Square();
int squareEdgesNumber = square.getEdgesNumber();
Shape circle = new Circle();
int circleEdgesNumber = circle.getEdgesNumber();
ASquare
と a のCircle
両方がgetEdgesNumber()
メソッドを実装します。特定の実装に基づいて、メソッドを呼び出して結果を取得するだけです。
Square
を扱っているか、を扱っているかを知る必要はありません。必要なCircle
メソッドを呼び出して、オブジェクトの基礎となる実装に依存するだけです。
また、ドキュメントがそれをどのように説明しているかを見てください。
これは実際には強力な例ではありませんが、コードは次のようになります。
Square c1 = new Square();
Shape p1 = c1;
(もちろん、SquareがShapeを拡張することを考えると)
はるかに良いですね。
「なぜそれが良いのか」については、他の回答がいくつかの重要なポイントを示しています。
一部の人々instanceof
は、時間と場所があり、ビジターパターンが必ずしもそれを完全かつ適切に置き換えるとは限らないと信じています。他の何人かの人々はヒスとしかめっ面をします。すすぎ、繰り返します。
OOP哲学は、例で示した状況を根本的に回避するため、意味のあること(描画、辺の数え方など)を試みることで、例を改善できると思います。たとえば、OOP設計は、ではなくc1
として宣言するか、単に変数を使用します。Shape
Square
p1
余談ですが、正方形でない場合はc1がnullになる状況を追いかけている場合、または正方形でない場合はp1に設定されている場合は、私が好きな同様の「as」演算子が存在します。
Shape p1 = (Math.random()>0.5) ? new Square() : new Circle();
Square c1 = p1 as Square;
// c1 is null or not, depending on p1's type.
それは私の見解よりもOOに過ぎませんが、それでも私は実際には「非OO」であるとinstanceof
は考えていません。instanceof
ポリモーフィズムを使用すると、型に応じて動作を変更できます。何らかの理由で Square であることが重要な場合は、すぐに Square に割り当てることができるため、例で説明する方法がわかりません。追加の動作などが含まれる可能性があるため、サブクラス化する必要がある場合もありますが、次の例を検討してください。
class Shape
{
abstract void draw();
}
class Square extends Shape
{
void draw()
{
// square drawing goes here
}
}
ここでの draw メソッドはポリモーフィズムの例です。基本クラスの Shape は、すべての図形が自分自身を描画する方法を示していますが、Square の描画方法を知っているのは Square だけです。
「Shape」はインターフェースであり、「Square」はそのインターフェースの実装であると想定しています。
ここで、Shape インターフェース用に宣言されたメソッド (典型的な例は Shape.getArea()) を呼び出す必要がある場合は、それが Square であるか他のものであるかを気にせず、その関数を呼び出す必要があります。