• Skip to main content
  • Skip to primary sidebar

Technical Notes Of
Ehi Kioya

Technical Notes Of Ehi Kioya

  • About
  • Contact
MENUMENU
  • Blog Home
  • AWS, Azure, Cloud
  • Backend (Server-Side)
  • Frontend (Client-Side)
  • SharePoint
  • Tools & Resources
    • CM/IN Ruler
    • URL Decoder
    • Text Hasher
    • Word Count
    • IP Lookup
  • Linux & Servers
  • Zero Code Tech
  • WordPress
  • Musings
  • More
    Categories
    • Cloud
    • Server-Side
    • Front-End
    • SharePoint
    • Tools
    • Linux
    • Zero Code
    • WordPress
    • Musings

SOLID Design Principles in C#: Part 1 – Single Responsibility

Tagged: .Net, C#, Coding Practices, Coding Right, Design Principles, Right design, SOLID

  • This topic has 0 replies, 1 voice, and was last updated 2 years, 5 months ago by Aruorihwo.
Viewing 1 post (of 1 total)
  • Author
    Posts
  • February 26, 2020 at 10:13 am #86685
    Spectator
    @aruorihwo

    Solid Principles banner

    The question that worries a lot of people is, “when I am writing code, am I doing it right?” and it should be a question that developers need to think through. Design patterns are best practice concepts that we can implement into our code to make it better, safe and more professional in some way. They are like railings for your application.

    The thing about design principles is that they make you think about your code. It is one thing to write a whole bunch of code and achieve a result but it’s a whole different thing to actually write code that is scalable, safe and standard. This article will focus on the popular SOLID principles and see what each of the principles mean and how they should change our programming style while also learning how far we should take it.

    SOLID principles are some of those design principles that give us the ability to manage most of the software design problems. It is an acronym for the five distinct principles applicable in object-oriented design. If you apply these principles properly, you can design a software that is extremely extendable and scalable. SOLID principles apply to any programming language, but we would be using C# for our examples in this article. Let’s start with the principles according to its acronym.

    S – Single Responsibility Principle

    This is the first principle in acronym S.O.L.I.D. It is a very straightforward principle that says that every software module must have only a single reason to change. This implies that in an object-oriented design, every class should have only one task – one job that it does. A class should be responsible for one job. Of course, this does not mean a class should have only one member or one method. A class may have many methods as long as they all relate to the single responsibility.

    Let’s see a practical example below:
    In a console application, we would write a code for a very simple order making application. First of all, lets create a class that would hold our properties. We will call it Order

    
    public class Order
    {
        public string ItemName { get; set; }
        public DateTime DeliveryDate { get; set; }
    }
    

    Then we will write code for the order making application in the main method of our Program class

    
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Welcome to Our Order Processing Appllication!");
    
            //Asks for Order details
            Order order = new Order();
    
            Console.WriteLine("What is the name of the item you are ordering?");
            order.ItemName = Console.ReadLine();
    
            Console.WriteLine("What date would you like the item delivered? (Date Format dd-MMM-yyyy)");
            DateTime deliveryDate;
            DateTime.TryParse(Console.ReadLine(), out deliveryDate);
            order.DeliveryDate = deliveryDate;
    
            //Validates order details
            if (string.IsNullOrWhiteSpace(order.ItemName))
            {
                Console.WriteLine("This is not a valid item name!");
                Console.ReadLine();
                return;
            }
    
            if (order.DeliveryDate < DateTime.Now)
            {
                Console.WriteLine("This is not a valid delivery date!");
                Console.ReadLine();
                return;
            }
    
            //Create order for the request
            Console.WriteLine($"You order for {order.ItemName} has been created with Order Id: {new Random().Next(1000, 9999)}");
    
            Console.ReadLine();
        }
    }
    

    This code is very simple. The application asks for the item that is required to be delivered and the desired delivery date. It then validates that the item name and also that the delivery date is a real future date and then generates a random id for the order. Very simple and straightforward. The only issue is that this code validates the Single Responsibility principle. How? Let’s see.

    If we recall, single responsibility principle states that a class should have only one reason for change. But in this code, if we decide to change how we respond to the user, either how we welcome them or how we end interactions with them, we will have to change the class. If we decide to change the way we validate entries, we will have to change this same class. If we also decide to change how we generate order ids for the order, we will have to change the class. This means we currently have numerous reasons to change the class, which isn’t in line with the single responsibility principle.

    How can we change this code to follow this principle?

    One of the first things we would need to change is the way the code communicates with the user. This means that we would have to create a Welcome message class and an End application message class. This however is an overkill. We must understand that these principles are just that – principles. They aren’t “do or die” rules as such. They just point our thinking and design patterns in the right way. What we would do is think of this as a messaging responsibility and have a Messages class to handle all messages to the user. The Messages class will then have the following code

    
    public class Messages
    {
        public static void WelcomeMessage()
        {
            Console.WriteLine("Welcome to Our Order Processing Appllication!");
        }
        public static void EndApplication()
        {
            Console.ReadLine();
        }
    }
    

    As we can see, this Messages class will the have only one responsibility, which is to handle all user messages. This will of course cause some changes to how the Main method is structured, making it look like below:

    
    class Program
    {
        static void Main(string[] args)
        {
            Messages.WelcomeMessage();
    
            //Asks for Order details
            Order order = new Order();
    
            Console.WriteLine("What is the name of the item you are ordering?");
            order.ItemName = Console.ReadLine();
    
            Console.WriteLine("What date would you like the item delivered? (Date Format dd-MMM-yyyy)");
            DateTime deliveryDate;
            DateTime.TryParse(Console.ReadLine(), out deliveryDate);
            order.DeliveryDate = deliveryDate;
    
            //Validates order details
            if (string.IsNullOrWhiteSpace(order.ItemName))
            {
                Console.WriteLine("This is not a valid item name!");
                Messages.EndApplication();
                return;
            }
    
            if (order.DeliveryDate < DateTime.Now)
            {
                Console.WriteLine("This is not a valid delivery date!");
                Messages.EndApplication();
                return;
            }
    
            //Create order for the request
            Console.WriteLine($"You order for {order.ItemName} has been created with Order Id: {new Random().Next(1000, 9999)}");
    
            Messages.EndApplication();
    
        }
    }
    

    The second responsibility we would extract from the main method is the way we capture our order details. We can then create a separate class whose responsibility is capturing order data. We will call this class OrderDataCapture.

    
    public class OrderDataCapture
    {
        public static Order Capture()
        {
            Order order = new Order();
    
            Console.WriteLine("What is the name of the item you are ordering?");
            order.ItemName = Console.ReadLine();
    
            Console.WriteLine("What date would you like the item delivered? (Date Format dd-MMM-yyyy)");
            DateTime deliveryDate;
            DateTime.TryParse(Console.ReadLine(), out deliveryDate);
            order.DeliveryDate = deliveryDate;
    
            return order;
        }
    }
    

    This class is responsible for getting the order information from application user. This is the only task it does. This means that it now follows the single responsibility principle. The main method will the have a new look

    
    class Program
    {
        static void Main(string[] args)
        {
            Messages.WelcomeMessage();
    
            //Asks for Order details
            var order = OrderDataCapture.Capture();
    
    
            //Validates order details
            if (string.IsNullOrWhiteSpace(order.ItemName))
            {
                Console.WriteLine("This is not a valid item name!");
                Messages.EndApplication();
                return;
            }
    
            if (order.DeliveryDate < DateTime.Now)
            {
                Console.WriteLine("This is not a valid delivery date!");
                Messages.EndApplication();
                return;
            }
    
            //Create order for the request
            Console.WriteLine($"You order for {order.ItemName} has been created with Order Id: {new Random().Next(1000, 9999)}");
    
            Messages.EndApplication();
        }
    }
    

    The next responsibility to take care of is the validation of entries. We don’t want to do validation in the Main method, so we would have to create another class for it. We can then create an OrderValidator class with the following code:

    
    public class OrderValidator
    {
        public static bool Validate(Order order)
        {
            var validationStatus = true;
            if (string.IsNullOrWhiteSpace(order.ItemName))
            {
                Messages.ShowValidatorError("item name");
                return !validationStatus;
            }
    
            if (order.DeliveryDate < DateTime.Now)
            {
                Messages.ShowValidatorError("order date");
                return !validationStatus;
            }
            return validationStatus;
        }
    }
    

    You may be wondering why we didn’t create separate methods for each validation but this would mean that the Main method would have to call separate methods for each validation which would also imply that changes to how we carryout the validation would also affect the main method and that would not be following the principle.

    Also, you would also notice that there is a method call to Messages.ShowValidatorError. This method was created to ensure we keep the responsibility in this class to just one. If we didn’t do that, the class will have to change if we decide to change how we show validation error messages. This responsibility has been taken to the Messages class which is responsible for messaging the user of the application. Here is the method below

    
    public static void ShowValidatorError(string entryName)
    {
        Console.WriteLine($"This is not a valid {entryName}!");
    }
    

    We will then update the main method to call the Validate method in the OrderValidator class and we would give it the freedom to decide how it would handle the process if the order isn’t valid. We may want to start the whole entry process again or do something else. So, we take that away from the OrderValidator class as it shouldn’t be concerned with how the flow is.

    
    class Program
    {
        static void Main(string[] args)
        {
            Messages.WelcomeMessage();
    
            //Asks for Order details
            var order = OrderDataCapture.Capture();
    
            //Validates order details
            var isOrderValid = OrderValidator.Validate(order);
            if (!isOrderValid)
            {
                Messages.EndApplication();
                return;
            }
    
            //Create order for the request
            Console.WriteLine($"You order for {order.ItemName} has been created with Order Id: {new Random().Next(1000, 9999)}");
    
            Messages.EndApplication();
    
        }
    }
    

    We then have to get move the order creation responsibility from the main method. This then mean we would create another class and call it OrderCreator and move the order creating code to that class.

    
    public class OrderCreator
    {
        public static void CreateOrder(Order order)
        {
            Console.WriteLine($"You order for {order.ItemName} has been created with Order Id: {new Random().Next(1000, 9999)}");
        }
    }
    

    We could move the message on successful creation of the order to the Messages class, but we decided to leave it here as it is very specific and may never be reused in the project.

    The Main method is now fully cleaned up based on the Single Responsibility principle. And that is the basics of the principle. There are other things we would have done if we are following all the principles such a using interfaces, etc. but we want to focus strictly on this one principle.

    
    class Program
    {
        static void Main(string[] args)
        {
            Messages.WelcomeMessage();
    
            //Asks for Order details
            var order = OrderDataCapture.Capture();
    
            //Validates order details
            var isOrderValid = OrderValidator.Validate(order);
            if (!isOrderValid)
            {
                Messages.EndApplication();
                return;
            }
    
            //Create order for the request
            OrderCreator.CreateOrder(order);
    
            Messages.EndApplication();
    
        }
    }
    

    The main method now does only one thing, which is control the flow of the application. There are numerous advantages to following this principle. Now, if you want to know where anything is, you can simply check the class that handles that task. If you name your classes well, you would at a glance know where to go to change any task that you desire.
    Also, you would see that the code is now cleaner and more readable. You would also notice that the methods are now shorter and lightweight. You do not have to read so many lines of code to know or understand what a class is doing and make changes accordingly. You also can easily make changes in one place and it changes everywhere that responsibility or task is needed.

    The most common complaint is that this principle would encourage making too many classes. The fact is this should not be an issue as the classes would be lightweight and would have no impact on disk space. Also, the navigation tools in Visual Studio would make moving between the classes easy and seamless. I do understand that there is a possibility of going overboard and making classes for every tiny thing but that is where you would have to make a balance. Like earlier stated, it’s a principle not a law. However, there is a general rule of thumb that says if you must scroll to read through the entirety of your class, then you are doing something wrong. You could also think that I have to change your class for more than one reason, you should break the class.

    That’s the Single Responsibility principle. You do not have to go changing all your existing projects, as that would be a lot of work, but you can make it a duty to use this principle moving forward.

  • Author
    Posts
Viewing 1 post (of 1 total)
  • You must be logged in to reply to this topic.
Log In

Primary Sidebar

FORUM   MEMBERSHIP

Log In
Register Lost Password

POPULAR   FORUM   TOPICS

  • How to find the title of a song without knowing the lyrics
  • How To Change Or Remove The WordPress Login Error Message
  • The Art of Exploratory Data Analysis (Part 1)
  • Welcome Message
  • Replacing The Default SQLite Database With PostgreSQL In Django
  • Getting Started with SQL: A Beginners Guide to Databases
  • How to Implement Local SEO On Your Business Website And Drive Traffic
  • About
  • Contact

© 2022   ·   Ehi Kioya   ·   All Rights Reserved
Privacy Policy