平时我们写代码呢,多数情况都是流水线式写代码 基本就可以实现业务逻辑了。如何在写代码中找到乐趣呢,我觉得,好的方式就是:使用设计模式优化自己的代码。今天记录下日常工作中,我都使用过哪些设计模式。
image.png
1.策略模式:
假设有这样的业务场景,扫码登录:根据移动端扫码动作采取不同的逻辑。
示例:
/**
* 如果是依靠if else来写这个场景:
* 如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低。
* 如果你需要接入一种新的解析类型,那只能在原有代码上修改。
* 说得专业一点的话,就是if else代码,违背了面向对象编程的开闭原则以及单一原则。
* 如果你的代码就是酱紫:有多个if...else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。
*/
/**
* 策略模式酱紫实现的: 一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法),和不同策略的差异化实现。
*/
public abstract class AbstractWebLoginStrategyService {
// 属于哪种动作类型
abstract WebLoginStrategyEnum getType();
// 封装的公用算法(具体的动作逻辑)
public abstract void userLogin();
}
/**
* @author xx
* @since 不同策略的差异化实现:扫码动作
*/
@Service
public class ScanQrServiceImpl extends AbstractWebLoginStrategyService {
@Autowired
public ScanQrServiceImpl() {}
@Override
public WebLoginStrategyEnum getType() {
return WebLoginStrategyEnum.SCAN_QR;
}
@Override
public void userLogin() {
System.out.println("二维码状态变更为已扫码");
}
}
/**
* @author xx
* @since 不同策略的差异化实现: 确认登录动作
*/
@Service
public class ConfirmLoginServiceImpl extends AbstractWebLoginStrategyService {
@Override
public WebLoginStrategyEnum getType() {
return WebLoginStrategyEnum.CONFIRM_LOGIN;
}
@Override
public void userLogin() {
System.out.println("二维码状态变更为已登录");
}
}
/**
* @author xx
* @since 不同策略的差异化实现:取消扫码动作
*/
@Service
public class CancelLoginServiceImpl extends AbstractWebLoginStrategyService {
@Override
public WebLoginStrategyEnum getType() {
return WebLoginStrategyEnum.CANCEL_LOGIN;
}
@Override
public void userLogin() {
System.out.println("二维码状态变更为已失效");
}
}
/**
* 工厂模式与策略模式配合使用
*/
@Component
public class WebLoginStrategyFactory{
private static final Map<WebLoginStrategyEnum, AbstractWebLoginStrategyService> STRATEGY_SERVICE_MAP = new HashMap<>();
/**
* 把对应的策略初始化到map中
* 借助spring的特性定义的工厂模式
* 一般情况下工厂与其它模式配合使用,如当前的策略模式
* @param strategyServices 所有注入的实现类
*/
@Autowired
public WebLoginStrategyFactory(List<AbstractWebLoginStrategyService> strategyServices) {
Map<WebLoginStrategyEnum, AbstractWebLoginStrategyService> itemDataMap = strategyServices.stream().collect(Collectors.toMap(AbstractWebLoginStrategyService::getType, service -> service));
STRATEGY_SERVICE_MAP.putAll(itemDataMap);
}
public AbstractWebLoginStrategyService getService(WebLoginStrategyEnum strategyEnum){
return STRATEGY_SERVICE_MAP.get(strategyEnum);
}
}
/**
* 动作枚举
*/
public enum WebLoginStrategyEnum {
SCAN_QR(1,"移动端扫描PC二维码"),
CONFIRM_LOGIN(2,"移动端确认登录"),
CANCEL_LOGIN(3,"移动端取消登录"),
;
WebLoginStrategyEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
private Integer code;
private String message;
/**
* Gets code.
*
* @return the code
*/
public Integer getCode() {
return code;
}
/**
* Gets message.
*
* @return the message
*/
public String getMessage() {
return message;
}
/**
* Get by code enable enum.
*
* @param code the code
* @return the enable enum
*/
public static WebLoginStrategyEnum getByCode(Integer code) {
if (code == null) {
return null;
}
return Arrays.stream(WebLoginStrategyEnum.values())
.filter(goodsTypeEnums -> goodsTypeEnums.getCode() == code).findFirst().orElse(null);
}
}
/**
* 策略模式使用
*/
@RequestMapping("/strategy")
public void strategy(@RequestParam(value = "qrCode") Integer type){
AbstractWebLoginStrategyService service = strategyFactory.getService(WebLoginStrategyEnum.getByCode(type));
service.userLogin();
}
2.责任链模式:
我们来看一个常见的业务场景,下订单接口基本的逻辑,一般有参数非空校验、安全校验、黑名单校验、规则拦截等等..,当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式。
示例:
/**
* 责任链模式实际上是一种处理请求的模式,它让多个处理器(对象节点)都有机会处理该请求,直到其中某个处理成功为止。责任链模式把多个处理器串成链,然后让请求在链上传递。
* 责任链的使用:1.一个接口或者抽象类,2.每个对象差异化处理,3.对象链(数组)初始化(连起来)
*/
public abstract class AbstractHandler {
//责任链中的下一个对象
private AbstractHandler nextHandler;
/**
* 责任链的下一个对象
*/
public void setNextHandler(AbstractHandler nextHandler){
this.nextHandler = nextHandler;
}
/**
* 具体参数拦截逻辑,给子类去实现
*/
public void filter(String param) {
boolean b = doFilter(param);
if(!b){
throw new RuntimeException("检查不通过");
}
if (getNextHandler() != null) {
getNextHandler().filter(param);
}
}
public AbstractHandler getNextHandler() {
return nextHandler;
}
abstract boolean doFilter(String param);
}
/**
* 差异化处理:黑名单校验对象
*/
@Component
@Order(3) //校验顺序排第3
public class CheckBlackFilterObject extends AbstractHandler {
@Override
public boolean doFilter(String param) {
//invoke black list check
System.out.println("校验黑名单");
return true;
}
}
@Component
@Order(1) //顺序排第1,最先校验
public class CheckParamFilterObject extends AbstractHandler {
@Override
public boolean doFilter(String param) {
System.out.println("非空参数检查");
return true;
}
}
@Component
@Order(2) //校验顺序排第2
public class CheckSecurityFilterObject extends AbstractHandler {
@Override
public boolean doFilter(String param) {
//invoke Security check
System.out.println("安全调用校验");
return true;
}
}
@Component
@Order(4) //校验顺序排第4
public class CheckRuleFilterObject extends AbstractHandler {
@Override
public boolean doFilter(String param) {
//check rule
System.out.println("check rule");
return true;
}
}
/**
* 责任链服务: 对象链连起来(初始化)&& 使用
*/
@Component("ChainPatternService")
public class ChainPatternService {
//自动注入各个责任链的对象
@Autowired
private List<AbstractHandler> abstractHandleList;
private AbstractHandler abstractHandler;
//spring注入后自动执行,责任链的对象连接起来
@PostConstruct
public void initializeChainFilter(){
for(int i = 0;i<abstractHandleList.size();i++){
if(i == 0){
abstractHandler = abstractHandleList.get(0);
}else{
AbstractHandler currentHander = abstractHandleList.get(i - 1);
AbstractHandler nextHander = abstractHandleList.get(i);
currentHander.setNextHandler(nextHander);
}
}
}
//直接调用这个方法使用
public void exec(String param) {
abstractHandler.filter(param);
}
}
/**
* 责任链模式:执行
*/
@RequestMapping("/responsibility")
public void responsibility(){
String param = "";
chainPatternService.exec(param);
System.out.println("执行具体业务");
}
3.模板方法模式:
若一些通用的方法却在每一个子类都重复写了一遍, 则用模板方法模式优化。
示例:
/**
* 假设我们有这么一个业务场景:内部系统不同商户,调用我们系统接口,去跟外部第三方系统交互(http方式)
* 会经历这几个流程:查询商户信息->对请求报文加签->发送http请求出去->对返回的报文验签
* 这里,有的商户可能是走代理请求出去的,有的是走直连请求。
* 那么除了发送请求是不一样 其它的(查询商户信息,对请求报文加密,对返回的报文验签)都是重复的。
* 那就把重复的代码用模板方法模式优化。
*/
/**
* 模板方法使用:一个骨架流程抽象类(抽象方法放一起)->确定的共同方法步骤放到抽象类(去除抽象方法标记)->不确定的步骤给子类去差异化实现。
* 抽象类定义骨架流程(查询商户信息,加签,http请求,验签)
*/
public abstract class AbstractMerchantService {
public void queryMerchantInfo(){
System.out.println("查询商户信息");
}
public void signature(){
System.out.println("加签");
}
public void httpRequest(){ System.out.println("http 请求");}
public void verifySinature(){ System.out.println("验签");}
//模板方法流程
String handlerTempPlate(String req){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http 请求
httpRequest();
// 验签
verifySinature();
return "result:".concat(req);
}
// Http是否走代理(提供给子类实现)
abstract boolean isRequestByProxy();
}
@Component
public class CompanyAServiceImpl extends AbstractMerchantService {
public String hander(String req){
return handlerTempPlate(req);
}
// 走http代理的
public boolean isRequestByProxy(){
System.out.println("差异化实现 true");
return true;
}
}
@Component
public class CompanyBServiceImpl extends AbstractMerchantService {
public String hander(String req) {
return handlerTempPlate(req);
}
// 不走代理的
public boolean isRequestByProxy() {
System.out.println("差异化实现 false");
return false;
}
}
/**
* 模板方法模式使用
*/
@RequestMapping("/templateMethod")
public void templateMethod(){
companyBServiceImpl.hander("参数1");
companyBServiceImpl.isRequestByProxy();
companyAServiceImpl.hander("参数2");
companyAServiceImpl.isRequestByProxy();
}
2.观察者模式:
使用场景: 完成某件事情后,异步通知场景。如登陆成功或注册成功,发个IM消息等等。
示例1:
/**
* 观察者模式使用: 一个被观察者的类Observerable->多个观察者Observer->观察者的差异化实现。
*/
/**
* 一个被观察的类
*/
@Component
public class Observerable {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
notifyAllObservers(state);
}
//添加观察者
public Observerable addServer(Observer observer){
observers.add(observer);
return this;
}
//移除观察者
public void removeServer(Observer observer){
observers.remove(observer);
}
//通知
public void notifyAllObservers(int state){
if(state != 1){
System.out.println("不是通知的状态");
return ;
}
for (Observer observer : observers) {
observer.doEvent();
}
}
}
/**
* 观察者
*/
interface Observer {
void doEvent();
}
//Im消息
public class EmailObserver implements Observer{
public void doEvent(){
System.out.println("发送Email消息");
}
}
//Im消息
public class IMMessageObserver implements Observer{
public void doEvent(){
System.out.println("发送IM消息");
}
}
//Im消息
public class MobileNoObserver implements Observer{
public void doEvent(){
System.out.println("发送短信消息");
}
}
/**
* 观察者模式
*/
@RequestMapping("/observer")
public void observer(){
// 没必要链式注入,其实可以考虑利用spring来初始化进观察者的。
observerableService.addServer(new EmailObserver())
.addServer(new IMMessageObserver())
.addServer(new MobileNoObserver());
observerableService.setState(1);
}
示例2:
/**
* 经典观察者模式封装:EventBus实战
* 自己写一套观察者模式的代码,还是有点小麻烦。实际上,Guava EventBus就封装好了,它提供一套基于注解的事件总线,api可以灵活的使用。
* <!--guava依赖-->
* <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
* <dependency>
* <groupId>com.google.guava</groupId>
* <artifactId>guava</artifactId>
* <version>22.0</version>
* </dependency>
*/
/**
* 类似被观察者
*/
public class EventBusCenter {
private static EventBus eventBus = new EventBus();
private EventBusCenter() {}
public static EventBus getInstance() {
return eventBus;
}
//添加观察者
public static void register(Object obj) {
eventBus.register(obj);
}
//移除观察者
public static void unregister(Object obj) {
eventBus.unregister(obj);
}
//把消息推给观察者
public static void post(Object obj) {
eventBus.post(obj);
}
}
/**
* 观察者
*/
public class EventListener {
@Subscribe //加了订阅,这里标记这个方法是事件处理方法
public void handle(NotifyEvent notifyEvent) {
System.out.println("发送IM消息" + notifyEvent.getImNo());
System.out.println("发送短信消息" + notifyEvent.getMobileNo());
System.out.println("发送Email消息" + notifyEvent.getEmailNo());
}
}
/**
* 通知事件类
*/
public class NotifyEvent {
private String mobileNo;
private String emailNo;
private String imNo;
public NotifyEvent(String mobileNo, String emailNo, String imNo) {
this.mobileNo = mobileNo;
this.emailNo = emailNo;
this.imNo = imNo;
}
public String getMobileNo() {
return mobileNo;
}
public String getEmailNo() {
return emailNo;
}
public String getImNo() {
return imNo;
}
}
/**
* 观察者模式之EventBus实战
*/
@RequestMapping("/observerEventBus")
public void observerEventBus(){
EventListener eventListener = new EventListener();
EventBusCenter.register(eventListener);
EventBusCenter.post(new NotifyEvent("13372817283", "123@qq.com", "666"));
}
2.单例模式:
业务场景: 保证一个类仅有一个实例并提供一个访问它的全局访问点。I/O与数据库的连接,一般就用单例模式实现的。Windows里面的Task Manager(任务管理器)也是很典型的单例模式。
示例:
/**
* 饿汉式: 实例在初始化的时候就已经建好了,不管你后面有没有用到,都先新建好实例再说。这个就没有线程安全的问题,但是呢,浪费内存空间。
* 不像懒汉式不加 synchronized 就会存在安全问题。(这里对于懒汉式示例,懒的写 ...)
*/
public class EHanSingleton {
private static EHanSingleton instance = new EHanSingleton();
private EHanSingleton(){}
public static EHanSingleton getInstance() {
return instance;
}
}
/**
* 双重锁:综合了懒汉式和饿汉式两者的优缺点,在synchronized内外都加了一层if条件,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
*/
public class DoubleCheckSingleton {
private volatile static DoubleCheckSingleton instance;
private DoubleCheckSingleton() { }
public static DoubleCheckSingleton getInstance(){
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
/**
* 静态内部类实现方式:效果有点类似双重锁。但这种方式只适用于静态域场景。
*/
public class InnerClassSingleton {
private static class InnerClassSingletonHolder{
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
private InnerClassSingleton(){}
public static final InnerClassSingleton getInstance(){
return InnerClassSingletonHolder.INSTANCE;
}
}
/**
* 枚举:代码简洁清晰。并且它还自动支持序列化机制,绝对防止多次实例化。
*/
public enum SingletonEnum {
INSTANCE;
public static SingletonEnum getInstance(){
return INSTANCE;
}
}
/**
* 单例模式使用
*/
@RequestMapping("/singleton")
public void singleton(){
EHanSingleton instance = EHanSingleton.getInstance();
EHanSingleton instance1 = EHanSingleton.getInstance();
System.out.println("饿汉式单例:" + Objects.equals(instance, instance1));
DoubleCheckSingleton instance2 = DoubleCheckSingleton.getInstance();
DoubleCheckSingleton instance3 = DoubleCheckSingleton.getInstance();
System.out.println("双重锁单例:" + Objects.equals(instance2, instance3));
InnerClassSingleton instance4 = InnerClassSingleton.getInstance();
InnerClassSingleton instance5 = InnerClassSingleton.getInstance();
System.out.println("静态内部类单例:" + Objects.equals(instance4, instance5));
SingletonEnum instance6 = SingletonEnum.getInstance();
SingletonEnum instance7 = SingletonEnum.getInstance();
System.out.println("枚举单例:" + Objects.equals(instance6, instance7));
}