Strategy mode
The policy pattern defines a variety of different algorithms for dealing with the same scenario, which can be replaced with each other without affecting users.
scene
Application scenario
There are two levels of members in a membership Mall: silver card members and Gold members, who enjoy 10% and 20% shopping discounts respectively. At the same time, different member users will give different birthday gifts when they shop on their birthday.
analysis
The scenario is relatively simple. The problem to be solved is to distinguish different types of customers who enjoy different rights (discounts and birthday gifts).
According to the usual coding habits, if judgment is usually added to the business that needs to distinguish user levels to realize the different rights and interests that customers of different levels should enjoy. This solution can quickly solve practical problems, but with business needs, shopping malls have to introduce higher qualified member types, such as platinum members, Diamond members, etc. At this point, you need to add if patches to the scattered business code. This approach will lead to the following problems:
- Business codes are scattered everywhere, which is easy to omit if and difficult to verify.
- Over time, there will be more and more if, resulting in more branches, fuzzy code, and affecting code maintenance.
Solution
The policy pattern is introduced to abstract the user level, define all the behaviors of users, and users of different levels realize the rights and interests of the level.
Class diagram
CShop: shopping malls. Realize discount settlement, birthday gifts, etc. for different levels of customers. Hold the CConsumer pointer and point to specific customer instances (CCommonUser, CSilverUser, CGoldUser) as needed.
CConsume: customer abstract class. Define all customer behavior interfaces.
CCommonUser, CSilverUser, CGoldUser: specific customer classes. Different levels of customers realize the interfaces of different parts.
effect
Execution effect
$ ./exe --------------------------------- All Cost : 1000.00. User Type : Common User. Discount : 1.00. Actual Payment: 1000.00. --------------------------------- All Cost : 1000.00. User Type : Silver User. Discount : 0.90. Actual Payment: 900.00. --------------------------------- All Cost : 1000.00. User Type : Gold User. Discount : 0.80. Actual Payment: 800.00.
Client implementation
int main(int argc, char *argv[]) { CShop theShop; float cost = 1000.0; // Ordinary users MAIN_LOG("\n---------------------------------\n"); MAIN_LOG(" All Cost : %0.2f. \n" " User Type : %s. \n" " Discount : %0.2f. \n" " Actual Payment: %0.2f. \n", cost, theShop.GetUserDesc().c_str(), theShop.GetCurrentDiscountRate(), theShop.GetRealPrice(cost)); // Switch Silver members MAIN_LOG("\n---------------------------------\n"); theShop.SetConsumer(COSUMER_SILVER); MAIN_LOG(" All Cost : %0.2f. \n" " User Type : %s. \n" " Discount : %0.2f. \n" " Actual Payment: %0.2f. \n", cost, theShop.GetUserDesc().c_str(), theShop.GetCurrentDiscountRate(), theShop.GetRealPrice(cost)); // Switch Gold members MAIN_LOG("\n---------------------------------\n"); theShop.SetConsumer(COSUMER_GOLD); MAIN_LOG(" All Cost : %0.2f. \n" " User Type : %s. \n" " Discount : %0.2f. \n" " Actual Payment: %0.2f. \n", cost, theShop.GetUserDesc().c_str(), theShop.GetCurrentDiscountRate(), theShop.GetRealPrice(cost)); return 0; }
summary
- The implementation principle of the policy mode is relatively simple. It mainly changes the direction of the holding pointer to realize the switching of different schemes. By changing the external condition input to match the corresponding instance, the client code can automatically switch different schemes without changing.
- Meet the opening and closing principle. When you need to add a policy, you only need to derive a new policy without modifying the existing code. Compared with the previous practice, it is safer and faster.
- You can also dynamically switch policies while the code is running.
- The policy mode is somewhat similar to the command mode. The implementation methods of the two are similar, and the scenarios are different. The policy pattern is aimed at the implementation of algorithms with different behaviors; Command mode is a solution to a command.
- All source code can be input in the background of official account.
Source code
Store class interface
class CShop { public: CShop(); ~CShop(); std::string GetUserDesc() { return mConsumer->mUserDesc; } float GetRealPrice(float price); int BirthdayPresent(); int SetConsumer(EConsumerType type); void SetCurrentDiscountRate(float rate); float GetCurrentDiscountRate(); private: CConsumer* mConsumer; };
Update customer type
int CShop::SetConsumer(EConsumerType type) { switch (type) { case COSUMER_COMMON: mConsumer = CCommonUser::GetInstance(); break; case COSUMER_SILVER: mConsumer = CSilverUser::GetInstance(); break; case COSUMER_GOLD: mConsumer = CGoldUser::GetInstance(); break; default: break; } if (NULL == mConsumer) { return -1; } return 0; }
Customer class abstract interface
class CConsumer { public: float mDiscountRate; std::string mUserDesc; CConsumer() : mDiscountRate(1.0) { } virtual ~CConsumer() { } void SetDiscountRate(float rate) { mDiscountRate = rate; } float GetDiscountRate() { return mDiscountRate; } float GetRealPrice(float price) { return mDiscountRate * price; } virtual int GetBirthdayPresent() = 0; };
Specific customers: Gold members
class CGoldUser : public CConsumer { public: CGoldUser(); ~CGoldUser(); static CGoldUser* GetInstance(); int GetBirthdayPresent(); };
Client interface
int main(int argc, char *argv[]) { CShop theShop; float cost = 1000.0; // Ordinary users MAIN_LOG("\n---------------------------------\n"); MAIN_LOG(" All Cost : %0.2f. \n" " User Type : %s. \n" " Discount : %0.2f. \n" " Actual Payment: %0.2f. \n", cost, theShop.GetUserDesc().c_str(), theShop.GetCurrentDiscountRate(), theShop.GetRealPrice(cost)); // Switch Silver members MAIN_LOG("\n---------------------------------\n"); theShop.SetConsumer(COSUMER_SILVER); MAIN_LOG(" All Cost : %0.2f. \n" " User Type : %s. \n" " Discount : %0.2f. \n" " Actual Payment: %0.2f. \n", cost, theShop.GetUserDesc().c_str(), theShop.GetCurrentDiscountRate(), theShop.GetRealPrice(cost)); // Switch Gold members MAIN_LOG("\n---------------------------------\n"); theShop.SetConsumer(COSUMER_GOLD); MAIN_LOG(" All Cost : %0.2f. \n" " User Type : %s. \n" " Discount : %0.2f. \n" " Actual Payment: %0.2f. \n", cost, theShop.GetUserDesc().c_str(), theShop.GetCurrentDiscountRate(), theShop.GetRealPrice(cost)); return 0; }