web-dev-qa-db-ja.com

SRPを維持するには、ムーバーとトラッカーを別々のクラスにする必要がありますか?

私はいくつかのJavaコードの実践を行って、単一責任の原則をよりよく理解しています。現在、データ構造のネットワークを使用しています。ここで、各構造はLocationというインターフェイスを実装しています。

public interface Location {

    void removeObject(Object o);
    void addObject(Object o);
}

場所によってオブジェクトの保存と取得方法が異なるため、オブジェクトを追加/削除するには、それぞれ独自のメソッドが必要です。目標は、オブジェクトをロケーション間で自由に移動し、オブジェクトが現在どこにあるかを調べることができるようにすることです。オブジェクトを移動するために呼び出される2つのクラスがあります。1つはオブジェクトの実際の移動(ムーバー)のため、もう1つはオブジェクトの場所を追跡してムーバークラス(フェッチャー)にフェッチできるようにするためです。

public class Database {

private Mover mover;
private Tracker tracker;

//Contains methods for moving specific Objects to specific Locations.

}

public class Mover {

void moveObject(Object o, Location a, Location b)
{
  a.removeObject(o);
  b.addObject(o);
}

}

public class Tracker {

Map<Object, Location> trackerMap;

void updateTracker(Object o, Location a, Location b)
{
    trackerMap.remove(a);
    trackerMap.put(b, o);
}

Object lookupObject(Object o)
{
    return trackerMap.get(o);
}

}

しかし、私はこのデザインについていくつかの懸念があります。ムーバーとトラッカーは別々のクラスに分割されているため、データ構造は基本的に2回格納されます。トラッカーのマップによって、およびムーバーが変更している実際のデータ構造によって。エラーが発生しやすいようです。何らかの理由で2つが同期しなくなった場合、Moverは存在しないものを移動しようとする可能性があり、トラッカーはそれらを見つけられない可能性があります。ムーバーは、オブジェクトが移動するときにトラッカーのマップを直接変更して、それらが常に同期していることを保証する必要があるという感覚を覚えています。ただし、ムーバーがトラッカーの変更を開始する場合、トラッカーは独自のクラスである必要はありません。そして、私が理解している限り、2つを組み合わせるとSRPに違反します。

SRPを維持しながら、このデータベース設計をどのように改善できますか?

2
MagerBlutooth

システムが環境とどのように相互作用するかについて考える必要があります。それはあなたがあなたのことを考える必要があるということです 範囲境界 API。開発者はこれをどのように使用することになっていますか?

まず第一に、Databaseを使用するコードは、オブジェクトがどこにあるかを追跡する責任がありますか?いいえ、そうではありません。したがって、Databaseには、どこにあるかに関係なく、Locationにオブジェクトを配置できるAPIが必要です。

次に、Databaseはオブジェクトを見つけ、必要に応じて移動する必要があります。そのため、DatabaseにはTrackerが必要です。

ちょっと待ってください、lookupObject APIは間違っているようです。 Locationがわかりません。見つけたいです。オブジェクトを受け取り、Locationを取得する必要があります。


わかりました、Trackerの目的は何ですか?オブジェクトがLocationであるかを知ることです。それは同じことではありません。反対方向への誘導性です。 TrackerMap<Object, Location>のように機能します(実際、これがTrackerにラップされます)。


では、Databaseに戻りましょう。 Databaseはオブジェクトを見つけ、必要に応じて移動する必要があります。そのため、DatabaseにはTrackerが必要です。 Trackerは、Databaseに、オブジェクトがどこにあるかを示します。

オブジェクトは次のとおりです。

  • どこにもありません(新しいオブジェクトなので、addObjectを呼び出すだけです)。
  • 別のLocation(移動する必要があります。つまり、removeObjectaddObjectを呼び出します)。
  • 同じ場所(何もしない)。

したがって、Moverインターフェースは間違っています。データベースはaddObjectを単独で呼び出す必要がある場合がありますが、Moverではこれを許可していません。

Moverは必要ないと思います。 DatabaseLocationを直接処理できます。

そしてもちろん、DatabaseTrackerを更新する必要があります。


だから私たちは:

  • Location、オブジェクトを追加、削除できます。 (それはおそらくそれが持っているオブジェクトを知っています)。 Setです。
  • Tracker、それはオブジェクトがLocationで何であるかを知っています。 Map<Object, Location>です。
  • Database、オブジェクトの場所を設定できます。 内部的にTrackerを使用しますが、使用することを知っている必要はありません。

Trackerには明確な責任があります。それも良いことです。間違いありません。

Locationの実装は、システムの外部にある可能性があります。それらの実装が変更された場合、それを使用するコード(Database)を変更する必要はありません。

LocationDatabaseを変更する理由にはなりません。何らかの理由でLocationの実装が変更された場合、最悪の場合、「場所」インターフェースへのアダプターが必要になります。それでも、Databaseは変更されませんでした。実際、Locationインターフェースは、Databaseから何か他のものが必要な場合に変更される可能性があります。

失敗する条件について心配する必要があります。設計どおり、Locationインターフェースは、失敗しないように装っています。


最後に、問題全体は次のとおりです。Databaseは2つのことを行う必要があります。

  • Locationに話しかける
  • Trackerに話しかける

上記を読むと、「場所」がDatabaseを変更する理由ではないことがわかります。したがって、これはSRPを壊すものではありません。


ああ、待って、ひねりがあります。 DatabaseはどのようにTrackerと通信しますか。基本的には、「場所」と同じことを行う必要があります。 操作を何らかの形でミラー化する必要があります。共通のインターフェースがあれば簡単になります。

仮に、Moverの概念を復活させます。オブジェクトを追加したい場合、古いLocationはnullであるとしましょう。 コードにnullチェックを追加する必要があります。

実際、同じAPIを使用して削除することができます。 これについては話しませんでした。この場合、新しいLocationはnullになる可能性があります。

まあ、まったく同じAPIがTrackerにも役立ちます。実際、updateTrackermoveObjectの署名は同じであることに注意してください。そのシグネチャを持つメソッドを公開するインターフェイスを作成できます...

現在、Databaseは、そのインターフェースを実装するオブジェクトのリストと通信しています。

もちろん、Trackerは特別です。なぜなら、このおもちゃシステムの場合、オブジェクトがどこにあるかについての唯一の真の情報源だからです。


ちなみに、オブジェクトをファイルに永続化するLocationの実装がある場合、実行の開始時にLocationにはすでにオブジェクトがあります。それに応じてTrackerを設定する必要があります。つまり、どのオブジェクトがあるかを知ることはLocationの懸念事項です。これは、Locationがオブジェクトのリストを提供することを意味します。これは、Locationがオブジェクトの作成を担当することになります...これは、設計にレンチを投げます。もう1つは、ファイルにアクセスする複数のプロセスからのものです。それは問題を超えているので、そこに行かないでください。しかし、これは、システムが環境とどのように相互作用するかについて考える必要があるということ、つまり、 インターフェース境界 システムの範囲。

3
Theraot