Structure of policy pattern
This pattern involves three roles:
Context role: holds a reference to a Strategy class.
Abstract Strategy role: This is an abstract role, usually implemented by an interface or abstract class. This role gives the interfaces required for all specific policy classes.
ConcreteStrategy role: encapsulates related algorithms or behaviors.
CashSuper written in the previous blog post is an abstract strategy, while CashNormal, CashRebate and CashReturn are three specific strategies, that is, the specific algorithm in the strategy mode.
Some codes of the previous blog post are attached
//Normal consumption, inheritance CashSuper class CashNormal:CashSuper { public override double acceptCash(double money) { return money; } }
//Discount charge consumption, inheritance CashSuper class CashRebate:CashSuper { private double moneyRebate = 1d; //During initialization, you must enter a discount rate, such as 20% discount, which is 0,8 public CashRebate(string moneyRebate) { //Interface transferring values to classes this.moneyRebate = double.Parse(moneyRebate); } public override double acceptCash(double money) { return money * moneyRebate; } }
//Rebate charge class CashReturn:CashSuper { private double moneyCondition = 0.0d; private double moneyReturn = 0.0d; //Rebate conditions and rebate values must be entered during initialization. For example, 100 will be returned after 300 //be moneyCondition 300,moneyReturn Is 100 public CashReturn(string moneyCondition, string moneyReturn) { this.moneyCondition =double.Parse(moneyCondition); this.moneyReturn = double.Parse(moneyReturn); } public override double acceptCash(double money) { double result = money; //If it is greater than the rebate condition, the rebate value needs to be subtracted if (money >= moneyCondition) { result = money - Math.Floor(money / moneyCondition) * moneyReturn; } return result; } }
//Cash collection parent abstract class CashSuper { //Abstract method:Receive cash, the parameter is original price, and the return is current price public abstract double acceptCash(double money); }
Added policy mode (factory mode can be discarded here)
1 namespace ExtendDiscountOfStrategyPattern 2 { 3 class CashContext 4 { 5 //Declare a cash charge parent class object 6 private CashSuper cs; 7 8 //Set policy behavior,The parameter is a specific cash charge subcategory(normal,Discount or rebate) 9 public void setBehavior(CashSuper csuper) 10 { 11 this.cs = csuper; 12 } 13 14 //Get the calculation results of cash promotion (using the polymorphic mechanism, different strategic behaviors lead to different results) 15 public double GetResult(double money) 16 { 17 return cs.acceptCash(money); 18 } 19 } 20 }
But the program still needs switch Case statement,
Core code (v1.3)
1 //Declare a double variable total To calculate the total 2 double total = 0.0d; 3 private void btnConfirm_Click(object sender, EventArgs e) 4 { 5 //Declare a double variable totalPrices 6 double totalPrices = 0d; 7 //Policy mode 8 CashContext cc = new CashContext(); 9 switch (cbxType.SelectedItem.ToString()) 10 { 11 case "Normal consumption": 12 cc.setBehavior(new CashNormal()); 13 break; 14 case "Return 100 after 300": 15 cc.setBehavior(new CashReturn("300", "100")); 16 break; 17 case "20% off": 18 cc.setBehavior(new CashRebate("0.8")); 19 break; 20 case "20% off": 21 cc.setBehavior(new CashRebate("0.7")); 22 break; 23 case "50% off": 24 cc.setBehavior(new CashRebate("0.5")); 25 break; 26 } 27 totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)); 28 //Total each item to total 29 total = total + totalPrices; 30 //Show information in list box 31 lbxList.Items.Add("unit price:" + txtPrice.Text + " quantity:" + txtNum.Text + " total:" + totalPrices.ToString()); 32 //stay lblTotalShow Show total count on label 33 lblTotalShow.Text = total.ToString(); 34 }
The original policy pattern has its drawbacks. The client must know all the policy classes and decide which policy class to use. This means that the client must understand the differences between these algorithms in order to select the appropriate algorithm class in time. In other words, the policy model applies only to customers
If the client knows all the algorithms or behaviors, the initial policy pattern has shortcomings. The client must know all the policy classes and decide which policy class to use. This means that the client must understand the differences between these algorithms in order to select the appropriate algorithm class in time. In other words, the policy pattern only applies when the client knows all the algorithms or behaviors.
Remove switch Case statement—— (this case uses a simple.net Technology: reflection)
The key operation code is assembly Load ("assembly name") CreateInstance("namespace. Class name");
Client code (v1.4)
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 //add 11 using CCWin; 12 using System.Data.SqlClient; 13 14 namespace ExtendDiscountOfStrategyPatternWithReflection 15 { 16 using System.Reflection; 17 18 public partial class frmMain :Skin_Metro 19 { 20 21 DataSet ds; //Used to store configuration file information 22 23 public frmMain() 24 { 25 InitializeComponent(); 26 } 27 28 //Declare a double variable total To calculate the total 29 double total = 0.0d; 30 private void btnConfirm_Click(object sender, EventArgs e) 31 { 32 //Declare a double variable totalPrices 33 double totalPrices = 0d; 34 //Policy mode 35 CashContext cc = new CashContext(); 36 //Query related rows of user selected items according to user options 37 DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + cbxType.SelectedItem.ToString() + "'"))[0]; 38 //An array of objects that declare a parameter 39 object[] args = null; 40 //If there are parameters, they will be divided into string arrays for the parameters used in instantiation 41 if (dr["para"].ToString() != "") 42 { 43 args = dr["para"].ToString().Split(','); 44 } 45 //Generate corresponding algorithm objects through reflection instantiation 46 cc.setBehavior((CashSuper)Assembly.Load("ExtendDiscountOfStrategyPatternWithReflection"). 47 CreateInstance("ExtendDiscountOfStrategyPatternWithReflection." + dr["class"].ToString(), false, 48 BindingFlags.Default, null, args, null, null)); 49 50 totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)); 51 //Total each item to total 52 total = total + totalPrices; 53 //Show information in list box 54 lbxList.Items.Add("unit price:" + txtPrice.Text + " quantity:" + txtNum.Text + " total:" + totalPrices.ToString()); 55 //stay lblTotalShow Show total count on label 56 lblTotalShow.Text = total.ToString(); 57 } 58 59 private void btnReset_Click(object sender, EventArgs e) 60 { 61 total = 0.0; 62 txtPrice.Text = ""; 63 txtNum.Text = ""; 64 lblTotalShow.Text = ""; 65 lbxList.Items.Clear(); 66 cbxType.SelectedIndex = 0; 67 } 68 69 private void txtNum_KeyPress(object sender, KeyPressEventArgs e) 70 { 71 //Number 0~9 Corresponding keychar Is 48~57 72 e.Handled = true; 73 //Enter 0-9 74 if ((e.KeyChar >= 47 && e.KeyChar <= 58) || e.KeyChar == 8) 75 { 76 e.Handled = false; 77 } 78 } 79 80 private void txtPrice_KeyPress(object sender, KeyPressEventArgs e) 81 { 82 //Number 0~9 Corresponding keychar Is 48~57 83 e.Handled = true; 84 //Enter 0-9 85 if ((e.KeyChar >= 47 && e.KeyChar <= 58) || (e.KeyChar == 8 || e.KeyChar==46)) 86 { 87 e.Handled = false; 88 } 89 } 90 91 private void frmMain_Load(object sender, EventArgs e) 92 { 93 //Read configuration file 94 ds = new DataSet(); 95 ds.ReadXml(Application.StartupPath + "\\CashAcceptType.xml"); 96 //Bind the read record to the drop-down list box 97 foreach(DataRowView dr in ds.Tables[0].DefaultView) 98 { 99 cbxType.Items.Add(dr["name"].ToString()); 100 } 101 102 //To select the element with index 0 when the drop-down selection box is loaded"Normal consumption" 103 cbxType.SelectedIndex = 0; 104 } 105 } 106 }
The program reads the XML configuration file to generate the drop-down list box. Then, according to the user's choice, the corresponding algorithm object is instantiated through reflection in real time. Finally, the final result is calculated by using the policy mode.
XML file - cashaccepttype The XML code is as follows
1 <?xml version="1.0" encoding="utf-8" ?> 2 <CashAcceptType> 3 <type> 4 <name>Normal consumption</name> 5 <class>CashNormal</class> 6 <para></para> 7 </type> 8 <type> 9 <name>Return 100 after 300</name> 10 <class>CashReturn</class> 11 <para>300,100</para> 12 </type> 13 <type> 14 <name>Return 50 after 200</name> 15 <class>CashReturn</class> 16 <para>200,50</para> 17 </type> 18 <type> 19 <name>20% off</name> 20 <class>CashRebate</class> 21 <para>0.8</para> 22 </type> 23 <type> 24 <name>20% off</name> 25 <class>CashRebate</class> 26 <para>0.7</para> 27 </type> 28 <type> 29 <name>50% off</name> 30 <class>CashRebate</class> 31 <para>0.5</para> 32 </type> 33 </CashAcceptType>
Now, no matter what the requirements are, with the current program, you just need to change the XML file to settle it all. For example, the boss thinks that it is too much to give 100 free when it is over 300. To change it to 80 free, I just need to change it in the XML file.
Note: if you want to add a new algorithm, the algorithm class inherits CashSuper, and then change the XML file.