Take the following instance for example:
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; }
}
And there is a CreditCardApplicationEvaluator class
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 is an enum.
Now as you can see there are certain private const defined inside CreditCardApplicationEvaluator class. In a real world application these values will most probably come from a database.
So the question is where do I make the database call to set these constants?
Inside the constructor? Or within Evaluate
method?
Should I create another class Thresholds
with the properties such as AutoReferralMaxAge
etc and pass it on to Evaluate
method as follows:
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);
}
Or should I just place unitOfWork.CreditcardRepository.GetThresholds()
inside Evaluate
method of CreditCardApplicationEvaluator class, or perhaps it's constructor?
Calling database inside a constructor is probably a bad idea as pointed out by @Greg. So now what is left unanswered is that should I :
- Have unitOfWork.CreditcardRepository.GetThresholds() inside main()
- or within Evaluate() method of CreditCardApplicationEvaluator
class
Best Answer
Constructors should not do work. The initialization of a new object should happen very quickly, and making database calls, or interacting with any resource outside of the current process, can take considerably longer.
Instead, your constructor should either require the data from the database as separate arguments, so that the database calls are made before creating the new object, or move that logic into an instance method.
Constructors should be limited to allocating new memory for the new object (like initializing empty collections or assigning default values to required fields) and ensuring the object is in a valid state upon first being created.
Anything beyond that belongs in an instance method, or arguments passed into the constructor.
To be specific to your use case, the three "const" integers should be arguments to the constructor. Furthermore, the income threshold is actually a good candidate for a new class, because you don't want a high threshold of 20,000 and a low threshold of 100,000.
Then your CreditCardApplicationEvaluator class becomes a little easier to construct with valid data:
The initialization of these objects in conjunction with a unit of work or repository ends up like this: