[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.增加代码阅读难度,降低代码的可读性,过多使用适配器会使系统代码变得凌乱。