-
-
Notifications
You must be signed in to change notification settings - Fork 362
Solid Principles Full Tutorial In C Interview Answers Design Patterns In NET Coding Examples
Solid Principles Full Tutorial In C# - Interview Answers - Design Patterns In .NET - Coding Examples
Full tutorial link > https://www.youtube.com/watch?v=034zM9MQdkU
In this lecture video I am explaining what are solid design principles. I am composing a .NET Core 7 WPF project to demonstrate each one of the principles of the solid design in C-Sharp programming language. I believe that the best methodology for learning something is doing an actual programming example. Therefore, we will make examples together.
SOLID is acronym of:
S - SRP - Single responsibility principle
O - OCP - Open/closed principle
L - LSP - Liskov substitution principle
I - ISP - Interface segregation principle
D - DIP - Dependency inversion principle
Foreach principle, I am composing 2 classes that shows violation and correct approach.
The source code of the lecture is available on : 🔗 https://github.com/FurkanGozukara/Solid-Principles-Full-Tutorial-In-C-Interview-Answers-Design-Patterns-In-.NET-Coding-Examples
The subtitles of the video is manually corrected so please watch with subtitles.
Please join Our Discord server for asking questions and have discussions: 🔗 https://discord.gg/rfttctFewW
Please follow us on Twitter: 🔗 https://twitter.com/SECourses
Please follow us on Facebook: 🔗 https://www.facebook.com/OfficialSECourses
If you are interested in programming our playlists will teach you how to program and code from scratch: 🔗 https://www.youtube.com/c/SECourses/playlists
[1] Introduction to Programming Full Course with C# playlist
[2] Advanced Programming with C# Full Course Playlist
[3] Object Oriented Programming Full Course with C# playlist
[4] Asp #NETCore V5 - MVC Pattern - Bootstrap V5 - Responsive Web Programming with C# Full Course Playlist
[5] Artificial Intelligence (AI) and Machine Learning (ML) Full Course with C# Examples playlist
[6] Software Engineering Full Course playlist
[7] Security of Information Systems Full Course playlist
-
00:00:00 Hello everyone. I am Dr. Furkan Gözükara. Today I am going to explain to you what are
-
00:00:06 solid design principles. I will compose a .NET Core 7 WPF project to demonstrate each one of
-
00:00:12 the principles of the solid design in C-Sharp programming language. I believe that the best
-
00:00:18 methodology for learning something is doing an actual programming example. The code written
-
00:00:22 in this lecture will be uploaded to our public GitHub repository so that you can download and
-
00:00:27 use it yourself. I will put the link of the repository in the description of the video.
-
00:00:33 So let's start with composing our WPF project. You see I have clicked create a new project in
-
00:00:40 Visual Studio Community Edition and I have filtered the result with WPF.
-
00:00:46 There are two versions of WPF. The first one is WPF application which is .NET Core and
-
00:00:53 there is also another version which is WPF of .NET Framework. .NET Framework is the older one and it
-
00:00:59 is not updated anymore. So be careful with that. I am picking the WPF application with .NET Core
-
00:01:09 and let's pick our folder name as solid design principles.
-
00:01:19 I will pick the .NET 7 standard term support. Okay, so our application is starting. So
-
00:01:26 let's quickly review and understand what are the solid design principles and why do we need them.
-
00:01:37 Okay. Why do we need solid design principles? As a developer, we start developing applications
-
00:01:46 using our experience and knowledge. But over time, the applications might arise bugs.
-
00:01:53 We need to alter the application design for every change request or for a new feature request.
-
00:01:59 After some time we might need to put a lot of effort, even for simple tasks, it might require
-
00:02:04 the full working knowledge of the entire system. But we can't blame change or new feature requests
-
00:02:11 as they are part of the software development. We can't stop them and refuse them either.
-
00:02:17 So who is the culprit here? Obviously, it is the design of the application.
-
00:02:23 Okay, this is so true. I have 13 years of software development experience and I assure you that you
-
00:02:30 will have to fix and improve so many parts of your application after its initial release.
-
00:02:39 You will encounter bugs and you will and you will get a new feature request.
-
00:02:44 So if you design your application with following the state of art principles, then you will have
-
00:02:55 less problems. Okay, this is why solid design principles is really important in application
-
00:03:03 development. So let's continue reading. Advantages of solid design principles in C#.
-
00:03:12 As a developer, while you are designing and developing any software applications,
-
00:03:17 then you need to consider the following points. Maintainability. Nowadays,
-
00:03:23 maintaining the software is the biggest challenge for the industry. Day by day, the business grows
-
00:03:29 for the organization and as the business grows, you need to enhance the software with new changes.
-
00:03:35 So you need to design the software in such a way that it should accept future changes with minimum
-
00:03:40 effort and without any problem. Okay, I am pretty sure that you have already noticed this in the
-
00:03:48 applications that you use in your real life. You see that your applications always gets new updates
-
00:03:55 and gets new features. So this is something that you can't avoid in your software development life.
-
00:04:07 Testability. Test-driven development TDD is one of the most important key aspects
-
00:04:14 nowadays when you need to design and develop a large-scale application. We need to design the
-
00:04:20 application in such a way that we should test each individual functionalities. Okay, this is
-
00:04:25 another thing in the nowadays programming trends that your code has to be automatically testable.
-
00:04:35 This is also mandatory in continuous development and continuous integration scheme
-
00:04:42 following application development. Okay.
-
00:04:50 Flexibility and Extensibility.
-
00:04:54 Nowadays, flexibility and extensibility both are very much required for enterprise applications.
-
00:05:01 So we should design the application in such a way that it should be flexible so that it
-
00:05:05 can be adapted to work in different ways and extensible so that we can add new features
-
00:05:10 easily with minimum modifications. Okay, this is also another important thing and
-
00:05:15 when we follow correct design principles, we won't have a hard time to achieve this.
-
00:05:24 Parallel Development. The parallel development of an application is one of the most important
-
00:05:31 key aspects. As we know, it is not possible to have the entire development team will work
-
00:05:36 on the same module at the same time. So we need to design the software in such a
-
00:05:42 way that different teams can work on different modules. The solid design principles and design
-
00:05:48 patterns play an important role in achieving all of the above key points. All right.
-
00:05:58 The solid design principles in C# are the design principles that basically used to manage most of
-
00:06:04 the software design problems that generally we encountered in our day-to-day programming.
-
00:06:09 These design principles are provided with some mechanism that will make the software designs
-
00:06:14 more understandable, flexible and maintainable. What is the main reason behind most of the
-
00:06:21 unsuccessful applications? The following are the main reasons for most of the software failures.
-
00:06:28 Putting more functionalities on classes. In the simple word a lot of functionalities we
-
00:06:34 are putting into the class even though they are not related to the class.
-
00:06:39 Implementing tight coupling between the classes. If the classes are dependent on each other,
-
00:06:45 then a change in one class will affect the other classes also.
-
00:06:49 How to overcome the unsuccessful application development problem? We need to use the correct
-
00:06:56 architecture, (i.e. MVC, Layered, 3-tier, MVP, and so on) as per the project requirements.
-
00:07:05 As a developer, we need to follow the design principles, i.e. solid principles.
-
00:07:11 Again, we need to choose the correct design patterns as per the project requirements.
-
00:07:17 Introduction to solid design principles in C-sharp. The solid design principles are
-
00:07:24 basically used to manage most of the software design problems that generally as a developer
-
00:07:28 we face in our day-to-day programming. Solid principles represent five design principles
-
00:07:34 that basically used to make the software designs more understandable, flexible, and maintainable.
-
00:07:41 Solid acronym: Okay, this is important. Usually, nowadays, in the job interviews,
-
00:07:48 they are asking you what does solid mean. They are asking you to explain each one of these
-
00:07:55 design principles. I have been asked actually. And so let's try to memorize and understand them.
-
00:08:05 S stands for the single responsibility principle, which is also known as SRP. Okay,
-
00:08:10 don't worry that I will explain each one of these principles with details.
-
00:08:18 O stands for the open-closed principle, which is also known as OSP.
-
00:08:24 L stands for the Liskov substitution principle, which is also known as LSP.
-
00:08:30 I stand for the interface segregation principle, which is also known as ISP.
-
00:08:36 D stands for dependency inversion principle, which is also known as DIP.
-
00:08:42 What are the advantages of using solid design principles in C-sharp?
-
00:08:47 We will get the following advantages of using solid design principles in C-sharp. Achieve the
-
00:08:54 reduction in complexity of the code. Increase readability, extensibility, and maintenance.
-
00:09:02 Reduce error and implement reusability. Achieve better testability. Reduce tight coupling. Okay,
-
00:09:11 so actually we can say that each one of these is matching these ones. The first
-
00:09:18 one achieves reduction in complexity of the code with single responsibility, because each
-
00:09:23 class is responsible for only one thing. Okay, one job, and it reduces complexity. You see,
-
00:09:31 open-closed principle increases readability, extensibility, and maintenance, because our
-
00:09:39 code will be closed to modify, but it will be open to extend, and with Liskov substitution
-
00:09:49 principle, we will reduce error and implement reusability. It will allow us to reuse our code
-
00:09:55 as much as possible with easiness, and with interface segregation principle, we will achieve
-
00:10:01 better testability, and with dependency inversion principle, we will reduce tight coupling. Okay,
-
00:10:09 so let's start programming with single responsibility principle.
-
00:10:15 Okay, so let's quickly read what does it do, what is the meaning of it.
-
00:10:24 What is the single responsibility principle in C#? The single responsibility principle
-
00:10:31 in C# states that each software module or class should have only one reason to change.
-
00:10:37 In other words, we can say that each module or class should have only one responsibility to do.
-
00:10:44 So we need to design the software in such a way that everything in a class or module should be
-
00:10:49 related to a single responsibility. That does not mean your class should contain only one method or
-
00:10:56 property, you can have multiple members, methods, or properties, as long as they are related to a
-
00:11:01 single responsibility or functionality. So with the help of SRP in C-sharp, the classes become
-
00:11:09 smaller and cleaner and thus easier to maintain. Okay, so you see why it is becoming easier to
-
00:11:15 maintain. So let's start with coding. The first example is the opposite of single responsibility.
-
00:11:22 Assume that we have invoice class. Let's start implementing it. All right. Okay,
-
00:11:32 so the first example will be an example that violates single responsibility principle.
-
00:11:48 Okay. By the way, I have written this correctly, I think. Okay, let's
-
00:11:58 make it like this. Okay, example that violates single responsibility. Okay.
-
00:12:13 All right. So let's add a class. I'm going to add single responsibility violation class.
-
00:12:31 And I will copy-paste the code here. All right.
-
00:12:42 Okay, so what do we have? Let's analyze it. We have a class invoice. It has a
-
00:12:51 long invoice amount. We have daytime invoice date. They are, as you can see, attributes.
-
00:13:03 Okay, and we have add invoice method. You see it in add invoice method. We are also sending
-
00:13:11 an email. Okay. And if if an error happens, we are also logging the error. So this method
-
00:13:19 actually doing three separate things. With direct implementation, we have delete invoice method.
-
00:13:38 Which also logs, as you can see here, and we have send invoice email. All right.
-
00:13:51 And okay. Okay, so with this way, we are violating our principle. Why? Because we are doing three
-
00:14:07 separate things in a single class like this. Okay, let me show single responsibility violation. Okay.
-
00:14:17 And
-
00:14:25 here we are just calling. Okay, these are not public or. Okay. We need to generate.
-
00:14:44 One second. Okay, we don't need actually this.
-
00:14:53 Like this. Violated invoice or let's say incorrect invoice.
-
00:15:06 And incorrect invoice. Add invoice. Okay, like this. So this usage is not the correct way. Okay.
-
00:15:16 How then if it is incorrect, then how can we fix this? To fix this, we need to separate the tasks
-
00:15:26 that each the task here inside here into different classes. Okay, so first let's start adding with
-
00:15:38 a logger system. That login logger class will handle the logging if an error happens. Okay.
-
00:15:46 So to adding the logger system, I will just copy and paste this. You see,
-
00:15:52 all of these can be inside the parent class, but they will have different subclasses names.
-
00:15:59 So we are separating the concern. I mean, they can be inside this same CS file. Or you could
-
00:16:06 also do you could also insert different CS files as well. Maybe we should add
-
00:16:15 different CS files so it will be easier to manage. So let's add a logger CS file. Okay,
-
00:16:25 in parent class logger, so I will just change it.
-
00:16:33 Like this. Okay, so we have an interface and with interface we define which methods this inheriting
-
00:16:43 class or yes, inheriting class should have. So we have a logger class that uses interface iLogger
-
00:16:51 and inside we are constructing the methods. We have constructor, we have info method,
-
00:16:58 we have debug method and we have error method. You can fill anything inside here. The purpose of this
-
00:17:05 lecture to demonstrate how to do proper solid design principles following programming. So we
-
00:17:14 also need to now modify our code. Oh, we also need to add another class for mail messaging. Okay.
-
00:17:26 So let's add a mail sender class.
-
00:17:34 All right, let's name it as mail sender. And inside here, make it as
-
00:17:44 public class mail sender. You see mail sender has only one purpose and it is send email.
-
00:17:50 All right. Okay, now finally we can modify our invoice class properly to be following solid
-
00:18:00 design principles. And for that, okay, let's add another button. You know, you can fill
-
00:18:09 anything inside these methods. Okay, and let's call it as proper single responsibility. Okay.
-
00:18:30 All right, so I am going to add another class and I will say proper invoice.
-
00:18:37 Okay, let's name it as proper invoice.
-
00:18:43 Okay, we can leave it as internal as well for the demo and let's copy and paste code.
-
00:18:52 Okay, so I also need to change the constructor name. All right. So what is the difference in
-
00:19:03 this case? In this case, you see, we have a logger and mail sender as injection dependency injection.
-
00:19:17 Actually, not this. Okay. And then we use those initialized objects
-
00:19:25 to add info or send email. Okay, you see, if an error occurs, then we are
-
00:19:37 using the error method like here. All right. And why does it show error?
-
00:19:47 Okay, because I need to send it like this. And in the other case, we also modify it like this.
-
00:19:57 You see, instead of sending email directly inside in the first case.
-
00:20:03 In the violation method. In the violation method methodology, we were sending mail directly like
-
00:20:11 this. The code was also written in the same class and we were also logging the errors like
-
00:20:17 this. However, now we have separate classes for each task. We have mail sender class and we have
-
00:20:25 file logger. Okay. And now we can use each one of them. So if we change anything in these separate
-
00:20:36 classes that it won't affect anything in our application workflow runtime. This is a single
-
00:20:48 responsibility principle. And let's see if the author of this article said anything.
-
00:20:58 Okay. As you can see, the invoice class delegating the logging activity to the logger class.
-
00:21:07 In the same way, delegate the email sending activity to the mail sender class.
-
00:21:13 Now the invoice class now only concentrates on invoice related activities.
-
00:21:18 All right, now we can move to the next principle, which is open-closed principle.
-
00:21:30 What is the open-closed principle in C-sharp?
-
00:21:35 The open-closed principle states that software entities such as modules, classes, functions,
-
00:21:41 etc. should be open for extension, but closed for modification.
-
00:21:46 Let us understand the above definition in simple words. Here we need to understand two things.
-
00:21:53 The first thing is open for extension and the second thing is closed for modification.
-
00:21:59 The open for extension means we need to design the software modules / classes in such a way
-
00:22:05 that the new responsibilities or functionalities should be added easily when new requirements come.
-
00:22:11 On the other hand, closed for modification means we should not modify the class / module until we
-
00:22:17 find some bugs. The reason for this is we have already developed a class / module and it has
-
00:22:24 gone through the unit testing phase. So we should not have to change this as it affects
-
00:22:29 the existing functionalities. In simple words, we can say that we should develop one module / class
-
00:22:36 in such a way that it should allow its behavior to be extended without altering its source code.
-
00:22:46 Implementation guidelines for the open-closed principle, OCP, in C-sharp. The easiest way
-
00:22:54 to implement the open-closed principle in C-sharp is to add the new functionalities
-
00:22:59 by creating new derived classes which should be inherited from the original base class.
-
00:23:05 Another way is to allow the client to access the original class with an abstract interface.
-
00:23:13 So, at any given point of time when there is a change in requirement or any new requirement
-
00:23:18 comes then instead of touching the existing functionality, it's always better and suggested
-
00:23:22 to create new derived classes and leave the original class implementation as it is. Okay,
-
00:23:28 so this is the key issue, the key point. Whenever you need a new functionality, then you shouldn't
-
00:23:36 change the existing implementation. You should extend it and then implement that new feature with
-
00:23:45 that extension. Okay, this is really important, the key part of the open-closed principle.
-
00:23:53 By the way, in an interview I was asked what is open-closed principle and I was asked to explain
-
00:24:00 it, so this is really important. Problems of not following the open-closed principle in C-sharp.
-
00:24:08 If you are not following the open-closed principle during the application development process,
-
00:24:13 then you may end up your application development with the following problems. If you allow a class
-
00:24:20 or function to add new logic then as a developer you need to test the entire functionalities which
-
00:24:25 include the old functionalities as well as new functionalities of the application.
-
00:24:31 As a developer, it is also your responsibility to tell the QA, quality assurance, team about
-
00:24:37 the changes in advance so that they can prepare themselves in advance for regression testing
-
00:24:41 along with the new feature testing. If you are not following the open-closed principle,
-
00:24:47 then it also breaks the single responsibility principle as the class or module is going to
-
00:24:52 perform multiple responsibilities. If you are implementing all the functionalities in
-
00:24:58 a single class, then the maintenance of the class becomes very difficult.
-
00:25:06 Because of the above key points, we need to follow the open-closed principle in C-sharp
-
00:25:12 while developing the application. Okay, so let's start coding it.
-
00:25:18 The first example will be an example that
-
00:25:23 breaks the open-closed principle. Let's name it as example breaks open-closed principle. Alright,
-
00:25:49 let's copy and paste the code.
-
00:26:12 So I'm going to add another class breaks open-closed.
-
00:26:25 Okay, so let's check it out. And what does that mean? We have invoice class
-
00:26:32 and that invoice class has get invoice discount, which takes two parameters,
-
00:26:38 which is amount and the invoice type. So according to the invoice type,
-
00:26:43 we are returning the discount amount. And what is wrong here? You see that this method
-
00:26:56 is doing different tasks based on the invoice type. Okay, so if I want to add another invoice
-
00:27:06 type here, let's say extra invoice, then what do I need to do for implementation? For implementation,
-
00:27:14 then I need to add more else if like this. And you see as I add more invoice types,
-
00:27:24 then I need to modify this existing class further. And this totally breaks open-closed
-
00:27:33 principle because I am modifying the existing class method for extending its functionality.
-
00:27:41 Okay, and this breaks the open-closed principle. And as explained in the article,
-
00:27:50 this forces us to retest all of the functionality and report to the quality assurance team and
-
00:27:59 other things. So I need to modify this in a way that it won't break it anymore.
-
00:28:06 Let's make example of this in our application. Okay,
-
00:28:30 now let's fix it in a way that it won't break our functionality. I mean the open-closed principle.
-
00:28:41 Okay, let's say proper open-closed principle.
-
00:29:05 So what do we need to do is we need to generate a base class and extend it. In this case, it will be
-
00:29:12 like this. Let's copy paste and analyze it. Let's add another class and name as proper open-closed.
-
00:29:25 Okay, so now we have a base class as invoice and this invoice class has a virtual method. Virtual
-
00:29:36 methods can be overridden by the inheriting classes and it has a get invoice discount
-
00:29:47 and double amount. Okay, and it's just deducts 10 point from the amount and return it. Then we have
-
00:29:57 final invoice, which has a get invoice discount method and it overrides the base method with
-
00:30:05 double amount. Then it gets the base. It calls the base get invoice and minus 50. This is the
-
00:30:14 final invoice. You see, these are the actual invoice types that we have in. Let me show again.
-
00:30:23 Okay, here we had final invoice, which was deducting 100 percent.
-
00:30:30 And we had proposed it, which was deducting 50 and we had extra invoice, which was deducting 75. So
-
00:30:38 for final invoice, we need to deduct 90 actually to make it equal and for proper proposed invoice,
-
00:30:47 it is minus 50. Yeah, this is correct. And okay, by the way, when we provide amount. Okay.
-
00:30:57 Yes, it's true. And there is a recurring invoice. Okay, let's fix this in this case.
-
00:31:18 Yeah, now fix it. It is minus 30. Let's make it minus 65. Okay. So you see, this is how
-
00:31:28 we are extending the functionality of the class with inheritance. Okay. I can add so many other
-
00:31:39 new invoice types here like super invoice. Okay. And I can say that just minus five.
-
00:31:49 Okay. So it is totally up to you to extend it. Now it looks much better. It is easier to read.
-
00:31:55 It is easier to manage and it won't break our existing code. It won't require retesting and
-
00:32:04 validating our existing code because it is just extending the base class. Okay. And let's make
-
00:32:13 an example call of that. By the way, this is not valid anymore. Yeah. In this case, proper. Okay.
-
00:32:28 So you see, now I can define each one of them. Let's say recurring invoice, recurring invoice.
-
00:32:40 And then just I can say get invoice discount with something like this. Okay.
-
00:32:49 All right. And yeah, they all made an example such that they.
-
00:32:59 Now the invoice class is closed for modification,
-
00:33:03 but it is open for the extension as it allows creating new classes deriving from the invoice
-
00:33:08 class, which clearly follows the open closed principle in C sharp. In the next article. Okay.
-
00:33:15 So let's move to Liskov substitution principle and let's just read its definition. All right. Okay.
-
00:33:27 What is the Liskov substitution principle in C sharp? The Liskov substitution principle is
-
00:33:35 a substitutability principle in object oriented programming language. This principle states that
-
00:33:41 if S is a subtype of T, then objects of type T should be replaced with the objects of type S.
-
00:33:49 In simple words we can say that, when we have a base class and child class relationships i.e.,
-
00:33:55 inheritance relationships, then, if we can successfully replace the object / instance
-
00:34:00 of a parent class with an object / instance of the child class, without affecting the
-
00:34:05 behavior of the base class instance, then it is said to be in Liskov substitution principle.
-
00:34:11 If you are not getting this point properly, don't worry, we will see some real time examples to
-
00:34:16 understand this concept. For example, a father is a teacher whereas his son is a doctor. So here,
-
00:34:25 in this case, the son can't simply replace his father even though both belong to the same family.
-
00:34:31 Okay, so let's make an example. First, we will make an example that violates Liskov's principle.
-
00:34:58 Alright, so to demonstrate a violation of Liskov's principle, I will just add a class here.
-
00:35:20 Okay, and we have two classes. The first class is apple and the second class is orange. You know,
-
00:35:28 apples and oranges are totally different things. So, we shouldn't be able to define apple
-
00:35:40 as an orange and get its color. Okay, this is a violation. Let's see what does that mean.
-
00:35:52 Okay, so let's define first, okay, Liskov violation, apple dot, okay, apple,
-
00:36:03 and let's see, MessageBox.Show apple, and let's say, apple color. One second, sorry about that.
-
00:36:23 And let's define orange. Okay, this will be an orange. Let's say orange color.
-
00:36:44 And now I am going to define
-
00:36:48 an apple from orange type. Okay, what does that mean? It means that something like this.
-
00:36:56 Let's say apple, and let's get its color. Okay, apple orange color. Okay, orange type apple color.
-
00:37:15 So, what we want in this case is to get the type of, get the color of orange, not the,
-
00:37:23 I mean to get the type of apple, not the orange. Because this is apple type and orange can't
-
00:37:30 replace it. However, in this case, we are going to get the color of orange instead of apple. Okay,
-
00:37:38 so let's run our application to verify. We have apple color red, we have orange color red, and
-
00:37:46 we have orange type color orange. Why do we have an error here? Let's fix it. Okay, now it will be,
-
00:37:55 okay, apple color red, orange color orange, and we have orange type apple color orange. And
-
00:38:02 this is the violation of Liskov principle. Why? Because, as you can see in the above example,
-
00:38:11 apple is the base class and orange is the child class i.e. there is a parent-child relationship.
-
00:38:18 So, we can store the child class object in the parent reference variable i.e. apple
-
00:38:23 apple equals new orange, and when we call the get color i.e. apple dot get color,
-
00:38:28 then we are getting the color of the orange not the color of apple. That means once the
-
00:38:33 child object is replaced i.e. apple storing the orange object, the behavior is also changed. Okay,
-
00:38:40 this is the key issue. The behavior is changed and this is violation of Liskov substitution
-
00:38:49 principle. This is against the LSP principle. The Liskov substitution principle in C sharp states
-
00:38:57 that even the child object is replaced with the parent, the behavior should not be changed. So,
-
00:39:03 in this case, if we are getting the color of apple instead of orange, then it follows the Liskov
-
00:39:09 substitution principle. So if we were seeing the color of apple then it would be following
-
00:39:14 the Liskov substitution principle. That means there is some issue with our software design.
-
00:39:21 Let us see how to overcome the design issue and makes the application follow
-
00:39:25 Liskov substitution principle. Okay, so let us make the proper example.
-
00:39:39 Okay, I will name it as Liskov.
-
00:39:55 All right, and let's add a new class as here proper Liskov. And let's copy and paste the code.
-
00:40:32 Okay, so let's check out the difference. So now we have an abstract class which is fruit. So in
-
00:40:40 this abstract class, we have get color method and it is also abstract as you can see. So any
-
00:40:48 class that inherits this abstract class has to define this method. So we have apple which has
-
00:40:56 red and it is overriding the abstract class in the fruit abstract class abstract method in the in the
-
00:41:06 abstract class of fruit and we have orange. You see, we didn't override the base class method. We
-
00:41:14 overrided the abstract class method different than the first example. And now let's make our test
-
00:41:26 in the same way. So this time it will be proper Liskov principle.
-
00:41:39 Okay, and you see now I am not able to define
-
00:41:44 apple with the type of orange. Okay, so let's call this as fruit orange.
-
00:41:53 And it will be fruit orange and you see how it makes sense now.
-
00:42:01 Okay, fruit orange and fruit apple. Okay, yes, and now it is much more sense making and it will work
-
00:42:16 correctly. Okay, apple color red, orange color orange, fruit orange color orange and fruit
-
00:42:23 apple color red. As expected, working perfectly fine and it's now follows the Liskov principle.
-
00:42:33 Okay, now time to see interface segregation principle.
-
00:42:45 What is the interface segregation principle in C sharp? The interface segregation principle states
-
00:42:52 that clients should not be forced to implement any methods they don't use. Rather than one fat
-
00:42:58 interface, numerous little interfaces are preferred based on groups of methods with
-
00:43:03 each interface serving one sub module. Let us break down the above definition into two parts.
-
00:43:11 First, no class should be forced to implement any methods of an interface they don't use.
-
00:43:18 Secondly, instead of creating large or you can say fat interfaces, create multiple smaller interfaces
-
00:43:24 with the aim that the clients should only think about the methods that are of interest to them.
-
00:43:29 Sorry about that, it was my baby child, my daughter.
-
00:43:37 Okay, as per the single responsibility principle of solid like classes, interfaces also should have
-
00:43:45 a single responsibility. That means we shouldn't force any class to implement any methods which
-
00:43:51 they don't require. Okay, so you see how these principles are, let's say, tightly coupled. So
-
00:43:59 one principle requires another principle and when you follow all of them, they are working like as
-
00:44:06 a team. Okay, so let's start with a violation of interface segregation principle. All right.
-
00:44:21 Okay, let's call it as violation of interface segregation.
-
00:44:42 All right. And let's copy and paste our class. So let's add a new class first. Interface violation.
-
00:45:03 And here, first, we have public interface, I printer tasks. Okay, and it defines four methods.
-
00:45:14 We can define this abstract class method like this return. Okay, let's say "white". Okay, this will
-
00:45:26 work. By the way, I need to. If I make it a method like this, I have to mark it as virtual so that it
-
00:45:35 can it can be overridden. If I make it abstract like this, then I can't it can't have a body.
-
00:45:45 Okay, so you can define body inside an abstract class with virtual methods that can be overridden.
-
00:45:54 Or you can make it abstract and it won't have a body. It won't have a body in method. Okay.
-
00:46:02 Okay, so for demonstration, let's also have another class here as
-
00:46:12 abstract class with body having method. And let's say virtual get color.
-
00:46:34 Okay, so both methodology is fine. Okay, let's continue with our interface violation,
-
00:46:41 interface principle violation. So we have I printer tasks and it has print, scan,
-
00:46:47 fax and print duplex methods. Interfaces can have method implementation, but they can't have they
-
00:46:57 can't have fields, data fields like public int. Okay, my property. Now it says that interfaces
-
00:47:07 cannot contain instance fields. Okay, so this is also valid. Okay, let's return back to our topic.
-
00:47:17 So we have I printer tasks. And not only that, we have. Okay, let's see.
-
00:47:28 HP laser jet printer, which inherits I printer task interface. So interfaces
-
00:47:36 are like blueprints and we have to implement each of the methods defined in the interface.
-
00:47:46 In the abstract class that we don't have to implement all of them. Actually, that was also
-
00:47:52 another mistaken information that I have given. For example, let's say we have abstract get color.
-
00:48:06 And these methods. Have to implement them, but. We can also define a body for that method and. Now.
-
00:48:29 Oh, it was true, so in both cases we have to implement both. Okay,
-
00:48:35 so let's just ignore that too. Okay, so I printer tasks is inherited with HP laser
-
00:48:45 jet printer and now we are defining each one of the methods and. You see, we have print, scan,
-
00:48:54 fax and print duplex. Each one of them, each one of the defined methods is also used here.
-
00:49:01 However, let's say I also have another class that inherits the I printer tasks. Interface and
-
00:49:12 this is liquid inject printer and in liquid inject printer. We don't have fax method or print duplex.
-
00:49:19 Therefore, they are not implemented exception. They are throwing not implemented exception.
-
00:49:27 And this is a violation of interface segregation principle. Why? Because
-
00:49:33 we are inheriting an interface. But we are not using all of the methods that it requires. Okay,
-
00:49:45 so how can we fix this problem? We can fix this problem by. Splitting this interface into multiple
-
00:49:54 interfaces and implementing necessary multiple interfaces in each class. Okay,
-
00:50:01 so let's first define this example. Then move to the proper interface design. Okay, for example.
-
00:50:28 Okay, now fix this interface violation problem.
-
00:50:41 Okay, proper interface segregation.
-
00:51:03 Let's fix the name segregation, yes.
-
00:51:17 Okay. All right, so let's add another class. And see the example proper interface
-
00:51:46 segregation. All right, let's copy paste. The fixed interface class implementations.
-
00:52:08 Okay, so we did split the IPrinter tasks in the first case in the first example into three
-
00:52:17 interfaces. The first one is IPrinter tasks. It has print and scan. What does this mean?
-
00:52:24 This means that every class that inherits IPrinter tasks interface will have these two methods. Okay,
-
00:52:33 so these are the basic methods that every printer will have print and scan. Then we
-
00:52:40 have I fax tasks, which means that whichever the class in inherits I fax tasks interface will have
-
00:52:48 fax method and whichever the class that inherits I printer duplex tasks will have print duplex.
-
00:52:55 So since these are interfaces, we can have multiple interfaces inheritance. However,
-
00:53:01 for abstract classes that you can have only one implementation.
-
00:53:09 Yes. Okay. For example, if we define two abstract classes, such as. Okay. And public abstract.
-
00:53:23 class ab2. Then our class can inherit only one of them. For example, it can inherit ab1. And
-
00:53:36 it has to come before any interfaces like this. However, if I also add ab2 here.
-
00:53:45 Then it will say that the base class cannot have multiple base classes. I mean, the child class
-
00:53:53 cannot have multiple base classes. However, it can have as many as interfaces as necessary.
-
00:54:01 Okay, so the HP laser jet printer will have both print, scan, fax and print duplex methods. All of
-
00:54:11 them and the liquid inject printer will have only print and scan, which are the very basic functions
-
00:54:17 of printers. Okay, so this is the corrected way, the proper way of interface segregation principle.
-
00:54:30 Okay, now time to move the last one.
-
00:54:34 The last principle in the solid design principles is what dependency inversion principle.
-
00:54:45 What is the dependency inversion principle in C sharp?
-
00:54:49 The dependency inversion principle DIP states that high level modules / classes should not
-
00:54:55 depend on low level modules / classes. Both should depend upon abstractions.
-
00:55:01 Secondly, abstractions should not depend upon details. Details should depend upon abstractions.
-
00:55:10 The most important point that you need to remember while developing real time applications,
-
00:55:14 always to try to keep the high level module and low level module as loosely coupled as possible.
-
00:55:22 When a class knows about the design and implementation of another class,
-
00:55:26 it raises the risk that if we do any changes to one class will break the other class. So we must
-
00:55:32 keep these high level and low level modules / classes loosely coupled as much as possible.
-
00:55:38 To do that, we need to make both of them dependent on abstractions instead of knowing each other.
-
00:55:45 Okay, so let's start with an example to understand better. Okay. Okay,
-
00:55:55 I'm adding a button.
-
00:56:01 Violation of dependency inversion.
-
00:56:23 Let's click and oh, we also need to fix this as proper Interface separation.
-
00:56:36 Okay. And then I can call. Okay, so let's add a new class for dependency inversion violation.
-
00:56:55 All right. And in here, let's copy and paste the code. Okay, we have a public
-
00:57:03 class employee and it has four attributes, which are ID, name, department and salary.
-
00:57:15 And we have public class employee business logic. Okay. And we have public class data
-
00:57:26 access factory. Let's paste it in above the employee data access and. Okay. We also have.
-
00:57:45 Okay, one second. Here. Yes. Now all is fixed. Okay.
-
00:58:01 So we have employee class, which holds the data of each employee. We have employee data access class,
-
00:58:08 which implements the logic of accessing employee data. You see, it has a public
-
00:58:15 method that returns employee data. It is named as get employee details. It takes ID of the employee.
-
00:58:24 Then it generates an employee, an example employee and returns it.
-
00:58:29 Okay. And we have two other classes, which are data access factory.
-
00:58:35 It has a public static method that returns a new instance of employee data access class.
-
00:58:45 Okay. We have employee business logic class, which implements employee data access.
-
00:58:55 Which uses employee data access and generates an instance of employee data access class in
-
00:59:04 the constructor. And it returns the details. Okay. So how is this violating the dependency inversion?
-
00:59:19 Okay. Let's read it to find out. Let us compare the above example with the dependency
-
00:59:29 inversion principle in C sharp. As per the dependency inversion principle definition,
-
00:59:35 a high level module should not depend on low level modules. Both should depend on the abstraction.
-
00:59:43 So first we need to figure out which one is the high level module class and which one is the
-
00:59:49 low level module class. In our example, a high level module is a module that always depends on
-
00:59:56 other modules. So in our example, the employee business logic class depends on employee data
-
01:00:03 access class. So here the employee business logic class is the high level module and the employee
-
01:00:09 data access class is the low level module. So as per the first rule of the dependency inversion
-
01:00:16 principle in C sharp, the employee business logic class / module should not depend on the concrete
-
01:00:22 employee data access class / module. Instead, both the classes should depend on abstraction.
-
01:00:29 The second rule of the dependency inversion principle state that abstractions should
-
01:00:34 not depend on details. Details should depend on abstractions.
-
01:00:39 Before understanding this, let us first understand what is an abstraction. What is abstraction? In
-
01:00:47 simple words, we can say that abstraction means something which is non concrete. So abstraction
-
01:00:54 in programming means we need to create either an interface or abstract class which is non
-
01:00:59 concrete so that we cannot create an instance of it. In our example, the employee business
-
01:01:04 logic and employee data access are concrete classes that mean we can create objects of it.
-
01:01:11 As per the dependency inversion principle in C sharp, the employee business logic high
-
01:01:16 level module should not depend on the concrete employee data access low level module class.
-
01:01:23 Both classes should depend on abstractions, meaning both classes should depend on either
-
01:01:28 an interface or an abstract class. OK, so let's see how employee business logic is depending on
-
01:01:35 concrete employee data access in our application. And we are right away seeing it. So you see,
-
01:01:43 OK. OK, as you can see, it is dependent on the concrete instance of employee data access class
-
01:01:53 with here. And that is a violation of dependency inversion principle. So we need to modify our
-
01:02:05 logic with abstraction abstract classes or interfaces. Instead of depending on concrete
-
01:02:15 instance of a class. OK, so let's make an example of this with violation. Actually,
-
01:02:41 that's the point is yes.
-
01:02:49 Yes,
-
01:03:09 OK, so let's also see the proper fixed version of the dependency inversion. Right,
-
01:03:23 it will be proper dependency inversion.
-
01:03:35 OK, and let's add another class and name it as
-
01:03:47 proper dependent version. OK, so how are we going to separate? We are going to separate it as
-
01:03:59 an interface IEmployeeDataAccess which has employee get employee details method.
-
01:04:06 And we are going to have. OK, employee data access class which inherits IEmployeeDataAccess
-
01:04:14 interface. We have data access factory which still has the same method and we have
-
01:04:33 employee business logic. You see, in this case, it has a field of IEmployeeDataAccess. OK, and
-
01:04:50 IEmployeeDataAccess is an interface. Therefore, it is using the interface instead of concrete
-
01:05:04 instance of a class. So employee data access was a class. Therefore, this was a concrete instance.
-
01:05:13 However, in here we are just using the interface instead of a concrete instance of a class.
-
01:05:22 And this is the proper way of doing dependency inversion. OK,
-
01:05:35 that's it. We have implemented the dependency inversion principle in our example where the
-
01:05:41 high level module employee business logic and low level module employee data access depend
-
01:05:47 on abstraction IEmployeeDataAccess. Also abstraction IEmployeeDataAccess
-
01:05:54 does not depend on details employee data access, but details depend on abstraction.
-
01:06:01 Advantages of implementing the dependency inversion principle in C sharp.
-
01:06:07 Now the employee business logic and employee data access classes are loosely coupled classes
-
01:06:12 because employee business logic does not depend on concrete employee data access class. Instead,
-
01:06:18 it includes a reference of IEmployeeDataAccess interface. So now we can easily use another
-
01:06:25 class that implements IEmployeeDataAccess with a different implementation. OK, this is it. And
-
01:06:34 let's also make an example of this. Proper dependency inversion. And we have IEmployee logic.
-
01:07:02 And now when we run our application, it will compile.
-
01:07:09 OK, if you have any questions, you can join our Discord channel. Also,
-
01:07:19 I will upload this into a GitHub repository and I will put the link of the repository in
-
01:07:25 the description of the video. So make sure that you check it out. Thank you for watching. I hope
-
01:07:32 you have enjoyed. Please make a comment and let me know what you think. Hopefully see you later.
