设计模式之(模板模式&适配器模式)

目录

[toc]

1.课程目标

1.学会用模板模式梳理使用工作中流程标准化的业务场景。

2.通过学习适配模式,优雅地解决代码功能的兼容问题。

3.了解JDK源码和spring源码中对模板模式的运用

2.内容定位

1.深刻了解模板模式和适配器模式的应用场景

2.定位高级课程

3.模板模式

3.1模板模式的定义

1.模板模式通常又叫做模板方法模式(Template Method Pattern)是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。

2.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

3.属于行为性设计模式

3.2 模板模式的适用场景

1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

2.各子类中公共的行为被提取出来并集到一个公共的父类中,从而避免代码重复

3.3 Demo案例

1.上课案例

课程模板规范抽象类

/**
 * @PackageName: com.raven.pattern.template.course
 * @ClassName: NetworkCourse
 * @Blame: raven
 * @Date: 2021-08-03 16:17
 * @Description: 上网课案例:
 * 定义课程模板类,规范创建课程的步骤
 * 模板方法模式使用抽象类规定整个流程
 */
public abstract class AbstractNetworkCourse {

    protected final void createCourse() {
        // 1.发布预习资料
        this.releasePreviewMaterials();
        // 2.准备PPT
        this.preparePPT();
        // 3.在线授课
        this.onlineDelivery();
        // 4.提交课件
        this.postNote();

        // 5.提交作业
        // 判断是否需要作业,子类继承时指定,如果布置了作业才需要提交作业
        if (needHomework()) {
            this.submitJob();
        }
    }

    /**
     * 钩子方法,实现流程的微调
     * @return
     */
    protected boolean needHomework() {
        return false;
    }

    /**
     * 如果布置作业,则需要提交作业,具体有没有作业,由子类自定义
     */
    abstract void submitJob();

    /**
     * 使用final定义方法,子类不能够覆盖重写
     */
    final void postNote() {
        System.out.println("老师发课件了~");
    }

    final void onlineDelivery() {
        System.out.println("老师在线授课啊");
    }

    final void preparePPT() {
        System.out.println("老师在准备PPT啊");
    }


    final void releasePreviewMaterials() {
        System.out.println("老师发预习资料了~");
    }
}

课程类

public class GoCourse extends AbstractNetworkCourse {

    @Override
    void submitJob() {
        System.out.println("Go 语言不需要作业");
    }
}

public class JavaCourse extends AbstractNetworkCourse {

    private boolean needHomeworkFlag;

    public JavaCourse(boolean needHomeworkFlag) {
        this.needHomeworkFlag = needHomeworkFlag;
    }

    /**
     * 流程微调,通过构造参数来确定是否执行 提交作业的逻辑
     * @return
     */
    @Override
    public boolean needHomework() {
        return needHomeworkFlag;
    }

    @Override
    void submitJob() {
        System.out.println("如果java老师有布置作业,我就写!");
    }
}

Main

public class CourseTest {

    public static void main(String[] args) {

        System.out.println("GO GO GO !");
        GoCourse goCourse = new GoCourse();
        goCourse.createCourse();

        System.out.println("==========================");
        System.out.println("JAVA JAVA JAVA !");
        JavaCourse javaCourse = new JavaCourse(true);
        javaCourse.createCourse();

        System.out.println("==========================");
        JavaCourse javaCourse2 = new JavaCourse(false);
        javaCourse2.createCourse();

    }
}

2.JDBC案例

jdbc模板规范接口

/**
 * @PackageName: com.raven.pattern.template.jdbc
 * @ClassName: RowMapper
 * @Blame: raven
 * @Date: 2021-08-03 20:44
 * @Description: ORM映射定制化的接口
 * 定义一个模板规范,所有的结果映射都需要执行这个接口
 */
public interface RowMapper<T> {
    /**
     * 规范结果映射
     * @param rs
     * @param rowNum 数据的行数
     * @return 映射返回的数据
     * @throws Exception
     */
    T mapRow(ResultSet rs, int rowNum) throws Exception;
}

jdbcTemplate

public class JdbcTemplate {

    private DataSource dataSource;

    public JdbcTemplate(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 根据sql 查询返回数据列表
     * @param sql 查询sql
     * @param rowMapper 数据RowMapper映射
     * @param values 查询参数列表
     * @return 数据列表
     */
    protected List<?> executeQuery(String sql,RowMapper<?> rowMapper, Object[] values){

        try {
            // 1.获取数据库连接
            Connection conn = this.getConnection();

            // 2.创建语句集
            PreparedStatement pstm =  this.createPrepareStatement(sql,conn);

            // 3.执行语句集
            ResultSet rs = this.executeQuery(pstm, values);

            // 4.处理结果集
           List<?> resultList =  this.parseResultSet(rs,rowMapper);

            // 5.关闭结果集
            this.closeResultSet(rs);

            // 6.关闭语句集
            this.closePreparedStatement(pstm);

            // 7.关闭数据库连接
            this.closeConnection(conn);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    protected void closeConnection(Connection conn) throws Exception{
        // 如果是数据库连接池,就是归还连接数,默认实现关闭
        conn.close();
    }

    private void closePreparedStatement(PreparedStatement pstm) throws Exception{
        pstm.close();
    }

    private void closeResultSet(ResultSet rs) throws Exception{
        rs.close();
    }

    /**
     * 遍历结果集数据,将结果集映射到对象上,处理结果集
     * @param rs 结果集
     * @param rowMapper 映射方法
     * @return 封装好的结果数据
     * @throws Exception
     */
    private List<?> parseResultSet(ResultSet rs, RowMapper<?> rowMapper) throws Exception{
        ArrayList<Object> resultList = Lists.newArrayList();
        int rowNum = 1;
        while (rs.next()){
            resultList.add(rowMapper.mapRow(rs,rowNum++));
        }
        return resultList;
    }

    /**
     * 设置请求参数,执行语句集
     * @param pstm
     * @param values
     * @return
     * @throws Exception
     */
    private ResultSet executeQuery(PreparedStatement pstm, Object[] values) throws Exception{
        // 遍历设置请求参数
        for (int i = 0; i < values.length; i++) {
            pstm.setObject(i,values[i]);
        }
        return pstm.executeQuery();
    }

    /**
     * 创建语句集
     * @param sql
     * @param conn
     * @return
     * @throws Exception
     */
    private PreparedStatement createPrepareStatement(String sql, Connection conn) throws Exception{
        return conn.prepareStatement(sql);
    }

    /**
     * 获取数据库连接
     * @return
     * @throws Exception
     */
    private Connection getConnection() throws Exception {
        return dataSource.getConnection();
    }
}

服务相关

@Data
public class User {
    private String username;
    private String password;
    private int age;
    private String address;
}

@Repository
public class UserDao extends JdbcTemplate {

    public UserDao(DataSource dataSource) {
        super(dataSource);
    }

    public List<?> selectAll() {
        String sql = "select * from t_user";
        return super.executeQuery(sql, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNum) throws Exception {
                User user = new User();
                user.setUsername(rs.getString("username"));
                user.setPassword(rs.getString("password"));
                user.setAge(rs.getInt("age"));
                user.setAddress(rs.getString("address"));
                return user;
            }
        }, null);
    }
}

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public List<User> selectAll(){
        List<?> list = userDao.selectAll();
        return (List<User>) list;
    }
}

3.4源码中的模板模式应用

1.jdk中:

abstractList get 方法
httpservlet service 方法 **2.mybatis中:**

baseExcutor ## 3.5模板模式的优点 **1.提高代码的复用性**

2.提高代码的拓展性

3.符合开闭原则

3.6模板模式的缺点

1.类数目的增加

2.间接地增加了系统实现的复杂度

3.继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。

4.适配器模式

4.1适配器模式的定义:

1.适配器模式(Adpater Pattern)是指将一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作。

2.属于结构型设计模式

4.2适配器模式的适用场景

1.已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。

2.适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品,不同厂家造成功能类似而接口不相同情况下的解决方案。

4.3 Demo案例

4.3.1.变压器案例

main

public class PowerAdapterTest {
    public static void main(String[] args) {
        PowerAdapter powerAdapter = new PowerAdapter(new Ac220());
        int i = powerAdapter.output5V();
        System.out.println(i);
    }
}

adapter

/**
 * @PackageName: com.raven.pattern.adapter.poweradapter
 * @ClassName: AC220
 * @Blame: raven
 * @Date: 2021-08-04 9:50
 * @Description: 220交流电
 */
public class Ac220 {

    public int outputAc220V(){
        int output = 220;
        System.out.println("输出电流" + output + "V");
        return output;
    }
}

/**
 * @PackageName: com.raven.pattern.adapter.poweradapter
 * @ClassName: Dc5
 * @Blame: raven
 * @Date: 2021-08-04 9:52
 * @Description: 直流电 5V转换器接口
 */
public interface Dc5 {

    /**
     * 输出5V电流
     * @return
     */
    int output5V();
}


/**
 * @PackageName: com.raven.pattern.adapter.poweradapter
 * @ClassName: PowerAdapter
 * @Blame: raven
 * @Date: 2021-08-04 9:53
 * @Description: 交流电适配器 实现接口,将Ac220 转为Ac5V
 */
public class PowerAdapter implements Dc5 {

    private Ac220 ac220;

    public PowerAdapter(Ac220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int output5V() {
        int adapterIntput = ac220.outputAc220V();
        int adapterOutput = adapterIntput / 44;
        System.out.println("使用PowerAdapter适配器输入AC" + adapterIntput  + "V,输出AC" + adapterOutput + "V");
        return adapterOutput;
    }
}

4.3.2.登录案例

V1:

main

public class SigninForThirdServiceTest {

    public static void main(String[] args) {
        SigninForThirdService signinForThirdService = new SigninForThirdService();
        // 原来的登录方式
        signinForThirdService.login("username", "password");

        // 通过继承实现新增其他的登录方式
        signinForThirdService.loginForPhone("18998986655", "8686");
        signinForThirdService.loginForQQ("asdasdwqeqw");
        signinForThirdService.loginForWechat("ssadasdasd");
    }
}

/**
 * @PackageName: com.raven.pattern.adapter.loginadapter.v1.service
 * @ClassName: SiginService
 * @Blame: raven
 * @Date: 2021-08-04 10:08
 * @Description: 模拟登录注册接口
 */
public class SigninService {

    public MsgResult regist(String username,String password){
        return MsgResult.success();
    }


    public MsgResult login(String username,String password){
        return MsgResult.success();
    }
}

/**
 * @PackageName: com.raven.pattern.adapter.loginadapter.v1.service
 * @ClassName: SigninForThridService
 * @Blame: raven
 * @Date: 2021-08-04 10:23
 * @Description: 系统升级,新增其他登录注册方式
 * 传统方式:通过继承之前的登录注册服务,提供更多的服务
 */
public class SigninForThirdService extends SigninService {

    /**
     * 新的注册登录方式,通过QQ登录
     *
     * @param openId
     * @return
     */
    public MsgResult loginForQQ(String openId) {
        // 1.openId是全局唯一,可以将它当成一个用户名(加长)
        // 2.密码默认为QQ_EMPTY
        // 3.注册(在原有系统里面创建一个新的用户)
        // 4.调用原来的登录方式
        return loginForRegist(openId, null);
    }

    public MsgResult loginForWechat(String openId) {
        return null;
    }

    public MsgResult loginForPhone(String phone, String code) {
        return null;
    }

    /**
     * 调用父类的注册登录方法,完成注册登录
     *
     * @param username
     * @param password
     * @return
     */
    private MsgResult loginForRegist(String username, String password) {
        super.regist(username, password);
        return super.login(username, password);
    }
}

V2: main

/**
 * @PackageName: com.raven.pattern.adapter.loginadapter.v1.service
 * @ClassName: SiginService
 * @Blame: raven
 * @Date: 2021-08-04 10:08
 * @Description: 模拟登录注册接口
 */
public class SigninService {

    public MsgResult regist(String username,String password){
        return MsgResult.success();
    }


    public MsgResult login(String username,String password){
        return MsgResult.success();
    }
}

/**
 * @PackageName: com.raven.pattern.adapter.loginadapter.v2
 * @ClassName: IPassportForThird
 * @Blame: raven
 * @Date: 2021-08-04 10:44
 * @Description: 定义登录接口规范
 */
public interface IPassportForThird {

    MsgResult loginForQQ(String openId);

    MsgResult loginForWechat(String openId);

    MsgResult loginForPhone(String phone,String code);

}

/**
 * @PackageName: com.raven.pattern.adapter.loginadapter.service.v2
 * @ClassName: PassportForThirdAdapter
 * @Blame: raven
 * @Date: 2021-08-04 10:51
 * @Description: 登录认证适配器
 * 继承原有的登录方式,实现新定义的登录方式
 */
public class PassportForThirdAdapter extends SigninService implements IPassportForThird {


    /**
     * 校验适配器,将真正的实现交给QQAdapter实现
     *
     * @param openId
     * @return
     */
    @Override
    public MsgResult loginForQQ(String openId) {
//        LoginAdapter adapter = new LoginForQQAdapter();
//        if (adapter.support(adapter)) {
//            return adapter.login(openId, adapter);
//        }
        return processLogin(openId, LoginForQQAdapter.class);
    }


    /**
     * 校验适配器,将真正的实现交给WechatAdapter实现
     *
     * @param openId
     * @return
     */
    @Override
    public MsgResult loginForWechat(String openId) {
//        LoginAdapter adapter = new LoginForWechatAdapter();
//        if (adapter.support(adapter)) {
//            return adapter.login(openId, adapter);
//        }
        return processLogin(openId, LoginForWechatAdapter.class);
    }

    /**
     * 校验适配器,将真正的实现交给PhoneAdapter实现
     *
     * @param phone
     * @param code
     * @return
     */
    @Override
    public MsgResult loginForPhone(String phone, String code) {
//        LoginAdapter adapter = new LoginForPhoneAdapter();
//        if (adapter.support(adapter)) {
//            return adapter.login(phone, adapter);
//        }
        return processLogin(phone, LoginForPhoneAdapter.class);
    }

    /**
     * 调用父类的注册登录方法,完成注册登录
     *
     * @param username
     * @param password
     * @return
     */
    private MsgResult loginForRegist(String username, String password) {
        super.regist(username, password);
        return super.login(username, password);
    }

    /**
     * 方法抽象
     *
     * @param key
     * @param clazz
     * @return
     */
    private MsgResult processLogin(String key, Class<? extends LoginAdapter> clazz) {
        try {
            LoginAdapter adapter = clazz.newInstance();
            if (adapter.support(adapter)) {
                return adapter.login(key, adapter);
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}


adpater


/**
 * @PackageName: com.raven.pattern.adapter.loginadapter.service.v2.adapter
 * @ClassName: LoginAdapter
 * @Blame: raven
 * @Date: 2021-08-04 10:52
 * @Description: 在适配器里面 这个接口可有可无,不要和模板模式混淆
 * 模板模式一定是抽象类,而这里仅仅只是一个接口
 */
public interface LoginAdapter {

    /**
     * 是否支持该适配器
     * @param adapter
     * @return
     */
    boolean support(Object adapter);

    /**
     * 登录
     * @param id
     * @param adapter
     * @return
     */
    MsgResult login(String id ,Object adapter);
}

public class LoginForPhoneAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForPhoneAdapter;
    }


    /**
     * 定义通过手机号登录的逻辑
     * @param id
     * @param adapter
     * @return
     */
    @Override
    public MsgResult login(String id, Object adapter) {
        return null;
    }
}

public class LoginForQQAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForQQAdapter;
    }

    /**
     * 定义通过QQ号登录的逻辑
     * @param id
     * @param adapter
     * @return
     */
    @Override
    public MsgResult login(String id, Object adapter) {
        return null;
    }
}

public class LoginForWechatAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForWechatAdapter;
    }

    /**
     * 定义通过Wechat登录的逻辑
     * @param id
     * @param adapter
     * @return
     */
    @Override
    public MsgResult login(String id, Object adapter) {
        return null;
    }
}

4.4 源码中的适配器

spring中的适配器: AdvisorAdapter

4.5适配器模式的优点

1.能够调高类的透明性和复用性,现有的类复用但不需要改变。

2.目标类和适配器类解耦,提高程序的拓展性。

3.在很多业务场景中符合开闭原则

4.6适配器模式的缺点

1.适配器编写过程需要全面考虑,可能会增加系统的复杂性。

2.增加代码阅读难度,降低代码的可读性,过多使用适配器会使系统代码变得凌乱。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦