web-dev-qa-db-ja.com

抽象クラスとインターフェイスのどちらを使用すればよいですか?

私はプログラミングにかなり慣れていません。学校では現在、Javaを使ったプログラミングを学んでいます。本、レコード、ボードゲームなどのコレクションを保存できるアプリケーションを構築したいと考えています。クラス図の作成を開始して、私が始めて、それが終わったらプログラミングを始めました。抽象的なArtikelクラスがあります。次に、Artikelを拡張した本のクラスを例に挙げます。

Example of class diagram

プログラミングを始めたとき、すべてのサブクラスで、属性をいくつか変更して、まったく同じことを行う必要があることに気付きました。 Artikelを抽象的ではなく、Artikelと他のクラスの間にインターフェースを置くことを考えましたが、それでも多くの作業をコピーすることになります。

すべてのメソッドを配置できるArtikelから継承する別のクラスを作成することも可能ですが、それでもメソッドは3回作成する必要があります(サブクラスごとに1つずつ)。

このようなものをどのように設計しますか? Artikelを抽象的であるとしたら、それは悪い設計の選択でしょうか?

1
Vogel

注:C#の構文はお許しください。ただし、JavaおよびC#。の場合も同じです。

プログラミングを始めたとき、すべてのサブクラスで基本的にまったく同じことをする必要があることに気付きました

これに基づいて、抽象クラスは抽象メソッドを宣言することしか許可されていないと思われるようです。これはそうではありません。

抽象クラスは、直接インスタンス化できないクラスです(派生のみをインスタンス化できます)。抽象メソッドは、派生クラスで実装する必要がある抽象クラスのメソッドです。

しかし、抽象クラスは非抽象メソッドを持つことができます:

public abstract class Artikel
{
    public int ArtikelId { get; set; }

    public string SayHello()
    {
        return "Hi, I'm artikel " + ArtikelId;
    }
}

Artikelをサブクラスに派生させる場合、SayHelloメソッドのメソッド本体を繰り返す必要はありません。その本体は基本クラスで宣言されており、すべての派生クラスで使用できます。

Artikelを抽象的ではなく、Artikelと他のクラスの間にインターフェースを置くことを考えました

インターフェイスprevent共通のメソッド本体を作成する機能。インターフェースを使用する場合:

public interface IArtikel
{
    string SayHello();
}

次に、このメソッドをすべてのクラスで個別に実装するために必須になります。

public class Book : IArtikel
{
    public string SayHello()
    {
        // custom book logic
    }
}

// And the same for all other derived classes.

すべてのメソッドを配置できるArtikelから継承する別のクラスを作成することも可能ですが、それでもメソッドは3回作成する必要があります(サブクラスごとに1つずつ)。

これを間違った方法で行わないでください。ただし、これを解決しようとする試みは、実際にはOOPをマスターしていないことを示しています。このSeparateClassArtikelから別の(4番目の)サブクラスとして作成された場合、どのように期待しますか。 Bookにあるメソッドに依存するSeparateClassクラス?

Artikelを抽象的であるとしたら、それは悪い設計の選択でしょうか?

Artikel抽象を保持しますが、すべてのサブクラス間でコピー/貼り付けしている各メソッドに対して、非抽象メソッド(つまり、メソッド本文を含む)を指定します

1
Flater

基本クラスは、Artikelインターフェースを実装する抽象クラスとして持つことができます。抽象クラスでは、共通の実装を定義できます。次に、そのスーパークラスからLP、ブック、ボードゲームを派生させることができます。私の意見では、3つのサブクラスすべてに同じコードをコピーするよりも、抽象クラスを使用する方が良いです。

1
Leni

あなたの問題は、抽象クラスやインターフェースとは関係がないと思います。この場合の抽象基本クラスの使用は完全に正しいです。

これが何を意味するのかは完全には明らかではありません。

すべてのサブクラスで、属性の変更をいくつか加えて、基本的にまったく同じことを行う必要があることに気付きました。

addArtikel()removeArtikel()について話していると思いますが、これらが何をしているのかは明確ではありませんが、データベースと関係があると思いますまたは、学習プロジェクトの場合は、メモリ内のリストまたはハッシュテーブルのみ。また、実装には、namepublisherなどの一般的なプロパティのコードが重複している可能性があります。

コードの重複を回避する最善の方法は、そのコードを見ずに語るのは難しいですが、その一部は、スーパークラスでaddArtikel()およびremoveArtikel()を作成することですnot抽象ですが、それらに共通のプロパティを処理させるため、サブクラスのオーバーライド実装はsuper.addArtikel()およびremoveArtikel()を呼び出すことでこのロジックを再利用できます。

ただし、ここでの実際の設計上の問題は、addArtikel()removeArtikel()がデータモデル自体とは何の関係もないため、Artikelクラスに含めるべきではないということです。それらは、アイテムが追加または削除されるものをモデル化するクラスの一部である必要があります。これの典型的な設計パターンには、DAO(データアクセスオブジェクト)またはリポジトリと呼ばれるクラスがあります。

1

まず、問題のステートメントについて、親Artikel、抽象クラスを用意し、同じ機能を持つ共通のメソッドをそのクラスに実装する必要があります。そのため、すべての子クラスで繰り返す必要はありません。抽象として設定された、子クラスで特定の方法で実装する必要があるメソッド。

クラスAbstractを作成するのはなぜですか?それはインスタンス化されるべきではなく、子供だけがインスタンス化できるからです。また、メソッドの実装を子クラスに任せる必要がある場合は、抽象クラスでのみ可能な抽象である必要があります。

今、インターフェイスとアブストラクト。理論的には、これを読んでください https://beginnersbook.com/2013/05/abstract-class-vs-interface-in-Java/

実際には、インターフェースは仕様の一種です。つまり、ロジックはありません。すべてのメソッドは、それを実装するクラスによって実装する必要があります(Java 10より前)。抽象クラスは、仕様と実装を持つこともできます。

一般に、モデル/エンティティクラスの場合、親は抽象クラスです。

1