web-dev-qa-db-ja.com

クラスのコンストラクタまたはメソッドでデータベースを呼び出す必要がありますか?

たとえば、次のインスタンスを例にとります。CreditCardApplication class

_ public class CreditCardApplication
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
        public decimal GrossAnnualIncome { get; set; }
        public string FrequentFlyerNumber { get; set; } 
    }
_

そしてCreditCardApplicationEvaluatorクラスがあります

_public class CreditCardApplicationEvaluator
    {
        private const int AutoReferralMaxAge = 20;
        private const int HighIncomeThreshhold = 100_000;
        private const int LowIncomeThreshhold = 20_000;

        public CreditCardApplicationDecision Evaluate(CreditCardApplication application)
        {
            if (application.GrossAnnualIncome >= HighIncomeThreshhold)
            {
                return CreditCardApplicationDecision.AutoAccepted;
            }

            if (application.Age <= AutoReferralMaxAge)
            {
                return CreditCardApplicationDecision.ReferredToHuman;
            }

            if (application.GrossAnnualIncome < LowIncomeThreshhold)
            {
                return CreditCardApplicationDecision.AutoDeclined;
            }

            return CreditCardApplicationDecision.ReferredToHuman;
        }       
    }
_

CreditCardApplicationDecisionは列挙型です。

ご覧のとおり、特定のプライベートconstがCreditCardApplicationEvaluatorクラス内で定義されています。実際のアプリケーションでは、これらの値はおそらくデータベースから取得されます。

だから問題は、これらの定数を設定するためにデータベース呼び出しをどこで行うのですか?コンストラクタの内部?またはEvaluateメソッド内ですか?

次のように、Thresholdsetcなどのプロパティを使用して別のクラスAutoReferralMaxAgeを作成し、Evaluateメソッドに渡す必要があります。

_static void Main(string[] args)
{
   //CreditCardApplication instance creation code goes here... 

   Thresholds thresholds =new Thresholds();
   thresholds = _unitOfWork.CreditcardRepository.GetThresholds();
   CreditCardApplicationEvaluator eval=new CreditCardApplicationEvaluator();
  var decision = eval.Evaluate(creditCardApplication,thresholds);
}
_

それとも、_CreditCardApplicationEvaluatorクラスのEvaluateメソッド内にunitOfWork.CreditcardRepository.GetThresholds()を配置するだけですか、それともコンストラクタですか?


@Gregが指摘しているように、コンストラクター内でデータベースを呼び出すことはおそらく悪い考えです。だから今答えられないままになっているのは、

  1. Main()内にunitOfWork.CreditcardRepository.GetThresholds()がある
  2. またはCreditCardApplicationEvaluatorクラスのEvaluate()メソッド内
4
SamuraiJack

コンストラクタは機能すべきではありません。新しいオブジェクトの初期化は非常に迅速に行われる必要があり、データベース呼び出しを行ったり、現在のプロセス外のリソースと対話したりするには、かなり時間がかかる場合があります。

代わりに、コンストラクターは、データベースからのデータを個別の引数として要求し、新しいオブジェクトを作成する前にデータベース呼び出しが行われるようにするか、そのロジックをインスタンスメソッドに移動する必要があります。

コンストラクターは、新しいオブジェクトに新しいメモリを割り当てること(空のコレクションの初期化や必須フィールドへのデフォルト値の割り当てなど)と、オブジェクトが最初に作成されたときにオブジェクトが有効な状態であることの確認に限定する必要があります。

それを超えるものは、インスタンスメソッド、またはコンストラクタに渡される引数に属します。

ユースケースに固有の3つの「const」整数は、コンストラクターの引数である必要があります。さらに、収入のしきい値は実際には新しいクラスの良い候補です。なぜなら、20,000の高いしきい値と100,000の低いしきい値は必要ないからです。

public class IncomeThreshhold
{
    public int Low { get; }
    public int High { get; }

    public IncomeThreshhold(int low, int high)
    {
        if (low < 0)
            throw new ArgumentOutOfRangeException();

        if (high < 0)
            throw new ArgumentOutOfRangeException();

        if (high < low)
            throw new ArgumentOutOfRangeException();

        Low = low;
        High = high;
    }
}

次に、有効なデータを使用してCreditCardApplicationEvaluatorクラスを構築するのが少し簡単になります。

public class CreditCardApplicationEvaluator
{
    private int AutoReferralMaxAge { get; }
    private IncomeThreshhold IncomeThreshhold { get; }

    public CreditCardApplicationEvaluator(int autoReferralMaxAge, IncomeThreshhold incomeThreshhold)
    {
        if (autoReferralMaxAge < someValueProbablyDrivenByRegulations)
            throw new ArgumentOutOfRangeException();

        AutoReferralMaxAge = autoReferralMaxAge;
        IncomeThreshhold = incomeThreshhold;
    }

    public CreditCardApplicationDecision Evaluate(CreditCardApplication application)
    {
        if (application.GrossAnnualIncome >= IncomeThreshhold.High)
        {
            return CreditCardApplicationDecision.AutoAccepted;
        }

        if (application.Age <= AutoReferralMaxAge)
        {
            return CreditCardApplicationDecision.ReferredToHuman;
        }

        if (application.GrossAnnualIncome < IncomeThreshhold.Low)
        {
            return CreditCardApplicationDecision.AutoDeclined;
        }

        return CreditCardApplicationDecision.ReferredToHuman;
    }
}

作業単位またはリポジトリと組み合わせたこれらのオブジェクトの初期化は、次のようになります。

static void Main(string[] args)
{
    var incomeThreshhold = unitOfWork.CreditcardRepository.GetThreshold(...);
    var autoReferralMaxAge = unitOfWork.CreditcardRepository.GetAutoReferralMaxAge(...);
    var evaluator = new CreditCardApplicationEvaluator(autoReferralMaxAge, incomeThreshhold);
    var application  unitOfWork.CreditcardRepository.GetApplication(...);
    var decision = evaluator.Evaluate(application);
}
7
Greg Burghardt

オブジェクトCreditCardApplicationEvaluatorをインスタンス化するとき、アプリケーションを評価する準備ができている必要があります。データベースからこのデータをプルできない場合を考えなければなりません。したがって、これらの定数のdb呼び出しをコンストラクターに配置すると、コンストラクターが戻らない場合があるため、オブジェクトをインスタンス化したり、失敗したりすることはありません。

また、コンストラクターに入れないもう1つの理由は、データベースにアクセスするすべてのインスタンス化です。これは頻繁にデータベースでデータが更新されないと思いますか?

また、db呼び出しは周辺にあり、ビジネスロジックに密接に結合されていない必要があります。これらの定数をどこから取得するかを変更したい場合はどうなりますか?たぶん、あるデータソースからのデータと、別のデータソースからのデータが必要になるのでしょうか。エバリュエータークラスは、一部のデータベースに依存するべきではありません。

1