What's a good place to put pure functions that have connections to a system?
public class Core {
System system;
}
public class System {
SubSystem subSystem;
// subSystem.Multiply(a, b);
}
public class SubSystem {
public float Multiply(float a, float b) { return a * b; }
}
In this case, the SubSystem
has a strong connection to the System
. The functions within the SubSystem
are pure/stateless.
The problem: If there is no state, why bother with the instance when a static class could do the same job?
Essentially it's Core.system.subSystem.Multiply(a, b);
over SubSystem.Multiply(a, b);
And, sure we can go with static classes. But what happens when you have multiple systems with multiple static subsystems spread around the project? The connection is lost, and it quickly becomes a mess.
The way I see it, there are the following options:
Core.system.subSystem.Multiply(a, b); // instances
SubSystem.Multiply(a, b); // static
SystemUtils.SubSystem.Multiply(a, b); // with namespace
But which one is best? And is there a better way?
-
If subsystems are stateles why would you want to have many of them?Stop harming Monica– Stop harming Monica2018年10月22日 13:18:34 +00:00Commented Oct 22, 2018 at 13:18
-
Sub-system may not be the correct word for it. It's all about separating functions into logical chunks that have some connection to the data that it modifies.Iggy– Iggy2018年10月22日 14:11:26 +00:00Commented Oct 22, 2018 at 14:11
-
Whatever you prefer to call it, if it is stateless why would you want to have many of them? And nothing in your question seems to be about modifying data.Stop harming Monica– Stop harming Monica2018年10月22日 16:58:39 +00:00Commented Oct 22, 2018 at 16:58
-
Subsystem doesn't have any reference to System, as you imply. You don't need instances for pure methods.Frank Hileman– Frank Hileman2018年10月22日 22:27:49 +00:00Commented Oct 22, 2018 at 22:27
1 Answer 1
The problem: If there is no state, why bother with the instance when a static class could do the same job?
Objects can be developed using interfaces and thus mocked. Static classes cannot implement interfaces.
On a related note, objects can be passed from one method to the other, but static classes cannot.
If there is no state, why bother with the instance when a static class could do the same job?
"no state" and "static" are not equivalent terms.
It is true that static classes and methods entail not having a state (though you can argue that static properties are still a form of state - albeit a global one).
It is not true that anything that doesn't use a state must therefore be static. Taking your example of a multiplication method, it's perfectly possible to create several classes which can perform this function:
NormalMultiplier
uses the simplea * b
approach.WolframAlphaMultiplier
asks Wolfram Alpha for the result.WindowsMultiplier
relies on the Windows Calculator to find the result.
It seems a bit silly for multiplication, but it makes more sense for e.g. getting an accurate value of pi. Your local computer won't be able to calculate pi to as many decimal digits as Wolfram Alpha can (in a reasonable timeframe); but then again, if you only need a handful of decimal digits, you don't want to have to rely on the network connection to Wolfram Alpha.
In all three cases, the Multiply(a,b)
method doesn't particularly requires a state; but how would you go about implementing these as static classes? You could do that, but then you make it impossible for these classes to have a common ancestor (e.g. Multiplier
) or implement the same interface (e.g. IMultiplier
).
This is where static classes fall apart. They are only useful in cases where there is exactly one implementation, never more, never less. And that's simply not always the case.
Indirectly, that also makes it impossible to mock static classes, which can be an issue when testing.
-
So, if you not gonna have different behaviour for
Multiply
function and you don't need to mock it for the tests(because function not depend on external resources) - you can use static method.Fabio– Fabio2018年10月22日 20:21:27 +00:00Commented Oct 22, 2018 at 20:21 -
The "pure" attribute is probably more important than hypothetical polymorphism or mocking. Pure methods are the best type of method you can create.Frank Hileman– Frank Hileman2018年10月22日 22:26:37 +00:00Commented Oct 22, 2018 at 22:26
-
@Fabio: Yes, although I disagree with yuor assertion that
you don't need to mock it for the tests(because function not depend on external resources)
Depending on external resources is irrelevant in regards to needing to test/mock something. For example, considerMyService
which depends onMultiplier
, with no external resources. Suppose I want to test howMyService
behaves when it receives a wrong multiplication result. I would then want to mock and inject aWrongMultiplier
to ensure that I can test this path, and thus I need to be able to mockMultiplier
.Flater– Flater2018年10月23日 06:29:11 +00:00Commented Oct 23, 2018 at 6:29 -
@FrankHileman: Probably more important indeed, but the purity of a method and its testability/mockability are not mutually exclusive. They are two independent considerations.Flater– Flater2018年10月23日 06:31:59 +00:00Commented Oct 23, 2018 at 6:31
-
@Fabio: Just to be clear: I'm aware that multiplication isn't the best example here as it's a mathematically proven operation that isn't liable to be changed at any point in the future, but the ability to test the behavior of
MyService
when its dependencies yield particular outcomes (correct result, wrong result, exception, ...) is a relevant test case, especially when the dependencies themselves are open to regressions or inaccuracies.Flater– Flater2018年10月23日 06:48:40 +00:00Commented Oct 23, 2018 at 6:48