I have an external thermometer connected via USB that is controlled by my SW. Different parts of my system will use it but never at the same time (all in one thread). However, it is a single device communicating over an established connection so for some reason I think either its method (GetTemperature) should be static or I could do it as Singleton.
I will be happy for suggestions. Thanks
2
No, it’s not a good candidate.
-
What if you buy a second thermometer in order to get the temperature in two different locations?
-
What if you want to create a mock or a stub of your thermometer in order to unit test parts which rely on it?
With singleton, you’ll end up doing something like:
public class TemperatureRealTimeDisplay
{
// Show the current temperature to the user.
public void Show()
{
var temperature = Thermometer.Instance.Measure();
...
}
}
public class TemperatureChart
{
// Record the temperature in order to display a chart over time.
public void OnTimer()
{
var temperature = Thermometer.Instance.Measure();
...
}
}
...
var display = new TemperatureRealTimeDisplay();
var chart = new TemperatureChart();
This code:
-
Cannot be tested, since it relies on the thermometer which cannot be replaced by a mock or a stub,
-
Is difficult to maintain or reuse given its dependency on the actual thermometer.
If, on the other hand, you use instances and Dependency Injection:
public interface IProbe<TResult>
{
public TResult Measure();
}
public class HardwareThermometer : IProbe<Temperature>
{
...
}
public class RealTimeDisplay<TMetric>
{
public RealTimeDisplay<TMetric>(IProbe<TMetric> probe)
{
this.probe = probe;
}
public void Show()
{
var current = this.probe.Measure();
...
}
}
public class Chart<TMetric>
{
public Chart<TMetric>(IProbe<TMetric> probe)
{
this.probe = probe;
}
public void OnTimer()
{
var current = this.probe.Measure();
...
}
}
...
var thermometer = new HardwareThermometer();
var temperatureDisplay = new RealTimeDisplay(thermometer);
var temperatureChart = new Chart(thermometer);
you remove the dependencies, making it possible to use general classes RealTimeDisplay
and Chart
which can then be reused for anything else, like measuring the CPU load or the number of WTFs per second during a coding review. With:
public class ProbeStub : IProbe<int>
{
...
}
they can easily be tested as well.
Can’t you use both dependency injection and singleton? You can, but the point of using a singleton is to being able to use the instance anywhere without having to pass it through parameters down the chain. In my example, you only instantiate thermometer
once, making a singleton useless.
The fact that the singleton guarantees you that only one instance will ever be created is not particularly useful in my example. RealTimeDisplay
and Chart
already receive an instance of a thermometer, so the incentive to create another thermometer is low. The only remaining risk is to end up with code like this in the entry point of the application:
var temperatureDisplay = new RealTimeDisplay(new HardwareThermometer());
var temperatureChart = new Chart(new HardwareThermometer());
but it’s unlikely that a maintainer will do such change.
12
You can inject a singleton just fine. Don’t understand why people are saying don’t use it. A singleton is just something guaranteed to have one and only one instance. Using a singleton doesn’t rule out injection.
In Java the recommended way to implement is to use an Enum. Again very injectable.
Have it implement a common interface so that more thermometers can be added/or replaced at a latter date/easily mocked.
public enum Thermometer implements IThermometer{
DEFAUlT;
public BigDecimal getTemperature(){
return null;
}
}
and then just use constructor injection (or your DI framework of choice) :
public class MyService{
public MyService(IThermometer thermometer){
this.thermometer - thermometer;}
}
// do some stuff that uses thermometer.getTemperature();
}
Injecting like this, using an interface, means you can easily change the code at a later date to use something that it is not a singleton, or is a the old fashioned getInstance() singleton.
3
From a maintainability and code reuse stand point I’d say no, however that assumes a lifetime to the project.
While you could use a Singleton here, adding more thermometers would become messy and difficult. Using RAII is definitely the way to go, and Singleton does that, but I’d still make the thermometer instance a non-global object.
If the code will live longer than the immediate task, it would be more useful to build a class which encapsulates all of the thermometer communication, as you would with a singleton, but then wrap/store that inside of a singleton. This is a little more complicated, but makes it much easier to reuse the code later and expand into having more thermometers.
So I just bought this USB hub with seven ports. And I had this great idea to put a thermometer into each room of my home. The problem is… there’s a singleton object!
The other problem is that other people don’t have a thermometer at all. In that case a singleton is one object too many.