灵活运用设计模式之装饰变种

灵活运用设计模式之装饰变种

背景需求

  开篇我们先吹一下要实现什么目标:假定根据需求,用户需要通过企业名称来查询该企业名称下的法人、注册资本、经营范围等信息。
  假定目前有两个通道可通过企业名称来获取该企业的信息(后面可能会再增加多个接口):

  • 通过接口爬取工示系统网站获取(免费)(响应速度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一下我们的实现类就可以了,连调用参数都不用写。

倘若要减少接口时,只需要在调用的地方把我们要去掉的实现类删除就可以了。

posted @ 2019-07-19 15:21  源码加特灵  阅读(260)  评论(0编辑  收藏  举报