We are picking up right where we left off from Part 1 of this series.
If multiple code flows of the program are instantiating and using the Logger class, then generated logs can get complex and might become a spaghetti when we start reading them.
Let’s say even if we don’t face the above errors and all this works just fine functionally by luck or due to our application being very less complex even then we can face performance issues also. Let’s explore that aspect.
The code we saw in the previous sections has got some costly bits involved from your application performance stand-point:
So, each of the above operations might take a fraction of a second or maybe in milliseconds and you might think that why should I care about the cost of creating a file handle (which is eventually an object instance) in this era where I have got 32 GB RAM computers and website server computers (or data centers) are even more powerful. I say we must care. You shouldn’t care about this aspect if you’re going to write few scores of log statements or going to open a few files handles here and there. But the point is that enterprise applications are very big in general. Really big applications can have even dozens or hundreds of log statements in a single flow of the program which ends up creating a hundred instances of StreamWriter class at the same time.
Let’s consider few points below on how the above discussion can contribute to performance issues:
So, we have some possible solutions that we can think currently:
And the fact remains that if the class is public, you can’t stop any developer on your team to instantiate it. The compiler treats all developers equally irrespective of designation. 😊
And let’s say even if I don’t face all these issues, even then our honest attempt should always be to reduce the memory footprint of our application. Isn’t it? An enterprise level application isn’t that simple as I’ve mentioned. Your server application requires memory for so many other things, e.g., threads, stack memory, I/O ports, etc. So, our intention should always be to minimize the main memory consumed by the process to make space for other activities and other processes running on the server.
So, all above pointers are giving us a push that we somehow need to restrict the possibilities of several files handles getting opened in parallel or if it happens, then it should happen in a controlled manner. How do we achieve it?
You must have heard the concept of sharing. At your home in general, we all have one television (TV) set. Why? TVs are costly, so it will not be affordable for every household to purchase a TV for every family member. Also, you’ve got limited space (which is again a resource) in your house. You can’t use the entire space just for installing several TVs. So essentially, you settle with only one TV in your house and share it with all the family members. The same concept in computer science world is known as resource sharing.
So, one question which comes to our mind is that – Can we do some resource sharing here to solve our problem. Yes. That shareable resource is an instance of Logger class which manages the file handles to our log file through StreamWriter class.
It is always the case that your entire application will have centralized log files which contain the logs created by various modules of your application. Logger class will eventually create a file handle to the same file always. Isn’t it?
So why create several instances within a single web request from a single user or across web calls? Essentially why to leave a scope in code that anybody can just instantiate the Logger class as and when they want it. Can we not stop it from happening at code level itself? Why not enforce to have only one (aka single) instance of the object. That should ring some bells now. Singleton (the title of this article) has the word Single in it 😊.
So how can we ensure in code that one and only one (aka singleton) instance of our class is possible?
The static keyword
C# has a keyword called static. You can check Microsoft docs here for some quick details. static can have different usages/meanings while used in different contexts of C# programming but while used in the context of classes, it essentially means a thing which is associated with a type and not an instance of the type. If you apply static keyword to the class itself, then all the properties and methods get associated to that type directly, so you need the type (not the instance) when you need to refer them in code, e.g. <ClassName>.<MethodName>
A class decorated with static keyword can’t be instantiated. Let’s try to understand it with help of our Logger class. Let us say I’ve marked the Logger class with static keyword right after the “public” access specifier as shown below:
Now at the call site in Database access layer, we try to consume “Logger” class using new keyword:
So the moment we try creating an instance of static class it starts throwing below compile-time error:
Cannot create an instance of the static class ‘Logger’
So that’s what we wanted. Isn’t it? No developer can instantiate the Logger class at their will and that is what we’ve got now. But what about using it. We would require at least one instance to log our messages into the log file. Let us see a quick implementation on how we can make it work using <className>.<MethodName> syntax for static classes/methods in C#:
Static Logger full implementation (refer to StaticClassTextLogger.cs):
How things will change at call-site: (refer to FetchDataWithStaticTextLogger.cs)
So, what all did we achieve in the above code snippet:
Are you able to notice any problem here so far in the static way of managing Singleton instance of a class? Please scribble few notes on a piece of paper. We will go through them in a later section.
By the way, I just used the phrase “managing Singleton instance”. But where the hell is the instance? You didn’t use “new” keyword even once to create any instance in memory. Isn’t it?
It is true that you have now been restricted by the compiler to create instances of the “Logger” class but you know that new-up operation which we apply on normal classes do the crucial work of allocating space to member variables of a class and returning a handle in the end with the help of which we do various operations on the object instance. So where is the instance here? How is memory getting allocated to static class members? You know it already that any code starts to work only after it gets loaded in the main memory of the computer which is RAM.
Yes. So that brings us all to an important concept in .NET called type instance. For every single class (static or non-static) that you have in your project, Common Language Runtime (CLR) creates a special instance for the class called Type instance. It contains all the meta information related to your class, e.g., fully qualified name, all method signatures and its code, etc.
So, all static stuff present in a class is associated directly with this special instance called type instance. If you declare static member variables, then they will lie in line with the memory area where type instance is residing. And that is how the <ClassName>.<MethodName> syntax came into being. By using that <ClassName>, you are referring to the type instance in memory and get your things done.
Please note that CLR guarantees that there will be one and only one type instance for a class per AppDomain in a .NET process. So, you can be rest assured that there will be only occurrences of all the static stuff associated with your class as they are all present with the type instance.
This is a nice concept to know. But is CLR behaving in-line with our expectations while managing static classes. Can there be any pit-falls in the way CLR manages static classes and their instantiation which can be a road block for my application later? Are statics really true Singletons? Let’s explore.
Ok. Let me jot down few problems that are there while managing singletons using static keyword. Take out your scribbled paper and start matching them.
The problems which are directly visible from looking at code:
So, if you aren’t bothered by the above issues in the application you’re writing, then you are all good. A static class should suffice your needs as all the heavy lifting has already been done by CLR on your behalf to ensure that one and only one instance of the class gets created. Singleton instance was our original intention/requirement. Isn’t it?
If you think that the above issues with static might crib you down the line as your enterprise application evolves, then we might have to think another route. The key concern that we need to solve is – I as a programmer want to have absolute control over the instantiation and lifetime of the singleton instance. I don’t want to rely on CLR or Java Virtual Machine (JVM – CLR’s counterpart in Java world) as when my singleton instance will get created, retired for the creation or destroyed. Can we do something ourselves? Can we have an implementation of Singleton on our own?
Hmmphhh! That was a lot to chew. Wasn’t it? Thanks for your patience to read this long article which sets the backdrop for the concluding blog. Let’s take some time to ponder and think of possible solutions for designing a singleton class ourselves. Once you’ve done the homework, then we can move to the concluding blog post: Singleton Design Pattern: The Pragmatic Approach – Part III.
Remember you can always check out Part 1 here.
You're doing great—Let's move on to Part 3!