Singleton Overuse Refactoring

Today I had an epiphany, and I did everything wrong. Some story: I inherited a C # application, which was really just a collection of static methods, a completely procedural mess of C # code. I reorganized this best that I knew at that time, bringing a lot of OOP knowledge after studying. In short, many entities in the code are Singletons.

Today I realized that I needed 3 new classes, each of which would follow one Singleton pattern to fit the rest of the software. If I continue to crumble along this slippery slope, in the end, each class in my application will be Singleton, which really will not logically differ from the original group of static methods.

I need help rethinking this. I know about Injection Dependency, and that would usually be the strategy used to break the Singleton curse. However, I have a few specific questions related to this refactoring, and all about best practices for this.

  • How acceptable is using static variables to encapsulate configuration information? I have a brain block on the use of statics, and I think this is due to the early class of OO in college, where the professor said that static was bad. But do I need to reconfigure the class every time I access it? When accessing hardware, is it possible to leave a static pointer to the addresses and variables it needs, or to constantly perform Open() and Close() operations?

  • Right now I have one method acting like a controller. In particular, I constantly poll several external tools (through hardware drivers) for data. Should this type of controller be a way, or should I create separate threads for each tool when the program starts? If the latter, how do I make this object oriented? Should I create classes named InstrumentAListener and InstrumentBListener ? Or is there some standard way to approach this?

  • Is there a better way to do a global configuration? Right now I just have Configuration.Instance.Foo distributed all over the code. Almost every class uses it, so maybe saving it as a singleton makes sense. Any thoughts?

  • A lot of my classes are things like SerialPortWriter or DataFileWriter , which should sit next to DataFileWriter other, waiting for that data to be broadcast. Since they are active all the time, how should I arrange them in order to listen to events generated during data entry?

Any other resources, books or comments on how to get away from singletones and other usage patterns are useful.

+45
c # oop design-patterns
May 27 '10 at 22:24
source share
7 answers

Ok, here is my best shot at attacking this question:

(1) Statics

The problem with static that you may have is that in .NET it means different things and they say C ++. Static basically means that it is available to the class itself. As for acceptability , this suggests what you use most to perform individual operations on the class. Or just common things like Math.Abs(...) . What you should use for global configuration is probably a statically available property for storing the current / active configuration. In addition, it is possible that some static classes for loading / saving specify the configuration, however, config should be an object, so it can be passed using manipulation, etc. public class MyConfiguration {public const string DefaultConfigPath = "./config.xml";

  protected static MyConfiguration _current; public static MyConfiguration Current { get { if (_current == null) Load(DefaultConfigPath); return _current; } } public static MyConfiguration Load(string path) { // Do your loading here _current = loadedConfig; return loadedConfig; } // Static save function //*********** Non-Static Members *********// public string MyVariable { get; set; } // etc.. } 

(2) Controller / Hardware

You should probably take a look at the reactive approach, IObserver<> or IObservable<> , this is part of the Reactive Framework (Rx) .

Another approach is to use ThreadPool to schedule your survey tasks, as you can get a large number of threads if you have a lot of equipment to combine. Before using any type of threading, make sure you learn a lot about it. It is very easy to make mistakes, you cannot even understand. This book is a great source and will teach you a lot.

In any case, you should probably create services (just a name) to manage your equipment, which is responsible for collecting information about the service (essentially a model model). From there, your central controller can use them to access data that supports software logic in the controller, and hardware logic in the service.

(3) Global configuration

Perhaps I touched on this topic in paragraph number 1, but usually when we go, if you type too much text, you can always get it out of it, assuming that the .Instance object is an object.

 MyConfiguration cfg = MyConfiguration.Current cfg.Foo // etc... 

(4) Listening to data

Again, a reactive framework can help you, or you can create an event-driven model that uses triggers for incoming data. This will allow you not to block the flow until data arrives. This can significantly reduce the complexity of your application.

+12
May 27 '10 at 22:51
source share

for starters, you can restrict the use of singleton with the "Registry" template, which actually means that you have one singleton that allows you to get into a bunch of other preconfigured objects.

This is not a ā€œfixā€, but an improvement; it makes many objects that are singletones a little more normal and verifiable. for example ... (a completely contrived example)

 HardwareRegistry.SerialPorts.Serial1.Send("blah"); 

but the real problem is that you are struggling with creating a set of objects that work great together. OO has two types of steps: setting up objects and the ability of objects to do their job.

so maybe see how you can configure non singleton objects to work together and then hang them from the registry.

Static: -

There are many exceptions to the rules here, but in general, avoid this, but it is useful for creating single games and creating methods that perform "general" calculations outside the context of an object. (e.g. Math.Min)

Data Monitoring: -

it is often better to do it, as you hint, to create a stream with a bunch of pre-configured objects that will perform your monitoring. Use messaging to communicate between threads (through a thread safe queue) to limit thread blocking issues. Use the registry template to access hardware resources.

you want something like InstrumentListner that uses InstrumentProtocol (which you subclass for each protocol) I failed, LogData. The command template can be useful here.

Configuration: -

enter your configuration information and use something like a ā€œbuilderā€ template to translate your configuration into a set of objects configured in a certain way. those. do not inform your classes about customization; create an object that customizes objects in a specific way.

Serial Ports: -

I do a bunch of work with them, I have a serial connection that generates a character stream, which it puts as an event. Then I have something that interprets the protocol flow in meaningful commands. My protocol classes work with a common "IConnection", from which SerialConnection inherits ..... I also have TcpConnections, MockConnections, etc., To be able to enter test data or sequentially connect serial ports from one computer to another, etc. .d. Protocol classes simply interpret the stream, have a statemachine command and send commands. The protocol is preconfigured using Connection, various things are registered in the protocol, so when it has meaningful data, they will start and do their job. All this is built from the configuration at the beginning or rebuilt on the fly if something changes.

+4
May 27 '10 at 10:38 p.m.
source share

Since you know about Injection Dependency, do you consider using an IoC container to manage lifetime? See my answer to the question about static classes.

+1
May 28 '10 at 2:25
source share
  • You (OP) seem to be preoccupied with OO design, well, I will say this while thinking about the things of static variables. The basic concept is encapsulation and reuse; then you could take care of reuse, but you almost always want encapsulation. If it is a static variable, it really is not encapsulated, is it? Think about who needs to access it, why and how far you can Hide it from client code. Good projects can often change the insides without significant damage to customers, that's what you want to think about . I agree with Scott Myers (Effective C ++) about a lot. OOP goes beyond the class keyword. If you've never heard of this, find the properties: yes, they can be static, and C # has a very good way to use them. In contrast to the literal use of a static variable. As I hinted at the beginning of this element of the list: think about how not to shoot yourself in the leg later, since the class changes over time, that there are not many programmers when designing classes.

  • Take a look at this Rx medium that someone mentioned. The threading model used for the situation you described cannot be easily resolved without additional information about using IMHO. Make sure you know what you are doing with threads. Many people cannot understand the threads to save their lives; this is not so difficult, since tread protection can be when the code is (re) used. Remember that controllers often need to be separated from objects that they control (for example, more than one class); if you don’t know, look at the MVC book and buy a gang of four.

  • Depends on what you need. For many applications, a class that is almost completely filled with static data is good enough; like singleton for free. This can be done very OO. Sometimes you prefer to have multiple instances or play with an injection, which makes it more difficult.

  • I offer topics and events. The ease of creating code driven events is actually one of the best things about C # IMHO.

Hmm, killing loners ...

In my experience, many of the most common uses that young programmers put on their own are nothing more than the loss of a class keyword. Namely, what they meant as a module with state, pumped into the highlander class; and there are some bad singleton implementations that you can find. Whether this is because they were unable to find out what they are doing, or just Java in college, I don’t know. Returning to Earth, he named the use of data in the file field and provided an API. In C # (and Java) you are attached to the fact that it is a class in more than several languages. OOP! = Class keyword; study lhs well.

A well-written class can use static data to efficiently inject a singleton and force the compiler to do footwork while preserving it, or as one when you ever get something. Do NOT replace inherited singletones if you don’t seriously know what you are doing. Inadequate inheritance of such things results in more fragile code that waaaay knows well. Classes must be dumb, data is smart. That sounds silly if you don't look deep into that expression. Using inheritance IMHO for such a thing is usually bad (tm), languages ​​have the concept of modules / packages for some reason.

If you want it, hey, did you translate it into annuals a long time ago? Sit down and think a little bit: how can I better structure this application to make it work in an XXX way, and then think about how this does the XXX way of influencing things, for example, does it one of the ways that will become a source of disagreement among the threads? In an hour you can go through a lot of things. When you get older, you will learn the best methods.

Here is one suggestion for XXX to start: (visualize) write (^ Hing) a controller controller class that works as a manager on the objects it refers to. These objects were your singles, not a controller, and they are just examples of these classes. This is not the best design for many applications (especially it may be a problem in heavily threaded IMHO), but in general it will solve what motivates most young people to reach one single, and it will be effectively used for a wide range of programs. This is uh, like the CS 102 design pattern. Forget the singleton you learned in CS 607.

This control class, perhaps an ā€œApplicationā€ would be more appropriate;) basically solves your need for single games and for storing the configuration. How to do this with the sublime OO method (if you understand OOP) and not take your foot off (again) is an exercise for your own education.

If this shows, I am not a fan of the so-called singleton pattern, especially as it is often misused. Moving a code base from it often depends on how much refactoring you are ready to use. Singletones are similar to global variables: convenient, but not oily. Hmm, I think I’ll put this in my quotes file, it will be a nice phrase with it ...

Honestly, you know more about the code base and the application in question, and then about someone here. Therefore, no one can design it, and the tips say less than the action, at least where I come from.

+1
May 28 '10 at 7:46 a.m.
source share

I limit myself to no more than two singleton in the application / process. One of them is usually called SysConfig and contains things that could otherwise become global variables or other corrupt concepts. I do not have a name for the second, since so far I have never reached my limit .:-)

Static member variables have their uses, but I view them as I consider proctologists. Lifeguard, when you need one, but the chances should be ā€œmillion to oneā€ (Seinfeld’s link) that you cannot find a better way to solve the problem.

Create a base tool class that implements a stream listener. Derived classes that will have specific tool drivers, etc. Create a derived class for each tool, then save the object in some kind of container. Simply clean the container during cleaning. Each instance of the instrument should be constructed by transmitting to it some registration information about where to send its output / status / whatever. Use your imagination here. OO material is becoming quite powerful.

0
May 27, '10 at 22:40
source share

I recently had to solve a similar problem, and what I did seemed to work well for me, maybe this will help you:

(1) Group all the ā€œglobalā€ information into one class. Let me call it Configuration .

(2) For all classes that used these static objects, change them (ultimately) inherit from a new abstract base class that looks something like

 abstract class MyBaseClass { protected Configuration config; // You can also wrap it in a property public MyBaseClass(Configuration config) { this.config = config; } } 

(3) Modify all class constructors derived from MyBaseClass accordingly. Then just create one instance of Configuration at startup and pass it everywhere.

Minuses:

  • You need to reorganize many of your constructors and each place in which they are called
  • This will not work unless you get top-level classes from Object. Well, you can add the config field to the derived class, it will be less elegant.

Arguments

  • There is not much effort to just change the inheritance and the constructors and bang - you can switch all Configuration.Instance to config .
  • You completely get rid of static variables; so no problem now if, for example, your application suddenly turns into a library, and someone is trying to call several methods or something else at the same time.
0
May 27 '10 at 23:25
source share

Great question. A few quick thoughts from me ...

static in C # should only be used for data that is exactly the same for all instances of this class. Since you're currently stuck in Singleton's Athena, you only have one instance of everything, but once you get out of this, this is a general rule (at least this is for me). If you start chopping your classes, you can opt out of static use, because then you have potential concurrency problems, but this is something that can be solved later.

I'm not sure how your equipment actually works, but assuming there are some basic functions that are the same for everyone (for example, how you interact with them at the raw data level or similar), then this is an ideal instance for creating a class hierarchy. The base class implements low-level / similar material with virtual methods for descendant classes to override to really interpret the data correctly / feed them forward / independently.

Good luck.

0
May 28 '10 at 2:33 a.m.
source share



All Articles