灵活运用设计模式之装饰变种
背景需求
开篇我们先吹一下要实现什么目标:假定根据需求,用户需要通过企业名称来查询该企业名称下的法人、注册资本、经营范围等信息。
假定目前有两个通道可通过企业名称来获取该企业的信息(后面可能会再增加多个接口):
- 通过接口爬取工示系统网站获取(免费)(响应速度30秒内)
- 通过第三方收费接口获取(收费)(响应速度1秒内)
调用需求及思路
用户查询要求录入企业名称后查询超时时间不超过60秒,所以为节约费用我们可以设定首先通过免费接口进行爬取,如果在限定的超时时间内获取不到企业信息,再通过第三方收费接口获取。
初级代码实现
一般初级代码实现可能设计如下:
var 返回数据 = string.Empty;
返回数据 = 免费查询接口(企业名称); //假定超时和无数据时返回null
if(返回数据 == null)
{
返回数据 = 收费查询接口(企业名称);
}
如果这里有N多个免费查询接口要接入,那么:
var 返回数据 = string.Empty;
返回数据 = 免费查询接口1(企业名称); //假定超时和无数据时返回null
if(返回数据 == null)
{
返回数据 = 免费查询接口2(企业名称);
}
if(返回数据 == null)
{
返回数据 = 免费查询接口3(企业名称);
}
if(返回数据 == null)
{
返回数据 = 免费查询接口4(企业名称);
}
if(返回数据 == null)
{
返回数据 = 免费查询接口N(企业名称);
}
//收费接口永远放在最后,万一我们的免费接口查询成功了呢 :)
if(返回数据 == null)
{
返回数据 = 收费查询接口(企业名称);
}
这样做最大的问题就是程序偶合度太高,最明显的地方就是一旦接口有增减,都要来大改上面的主程序,依赖非常高。其它的问题不想再吐槽!
代码实现的正确姿势
"talk is cheap,show me the code":
- 第一步 我们先设计一个带入参的抽象接口
public abstract class IQuery
{
public abstract string GetQCCSelect(string creditCode, string subjectName);
}
- 第二步 我们得实现上面的抽象接口,因为这里我们要判断上一个方法执行返回的内容是否为空,恰好这个判断是共用的,所以我们引入了模板方法
public abstract class AbsIQuery : IQuery
{
IQuery Iq;
public AbsIQuery(IQuery iq)
{
Iq = iq;
}
public override string GetQCCSelect(string creditCode, string subjectName)
{
string ret = string.Empty;
ret = Iq?.GetQCCSelect(creditCode, subjectName) ?? string.Empty;
// 这里判断如果上一个查询方法查询超时或者返回为空,则进行下一个方法的查询
if (string.IsNullOrEmpty(ret))
{
ret = Excute(creditCode, subjectName);
}
return ret;
}
public abstract string Excute(string creditCode, string subjectName);
}
- 第三步 开始实现我们的N多个查询的各个方法,继承并实现AbsIQuery抽象类,假如目前只有两个接口,一个免费接口一个收费接口
免费接口如下:
public class GSXTSelect : AbsIQuery
{
public GSXTSelect(IQuery iq) : base(iq)
{
}
public override string Excute(string creditCode, string subjectName)
{
string ret = string.Empty;
string paramas = $"creditCode={creditCode}&subjectName={subjectName}";
//假定我们这里用百度返回的值
ret = HttpHelper.GetResponse("https://www.baidu.com", "POST", paramas);
return ret;
}
}
收费接口如下:
public class QCCSelect : AbsIQuery
{
public QCCSelect(AbsIQuery iq) : base(iq)
{
}
public override string Excute(string creditCode, string subjectName)
{
string ret = string.Empty;
string paramas = $"creditCode={creditCode}&subjectName={subjectName}";
ret = HttpHelper.GetResponse("https://xxxxx.xxxx.com/GetEnterpriceInfo", "POST", paramas);
return ret;
}
}
- 第四步 主程序调用如下:
IQuery iq = new QCCSelect(new GSXTSelect(null)); //这一步可通过配置反射来实现,这样就不用当接口有增减的时候再来改这里了!
string iqStr = iq.GetQCCSelect("", "阿里巴巴");
总结
如上,这样我们的程序就写完了,倘若过几天领导要求再加一个免费接口接入,我们怎么办呢?其实我们只需要做第三步,增加你想要增加的接口就可以了。比如要增加的接口名称就叫“免费接口N”,则:
public class 免费接口N : AbsIQuery
{
public 免费接口N(AbsIQuery iq) : base(iq)
{
}
public override string Excute(string creditCode, string subjectName)
{
string ret = string.Empty;
string paramas = $"creditCode={creditCode}&subjectName={subjectName}";
ret = HttpHelper.GetResponse("https://免费接口N的地址", "POST", paramas);
return ret;
}
}
最后,再主程序上面加上这个接口方法就可以了:
IQuery iq = new QCCSelect(new GSXTSelect(new 免费接口N(null))); //这一步可通过配置反射来实现,这样就不用当接口有增减的时候再来改这里了!
string iqStr = iq.GetQCCSelect("", "阿里巴巴");
可以让我们清楚的看到:
倘若要增加接口时只管 继承并实现AbsIQuery抽象类 就可以了,不关心其它任何逻辑判断,当接口增加完后,只需要在调用的地方再new一下我们的实现类就可以了,连调用参数都不用写。
倘若要减少接口时,只需要在调用的地方把我们要去掉的实现类删除就可以了。