Solid principles: 2. Open-closed principle

in #solid7 years ago

SOLID-Open_Close-Principle1080x606-1024x575.jpg

The second SOLID principle, open-closed, ie open to extension, closed to modifications. What is going on? About this further.

This rule says that the class and its modules must remain open for expansion, i.e. you can add new methods and functionalities, but with this expansion you can not modify anything in existing modules, change their activities because it would be breaking this rule.

Modification of the class is strictly prohibited, because the change in the operation of existing classes may affect the operation of others, which will disturb the operation of the whole system and will disintegrate. A good solution does not create further problems.

This rule is especially important in large projects, for the creators of all extensions, plugins and programming libraries.

For sure, each of you had to change the class along with the changing requirements of the application. Thanks to this principle, your project will be flexible and easy to expand.

While sticking to the first SOLID principle, we must also stick to the second, so let’s move to practice.

In our store logic according to the second SOLID principle, this piece of code is bad:

private void CheckTypeProducts(string typeproduct)
{
     if (typeproduct == "candys")
     {
         GetCandysCount();
     }
     else if (typeproduct == "drink")
     {
         GetDrinksCount();
     }
}
 
private void GetCandysCount()
{
     NumberCandys += 1;
}
 
private void GetDrinksCount()
{
     NumberDrinks += 1;
}

Why? Let’s add a new type of product, eg fruit, so now it will look like

private void CheckTypeProducts(string typeproduct)
{
     if (typeproduct == "candys")
     {
         GetCandysCount();
     }
     else if (typeproduct == "drink")
     {
         GetDrinksCount();
     }
     else if (typeproduct == "fruit")
     {
         GetFruitsCount();
     }
}
 
private void GetCandysCount()
{
     NumberCandys += 1;
}
 
private void GetDrinksCount()
{
     NumberDrinks += 1;
}
 
private void GetFruitsCount()
{
     NumberFruits += 1;
}

We had to change the CheckTypeProducts method and add a new variable and another method, so we violated the open-closed SOLID principle, polymorphism and inheritance are often good in such situations.

Correctly, it should look like this:

namespace OpenClosed
{
    abstract class TypeProduct
    {
        public abstract void GetTypeProductsCount(int quantity);
    }
 
    class Fruit : TypeProduct
    {
        public int NumberFruits { get; set; }
 
        public override void GetTypeProductsCount(int quantity)
        {
            NumberFruits += quantity;
        }
    }
 
    class Drink : TypeProduct
    {
        public int NumberDrinks { get; set; }
 
        public override void GetTypeProductsCount(int quantity)
        {
            NumberDrinks += quantity;
        }
    }
 
    class Candy : TypeProduct
    {
        public int NumberCandys { get; set; }
 
        public override void GetTypeProductsCount(int quantity)
        {
            NumberCandys += quantity;
        }
    }
 
    class ClientCart
    {
        public List<string> Products { get; set; }
        public List<string> TypesProducts { get; set; }
 
        public int NumberProducts { get; set; }
        public double PriceAllProducts { get; set; }
 
        public void SaveTypeProducts(string typeproduct)
        {
            if (TypesProducts.Contains(typeproduct))
            {
                Console.WriteLine("This type of product is already in the cart!");
            }
            else
            {
                TypesProducts.Add(typeproduct);
            }
        }
 
        public void GetTypeProductsCount(TypeProduct typeproduct, int quantity)
        {
            typeproduct.GetTypeProductsCount(quantity);
        }
 
        //another methods
    }
}

Efficiently using polymorphism and inheritance, we took care of the correctness of the open-closed principle, now if we would like to add some type of product again, it would be enough to add a new class and we would not have to change nothing in existing classes and methods.

However, one more thing is wrong here – the constructor in the Client class, we do not want to add new arguments to it, new variables, moreover, if such a constructor had 50 arguments, it would look … in a word … tragic and illegible, readable constructor together with the rest of the code it should look like this:

namespace OpenClosed
{
    class Client
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string ZipCode { get; set; }
        private double _myWallet;
        public double MyWallet { get => _myWallet; set => _myWallet = value; }
 
        public Client(ClientConstructor myconfiguration)
        {
            FirstName = myconfiguration.firstname;
            LastName = myconfiguration.lastname;
            ZipCode = myconfiguration.zipcode;
            _myWallet = myconfiguration.myWallet;
        }
    }
 
    public class ClientConstructor
    {
        public string firstname;
        public string lastname;
        public string zipcode;
        public double myWallet;
    }
 
   //Shop class without changes
 
    abstract class TypeProduct
    {
        public abstract void GetTypeProductsCount(int quantity);
    }
 
    class Fruit : TypeProduct
    {
        public int NumberFruits { get; set; }
 
        public override void GetTypeProductsCount(int quantity)
        {
            NumberFruits += quantity;
        }
    }
 
    class Drink : TypeProduct
    {
        public int NumberDrinks { get; set; }
 
        public override void GetTypeProductsCount(int quantity)
        {
            NumberDrinks += quantity;
        }
    }
 
    class Candy : TypeProduct
    {
        public int NumberCandys { get; set; }
 
        public override void GetTypeProductsCount(int quantity)
        {
            NumberCandys += quantity;
        }
    }
 
    class ClientCart
    {
        public List<string> Products { get; set; }
        public List<string> TypesProducts { get; set; }
 
        public int NumberProducts { get; set; }
        public double PriceAllProducts { get; set; }
 
        public void SaveTypeProducts(string typeproduct)
        {
            if (TypesProducts.Contains(typeproduct))
            {
                Console.WriteLine("This type of product is already in the cart!");
            }
            else
            {
                TypesProducts.Add(typeproduct);
            }
        }
 
        public void GetTypeProductsCount(TypeProduct typeproduct, int quantity)
        {
            typeproduct.GetTypeProductsCount(quantity);
        }
    }
    //Order class without changes
}

As you can see now adding arguments in the constructor does not fully comply with the open-closed principle because we have to adding new variables anyway, but it still looks better than the previous constructor. Of course, there is also something like Builder Fluent Interface, which allows you to write the constructor even more readily, but later about it, as we will discuss the Builder design pattern.

Finally, the call in the Main method:

namespace OpenClosed
{
    class Program
    {
        static void Main(string[] args)
        {
            double MyWallet = 30;
 
            Client client = new Client(new ClientConstructor
            {
                firstname = "Slawomir",
                lastname = "Kowalski",
                zipcode = "81-198",
                myWallet = MyWallet
            });
 
            ClientCart clientCart = new ClientCart();
 
            clientCart.Products = new List<string>();
            clientCart.TypesProducts = new List<string>();
 
            Shop shop = new Shop(100);
 
            shop.NewClient();
            clientCart.AddProduct("cola");
            clientCart.AddProduct("sprite");
            clientCart.SaveTypeProducts("drink");
            clientCart.SaveTypeProducts("drink");
 
            Drink drinksInBag = new Drink();
            drinksInBag.GetTypeProductsCount(2);
            clientCart.SumProductsPrice(5.6);
            clientCart.SumProductsPrice(3.5);
 
            clientCart.AddProduct("candy");
            clientCart.SaveTypeProducts("candys");
 
            Candy candysInBag = new Candy();
            candysInBag.GetTypeProductsCount(1);
            clientCart.SumProductsPrice(1.4);
 
            clientCart.AddProduct("orange");
            clientCart.SaveTypeProducts("fruit");
 
            Fruit fruitsInBag = new Fruit();
            fruitsInBag.GetTypeProductsCount(1);
            clientCart.SumProductsPrice(2.2);
 
            Order order = new Order();
 
            Console.WriteLine(order.GiveOrder() +
            "\nThe number of products in the cart: " + clientCart.GetProductsCount() +
            "\nOrder placed by: " + client.FirstName + " " + client.LastName
            + "\nNumber of drinks in the cart: " + drinksInBag.NumberDrinks
            + "\nNumber of fruits in the cart: " + fruitsInBag.NumberFruits
            + "\nThe number of sweets in the cart: " + candysInBag.NumberCandys
            + "\nPrice of all products: " + clientCart.PriceAllProducts);
 
            Console.WriteLine("It's in my wallet: " + (client.MyWallet - clientCart.PriceAllProducts) + " PLN");
 
            Console.ReadKey();
        }
    }
}

Result:

openclosed.png

Summary

And that’s all about the open-closed principle, I hope you have seen how useful it is, the importance of it increases the flexibility, the ease of expansion and reduces the number of bugs in the future expansion of the project.

Link to github with the whole code from this article: https://github.com/Slaw145/Open-Closed-principle

This content also you can find on my blog http://devman.pl/programtech/solid-principles-2-open-closed-principle/

If you recognise it as useful, share it with others so that others can also use it.

Leave upvote and follow and wait for next articles :) .

And NECESSERILY join the DevmanCommunity community on fb, part of the community is in one place 🙂

– site on fb: Devman.pl-Sławomir Kowalski

– group on fb: DevmanCommunity

Ask, comment underneath at the end of the post, share it, rate it, whatever you want🙂.

Coin Marketplace

STEEM 0.19
TRX 0.15
JST 0.029
BTC 62817.54
ETH 2573.14
USDT 1.00
SBD 2.74