- 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
- 使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
- 策略模式是oop中最著名的设计模式之一,是对方法行为的抽象,可以归类为行为设计模式,也是oop中interface经典的应用。其特点简单又实用,是我最喜欢的模式之一。策略模式定义了一个拥有共同行为的算法族,每个算法都被封装起来,可以互相替换,独立于客户端而变化。策略模式本身的实现比较简单,但是结合单例模式+简单工厂模式+注解+反射,可以构造出近乎完善的策略模式,彻底的消除if-else。
- 工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现
大家假设这样一个场景,比如很多类型分别处理不同的业务逻辑,这些类型以后可能会持续增加到很多很多,最简单的办法就是是一直使用if else,这样会是我们的代码维护变得越来发咋,但根据“开-闭原则”的宗旨:对扩展开放,对修改关闭。后面就想到使用工厂模式+策略模式替代ifelse;
业务场景:
我们常见的登录业务,是每个系统都必不可少的
登录:可以分为如下几种登录方式:
1、 账号密码登录
2、 手机号验证码登录
3、 手机号一键登录
4、 微信登录
这时候我们按照常规的开发不周的话就会使用到很多的if else 如下:
@ApiOperation(value = "登录", notes = "用户登录")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名(手机号)", required = true, paramType = "query"),
@ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query"),
@ApiImplicitParam(name = "loginType", value = "登录方式", defaultValue = "password:密码 code:验证码 onekey:手机号一键登陆", required = true, paramType = "query")
})
@PostMapping("/login")
public Result login(@RequestParam(value = "username") String username,
@RequestParam(value = "password") String password,
@RequestParam(value = "loginType") int loginType) {
if(loginType == 1){
System.out.println("账号密码方式登录!!!");
}else if(loginType == 2){
System.out.println("手机号验证码方式登录!!!");
}if(loginType == 3){
System.out.println("手机号一键登录!!!");
}else {
System.out.println("登录方式错误!!!");
}
return Result.ok();
// return loginService.login(dto.getLoginType(), dto.getUsername(), dto.getPassword(), null);
}
当使用抽象工厂模式+工厂方法模式+策略模式 这些设计模式结合使用的话,就会使我们的代码变得非常简洁,维护起来也非常简单,下面直接上改进实现步骤
具体结构:
1、登录类型自动义注解类
import java.lang.annotation.*;
@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface LoginChannel {
String code();
}
2、登录类型code常量类
public final class LoginChannelCode {
private LoginChannelCode() {
throw new AssertionError("不能产生实例");
}
public static final String PASSWORD_LOGIN_CODE = "password";
public static final String VERIFCODE_LOGIN_CODE = "code";
public static final String ONEKEY_LOGIN_CODE = "onekey";
}
3、LoginService 登录接口入口类
import com.whcloud.wmp.utils.Result;
public interface LoginService {
Result login(String loginType, String userName, String password, String code);
}
响应结果类: import com.alibaba.fastjson.annotation.JSONField; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.Maps; import com.whcloud.wmp.domain.Enum.ErrorCode; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.util.Map; @ApiModel(value = "响应结果") @Data public class Resultimplements Serializable { private static final long serialVersionUID = -6190689122701100762L; @ApiModelProperty(value = "响应编码:0-请求处理成功") private Integer code = 0; @ApiModelProperty(value = "提示消息") private String message; @ApiModelProperty(value = "请求路径") private String path; @ApiModelProperty(value = "响应数据") private T data; private Integer httpStatus; @ApiModelProperty(value = "附加数据") private Map extra; @ApiModelProperty(value = "响应时间") private Long timestamp = System.currentTimeMillis(); @JSonField(serialize = false, deserialize = false) @JsonIgnore public Integer getHttpStatus() { return httpStatus; } @JSonField(serialize = false, deserialize = false) @JsonIgnore public boolean isOk() { return this.code == ErrorCode.OK.getCode(); } public static Result ok() { return new Result().code(ErrorCode.OK.getCode()); } public static Result failed() { return new Result().code(ErrorCode.FAIL.getCode()); } public Result code(Integer code) { this.code = code; return this; } public Result msg(String message) { if (message == null) { message = ""; } // this.message = i18n(message,message); this.message = message; return this; } public Result data(T data) { this.data = data; return this; } public Result path(String path) { this.path = path; return this; } public Result httpStatus(int httpStatus) { this.httpStatus = httpStatus; return this; } public Result put(String key, Object value) { if (this.extra == null) { this.extra = Maps.newHashMap(); } this.extra.put(key, value); return this; } }
4、登录接口适配器
import com.whcloud.wmp.utils.Result;
public abstract class LoginAdapter implements LoginService {
@Override
public Result login(String loginType, String userName, String password, String code) {
return new Result();
}
}
5、登录方式 代理类
import com.whcloud.wmp.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import java.util.Map;
@Slf4j
public class LoginDelegate extends LoginAdapter {
@Override
public Result login(String loginType, String userName, String password, String code) {
LoginService loginService = getLoginService(loginType);
if(ObjectUtils.isEmpty(loginService)){
return Result.failed().msg("登录方式错误!!!");
}
return loginService.login(loginType, userName, password, code);
}
private Map map;
public LoginDelegate(Map map) {
log.info("初始化登录方式====>{}种--{}", map.size(), map);
this.map = map;
}
private LoginService getLoginService(String loginType) {
if (map.containsKey(loginType)) {
return map.get(loginType);
}
return null;
}
}
6、登录方式 工厂类
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.lang.NonNull; import java.util.HashMap; import java.util.List; import java.util.Map; public class LoginFactoryBean implements FactoryBean, InitializingBean { private LoginService loginService; @NonNull private List loginServiceList; public List getLoginServiceList() { return loginServiceList; } public void setLoginServiceList(@NonNull List loginServiceList) { this.loginServiceList = loginServiceList; } @Override public void afterPropertiesSet() throws Exception { Map map = new HashMap<>(loginServiceList.size() << 1); for (LoginService r : loginServiceList) { LoginChannel channel = r.getClass().getAnnotation(LoginChannel.class); if (channel == null) { continue; } map.put(channel.code(), r); } loginService = new LoginDelegate(map); } @Override public LoginService getObject() throws Exception { return loginService; } @Override public Class> getObjectType() { return loginService.getClass(); } }
7、登录方式 config配置类(这个是关键)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.util.List;
@Configuration
public class LoginChannelConfiguration {
@Bean
@Primary
public LoginFactoryBean repayFactoryBean(List list) {
LoginFactoryBean factoryBean = new LoginFactoryBean();
factoryBean.setLoginServiceList(list);
return factoryBean;
}
}
8,、具体的登录方法实现类(这里之写了三种,如有增加只需要增加一个实现类即可,注意的是的类上的自定义注解 @LoginChannel(code = LoginChannelCode.VERIFCODE_LOGIN_CODE) ),
(1)账号密码登录实现类
import com.whcloud.wmp.channel.LoginAdapter;
import com.whcloud.wmp.channel.LoginChannel;
import com.whcloud.wmp.channel.LoginChannelCode;
import com.whcloud.wmp.utils.Result;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
@LoginChannel(code = LoginChannelCode.PASSWORD_LOGIN_CODE)
public class PasswordLogin extends LoginAdapter {
@Override
public Result login(String loginType, String userName, String password, String code) {
//用户名登录方式一些参数验证逻辑
if(StringUtils.isEmpty(userName) || StringUtils.isEmpty(password) ){
return Result.failed().msg("用户名密码不能为空!!!");
}
if(!"admin".equals(userName) || !"111111".equals(password) ){
return Result.failed().msg("用户名或密码错误!!!");
}
return Result.ok().msg("密码方式登陆成功!!!");
}
}
(2)手机号验证码登录实现类
import com.whcloud.wmp.channel.LoginAdapter;
import com.whcloud.wmp.channel.LoginChannel;
import com.whcloud.wmp.channel.LoginChannelCode;
import com.whcloud.wmp.utils.Result;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
@LoginChannel(code = LoginChannelCode.VERIFCODE_LOGIN_CODE)
public class VerifCodeLogin extends LoginAdapter {
@Override
public Result login(String loginType, String userName, String password, String code) {
//验证码登录方式一些参数验证逻辑
if(StringUtils.isEmpty(userName) || StringUtils.isEmpty(code) ){
return Result.failed().msg("手机号或验证码不能为空!!!");
}
return Result.ok().msg("验证码方式登陆成功!!!");
}
}
(3)手机号一键登录实现类
import com.whcloud.wmp.channel.LoginAdapter;
import com.whcloud.wmp.channel.LoginChannel;
import com.whcloud.wmp.channel.LoginChannelCode;
import com.whcloud.wmp.utils.Result;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
@LoginChannel(code = LoginChannelCode.ONEKEY_LOGIN_CODE)
public class onkeyLogin extends LoginAdapter {
@Override
public Result login(String loginType, String userName, String password, String code) {
//一键登录方式一些参数验证逻辑
if(StringUtils.isEmpty(userName)){
return Result.failed().msg("手机号不能为空!!!");
}
return Result.ok().msg("一键登录方式登陆成功!!!");
}
}
9、Controller接口入口
@Autowired
private LoginService loginService;
@ApiOperation(value = "登录", notes = "用户登录")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名(手机号)", required = true, paramType = "query"),
@ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query"),
@ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query"),
@ApiImplicitParam(name = "loginType", value = "登录方式", defaultValue = "password:密码 code:验证码 onekey:手机号一键登陆", required = true, paramType = "query")
})
@PostMapping("/login")
public Result login(@RequestParam(value = "username") String username,
@RequestParam(value = "password") String password,
@RequestParam(value = "code") String code,
@RequestParam(value = "loginType") String loginType) {
// if(loginType == 1){
// System.out.println("账号密码方式登录!!!");
// }else if(loginType == 2){
// System.out.println("手机号验证码方式登录!!!");
// }if(loginType == 3){
// System.out.println("手机号一键登录!!!");
// }else {
// System.out.println("登录方式错误!!!");
// }
// return Result.ok();
Result result = loginService.login(loginType, username, password, code);
return result;
}
10、实现效果



