Code refactoring and unit testing refactoring 6: replacing conditional expressions with “polymorphism” (IX)

Time:2022-5-9

 

 

9、 Reconstruction6: usepolymorphicReplace conditional expression

After the previous eight articles and five times of reconstruction, the reconstruction of this charging treasure billing project has been basically completed. Today is the last article in this series. Let’s talk about how to reconstruct conditional expressions. One of the most common ways to reconstruct conditional expressions is to use class polymorphism. Next, we will use this rule to reconstruct the getamount() and getfrequentreenterpoints() functions in the powerbank class.

When we observe the switch case structure in the getamount () method in the powerbank class, we find that the code here can be replaced by the polymorphism of the class. The specific implementation method is to extract different price calculation methods into our newly created price class. The charging treasure in each section has its own price class, and these price classes implement the same interface. In this way, multiple classes can be used in powerbank class to obtain the total amount and points.

1. First, let’s create a price interface. Stop the cursor over the powerbank class name, right-click, select “quick operation and refactoring” in the pop-up menu, and then select “extract interface” in the pop-up shortcut menu. As shown below.

 

2. As shown in the figure above, fill in the interface name and interface file name in the “extract interface” dialog box, check the corresponding method name in the “select public members constituting the interface” list box, and click the “OK” button with the mouse. Visual studio 2019 will automatically generate an interface file. We modify the automatically generated interface file. The specific codes are as follows:

namespace LeasePowerBank
{
    public interface IPrice
    {

        int GetPriceCode();
        decimal GetAmount(int RentedTime);
        int GetFrequentRenterPoints(int RentedTime);
    }
}

3. In solution explorer, select the “leasepower bank” project, right-click, and select “add – class” in the pop-up shortcut menu. As shown below.

 

4. In the “add new item” dialog box, select the class, enter “lowtraffic” in the name, and then click the “add” button. As shown below.

5. In the code editor of visual studio 2019, inherit the iprice interface in the lowtraffic class. Stop the cursor on the interface iprice, then right-click, and select “shortcut and Reconstruction – implement interface” in the pop-up shortcut menu. As shown below.

 

 

6. Visual studio 2019 will automatically add the corresponding code to implement the interface in the lowtraffic class. We only need to modify it. The specific codes are as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace LeasePowerBank
{
    public class LowTraffic : IPrice
    {
        public decimal GetAmount(int RentedTime)
        {
           decimal amount = RentedTime;
            if (RentedTime > 12)

            {
                amount = 12;
            }
            return amount;
        }

        public int GetFrequentRenterPoints(int RentedTime)
        {
            decimal amount = GetAmount(RentedTime);           
                int frequentRenterPoints = (int)Math.Ceiling(amount);
            return frequentRenterPoints;
        }

        public int GetPriceCode()
        {
            return PowerBank.LowTraffic;
        }

   }
}

7. Repeat steps 3 to 6 above to implement middletraffic and hightraffic classes respectively. The codes of these two classes are as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 

namespace LeasePowerBank
{

    public class MiddleTraffic:IPrice
    {
        public decimal GetAmount(int RentedTime)
        {
            decimal amount = RentedTime;
            amount = RentedTime * 3;
            if (RentedTime > 8)
            {
                amount = 24;
            }
            return amount;
        }


        public int GetFrequentRenterPoints(int RentedTime)
        {
            decimal amount = GetAmount(RentedTime);
            int frequentRenterPoints = (int)Math.Ceiling(amount);
            return frequentRenterPoints;
        }
 
        public int GetPriceCode()
        {
            return PowerBank.MiddleTraffic;
        }
    }
}

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace LeasePowerBank
{
    public class HighTraffic:IPrice
    {
 
        public decimal GetAmount(int RentedTime)
        {
            decimal amount = RentedTime;
            amount = RentedTime * 5;
            if (RentedTime > 10)
            {
                amount = 50;
            }
            return amount;
        }
 
        public int GetFrequentRenterPoints(int RentedTime)
        {
            decimal amount = GetAmount(RentedTime);
            int frequentRenterPoints = (int)Math.Ceiling(amount);
            return frequentRenterPoints;
        }
 
        public int GetPriceCode()
        {
            return PowerBank.HighTraffic;
        }
 
    }
}

 

8. After adding one of the above codes, our powerbank should also make corresponding modifications to use multiple classes to realize the billing calculation and integral calculation of charging treasure in different sections. Add an object declared by iprice in powerbank, and we will assign different objects to the price variable according to different pricecode. When calculating the amount, just call the getamount() method of price in getamount(). Integral calculation is similar. The specific code is as follows.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 

namespace LeasePowerBank
{
 

    /// 
    ///Charging treasure
    /// 
    public class PowerBank
    {
        //Type of passenger flow in the section
        public static int LowTraffic = 0;// Low traffic section
       public static int MiddleTraffic = 1;// Medium traffic section
        public static int HighTraffic = 2; // High traffic section
 

        public int PriceCode; // Price code
        public string Title;// Name of power bank
        public PowerBank(string title, int priceCode)

        {
            SetPriceCode(priceCode);
            Title = title;
        }

        IPrice price = null;

        private void SetPriceCode(int priceCode)
        {

            PriceCode = priceCode;
            switch (priceCode)
            {

               case 0:
                    price = new LowTraffic();

                   break;
                case 1:
                    price = new MiddleTraffic();
                    break;
                case 2:
                    price = new HighTraffic();
                    break;
                default:

                   break;
            }
        }
 
        /// 
        ///Calculate the points according to the consumption amount and the location of the power bank
        /// 
        ///Lease time
        /// 
 
        public int GetFrequentRenterPoints(int RentedTime)
        {
            return price.GetFrequentRenterPoints(RentedTime);
        }
        /// 
        ///Calculate the total amount according to the order of power bank
        /// 
        ///Lease time
        /// 
        public decimal GetAmount(int RentedTime)
        {
            return price.GetAmount(RentedTime);
        }
    }
}

 

9. Our test cases remain unchanged. After each refactoring, we need to call the above test cases to check whether the refactoring has side effects. At present, the dependencies between our classes have not changed much, but the methods in the corresponding classes have changed. Find the menu item “test — > run all tests” on the menu bar of visual studio 2019. Or select “run all tests in view” button in “test Explorer” to run test cases. Monitor whether the reconstruction results are correct. As shown below.

 

 

After the above refactoring, our project is almost the same. Refactoring is like this. Step by step, don’t worry. Every refactoring should always develop in a good direction. If you refactor the project code directly from the beginning to refactor 6 the code, it seems a little difficult. After the above refactoring steps from refactoring 1 to refactoring 6, it is actually quite simple.