问题
I want to have a global/app level setting that I want to use in my rich domain models.
I have a method that does some calculations based on the argument passed.
This might not be the best example and might not look like a method belonging to the domain model but I try to keep it simple so you could understand the problem.
public decimal Calculate(CalculationMethod calculationMethod)
{
switch (calculationMethod)
{
case CalculationMethod.Multiply:
return _x * _y; // _x and _y are fields
case CalculationMethod.Divide:
return _x / _y;
}
}
Now let's say I have many methods like this in my other domain models that also accept CalculationMethod
in their methods. I would like to have a global setting so I can set a calculation method globally so it could be used by all methods that take it as a parameter.
One solution would be to read the configuration every time I call this method.
I wonder if there's any better way so I can set CalculationMethod
globally and never pass it and instead, having some kind of static variable(singleton) that holds the calculation method and reading it directly inside my methods without passing it. But I think there would be a problem of thread-safety then.
public decimal Calculate()
{
// or read it from file here?
switch (GlobalSettings.CalculationMethod)
{
case CalculationMethod.Multiply:
return _x * _y; // _x and _y are fields
case CalculationMethod.Divide:
return _x / _y;
}
}
I can't pass it in the constructor because it's not something that belongs to my domain model.
How to approach this kind of problem? Is there any way better than the two I mentioned?
I asked this question in the comment under Mark Seemann's answer here: App-level settings in DDD?
回答1:
As Clean Code explains, passing flags to methods is generally considered sub-optimal design. I understand that the OP is a stand-in for another, more complicated problem, but I'd be inclined to recommend to refactor to a polymorphic object model:
public interface ICalculator
{
decimal Calculate();
}
You can now define implementations as required:
public class Multiplier : ICalculator
{
public decimal Calculate()
{
return _x * _y; // _x and _y are fields
}
}
public class Divider : ICalculator
{
public decimal Calculate()
{
return _x / _y;
}
}
You can inject an ICalculator
object into any class that has a need for it, using Constructor Injection. In your Composition Root, you can read a configuration file, or in some other way decide which implementation to use, and then create only a single instance of that class. This gives the object Singleton lifetime, so that the calculation method is entirely selected at startup time, and shared among all objects in the application.
来源:https://stackoverflow.com/questions/52327508/using-app-level-settings-in-rich-domain-models