日常中碰到的设计模式——模板模式


日常中碰到的设计模式——模板模式

一、引言

设计模式,对于我们平时写代码而言,似乎离得很近(比如Spring里用到的工厂模式、单例模式这些),但需要自己去写的设计模式又几乎没有,毕竟框架里轮子都造好了。而我平时用到的也仅有策略模式,就是整个枚举,然后根据传进来的参数不同,看看在switch下匹配到哪个,然后去执行相应的方法,算是比较简单且常用的设计模式了。

二、模板模式简介

所谓的模板模式,也就是写一个抽象父类,这个父类公开定义了执行它的方法的方式/模板。然后它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。

说白了就是,需要某些类需要执行相似的操作,这时候可以整个抽象类作为它们的父类,然后在不同的业务场景下去调子类从父类中重写的方法就可以了。这么讲似乎还是有点绕,下面举个例子吧:

假设有抽象父类A作为模板
public abstract class A {
    public abstract String aa();
}

子类AImpl集成了这个模板,并重写了它的方法
public class AImpl extends A {
    @Override
    public String aa() {
        return "AImpl";
    }
}

那么现在来调用下
public static void main(String[] args) {
    A a = new AImpl();
    String aa = a.aa();
    System.out.println(aa);
}

这个输出应该毫无悬念,是:

AImpl

三、实际应用

以上是个简单的模板模式的应用,看起来毫无技术含量,似乎平时也用不上的样子。下面将举个我最近实际中遇到的场景:

1、业务场景

因为项目需要对客户的一些数据做同步,比如:用户信息,项目信息,楼栋信息之类的。通过HTTP接口接受客户传来的JSON数据,然后传进数据库里。数据类似这样:

{
     "name":"user",
     "value":"\"LoginName\":\"aaa\",\"Password\":\"66f0429cac5a857beb41f8f091ac4c2e\""
}

name的值是需要同步到的表名,value的值则是需要同步的内容。看到这里大概思路应该都有了,先做个枚举,然后用策略模式,根据传来的不同的表名去做判断。然后,根据不同的策略去执行相应的操作。但是,这里发现了一个问题——无论是什么表,我们要对它做的操作都是:

1. 查,查看这个数据在表中存不存在;
2. 增,数据不存在就去新增;
3. 改,数据存在就去修改。

这些操作都是一样的,代码要是这么反反复复的写,要是这个要同步的表有很多,那不是要写吐了吗?

作为一个脱离了低级趣味的程序员,对于代码基本的审美还是要有的。对于这些反反复复,长得又差不多的操作,我们必须把他抽象出来,这时候,就需要模板模式登场了。。。

2、建立模板

首先,我们得先明确下,有哪些类需要去整个模板出来?这个其实看下增查改这几个步骤涉及了哪些类就知道了:

1. 实体类。JSON数据传进来后,对应于各个表的不同字段,就需要去建对应的实体,在数据库的增查改操作中也涉及到这些实体类,所以这里需要去做个实体类的模板;
2. dao接口。毕竟是要去操作数据库的,少了dao可不行,而且,对应于不同的表,也有不同的dao,所以这里需要做一个dao接口的模板。

接下来看下具体的操作(因为涉及项目的信息,所以这里实体类我把它简化了):

这里实体类举User作为例子,其他几个类似,就不写了: 
public class User{

    //用户ID
    private String userId;
    //用户登陆名
    private String loginname;
    //用户登陆密码
    private String password;

    public String getUserId() {
        return userid;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getLoginname() {
        return loginname;
    }

    public void setLoginname(String loginname) {
        this.loginname = loginname;
    }

    public String getPassword() {
        return password;
    }

    public String setPassword(String password) {
        this.password = password;
    }
}

现在来写模板类,因为在查的时候需要实体类某个关键字作为筛选条件来查(譬如说ID),所以这里模板类先给定一个方法用来获取筛选条件的关键字(若有其他需要,再添加方法),如下:

public abstract class TemplateClass {
    public abstract String getClassKey();
}   

这样,一个实体类模板就完成了,下面来继承它:

public class User extends TemplateClass{
    //用户ID
    private String userId;
    //用户登陆名
    private String loginname;
    //用户登陆密码
    private String password;

    public String getUserId() {
        return userid;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getLoginname() {
        return loginname;
    }

    public void setLoginname(String loginname) {
        this.loginname = loginname;
    }

    public String getPassword() {
        return password;
    }

    public String setPassword(String password) {
        this.password = password;
    }

    @Override
    public String getClassKey() {
        return userid;
    }
}

那么要获取这个查询条件,通过去调用这个getClassKey方法就可以了,接下来看下增和改,对于实体类模板这儿,应该不需要再添加额外的方法了,相对于需要传查询条件的查方法而言,这两个操作其实都是传实体类进去,然后去执行相应的sql,不需要去获取实体类的其他信息了。

那么下面就需要来编写dao的模板了,我们先看下User的dao接口:

public interface UserDao{
    /**
     * 通过ID查询单条数据
     */
    User query(String userId);

    /**
     * 新增数据
     */
    int insert(User user);

    /**
     * 修改数据
     */
    int update(User User);
}

因为模板的dao接口需要UserDao去重写到它所有的方法,所以它可以这么写:

public interface TemplateDao{
    /**
     * 通过ID查询单条数据
     */
    TemplateClass query(String id);

    /**
     * 新增数据
     */
    int insert(TemplateClass templateClass);

    /**
     * 修改数据
     */
    int update(TemplateClass templateClass);
}

既然是模板dao,那么它里面涉及到的实体类也必须是模板实体类才行,接下来让UserDao去继承模板dao:

public interface UserDao extends TemplateDao{
    /**
     * 通过ID查询单条数据
     */
    @Override
    User query(String userid);

    /**
     * 新增数据
     */
    @Override 
    int insert(TemplateClass user);

    /**
     * 修改数据
     */
    @Override 
    int update(TemplateClass User);
}

这里要注意,这几个重写的方法入参类型必须与父类一致才行,返回的类型可以是父类里定义的返回类型的子类(这个一般IDE里也会提示的)。

3、模板方法

好了模板定义好了,可以开始整去调用这些模板的方法了,方法如下:

//这里入参得是模板类,那么调用模板方法时,根据不同的场景,传相应的继承模板类的实体类进来就可以了,如果这里User类的作为入参,templateClass相当于user,templateDao相当于userDao
public void templateMethod(RecieveRequestParam recieveRequestParam,TemplateClass templateClass, TemplateDao templateDao){
        //解析JSON成相应的实体,如果这里User类的作为入参,则会去解析成User类实例
        templateClass = JSONObject.parseObject(recieveRequestParam.getValue(), templateClass.getClass());
        //获取关键字用于查询,如果这里User类的作为入参,会去获取userId来查
        String key = templateClass.getClassKey();
        //这里其实相当于user = userDao.query(userId);
        TemplateClass template = templateDao.query(key);
        if(null == template) {
            //这里相当于userDao.insert(user);
            templateDao.insert(templateClass);
        }else {
            //这里相当于userDao.udpate(user);
            templateDao.update(templateClass);
        }
}

然后去调用它:

switch (enum){
            case USER:
                templateMethod(recieveRequestParam,resultValue,new User(),userDao);
                break;
            case PROJECT:
                templateMethod(recieveRequestParam,resultValue,new Position(),positionDao);
                break;
            case BUILDING:
                templateMethod(recieveRequestParam,resultValue,new Building(),buildingDao);
                break;
            default:
                break;
        }

看到这里,想必模板模式如何使用已经很清晰了。后续有碰到其他设计模式的使用场景,再来记录一波。

参考内容来源:

https://www.runoob.com/design-pattern/template-pattern.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值