RealNumberおよびComplexNumber継承を実装する方法は?


11

うまくいけば、あまりにも学術的ではありません...

SWライブラリに実数と複素数が必要だとしましょう。

is-a(またはhere)関係に基づいて、実数複素数であり、複素数の虚数部のbは単に0です。

一方、私の実装は、その子は親を拡張するため、親のRealNumberには実数部があり、子ComplexNumberには架空のアートが追加されます。

また、相続は悪だという意見もあります。

昨日のように、私が大学でOOPを学んでいたとき、私の教授は、これらの2つの絶対値が異なる方法で計算されるため、これは継承の良い例ではないことを思い出します(ただし、そのためにメソッドのオーバーロード/ポリモーフィズムがありますよね?)。 。

私の経験では、継承を使用してDRYを解決することがよくあります。その結果、階層に人工的な抽象クラスが含まれることがよくあります(実際のオブジェクトを表さないため、名前を見つけるのに問題があることがよくあります)。


7
これは前の質問でカバーされたように見えます:長方形は正方形から継承する必要がありますか?
gnat

1
@gnatおお、それは私が使いたかったもう1つの例でした...ありがとう!
Betlista

7
...数学的な意味での「実数は複素数です」という文は不変の数に対してのみ有効であるため、不変のオブジェクトを使用すると、LSP違反を回避できます(正方形と長方形についても同様です。これを参照してください)だから答え)。
Doc Brown、

5
...さらに、複素数の絶対値計算は実数でも機能するため、教授の意味がわかりません。「Abs()」メソッドを不変の複素数で正しく実装し、それから「実数」を派生させる場合でも、Abs()メソッドは正しい結果を提供します。
Doc Brown、

回答:


17

数学的な意味で実数が複素数であっても、複素数から実数を導出することはお勧めできません。Liskov Substitution Principleに違反しており、派生クラスは基本クラスのプロパティを非表示にすべきではありません。

この場合、実数は複素数の虚数部を隠す必要があります。実数部だけが必要な場合は、非表示の浮動小数点数(虚数部)を保存しても意味がないことは明らかです。

これは、コメントで述べられている長方形/正方形の例と基本的に同じ問題です。


2
今日、この「リスコウ置換の原則」を何度か見ました。知らないので、もっと読む必要があります。
Betlista

7
実数の虚数部をゼロとして報告することは完全に問題ありません。例えば、読み取り専用の方法で。しかし、虚数部がゼロに設定されている複素数として実数を実装しても意味がありません。これは、継承が誤解を招くケースです。インターフェイスの継承は間違いなく問題ありませんが、実装の継承は問題のある設計を引き起こします。
アモン

4
両方が不変である限り、実数に複素数を継承させることは完全に理にかなっています。そして、あなたはオーバーヘッドを気にしません。
Deduplicator

@Deduplicator:興味深い点。不変性は多くの問題を解決しますが、この場合はまだ完全には確信していません。それについて考えなければならない。
フランクパファー

3

これらの2つの絶対値は異なる方法で計算されるため、継承の良い例ではありません

これは実際には、ここでのすべての継承に対する説得力のある理由ではなく、提案されたclass RealNumber<-> class ComplexNumberモデルだけです。

あなたは合理的インターフェース定義できNumber両方を RealNumberしてComplexNumber実装します。

それは次のようになるかもしれません

interface Number
{
    Number Add(Number rhs);
    Number Subtract(Number rhs);
    // ... etc
}

ただしNumber、これらの操作の他のパラメーターをと同じ派生型に制約するthis必要があります。これは、

interface Number<T>
{
    Number<T> Add(Number<T> rhs);
    Number<T> Subtract(Number<T> rhs);
    // ... etc
}

または、サブタイプの多型ではなく、構造の多型を許可する言語を使用します。数値の特定のケースでは、算術演算子をオーバーロードする機能のみが必要な場合があります。

complex operator + (complex lhs, complex rhs);
complex operator - (complex lhs, complex rhs);
// ... etc

Number frobnicate<Number>(List<Number> foos, Number bar); // uses arithmetic operations

0

解決策:パブリックRealNumberクラスがない

虚数がゼロである複素数を返すComplexNumber静的なファクトリメソッドfromDouble(double)があれば、それはまったく問題ありません。その後RealNumber、このComplexNumberインスタンスのインスタンスで使用するすべての操作を使用できます。

しかし、パブリック継承RealNumberクラスが必要な理由/必要がある理由を理解するのは困難です。通常、継承はこれらの理由で使用されます(私の頭から、いくつか見逃した場合は修正してください)

  • 行動を拡張する。RealNumbers複素数では実行できない余分な操作を実行できないため、これを実行しても意味がありません。

  • 特定の実装で抽象的な動作を実装する。以来ComplexNumber抽象的であってはならない、これはまた、適用されません。

  • コードの再利用。ComplexNumberクラスだけを使用する場合は、コードの100%を再利用します。

  • 特定のタスクのためのより具体的/効率的/正確な実装。これはここで適用でき、RealNumbersいくつかの機能をより速く実装できます。しかし、このサブクラスはstaticの背後に隠されているfromDouble(double)必要があり、外部で認識されるべきではありません。この方法では、架空の部分を隠す必要はありません。外部の場合、複素数(実数)のみが存在する必要があります。また、実数をもたらす複素数クラスの任意の操作からこのプライベートRealNumberクラスを返すこともできます。(これは、クラスがほとんどの数値クラスと同じように不変であることを前提としています。)

これは、ゼロと呼ばれる整数のサブクラスを実装するようなものであり、ゼロに対して自明なので、いくつかの操作をハードコードします。すべてのゼロは整数であるため、これを行うことができますが、公開せずに、ファクトリメソッドの背後に隠します。


証明する情報源がないので、私はいくつかの反対票を得ても驚かない。また、他に誰もアイデアを持っていなかった場合、私はいつもそれには何らかの理由があるのではないかと疑っています。しかし、なぜそれが間違っていると思うのか、どうすればそれを改善できるのか教えてください。
findusl

0

実数が複素数であると言うことは、数学、特に集合論において、コンピュータサイエンスよりも意味があります。
数学では次のように言います。

  • 複素数のセットには実数のセットが含まれるため、実数は複素数です。
  • 実数のセットには有理数のセット(および無理数のセット)が含まれるため、有理数は実数です。
  • 有理数のセットには整数のセットが含まれるため、整数は有理数です。

ただし、これは、ライブラリを設計してRealNumberクラスとComplexNumberクラスを含めるときに継承を使用する必要がある、または使用する必要があるという意味ではありません。で効果的なJavaの第2版 ;ジョシュアブロッホ 項目16は「継承よりも好きな構成」です。その項目で言及されている問題を回避するには、RealNumberクラスを定義したら、それをComplexNumberクラスで使用できます。

public class ComplexNumber {
    private RealNumber realPart;
    private RealNumber imaginaryPart;

    // Implementation details are for you to write
}

これにより、Joshua Blochによって特定された問題を回避しながら、コードをDRYに保つためにRealNumberクラスを再利用するすべての能力を利用できます。


0

ここには2つの問題があります。1つ目は、コンテナのタイプとその内容のタイプに同じ用語を使用することは一般的であり、特に数値のようなプリミティブタイプの場合です。doubleたとえば、という用語は、倍精度浮動小数点値と、値を格納できるコンテナの両方を表すために使用されます。

2番目の問題は、さまざまなタイプのオブジェクトを読み取ることができるコンテナ間の関係はオブジェクト自体間の関係と同じように動作しますが、さまざまなタイプのオブジェクトを配置できるコンテナ間の関係は、コンテンツ間の関係とは逆になります。 。のインスタンスを保持することがわかっているすべてのケージCatは、のインスタンスを保持するケージになりますが、のインスタンスを保持するケージであるAnimal必要はありませんSiameseCat。一方、のすべてのインスタンスをCat保持できるケージはSiameseCat、のすべてのインスタンスを保持できるケージになりますが、のすべてのインスタンスを保持できるケージである必要はありませんAnimal。のすべてのインスタンスを保持Catでき、インスタンス以外のインスタンスを保持できないことが保証できる唯一の種類のケージCat、のケージですCat。他の種類のケージは、受け入れる必要のあるインスタンスを受け入れることができないか、のインスタンスでCatはないものを受け入れることができますCat

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