Today I had an epiphany, and it was that I was doing everything wrong. Some history: I inherited a C# application, which was really just a collection of static methods, a completely procedural mess of C# code. I refactored this the best I knew at the time, bringing in lots of post-college OOP knowledge. To make a long story short, many of the entities in code have turned out to be Singletons.
Today I realized I needed 3 new classes, which would each follow the same Singleton pattern to match the rest of the software. If I keep tumbling down this slippery slope, eventually every class in my application will be Singleton, which will really be no logically different from the original group of static methods.
I need help on rethinking this. I know about Dependency Injection, and that would generally be the strategy to use in breaking the Singleton curse. However, I have a few specific questions related to this refactoring, and all about best practices for doing so.
-
How acceptable is the use of static variables to encapsulate configuration information? I have a brain block on using static, and I think it is due to an early OO class in college where the professor said static was bad. But, should I have to reconfigure the class every time I access it? When accessing hardware, is it ok to leave a static pointer to the addresses and variables needed, or should I continually perform
Open()andClose()operations? -
Right now I have a single method acting as the controller. Specifically, I continually poll several external instruments (via hardware drivers) for data. Should this type of controller be the way to go, or should I spawn separate threads for each instrument at the program’s startup? If the latter, how do I make this object oriented? Should I create classes called
InstrumentAListenerandInstrumentBListener? Or is there some standard way to approach this? -
Is there a better way to do global configuration? Right now I simply have
Configuration.Instance.Foosprinkled liberally throughout the code. Almost every class uses it, so perhaps keeping it as a Singleton makes sense. Any thoughts? -
A lot of my classes are things like
SerialPortWriterorDataFileWriter, which must sit around waiting for this data to stream in. Since they are active the entire time, how should I arrange these in order to listen for the events generated when data comes in?
Any other resources, books, or comments about how to get away from Singletons and other pattern overuse would be helpful.
Alright, here’s my best shot at attacking this question:
(1) Statics
The Problem with
staticthat you may be having is that it means different things in .NET and say, C++. Static basically means it’s accessible on the class itself. As for it’s acceptability id say it’s more of something you’d use to do non-instance specific operations on a class. Or just general things likeMath.Abs(...). What you should use for a global config is probably a statically accessed property for holding the current/active configuration. Also maybe some static classes for loading/saving setting the config, however the config should be an Object so it can be passed around manipulated, etc.public class MyConfiguration
{
public const string DefaultConfigPath = “./config.xml”;
(2) Controller/Hardware
You should probably look into a reactive approach,
IObserver<>orIObservable<>, it’s part of the Reactive Framework (Rx).Another approach is using a ThreadPool to schedule your polling tasks, as you may get a large number of threads if you have a lot of hardware to pool. Please make sure before using any kind of Threading to learn a lot about it. It’s very easy to make mistakes you may not even realize. This Book is an excelent source and will teach you lots.
Either way you should probably build services (just a name really) for managing your hardware which are responsible for collecting information about a service (essentially a model-pattern). From there your central controller can use them to access the data keeping the program logic in the controller, and the hardware logic in the service.
(3) Global Configuration
I may have touched this subject in point #1 but generally that’s where we go, if you find yourself typing too much you can always pull it out of there assuming the
.Instanceis an object.(4) Listening For data
Again the reactive framework could help you out, or you could build up an event-driven model that uses triggers for incoming data. This will make sure you’re not blocking on a thread till data comes in. It can reduce the complexity of your application greatly.