DEV Community

Generic Singletons

Andreas Jakof on October 07, 2019

When design patterns are concerned, the most commonly used might be the factory and the singleton. As a quick disclaimer: This works with C#, but I...
Collapse
 
thebuzzsaw profile image
Kelly Brown

Good.

Now never use singletons in your code. :D

Collapse
 
andreasjakof profile image
Andreas Jakof

Since I read the "Clean Code", I understand you better now.

Well, DI has it's own flaws (some errors only occure during runtime, which would have been compile time errors), but I am getting used to it and you could check for these errors in a separate unit test.

I still don't mind singletones, but you should not use static classes or objects you only get from a static class. Use Factories instead or DI, which handles the singleton problem for you.

I still like generics, though...

Collapse
 
thebuzzsaw profile image
Kelly Brown

Currently, the only true singletons I put up with are classes that are completely immutable with no configuration options or side effects. Even then, I'm hesitant to lock them down, but sometimes I am OK with hiding the constructor and exposing an Instance simply because creating new ones literally just wastes memory.

Collapse
 
andreasjakof profile image
Andreas Jakof

Now I am curious... Why?

Collapse
 
thebuzzsaw profile image
Kelly Brown

I can write many paragraphs in response, but I want to start small. The short version is that singletons offer incredibly low value and bring a fair number of problems along for the ride. I actively avoid singletons in all my code now.

Thread Thread
 
andreasjakof profile image
Andreas Jakof

I usually do too, but in my case, creating a new instance takes a considerable amount of time. Having it instantiated once and then just using it, will have quite an impact on performance, considering about 120000 items, that are to be used with it in a go. Adding 1/2 second each time sums up to about 17 hours, just for creating new instances... not even considering memory fragmentation, which causes other issues.

So in my case, it is a tradeoff beween two evils, chosing the lesser one.
And again, there is no general right or wrong.

I know, that one can achieve almost the same result, with a slightly different design without Singletons, but then again, I know that, others won't necessarily.

And to be honest: having a good documentation is not always enough to keep others from using it wrong, so creating it in a way, other just cannot use it wrongly, is again the lesser evil.

Thread Thread
 
thebuzzsaw profile image
Kelly Brown • Edited

The situation you are describing does not call for a singleton. This is a mistake I made early on in my career. It's completely normal and healthy to have situations where you need to reuse one instance instead of creating new ones. A singleton is so much more than that, though. A singleton is a heavy-handed approach that introduces so many new restrictions on your application.

If you need one instance... then make and share one instance! That's it! Sharing one instance does not mean you need to make it global, statically initialized, and (henceforth) completely invisible as a dependency in method signatures. You don't need to choose evil at all! Just code pragmatically.

I can't remember where I read it, but I really liked what someone said: you should only use a singleton for a subsystem where there literally cannot be more than one instance and/or allowing the user to instantiate that class would lead to danger/instability. There are very few things in programming that fall under this category.

When design patterns are concerned, the most commonly used might be the factory and the singleton.

Goodness, I hope not. How many singletons are you seeing in your code bases? :(

Thread Thread
 
andreasjakof profile image
Andreas Jakof

The situation you are describing does not call for a singleton.

I know, but I really want to enforce the usage of just ONE instance of a type. And experience told me, that I need to enforce it, otherwise it won't be used like that.

If you need one instance... then make and share one instance! That's it! ... Just code pragmatically.

I'd prefer that approach, too, but then again... I saw, how others in the team used a class, I designed to load encrypted credentials... and that told me a lot about, what I need to enforce and how they are going to use it. It was as if the two pages extensive step-by-step examples were not there at all. And I made it as simple as var creds = MyCreds.Load(Certificate, encryptedData);

When design patterns are concerned, the most commonly used might be the factory and the singleton.

Goodness, I hope not. How many singletons are you seeing in your code bases? :(

Okay, maybe not the most commonly used, but the most well known.
Actually, there currently are 10 Singletons all of the same "kind", encapsulating access to an Azure AD Group each. I like to have a name for something instead of a GUID. User.HasService<Android>() is better readable than User.HasService(someGuid).

Actually, the Singletons I described in my initial blog post, weren't singletons at first, but became such only afterwards, when I saw, that even I not always followed my own intention of using just ONE instance of it. So I made it, that I had to and the compiler just won't let me do it otherwise. I like the compiler, he is my friend and if I am doing it right, he can tell me, whether I did something wrong or not.

Thread Thread
 
thebuzzsaw profile image
Kelly Brown • Edited

I totally understand the desire to steer developers into the "pit of success". This is just an area I feel is more appropriate for something like a Roslyn Analyzer or at the very least code reviews. (And again, your blog post was good. I just had to be first to share the dissenting voice.)

The common issues you'll hear come around testability. Having to 'reset' the singleton is non-trivial at times, and it basically means tests cannot run in parallel. Plus, I don't know about you, but I've had times where I later realized I do need more than one instance. This is where I feel it's important to always use dependency injection so that you are insulated from such decisions. When a method can freely grab SomeSingleton<SomeWidget>.Instance, that dependency is less visible. You'd have to know to "Find All References" on the singleton itself.

I love the discourse. :)

Thread Thread
 
andreasjakof profile image
Andreas Jakof

I like discussing such things as well. If you look at my post „Brilliant but also horribly wrong“, I had like to learn from others, who know more than me.

And I usually don‘t use Singletons much. But in this case, I think, it is fine. The Singletons are usable in multiple threads and they mostly let me compare the membership of a user in an AAD-Group. And they NEVER change. If they would, they would no longer be of the same kind, not only the same instance.

And I know about the issues with testability as well... this is where I have a considerable lack of experience, even though I am already in the business for about 10 years now.

That’s why I am currently looking into DI in .Net Core. To make my code more testable. But then again, the code is mostly glue between systems and I am struggling with the basic approach, hie I will test it. Currently there are some basic unit tests, but lots of untested code, because the CD/CI server does not have access to all the credentials for the different systems I am glueing together. 😳

Collapse
 
devestacion profile image
DevEstacion

It's probably better you inject these instances instead of creating a static class to access a singleton objects.

Collapse
 
bradwellsb profile image
Bradley Wells • Edited

I don't like "never." What would you use in a client-side Blazor application for keeping up with app state if not a singleton service? Which then, by the way, still has to be injected.

Collapse
 
thebuzzsaw profile image
Kelly Brown • Edited

I'm actually super happy you brought this up. You're referring to services.AddSingleton<T>, yes? I argue that this method is misnamed. I think it should be renamed to AddShared or something like that. When you inject an object in this manner, it's merely instructing the framework to reuse a single instance. It doesn't suddenly bestow all the qualities of a singleton upon that class. I can still voluntarily create another instance somewhere. The constructor is not locked down per the singleton design pattern.

(Oh you said client-side Blazor. I've not made one of those. Regardless, you don't need a true singleton.)