使用策略模式和工厂方法改进if-else的实现

使用策略模式和工厂方法改进if-else的实现

假设我们要做一个外卖平台,有这样的需求:

1、外卖平台上的某家店铺为了促销,设置了多种会员优惠,其中包含超级会员折扣8折、普通会员折扣9折和普通用户没有折扣三种。

2、希望用户在付款的时候,根据用户的会员等级,就可以知道用户符合哪种折扣策略,进而进行打折,计算出应付金额。

3、随着业务发展,新的需求要求专属会员要在店铺下单金额大于30元的时候才可以享受优惠。

4、接着,又有一个变态的需求,如果用户的超级会员已经到期了,并且到期时间在一周内,那么就对用户的单笔订单按照超级会员进行折扣,并在收银台进行强提醒,引导用户再次开通会员,而且折扣只进行一次。

那么,我们可以看到以下伪代码:

double calPrice(double orderPrice, String buyerType) {
    if (用户是专属会员) {
        if (订单金额大于30) {
            returen 7折价格;
        }
    }

    if (用户是超级会员) {
        return 8折价格;
    }

    if (用户是普通会员) {
        if(该用户超级会员刚过期并且尚未使用过临时折扣){
            临时折扣使用次数更新();
            returen 8折价格;
        }
        return 9折价格;
    }
    return 原价;
}

以上,就是对于这个需求的一段价格计算逻辑,使用伪代码都这么复杂,如果是真的写代码,那复杂度可想而知。

这样的代码中,有很多if-else,并且还有很多的if-else的嵌套,无论是可读性还是可维护性都非常低。

那么,如何改善呢?

首先定义一个借口:

class UserPayService
{
public:
	virtual ~UserPayService() {}
	virtual double quote(double orderPrice) = 0;
};

接着定义几个策略类:

class ParticularlyVipService : public UserPayService
{
public:
	double quote(double orderPrice)override
	{
		if (orderPrice > 30)
		{
			return 0.7*orderPrice;
		}
	}
};

class SuperVipPayService : public UserPayService
{
public:
	double quote(double orderPrice)override
	{
		return 0.8*orderPrice;
	}
};

class VipPayService : public UserPayService
{
public:
	double quote(double orderPrice)override
	{
		if (hasTempCostRight)
		{
			hasTempCostRight = false;
			return 0.8*orderPrice;
		}
		return 0.9*orderPrice;
	}
	bool hasTempCostRight{ true };
};

在引入了策略之后,我们可以按照如下方式进行计算:

#include"Strategy_Factory.h"
#include<iostream>
int main()
{
	UserPayService* ups = new ParticularlyVipService();
	double quoteInst = ups->quote(300);
	std::cout << "ParticularlyVipService quote = " << quoteInst << std::endl;

	ups = new SuperVipPayService();
	double quoteInst2 = ups->quote(300);
	std::cout << "SuperVipPayService quote = " << quoteInst2 << std::endl;

	ups = new VipPayService();
	double quoteInst3 = ups->quote(300);
	std::cout << "VipPayService quote = " << quoteInst3 << std::endl;

	return 0;
}

由此我们就是使用策略模式根据new出的不同会员的策略类进行不同的计算价格方法。

但是在真实的项目中,我们需要先得到用户的会员等级,比如从数据库中查出会员的等级,然后根据等级获取不同的策略类执行计算价格方法。那么真真正的计算价格的方法伪代码应该是:

double calPrice(double orderPrice,User user) {

     String vipType = user.getVipType();

     if (vipType == 专属会员) {
        //伪代码:从Spring中获取超级会员的策略对象
        UserPayService strategy = new ParticularlyVipService();
        return strategy.quote(orderPrice);
     }

     if (vipType == 超级会员) {
        UserPayService strategy = new SuperVipPayService();
        return strategy.quote(orderPrice);
     }

     if (vipType == 普通会员) {
        UserPayService strategy = new VipPayService();
        return strategy.quote(orderPrice);
     }
     return 原价;
}

我们知道策略模式的一个缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。

也就是说,虽然在计算价格的时候没有if-else了,但是选择具体的策略的时候还是不可避免的还是要有一些if-else。

为了改进这一点,我们可以定义并暴露统一稳定的接口,将内部可能变化的实现隔离,面向接口而非实现,这样客户端不需对所有策略有过多的了解,只要提供接口的入参即可得到相应的策略。(客户端只要提供用户类型标识,即可得到相应的策略)

对于会员的策略对象我们可以使用工厂模式获取:
创建一个工厂类:

class UserPayServiceStrategyFactory
{
private:
	static std::unordered_map<std::string, UserPayService*> services;

public:
	static UserPayService* getByUserType(std::string userType)
	{
		return services[userType];
	}

	static void reg(std::string userType, UserPayService* userPayService)
	{
			= std::make_pair(userType, userPayService);
		services.insert(newService);
	}
};
std::unordered_map<std::string, UserPayService*> UserPayServiceStrategyFactory::services;

此时我们可以由会员类型直接由工厂得到会员对象(指针),并以此(策略方法)计算得到应付价格:

	//注册对象指针表
	UserPayServiceStrategyFactory userPayService;
	UserPayService* superVipPay = new SuperVipPayService();
	userPayService.reg("SuperVip", superVipPay);
	UserPayService* vipPay = new VipPayService();
	userPayService.reg("Vip", vipPay);
	UserPayService* particularlyVipPay = new ParticularlyVipService();
	userPayService.reg("ParticularlyVip", particularlyVipPay);

	double quoteInst1 = userPayService.getByUserType("ParticularlyVip")->quote(300);
	std::cout << "ParticularlyVipService quote = " << quoteInst1 << std::endl;

	double quoteInst2 = userPayService.getByUserType("SuperVip")->quote(300);
	std::cout << "SuperVipPayService quote = " << quoteInst2 << std::endl;

	double quoteInst3 = userPayService.getByUserType("Vip")->quote(300);
	std::cout << "VipPayService quote = " << quoteInst3 << std::endl;

此时我们已经可以仅给出会员类型及消费金额即得到相应的对象,并且直接调用统一的接口即可计算应付金额。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值