web-dev-qa-db-ja.com

コードの抽象化は、SOLID Principlesの悪用です。

友人との未解決の議論の後、私はスタックオーバーフローコミュニティに質問することにしました。コードを抽象化しすぎるようなことはありますか?次のうちどれがより良いオプションです。私たちはさまざまなコーディング言語から来ているので、私はこれを彼と同じように説明しています。以下のすべてを、それぞれの言語が何をするかの要約として想定してください。

常に存在する重要な条件:

  • テーブルとデータベースは1つだけです
  • GET_ALLのみが存在します。部分的または特定のコレクションはありません。
  • FACTORY CLASSとHELPER CLASSは常に一緒に呼び出されます。


FACTORY CLASS {

  //This method will check cache and create a repository instance of the table if one does not exist
  createNewRepository();

  //This will collect the records from the instantiated repository  
  getAllFromRepository();

  //This will return the data that was collected   
  returnData; 
} 

または

FACTORY CLASS {

  //This method will check cache and create a repository instance of the table if one does not exist   
  createNewRepository(); 
}

HELPER CLASS {

  //This will collect the records from the instantiated repository  
  getAllFromRepository();

  //This will return the data that was collected   
  returnData; 
}

SOLIDの原則については、明らかに2人の見方が異なります。

これを達成するために必要な機能を含む「このファクトリークラスの責任は、dbからデータを取得すること」という意味で、クラスは単一の責任を持つべきだと思います。

彼は、「このファクトリクラスの責任はリポジトリを起動し、データを呼び出さないことです。ヘルパークラスがデータを呼び出すこと」という意味で、クラスは単一の責任を持つべきであると考えています。

データの呼び出し方法にさまざまなバリエーションがあった場合は彼に同意しますが、データは常にチェックされ、> createdIfNull> collectedであり、このプロセス全体が「使用するデータへのアクセス」の機能であるためです。それはシングルクラスですか?

3

私はこれについてあなたの敵を支持します。

  • 「データベースとテーブルロジックを作成する」ことは、技術的には「データベースからデータを取得する」ロジックとは明らかに別の責任です。

  • データベースの移行などを考慮すると、作成コードがかなり複雑になることを想像できます。別のファイルを正当化するのに十分なコード行。

  • 呼び出しコードを作成または取得のいずれかに制限したい場合があります

一般に、このような一般的なものを作成するための時間コストは判断が困難です。ある人はそれが面倒で長々としたものであると感じ、別の人はそれを標準として行い、より速く考えるかもしれません。

私の見解では、YAGNIは決して良い議論ではありません。それが正しく、完了しているか、または迅速に実行できる場合、その学問は、誰かがそれを必要としない可能性があると考えているかどうかを示します。

8
Ewan

「このファクトリクラスの責任は、データベースからデータを取得することです」

「この自動車メーカーを使って車で通勤する」と言っているようなものです。あなたではない。このメーカーが製造した自動車を使用して運転します。

車の責任はあなたが車を運転することです。自動車メーカーの責任は、自動車を製造することです。

どこから来たのか理解しました。私たちは皆、同じような「ショートカットの間違い」を一度に犯します。しかし、あなたの仮定は、リポジトリはすべてのデータを収集するためだけに使用されるという概念に依存しています

todayの場合はそうかもしれませんが、tomorrowの場合はそうではないかもしれません。

SOLIDは主に未来に焦点を当てています。 SOLIDを実装しても、(すでに機能している、SOLID以外の)コードはうまく機能しません。SOLIDを実装すると、実装が容易になります将来の変更

常に存在する重要な条件:

私があなたの主張を中心にすると、何も変わらないということを実装し、SOLIDを実装することは無関係です。

ただし、このような発言が行われるときは常に、話し手は状況が変化しないことを保証する立場にはありません。彼らは明日の観点からではなく、今日の観点からそれを見ています。
将来何が起こるかを常に知ることはできません。別のデータプロバイダーの使用を開始したり、2つのデータプロバイダーのデータをマージしたりする必要があるかもしれません...


そうは言っても、ヘルパーの目的が何であるかはよくわかりません。

  • このヘルパーのメソッドがリポジトリ自体の一部ではないのはなぜですか?
  • リポジトリメソッドを直接呼び出すことと、リポジトリメソッドを呼び出すヘルパーメソッドを呼び出すことの間に意味のある違いは何ですか?

これは、明確にするために質問から意図的に省略した詳細である場合があります。どちらの場合でも、私の答えは立っています。データを取得することと、データを取得するクラスをインスタンス化することは、2つのまったく別のものです。

石工はレンガの壁ではありません。自動車整備士は自動車ではありません。小麦農家は小麦ではありません。リポジトリファクトリはリポジトリではありません。

1
Flater

ファクトリがある場合は、オブジェクトの実装をそのコントラクトから切り離す必要があることがわかりました。工場はそれが得意です。この場合、依存関係はデータを取得するものに依存します。

ファクトリがデータの取得も担当している場合、全体のポイントは何でしたか?現在、依存関係はファクトリーにあります。工場のようには見えなくなります。静的リポジトリがあり、その静的リポジトリにアクセスするための静的メソッドがあります。このファクトリを使用するクラスはすべて、ファクトリがデータを取得する方法に結合されています。

目標は、キャッシュされたリポジトリをこのシステム全体で唯一の静的要素にすることです。この方法では、リポジトリ自体の動作は、それを要求する人には関係ありません。


これは、ファクトリの作成されたリポジトリを操作することで実行できます。

_// Creates and/or gets a cache of the repository
Repository repository = RepositoryFactory.GetRepository();

// Get all the data from the repository
data = repository.GetAllData();
_

Singleton Pattern を実装することでこれを行うことができます:

_class Repository 
{
    public static readonly Instance = new Repository();

    private Repository() {
        // Connect to the repository here.
    }
}
_

そしてそれに基づいて行動する:

_Repository repository = Repository.Instance;
data = repository.GetAllData();
_

Factory Method を使用してこれを行うことができます:

_class Repository {
    private Repository() {
       // Connect to repository here
    }

    public static Repository GetRepository() {
        if (cached) {
            return CachedRepository;
        }
        else {
            return new Repository();
        }
    }
}
_

そしてそれに基づいて行動する:

_Repository repository = Repository.GetRepository();
data = repository.GetAllData();
_

すべてのケースで依存関係を注入できますが、それを必要とするクラスに必要です。

NeedsTheData d = new NeedsTheData(Repository.Instance);

NeedsTheData d = new NeedsTheData(Repository.GetRepository());

また、ファクトリの場合、ファクトリはデカップリングを処理するファクトリでなければならないため、Repositoryクラスを挿入する必要はありません。

1
etberg

元のコード例は想像力に大きな影響を与えていると思います。

最初の例:

public class Repsitory<TModel>
{
    public TModel GetAll(Func<TModel, bool> where)
    {
        //Will retrieve the data based on the where clause from the db
    }
}

public class RepositoryFactory
{
    public TModel GetAll<TModel>(Func<TModel, bool> where)
    {
        return GetRepository<TModel>().GetAll(where);
    }

    private Repsitory<TModel> GetRepository<TModel>()
    {
        //Checks if the cache contains an instance of the Repository<TModel> and returns it

        //IF no instance exists will create a new instance and cache it

        //Returns the new instance
    }
}

2番目の例:

public class Repsitory<TModel>
{
    public TModel GetAll(Func<TModel, bool> where)
    {
        //Will retrieve the data based on the where clause
    }
}

public class RepositoryFactory
{
    public Repsitory<TModel> GetRepository<TModel>()
    {
        //Checks if the cache contains an instance of the Repository<TModel> and returns it

        //IF no instance exists will create a new instance and cache it

        //Returns the new instance
    }
}

public class RepositoryHelper
{
    public TModel GetAll<TModel>(Func<TModel, bool> where)
    {
        return RepositoryFactory.GetRepository<TModel>().GetAll(where);
    }
}
0
Gys Rademeyer