- Overview
- Transcript
2.2 Singleton
Another common pattern is the singleton. It's a class with only a single instance, and you'll learn how to implement it in this lesson.
1.Introduction1 lesson, 01:40
1.1Introduction01:40
2.Creational Patterns2 lessons, 11:52
2.1Factory05:30
2.2Singleton06:22
3.Structural Patterns4 lessons, 33:47
3.1Adapter07:08
3.2Composite11:33
3.3Decorator08:02
3.4Facade07:04
4.Behavior Patterns4 lessons, 36:32
4.1Chain of Responsibility12:12
4.2Command07:34
4.3Observer09:28
4.4Strategy07:18
5.Conclusion1 lesson, 01:06
5.1Conclusion01:06
2.2 Singleton
One of the more popular programming patterns is the singleton, and its purpose is to supply a single instance of a given class, and only a single instance. And I use the term supply specifically because a class supplies the instance. We don't create it outside of the class. It's created inside of the class, and the class provides access to that instance. So there's a few things that a singleton needs to implement. The first is that it needs to be final, because you are not supposed to be able to subclass a singleton. If you can subclass a singleton, then you can have more than one instance, and that violates the whole idea of having a singleton to begin with. So your class needs to be final. And let's call this Product DB, the idea being that this is the class that we would use to interact with our database that contains all of our information related to products. Now let me say that I personally wouldn't do this. Some people do like to use singletons in this way, so we're just going to run with this. So the first thing is that it needs to be final. The second thing is that it needs a private constructor. Because we do not create the object outside of the class. The object is created from inside. So we will have a private constructor. And the constructor typically doesn't have any parameters. There are some rare cases where it does, but for the most part they do not. And since this is going to be a class interacting with the database, we would need a handle for that database. So we'll have a private field called db, and then inside of the constructor is where we would set db to something. So we'll just say set db, let's go ahead and initialize that with null. So we have a private constructor, we have a final class. Now we just need to be able to access our instance. But we also need to create our instance and ensure that there is only one instance. So in order to access our instance, we need a static function. And we can call this function whatever we want. But a lot of times, you will see the term instance being used. There are some other terms that you can see, but for the most part, instance is what you would probably see. And then we can use a static variable in order to store our instance. So we're going to initialize it as null, and then we're going to check to see if it is null. And if it is, then we are simply going to new up our constructor. So we will create our instance there, and then we will simply return our instance. In order to access this, we would do this. Let's say db1 = and then ProductDB, and then Instance. And that would give us our single instance. So we could do this as well. We can say db2 = ProductDb::Instance, and then we can check to see if these are equal. And if they're aren't, then something is definitely wrong, you do not have a singleton. But if it's true, then everything will be a-okay. So let's hop on over to the console, we will run the file. And yes, they are equal. So let's add an instance method here just so that we could see how this would actually work in the real world. So we would have public function, and let's just call it query. We will accept a SQL statement so that we would interact with our database, and then return a result set. But in this case, we are simply just going to output something that says that, hey, we're doing something. So let's say Performing query, and then we will supply the SQL that we are executing. Okay, so let's get rid of our db2. Let's also get rid of this line, or at least comment it out. And then we will do db1, and then we will call the query method, passing in our SQL statement. So we can say select all from product types. And then that would execute our query. And if we hop on over to the console, you'll hopefully see performing query, select all from ProductTypes. So that is basically a singleton. It's useful when you want only a single instance of a class, and it's fairly simple to implement. However, there are some issues with singletons. For one, it's hard to test with them because these are essentially global. And just like any other global, you are introducing some kind of global state into your application. Like for example, let's add a method called execute, so that we have a method for querying the database. We have a method for executing things, like insertion, updating, deleting, things like that. And so, if we are executing SQL statements with our singleton while we are testing, we are essentially changing the state of our application. And that makes it difficult to test, because we want to run multiple tests, one right after another. And each one of our tests shouldn't be changing the state of our application, because in order to ensure that our tests always do what they are supposed to do, we would have to reset the state. And while we could do that with our singleton, we could add another method for resetting everything. That's really not something that we need to do, because then that method is actually part of the class. And for anyone else using our code, that would be confusing for them. Singletons are also more difficult to replace. By that I mean, if we ever needed to replace our ProductDb with some other class, for example like dependency injection, we can't really do that with a singleton. So while it might make sense to use a singleton in some cases, be sure that that is absolutely what you need to do. You are introducing a global to your application, and you should probably avoid that as much as necessary.