Ruby Standard Library

The Singleton Module and Its Pattern In Ruby

The Singleton Module and Its Pattern In Ruby

by Mike Calhoun

In this talk titled 'The Singleton Module and Its Pattern In Ruby,' Mike Calhoun presents an in-depth exploration of the Singleton design pattern, particularly its implementation in Ruby through the Singleton module. Key points of the talk include:

  • Introduction to Singly Instances: The Singleton pattern ensures that a class has only one instance and provides a global access point to that instance. Unlike global variables, Singleton instances do not occupy global namespace and are not instantiated until explicitly called.
  • Controversies Around Singletons: Calhoun discusses the mixed opinions within the programming community regarding Singletons. Critics highlight issues such as tight coupling, potential for mutable shared state, and challenges with testability due to global accessibility.
  • Applications of Singletons: While Singletons are often associated with problematic practices, there are valid use cases, such as logging and configuration management, where single global instances work effectively and offer a consistent state.
  • Ruby's Singleton Module: The talk introduces Ruby's Singleton module (singleton.rb), which ensures a single instance by privatizing methods like new and allocate. The module also prevents cloning to maintain the uniqueness of the instance.
  • Practical Examples: Calhoun shares practical examples using a sample class called 'SingletonExample,' demonstrating how to correctly instantiate a Singleton and observe its object identity in Ruby. He also touches on the Singleton class's utilization within Ruby's object model, showcasing its significance for encapsulation and method definition.
  • Insights from the Talk: Concluding the talk, Calhoun emphasizes that while Singletons can be misused, understanding their structure and context is vital. Proper use cases can enhance programming efficiency, particularly in situations with a unidirectional flow of data.

The presentation wraps up by inviting attendees to reconsider how Singleton patterns can be effectively integrated into Ruby applications, fostering a deeper understanding within the developer community about design patterns, object modeling, and best practices.

00:00:13.130 Go ahead, thumbs up! I have to be honest; this room has a lot more people than I expected. Thank you all for coming! It's great to see so many smart people choosing to spend 45 minutes of your time here with me. I'll try to make it worth your while.
00:00:26.369 This is a talk about the Ruby Singleton Module, the Singleton pattern itself, and an interesting realization I came to while learning about these topics.
00:00:38.069 A little bit about me: my name is Mike, and I live in Vancouver, Washington, just a suburb of Portland, Oregon. I spent about nine consecutive years in Nashville, so it feels a bit like being back in my old stomping grounds.
00:00:49.589 You can usually find me on the internet at comike 011, where there’s a humorous photo of me passed out on a train. It was an exceptionally hot day, and that’s probably the reason behind it! I work as a senior engineer with a company called VOCA, based out of Santa Barbara, California. They let me work remotely, and we specialize in AI-powered call tracking and analytics.
00:01:07.280 Oh, and we're hiring! I wanted to emphasize this, so for a brief moment, please enjoy this multimedia experience. Come and talk to me or anyone wearing a VOCA t-shirt; we’re awesome people!
00:01:17.950 Now, about this talk: once it was accepted for the conference, it underwent an evolution that I didn't expect. I realized it had three primary parts: the Singleton pattern, the Singleton module, and the Ruby Singleton class.
00:01:26.590 Let’s dive into how we got here. The RubyConf call for proposals opened in July, and I submitted this talk about half a month later. I wasn’t very quick to it.
00:01:38.230 Originally, it found its home in the general track, but its origins were rooted in Ruby's documentation.
00:01:44.800 When I decided to submit the talk, I didn't really know what I was looking for; I mainly turned to the standard library to find something interesting.
00:01:51.400 I stumbled upon the Singleton pattern. I had heard of Singletons before but didn't know much about them. The documentation regarding the Singleton module was exceptional. There’s a very succinct description that states, 'The Singleton module implements the Singleton pattern.'
00:02:06.170 So again, thank you very much. I appreciate it, and I’ll take questions later. To begin, let’s start in the middle and work our way back to the beginning. What exactly is the Singleton pattern?
00:02:13.310 The Singleton pattern is one of the Gang of Four design patterns detailed in the book, 'Design Patterns: Elements of Reusable Object-Oriented Software.' There are several characteristics of Singletons that I find both objective and somewhat subjective.
00:02:26.590 For starters, Singletons restrict the creation of a class to a single instance, providing a single access point to that single resource.
00:02:38.230 In other words, there is one instance of a class, and we always have a mechanism to gain access to it no matter where we are in our application.
00:02:45.540 While it has a global scope, it's not necessarily a global variable. Singleton instances don't occupy space in the global namespace like global variables do.
00:02:57.880 Furthermore, Singletons are not instantiated until explicitly called, unlike global variables.
00:03:08.670 Now regarding the application of Singletons, there’s a more subjective aspect to consider. Many of you may already be aware of the concerns surrounding global variables; much of the programming community tends to distrust them.
00:03:18.890 Tightly coupled code is often a point of contention, which is why a lot of developers shy away from using global variables, leading to testing files that are more of stubs and mocks than actual tests.
00:03:29.860 You wouldn’t be alone in this sentiment. The more I read about Singletons, the more opinionated the discourse seemed to be.
00:03:35.860 For instance, one article pointedly asked, 'Are Singletons bad?' and did so with a lengthy critique.
00:03:43.290 This author compiled six reasons to avoid Singletons. He remarked, 'Yes, I’m writing a blog post about why the Singleton pattern is bad in 2016,' but the subject continually arises in discussions.
00:03:52.420 Here's a list of reasons often cited for avoiding Singletons. The downside is that this post emerged because the author was frequently forced to discuss Singletons. He thought to document his points so he wouldn’t have to bring it up again.
00:04:00.520 He claimed: 'Most patterns from the Gang of Four are straightforward and logical. However, there’s one specific pattern that receives universal disdain from every programming-related website: the infamous Singleton pattern.'
00:04:11.690 This has been labeled as the most overused and frequently misapplied programming anti-pattern out there.
00:04:21.060 In the same vein, various articles have labeled Singletons as 'pathological liars' and many others have stated that they are untrustworthy.
00:04:27.360 I love the band LCD Soundsystem, and this notion of ‘Single being bad’ strikes a chord with me.
00:04:34.430 So what are these concerns regarding Singletons? I previously mentioned problematic misuse, but let’s explore that in greater detail.
00:04:47.160 Single instances allow for multiple points of access to a shared resource. This isn’t inherently problematic if it’s immutable.
00:04:55.640 However, if shared resources can be altered, that can lead to significant issues.
00:05:01.950 Additionally, a Singleton can introduce objects that are globally accessible and modifiable, which means that if one area of your application manipulates it, it can have unpredictable effects elsewhere.
00:05:07.830 This leads to a violation of the single responsibility principle. Such objects are intended to maintain a single instance of themselves but may also be responsible for various other functionalities.
00:05:14.160 Moreover, this raises questions about testability. If we employ proper practices, one might find themselves stubbing or mocking the Singleton for every test.
00:05:21.570 This leads us to the final concern: a single point of access to a shared resource leads to tight coupling.
00:05:29.810 I came across an illustrative, albeit somewhat contrived, narrative about a user management application. If an application is designed to manage a single logged-in user, the attributes of that user must be accessible throughout the application.
00:05:41.200 Thus, making the user a Singleton allows access in whatever context it’s needed, without the need to consistently reload it.
00:05:49.460 However, consider a situation where a user changes their email address or last name; that could change in one place, but other parts of the application might not have received that information yet.
00:05:58.320 This leads to a tangled mess where users' details can fall out of sync, resulting in prediction and functionality problems.
00:06:06.480 Despite this, I don’t want to imply that Singletons are universally bad ideas. Many writers and experienced developers see them as usable design patterns for valid reasons.
00:06:15.480 I will suggest that Singletons are particularly practical in circumstances that feature a unidirectional flow of data. In other words, exchanges where there is no expectation of values being altered based on requests.
00:06:28.550 In scenarios where this class is utilized universally or across disparate areas, one could achieve similar outcomes through dependency injection.
00:06:38.630 However, this can become tedious, as you would have to pass that object to many areas that don’t necessarily need to work with it.
00:06:44.930 A typical use case is logging, where having a single logger class accessible globally may not be a bad idea. It provides consistency.
00:06:52.080 For instance, one could send information to log and receive a simple response indicating whether the log was successful—how often, after all, do loggers fail?
00:07:02.240 The information flows in one direction from the application to the logger, which works well since loggers will likely be needed universally.
00:07:10.360 Again, if we introduced logging through dependency injection, we would need to pass a logger into every class.
00:07:18.510 Another example would be configurations. Ideally, our configs, for our passwords and such, aren’t changing often.
00:07:25.410 We might want to access API authentication tokens globally, and ideally, those too should remain unaltered.
00:07:34.750 While we want to avoid anything globally accessible that can be changed by our application, we may still want to utilize external files for settings.
00:07:43.040 Thus, we could have a Singleton that pulls in these external configurations while being immutable within the application, resulting in a consistent state.
00:07:51.780 Now that we’ve established the concept of the Singleton pattern, let’s discuss the Ruby Singleton library, Singleton.rb.
00:08:05.200 We’ve already read through the docs, so we know what’s there. Singleton.rb is where this started for me.
00:08:17.360 I found it interesting that there is a module specifically dedicated to this pattern. Though it's not in the standard library, it can be easily included and leveraged.
00:08:28.390 The module does a lot more, but I want to highlight some key characteristics.
00:08:37.630 A key characteristic is that there is a single instance of this designated class. To ensure this, the 'new' method is made private.
00:08:46.930 The 'allocate' method is also made private, meaning they cannot be directly called. Instead, they are replaced with 'instance', which pulls the existing Singleton into your context.
00:08:56.860 Furthermore, cloning is not allowed. Calling 'clone' or 'dup' will produce errors, reinforcing that there is only one instance.
00:09:06.800 To illustrate how this works, I have an aptly named 'SingletonExample' class with an attribute called 'goggles'.
00:09:15.440 When I try to create a new instance at first, it fails because we’re attempting to call a private method.
00:09:24.870 The error message is informative but doesn’t explain why, hence we start over and use the appropriate method to instantiate our Singleton.
00:09:35.090 I’ll show the object ID. With a few exceptions, the object ID points to a memory location where the data is stored.
00:09:43.450 Next, we’ll attempt to duplicate our Singleton object. Just like before, error messages clarify what happens.
00:09:51.970 Indeed, we can create a second reference so we can see they share the same object ID, confirming there's only one instance.
00:10:03.380 For completeness, we’ll check equality using 'equal?' which should indicate they are the same since they have the same object ID.
00:10:12.700 Now let’s shift back to what Singletons are and what we’ve learned about them.
00:10:22.530 Now moving on to Ruby's Singleton class and its significance. My interest in the Singleton pattern arose during this proposal.
00:10:29.530 Unlike the significant criticism on the internet, I stumbled onto this small module, which seemed to implement a controversial design pattern.
00:10:37.000 The Singleton class is vital to Ruby’s framework as it allows for the encapsulation of instance methods.
00:10:46.000 These classes are accessed via 'singleton_class' calls. One may find it hidden, as it can be accessed fairly easily but doesn’t show up unless you specifically look for it.
00:10:54.240 I find it reminiscent of gravity; while it can’t be seen, it nonetheless has measurable effects!
00:11:02.960 There are fundamental rules regarding Ruby object models. Everything is an object in Ruby.
00:11:13.900 Beginning from the basic 'Object' class, to defined classes, each one inherits the properties of the previous levels.
00:11:22.700 When we include modules in our classes, their hierarchy is adjusted accordingly.
00:11:28.380 Let me illustrate this by utilizing 'ancestors' to visualize how Ruby manages its hierarchy.
00:11:38.320 Here's an example from the class 'ConferenceTalk' to visualize the object model.
00:11:45.720 As reflected in our defined class, the designated 'ConferenceTalk' class also includes an ancestor hierarchy.
00:11:54.380 For example, the 'String' class reveals the same structural hierarchy, with included modules visible to see how inheritance interacts.
00:12:03.460 Next, let’s discuss how the Singleton class can be employed practically.
00:12:11.620 Cats have dominated discussions around technical elements at conferences. Here are my two cats to illustrate the topic.
00:12:19.480 On the left is PK, or Porch Kitty, who meandered her way into my life post-flooding.
00:12:25.800 On the right is Pearl, who is an adapted indoor cat, living an entirely different life!
00:12:31.420 I crafted a basic application that simulates their activities—very little is required to call them into action.
00:12:38.640 As we add attributes and methods, we can see how they interact and how the functionality of each instance holds up.
00:12:47.960 In this instance, Pearl can 'speak' through its instance method, while showcasing the Singleton structure.
00:12:55.400 As I invoke a private method, the message may not clarify why it fails, but the output maintains the integrity of the Singleton.
00:13:02.760 If we try to define additional attributes or methods, the application reveals that behavior distinctly reflects the underlying principles of Singletons.
00:13:10.320 This leads us to further explore how the Singleton class can be modified to include various practices.
00:13:19.360 We can assess capabilities, differentiating methods defined across classes versus instance methods for tailored functionality.
00:13:26.880 This layered structure allows us to dynamically modify and enhance functionality while preserving core implementations.
00:13:33.900 Returning to the thematic notion of cats, the famous Schrödinger's cat illustrates an interesting principle.
00:13:42.860 Schrödinger's cat represents a thought experiment that gets to the heart of observation in quantum mechanics.
00:13:49.700 By confining a cat to a sealed box with a randomly applied peril, it forces an essential questioning of dual states.
00:13:56.790 This analogy is pertinent to private methods and visibility within classes, shedding light on how we perceive things in the Ruby structure.
00:14:05.450 We can further consider how different methodologies affect learning about these singleton approaches.
00:14:12.490 Ultimately, how it relates back to different aspects of programming and applying these methodologies.
00:14:19.850 As we move forward, it's crucial to identify how these modular forms further our understanding of varied programming constructs.
00:14:29.040 We revisit where we started and the significance of Singleton patterns throughout our programming experiences.
00:14:37.660 As this unfolds, our fascination should remain in line with how intricately these patterns exhibit functionality, leveraging structures that persist.
00:14:46.490 Thank you again for spending this time with me and engaging with the journey through Ruby's rich landscape of Singleton strategies.