web-dev-qa-db-ja.com

抽象クラスは、独自の抽象メソッドを定義するのではなく、インターフェースを実装する必要がありますか?

cassandraデータベースに永続化するためのクラス構造を定義しています。抽象クラスとインターフェイスの組み合わせを使用するかどうかわかりません。レポートを永続化するための2つの具象クラスがあります。もう1つは、entityMapperが使用する別のエンティティクラスが定義されていることです。私は this post を非常によく似ていますが、それらは考えていません。例は本当に私が知りたいことを強調しています。

私の構造は次のようになります:

インターフェース

public interface CassandraRepository<T> {

    void save(T object);

    void delete(T object);
}

抽象クラス

import com.datastax.driver.core.Session;
import com.datastax.driver.mapping.Mapper;
import com.datastax.driver.mapping.MappingManager;

abstract class AbstractGenericRepository<T> implements CassandraRepository<T> {

    private final Session sesion;
    Mapper<T> entityMapper;

    AbstractGenericRepository(final Session session) {
        this.session = session;
        final MappingManager manager = new MappingManager(this.session);
        this.entityMapper = manager.mapper(getRepositoryClass());
    }

    protected abstract Class<T> getRepositoryClass();
}

構成クラス

import com.model.Configuration;
import com.datastax.driver.core.Session;

public class ConfigurationRepository extends AbstractGenericRepository<Configuration> {

    public ConfigurationRepository(final Session session) {
        super(session);
    }

    @Override
    public void delete(final Configuration object) {
        this.entityMapper.delete(object);
    }

    @Override
    public void save(final Configuration object) {
        this.entityMapper.save(object);
    }

    @Override
    protected Class<Configuration> getRepositoryClass() {
        return Configuration.class;
    }
}

レポートクラス

import com.model.Report;
import com.datastax.driver.core.Session;

public class ReportRepository  extends AbstractGenericRepository<Report> {

    ReportRepository(Session session) {
        super(session);
    }

    @Override
    public void save(Report object) {
        this.entityMapper.save(object);
    }

    @Override
    public void delete(Report object) {
        this.entityMapper.delete(object);
    }

    @Override
    protected Class<Report> getRepositoryClass() {
        return Report.class;
    }
}

私の質問は次のとおりです。インターフェイスを持つことのポイントは何ですか? saveメソッドとdeleteメソッドの両方を抽象クラス自体の抽象メソッドとして単純に定義して、インターフェイスを排除できますか?.

また、これをタイプしているときに、saveとdeleteの両方の実装を抽象自体に移動できるので、それらは同じことを行うことに気付きました。しかし、それぞれが異なる方法でメソッドを実装した場合、インターフェースを持つことには何の利点もありますか?

サブクラスで実装する必要のある抽象メソッド(インターフェースなど)を提供できるだけでなく、それらの間の共通コード実装を定義できるため、インターフェースで抽象クラスを常に使用できない場合がある理由(インターフェースにはない)許可)?

[〜#〜]編集[〜#〜]

重複の可能性があるとしてタグ付けされた投稿に応答して、他の投稿では、特に抽象クラスがインターフェイスを実装する必要があるかどうか、およびそうすることの賛否両論の場合、私には何も明確になりません

1
Eoin

最初に、リポジトリとインターフェースの名前を変更しましょう。次に、抽象化の追加レイヤー(インターフェース)を作成することが有益である理由について説明します。

CassandraRepository<T>インターフェースに名前の問題があります。 「Cassandra」という単語が含まれているため、データベースベンダーに結び付けられています。

代わりに、これを単にRepository<T>に名前変更してみましょう。

public interface Repository<T>
{
    void save(T object);
    void delete(T object);
}

現在、AbstractGenericRepository<T>クラスには命名の問題もあります。ここから、リポジトリクラスとデータベースベンダーの結合を開始する必要があります。

これをCassandraRepository<T>に名前変更して、インターフェースを実装してみましょう。

abstract class CassandraRepository<T> implements Repository<T>
{
    private final Session sesion;
    Mapper<T> entityMapper;

    CassandraRepository(final Session session) {
        this.session = session;
        final MappingManager manager = new MappingManager(this.session);
        this.entityMapper = manager.mapper(getRepositoryClass());
    }

    protected abstract Class<T> getRepositoryClass();
}

ReportRepositoryクラスにも名前の問題があります。データベースベンダーに関連付けられていますが、レポートには特定の方法が必要です。このクラスの名前をCassandraReportRepositoryに変更し、新しいインターフェース `ReportRepository 'を実装してください:

public class CassandraReportRepository extends CassandraRepository<Report> implements ReportRepository
{
    ...
}

public interface ReportRepository extends Repository<Report>
{
    ReportResult run(Report reportToRun);
}

CassandraReportRepositoryは、特定のデータベースベンダーに結合された抽象クラスから継承します。これは、これらの名前の付け方から明らかです。

この次のビットは、抽象クラスのインターフェースを定義する理由です。

public class ReportService
{
    private final ReportRepository  repository;

    public ReportService(ReportRepository repository) {
        this.repository = repository;
    }

    // Methods that use this.repository
}

ReportServiceは、データベースベンダー固有の抽象クラス(またはデータベースベンダー固有のconcreteクラス)ではなく、ReportRepositoryインターフェイスに依存しています。ユニットテストでこのサービスクラスを分離し、モックレポートリポジトリに渡して、サービスクラスの動作を単体でテストできます。

これは、抽象クラスのインターフェースを定義する主な利点の1つです。疎結合は、簡単にテスト可能なコードに役立ちます。データベースベンダーを切り替える場合、コードはデータアクセスレイヤー内でのみリファクタリングする必要があります。

たとえば、MongoDbRepository<T>という新しいクラスを定義できます。 ReportRepositoryインターフェースを実装するMongoDbReportRepositoryという新しいクラスを作成します。 ReportRepositoryインターフェースは変更されなかったため、このインターフェースに依存するオブジェクトnotを変更する必要があります。

3
Greg Burghardt