Retiring the Singleton Pattern: Concrete Suggestions for What to use Instead - Peter Muldoon

  Рет қаралды 39,275

CppCon

CppCon

3 жыл бұрын

cppcon.org/
github.com/CppCon/CppCon2020/...
---
“The worst part of this whole topic is that the people who hate singletons rarely give concrete suggestions for what to use instead.” - stackoverflow
In this talk, we will explore just such an approach that will transform currently untestable code containing underlying singletons with a fully testable solution. These code changes are transparent to the original callers of that function so no calling code changes are required.
This approach will be further expanded to handle multiple interdependent singletons. The replacement of error prone specific order of initialization calls to the singletons will be exchanged for "hard to misuse" automatic initialization using features of the language.
A host of other potential real world problems with replacing singletons are shown with solutions presented for both legacy and modern C++.
This alternative approach has been successfully employed in multiple areas in Bloomberg where developers believed there was no other feasible choice.
---
Peter Muldoon
Bloomberg
Senior developer
NYC
---
Streamed & Edited by Digital Medium Ltd - events.digital-medium.co.uk
events@digital-medium.co.uk
*-----*
Register Now For CppCon 2022: cppcon.org/registration/
*-----*

Пікірлер: 47
@tonyd6514
@tonyd6514 3 жыл бұрын
The "Classic Singleton" code presented around 7:00 minutes in is not the normal or a good way to create a singleton at all, as it's not thread safe. For more than 10 years, the "Meyer's Singleton" has been considered the classic C++ implementation: the singleton object should be a static local variable, rather than having a static local pointer variable that you compare to nullptr before deciding whether the create an object with new. That's just begging for trouble.
@triskeldeian4989
@triskeldeian4989 2 жыл бұрын
That's very true, but something tells me that if most of the times the singleton is still implemented "the wrong way", that is with a pointer
@kuhluhOG
@kuhluhOG 2 жыл бұрын
one way, which does actually work and is threadsafe, is: T* get_singleton_T() { static T* t = new T; return t; }
@ebakes
@ebakes 2 жыл бұрын
I was going to say, I've never seen such a verbose singleton. template class singleton { public: static T& get() { static T t; return t; } }
@qqi239z123
@qqi239z123 Жыл бұрын
@@ebakes Myer singleton is not unit-test friendly
@zeph9977
@zeph9977 2 жыл бұрын
This talk is significantly better his previous talk. Thanks!
@khatdubell
@khatdubell 3 жыл бұрын
"unless you some dicy stuff like ....define private as public." This is was actually in our codebase when i started. I killed it as soon as i saw it.
@origamibulldoser1618
@origamibulldoser1618 4 ай бұрын
I've never seen a more relevant talk for the types of problems I face every single day. Throw template member functions into the mix whilst still maintaining testability, and you've officially beat corporate c++
@user-tp6mh9ut9l
@user-tp6mh9ut9l 8 ай бұрын
Excellent Discussion about what the client code should look like, to allow testability , when having dependency on a singleton
@megaTherionXX
@megaTherionXX 3 жыл бұрын
I use Singleton's somtimes, also in modern C++ codebases. But funny enough not for the reasons which are publicated here, I also dont suffer (all) the drawbacks mentioned. Its always quite hard for me to understand when people say "dont use pattern X"... but dont elaborate why you couldnt use it... sure you can misuse Singletons, just like you can misuse everything else in C++ (thats especially easy in C++). However if I have a certain class/type which cannot and shall not be instanced twice, also needs to live for the program lifetime (single resource, maybe database, maybe something physical of the machine).. then why not?
@enerjazzer
@enerjazzer 3 жыл бұрын
But std::function is equivalent to a virtual call, isn't it?
@embeddor2230
@embeddor2230 3 жыл бұрын
even worse, as it might dynamically allocate memory to contain the state of the lambda.
@llothar68
@llothar68 Жыл бұрын
@@embeddor2230 But this allocation is once per lifetime and not once per call, so in every use case i have seen it's pretty neglectable
@mytech6779
@mytech6779 2 жыл бұрын
As an amateur my mind shutdown and melted at some point.
@GaryChike
@GaryChike 2 жыл бұрын
Right? I now only watch the videos by CppCon that begin with "Back to Basics: ..." 😆
@roilev
@roilev 2 жыл бұрын
Slide 28 is awful - "green on the left" is blueish cyan. There is another green. Is bigger graph better or worse? What do the names below mean (direct/indirect/basefunc/nonbasefunc)? They should be defined in the slides
@zackyezek3760
@zackyezek3760 2 жыл бұрын
Singletons are like any other tool. Only use them when they're the right tool for the job, and implement them correctly (e.g. modern Meyer pattern). Refusing to ever use something because "it's bad" is a bad mindset promulgated by bad engineers. There's places where reaching for goto is the correct move. Ditto macros, function pointers, etc. By all means use newer and superior alternatives in the cases where they ARE better, but don't act like hammers are somehow obsolete, in a world still full of nails, just because we've now got screwdrivers too.
@anon_y_mousse
@anon_y_mousse Жыл бұрын
Thank you. I've been saying for years that goto's have a correct usage and shouldn't be deleted from a programming language just because people misuse them. I despise all of these trendy languages that remove them because of bad actors.
@AminAramoon
@AminAramoon 2 жыл бұрын
there is one issue with this talk. std::function uses type erasure which uses virtual function call as well as dynamic memory allocation.
@hannesw928
@hannesw928 Жыл бұрын
That was my thought too :)
@germanassasin1046
@germanassasin1046 3 ай бұрын
I think std::function is allowed to use small buffer optimisation, so no heap allocation if you have a function pointer or a captureless lambda
@IsaacClancy
@IsaacClancy 3 жыл бұрын
First off I agree that the singleton pattern is oven misused as it gives the instance it's own type which makes for example testing functions that work with a database on a database that isn't the production database harder. However, most of the suggestions shown here work just as well if you keep the singleton. Keeping the singleton, sendData could be class CommSingleton : public CommInterface { ... }; Response sendData(const Data& data, CommInterface& comms) { ... } Response sendData(const Data& data) { return sendData(data, CommSingleton::instance()); } Also, removing the init methods from the singletons can't be done in the way shown if one of the init methods requires arguments, e.g. an ip address that is read from a config file.
@petermuldoon8111
@petermuldoon8111 3 жыл бұрын
The biggest point here is being able to have multiple instances of what was previously a system-wide single instance accessable only by its static instance() method. None of the methods supplied in this talk will work if that part of the pattern is not discarded as by definition, the Singleton pattern precludes dependency injection.
@petermuldoon5751
@petermuldoon5751 3 жыл бұрын
The CommInterface class above is now the interface for all functions needing that comm functionality. The CommSingleton above is now just an implementation detail by maybe one user of the CommInterface but other users are free to disgard it ergo the Singleton paradigm is gone - as intended In the circumstance where you would like to set configuration for a bunch of init methods via an initial config file, I can extend the paradigm to create a plausible approach. First call to defaultConfig would be from the main with a filename, all subsequent calls are retrieving configuration CfgObj defaultConfig(const std::string"& filename="") { static CfgObj def_cfg(filename); // if passed "", can choose to throw or use a default cfg file. return CfgObj; } Comms getDefaultComms() { static Comms def_comms(defaultConfig()); return def_comms; }
@richardskarphagen177
@richardskarphagen177 2 жыл бұрын
@@petermuldoon5751 œ
@simonfarre4907
@simonfarre4907 2 жыл бұрын
I mean spooky action at a distance issue is not an argument that can be made in 2022 for low level software. Today we have hardware watchpoint registers and we have had them for over a decade at this point. In a sense, they make singletons where singletons are appropriate, which is not very often but some times they are; extremely easy to track and watch. And its dirt cheap since the functionality is quite literally on the chip.
@Omnifarious0
@Omnifarious0 2 жыл бұрын
25:20 - A virtual function by another name... *chuckle* The lesson here is that virtual functions are NOT inherently evil! Deciding at runtime which function to call is not some kind of moral weakness or design flaw. Thinking that virtual functions are somehow not "modern C++" is the problem.
@user-gh4lv2ub2j
@user-gh4lv2ub2j 2 ай бұрын
I use a functional singleton via a closure.
@Danielle_1234
@Danielle_1234 Жыл бұрын
I wish he would summarize the design pattern he's doing into terminology. It looks like he's doing the monostate pattern, but I could be wrong.
@JohnWilliams-gy5yc
@JohnWilliams-gy5yc Жыл бұрын
I just wonder whether the singleton pattern could make a graceful come back by using the new hi-level lock-free (cmpxchg) abstraction of hazard_ptr and/or rcu? If it could, I also wonder if this time we can save some memory by destroying/recreating it safely when needed?
@stephenhowe4107
@stephenhowe4107 3 жыл бұрын
Slide 8, 9:01 Not mentioned is how you destroy a singleton. You obviously want to tear it down after last use, but when is that? His example of a singleton does not recover resources. And suppose the singleton is managing a critical OS resource, you want to give it back to the OS before your program terminates, or hand the resource back to the OS early if your program does not need the resource for a while. Yes I know of John Vlissides article "To kill a Singleton" and I have seen Andrei Alexandrescu's Modern C++ which discusses this problem at length.
@petermuldoon5751
@petermuldoon5751 3 жыл бұрын
There are no Singletons left just some well defined globals for API stability for legacy users. but assuming this is what you want to kill, convince the user base to use the 2 parameter version and they are now in control of the lifetime of the dependency. That will be a hard sell
@vasiliynkudryavtsev
@vasiliynkudryavtsev Жыл бұрын
I agree. Usually developers don't bother returning OS resources, but with singleton the issue could be something of a stuck authentication token.
@anon_y_mousse
@anon_y_mousse Жыл бұрын
I'm honestly not sure why we come up with these names for things and then either abuse them or are rigid with how they must be defined and/or used. The fact of the matter is, sometimes you have a singular resource that should only be accessed a certain way, such as only initializing it once or making requests of it when it's ready to handle them, and if a given "pattern" is the way people feel comfortable with handling that, you either need to come up with a good alternative or explain why in the situation they're using it that it shouldn't be used. So if what you're trying to say with all of this is that the method in which people lock singular resources to prevent them from being mucked up is the wrong method, then you probably need to communicate that more effectively. This is perhaps an argument for why we shouldn't use "buzz" words, because it can confuse the issue greatly. Especially when the definition of such words is just flat out wrong, not as in different to what the majority defines it as, but wrong as in it's too rigidly applied to one method when it should be applied to a generic set of methods. Like calling the object you're constructing a rectangle every time when sometimes it's a square. Sure a square is always a rectangle, but not vice versa.
@pushqrdx
@pushqrdx 3 жыл бұрын
i use a better pattern, called Simpleton Pattern
@khatdubell
@khatdubell 3 жыл бұрын
I wish i could convince people at my company to abandon the Singleton pattern. I refactor them whenever i can, but more keep getting added, and i'm only one person.
@stephenhowe4107
@stephenhowe4107 3 жыл бұрын
_I wish i could convince people at my company to abandon the Singleton pattern_ Why? I am not pro-Singleton but I want convincing and so far I have not been convinced. Some of the reasons against outlined in the clip are minor implementation drawbacks that could be reworked. And exactly what are going to do about global state in a program? Pass it perpetually around? Sub-delegate the parts on a need-to-know basis? Which solution has minimal maintenance costs? There is also the MonoState pattern which resembles Singleton. Problems with Singleton: When your program needs more than 1 When do resources for Singleton get reclaimed? Can this be done automatically (reference counting)? Singletons that depend on other Singletons Lifetime issues A balanced article on Singletons would highlight the virtues of them (and possibly mention other solutions to obtain these virtues).
@khatdubell
@khatdubell 2 жыл бұрын
@@stephenhowe4107 "Why?" my biggest problem with singletons is that they, like random goto statements, while innocuous on their own tend to proliferate throughout the codebase. In the case of goto, you end up with spaghetti code. In the case of the singleton you end up with hidden dependencies that make your code impossible to reason about at any particular state, harder to test, etc,.. If you could manage to responsibly introduce a singleton into a codebase and guarantee that it will never be used in ways that are bad coding practices, I wouldn't be against it. That being said, if you stop and examine that scenario, what is the difference between that and only creating one instance of an object without using the singleton pattern? At the end of the day the only thing the singleton pattern is supposed to buy you is the guarantee that only 1 exists and will ever exist. You can do that without introducing what amounts to a global variable.
@Solarbonite
@Solarbonite 2 жыл бұрын
I favor singletons for when code is used everywhere. Like loggers printing to only the screen, or error aggregation across multiple threads if exception = shutdown. There's a reason why std::cout is a singleton. Passing it everywhere would royally suck! And the value for test is very small.
@anon_y_mousse
@anon_y_mousse Жыл бұрын
@@Solarbonite Technically it's not a singleton because it's not a type, but yeah, having more than one would be annoying.
@szirsp
@szirsp 2 жыл бұрын
That's great that you want to replace essentially global variables that shouldn't have been global the first place, because you want different behavior (testing). But do we need to retire singleton pattern? It doesn't seem like it is a problem with something being a singleton, rather than using singleton where shouldn't... But what if I want something to only exist once, or more precisely only initialized once. For example a systick timer class on a Cortex M MCU. Do I want everyone to have their own (and reinitialize the HW every time something constructs a new instance and reset the registers and mess up the running counter)? F*** NO, I don't! I want it initialized at most once (if anything needs it). And I mostly don't care about the object not being destroyed and the resource is not released (it's probably gonna be needed the entire time, and RAM is statically allocated anyway). If I wanted I would add a usage counter that increases on instance() call and decreases on release(), and on 0 would disable/power down the peripheral (like an SPI, UART...) Am I wrong? Is there a better way? How would systick being a singleton bad for testing? Do you also not use time() or any OS services without a wrapper, because you want to inject fake data into everything for testing? For testing you could have a separate implementation of the singleton (simulator) that have the desired behavior and injects the needed test vectors and compile it with that. HW in the loop testing is a bit more complicated... but just stuffing your code with a bunch of overhead in the form of virtual methods might not be what you want...
@hannesw928
@hannesw928 Жыл бұрын
I don't make my drivers in embedded Singletons. Often you have more than one UART, SPI, I2C, etc at different addresses. So why Singleton? Now I would need a UART1, UART2, etc ... Singleton? std::cout is not a Singleton yet people seem to not be doing weird stuff, like assigning to it, regularly. So why should Systick be a Singleton? Why would programmers contruct a new instance of it? What in multicore systems? Do you have one Systick instance for all cores? How does that work?
@richardbowen3257
@richardbowen3257 3 жыл бұрын
Nice, but the original Singleton looks an awful lot simpler.
@petermuldoon5751
@petermuldoon5751 3 жыл бұрын
but you have no testability !!!
OO Considered Harmful - Phil Nash - CppCon 2020
57:39
CppCon
Рет қаралды 45 М.
Back to Basics: Exceptions - Klaus Iglberger - CppCon 2020
1:01:45
О, сосисочки! (Или корейская уличная еда?)
00:32
Кушать Хочу
Рет қаралды 4,1 МЛН
小路飞姐姐居然让路飞小路飞都消失了#海贼王  #路飞
00:47
路飞与唐舞桐
Рет қаралды 46 МЛН
Зу-зу Күлпәш. Агроном. (5-бөлім)
55:20
ASTANATV Movie
Рет қаралды 538 М.
Why I don't hate Singletons?
11:30
Jason Weimann
Рет қаралды 26 М.
"Clean" Code, Horrible Performance
22:41
Molly Rocket
Рет қаралды 838 М.
5 Signs of an Inexperienced Self-Taught Developer (and how to fix)
8:40
31 nooby C++ habits you need to ditch
16:18
mCoding
Рет қаралды 707 М.
Back to Basics: Move Semantics - David Olsen - CppCon 2020
59:08
why do header files even exist?
10:53
Low Level Learning
Рет қаралды 339 М.
What is Dependency Injection?
6:48
Scott Bailey
Рет қаралды 110 М.
Back to Basics: Pointers and Memory - Ben Saks - CppCon 2020
1:00:56
M4 iPad Pro Impressions: Well This is Awkward
12:51
Marques Brownlee
Рет қаралды 6 МЛН
Он Отказался от БЕСПЛАТНОЙ видеокарты
0:40
ЖЕЛЕЗНЫЙ КОРОЛЬ
Рет қаралды 1,8 МЛН
СЛОМАЛСЯ ПК ЗА 2000$🤬
0:59
Корнеич
Рет қаралды 2,3 МЛН
Any Sound & Call Recording Option Amazing Keypad Mobile 📱
0:48
Tech Official
Рет қаралды 325 М.