- 创建型设计模式主要解决“对象的创建”问题
- 结构型设计模式主要解决“类或对象的组合或组装”问题
- 行为型设计模式主要解决的就是“类或对象之间的交互”问题
观察者模式
观察者模式也被称为发布订阅模式, 在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers(Message message);}public interface Observer {void update(Message message);}public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<Observer>();@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers(Message message) {for (Observer observer : observers) {observer.update(message);}}}public class ConcreteObserverOne implements Observer {@Overridepublic void update(Message message) {//TODO: 获取消息通知,执行自己的逻辑...System.out.println("ConcreteObserverOne is notified.");}}public class ConcreteObserverTwo implements Observer {@Overridepublic void update(Message message) {//TODO: 获取消息通知,执行自己的逻辑...System.out.println("ConcreteObserverTwo is notified.");}}public class Demo {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();subject.registerObserver(new ConcreteObserverOne());subject.registerObserver(new ConcreteObserverTwo());subject.notifyObservers(new Message());}}
设计模式要干的事情就是解耦。
创建型模式是将创建和使用代码解耦,结构型模式是将不同功能代码解耦,行为型模式是将不同的行为代码解耦,具体到观察者模式,它是将观察者和被观察者代码解耦。
借助设计模式,我们利用更好的代码结构,将一大坨代码拆分成职责更单一的小类,让其满足开闭原则、高内聚松耦合等特性,以此来控制和应对代码的复杂性,提高代码的可扩展性。
模板模式
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。
模板模式有两大作用:复用和扩展。其中,复用指的是,所有的子类可以复用父类中提供的模板方法的代码。扩展指的是,框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
public abstract class AbstractClass {public final void templateMethod() {//...method1();//...method2();//...}protected abstract void method1();protected abstract void method2();}public class ConcreteClass1 extends AbstractClass {@Overrideprotected void method1() {//...}@Overrideprotected void method2() {//...}}public class ConcreteClass2 extends AbstractClass {@Overrideprotected void method1() {//...}@Overrideprotected void method2() {//...}}AbstractClass demo = ConcreteClass1();demo.templateMethod();
策略模式
策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。
通过策略模式来移除If/else或switch的分支判断逻辑
示例代码
public class OrderService {public double discount(Order order) {double discount = 0.0;OrderType type = order.getType();if (type.equals(OrderType.NORMAL)) { // 普通订单//...省略折扣计算算法代码} else if (type.equals(OrderType.GROUPON)) { // 团购订单//...省略折扣计算算法代码} else if (type.equals(OrderType.PROMOTION)) { // 促销订单//...省略折扣计算算法代码}return discount;}}
使用策略模式+工厂方法模式优化后
// 策略的定义public interface DiscountStrategy {double calDiscount(Order order);}// 省略NormalDiscountStrategy、GrouponDiscountStrategy、PromotionDiscountStrategy类代码...// 策略的创建public class DiscountStrategyFactory {private static final Map<OrderType, DiscountStrategy> strategies = new HashMap<>();static {strategies.put(OrderType.NORMAL, new NormalDiscountStrategy());strategies.put(OrderType.GROUPON, new GrouponDiscountStrategy());strategies.put(OrderType.PROMOTION, new PromotionDiscountStrategy());}public static DiscountStrategy getDiscountStrategy(OrderType type) {return strategies.get(type);}}// 策略的使用public class OrderService {public double discount(Order order) {OrderType type = order.getType();DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStrategy(type);return discountStrategy.calDiscount(order);}}
职责链模式
将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。
也可以有变种, 比如springSecurity, 经过所有的校验器, 只要有一个成功就成功.
示例代码:
public interface IHandler {boolean handle();}public class HandlerA implements IHandler {@Overridepublic boolean handle() {boolean handled = false;//...return handled;}}public class HandlerB implements IHandler {@Overridepublic boolean handle() {boolean handled = false;//...return handled;}}public class HandlerChain {private List<IHandler> handlers = new ArrayList<>();public void addHandler(IHandler handler) {this.handlers.add(handler);}public void handle() {for (IHandler handler : handlers) {boolean handled = handler.handle();if (handled) {break;}}}}// 使用举例public class Application {public static void main(String[] args) {HandlerChain chain = new HandlerChain();chain.addHandler(new HandlerA());chain.addHandler(new HandlerB());chain.handle();}}
可以利用spring的IOC在HandlerChain做非常方便的构造方法注入:
@Componentpublic class HandlerChain {private final List<Handler> HANDLERS = new ArrayList<>();public HandlerChain(List<Handler> handlers) {HANDLERS.addAll(handlers);}public void handle() {for (Handler handler : HANDLERS) {if (handler.handle()) {break;}}}}
职责链模式在Servlet Filter中的应用
Servlet Filter 是 Java Servlet 规范中定义的组件
使用示例
public class LogFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 在创建Filter时自动调用,// 其中filterConfig包含这个Filter的配置参数,比如name之类的(从配置文件中读取的)}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("拦截客户端发送来的请求.");chain.doFilter(request, response);System.out.println("拦截发送给客户端的响应.");}@Overridepublic void destroy() {// 在销毁Filter时自动调用}}// 在web.xml配置文件中如下配置:<filter><filter-name>logFilter</filter-name><filter-class>com.xzg.cd.LogFilter</filter-class></filter><filter-mapping><filter-name>logFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
ApplicationFilterChain 类就是 Tomcat 提供的 FilterChain 的实现类,源码如下所示。
public final class ApplicationFilterChain implements FilterChain {private int pos = 0; //当前执行到了哪个filterprivate int n; //filter的个数private ApplicationFilterConfig[] filters;private Servlet servlet;@Overridepublic void doFilter(ServletRequest request, ServletResponse response) {if (pos < n) {ApplicationFilterConfig filterConfig = filters[pos++];Filter filter = filterConfig.getFilter();filter.doFilter(request, response, this);} else {// filter都处理完毕后,执行servletservlet.service(request, response);}}public void addFilter(ApplicationFilterConfig filterConfig) {for (ApplicationFilterConfig filter:filters)if (filter==filterConfig)return;if (n == filters.length) {//扩容ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT];System.arraycopy(filters, 0, newFilters, 0, n);filters = newFilters;}filters[n++] = filterConfig;}}
职责链模式在Spring Interceptor中的应用
Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,
使用示例
public class LogInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("拦截客户端发送来的请求.");return true; // 继续后续的处理}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("拦截发送给客户端的响应.");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("这里总是被执行.");}}//在Spring MVC配置文件中配置interceptors<mvc:interceptors><mvc:interceptor><mvc:mapping path="/*"/><bean class="com.xzg.cd.LogInterceptor" /></mvc:interceptor></mvc:interceptors>
HandlerExecutionChain 的源码如下
public class HandlerExecutionChain {private final Object handler;private HandlerInterceptor[] interceptors;public void addInterceptor(HandlerInterceptor interceptor) {initInterceptorList().add(interceptor);}boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}}}return true;}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}}void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);} catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}}}
那在项目开发中,类似权限这样的访问控制功能,我们该选择三者(AOP、Servlet Filter、Spring Interceptor)中的哪个来实现呢?有什么参考标准吗?
三者应用范围不同: web filter 作用于容器,应用范围影响最大;spring interceptor 作用于框架,范围影响适中;aop 作用于业务逻辑,精细化处理,范围影响最小。
状态模式
有限状态机,英文翻译是 Finite State Machine,缩写为 FSM,简称为状态机。状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。
状态机示例:
骨架代码:
public enum State {SMALL(0),SUPER(1),FIRE(2),CAPE(3);private int value;private State(int value) {this.value = value;}public int getValue() {return this.value;}}public class MarioStateMachine {private int score;private State currentState;public MarioStateMachine() {this.score = 0;this.currentState = State.SMALL;}public void obtainMushRoom() {//TODO}public void obtainCape() {//TODO}public void obtainFireFlower() {//TODO}public void meetMonster() {//TODO}public int getScore() {return this.score;}public State getCurrentState() {return this.currentState;}}public class ApplicationDemo {public static void main(String[] args) {MarioStateMachine mario = new MarioStateMachine();mario.obtainMushRoom();int score = mario.getScore();State state = mario.getCurrentState();System.out.println("mario score: " + score + "; state: " + state);}}
状态机实现方式一:分支逻辑法
public class MarioStateMachine {private int score;private State currentState;public MarioStateMachine() {this.score = 0;this.currentState = State.SMALL;}public void obtainMushRoom() {if (currentState.equals(State.SMALL)) {this.currentState = State.SUPER;this.score += 100;}}public void obtainCape() {if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER) ) {this.currentState = State.CAPE;this.score += 200;}}public void obtainFireFlower() {if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER) ) {this.currentState = State.FIRE;this.score += 300;}}public void meetMonster() {if (currentState.equals(State.SUPER)) {this.currentState = State.SMALL;this.score -= 100;return;}if (currentState.equals(State.CAPE)) {this.currentState = State.SMALL;this.score -= 200;return;}if (currentState.equals(State.FIRE)) {this.currentState = State.SMALL;this.score -= 300;return;}}public int getScore() {return this.score;}public State getCurrentState() {return this.currentState;}}
状态机实现方式二:查表法
public enum Event {GOT_MUSHROOM(0),GOT_CAPE(1),GOT_FIRE(2),MET_MONSTER(3);private int value;private Event(int value) {this.value = value;}public int getValue() {return this.value;}}public class MarioStateMachine {private int score;private State currentState;private static final State[][] transitionTable = {{SUPER, CAPE, FIRE, SMALL},{SUPER, CAPE, FIRE, SMALL},{CAPE, CAPE, CAPE, SMALL},{FIRE, FIRE, FIRE, SMALL}};private static final int[][] actionTable = {{+100, +200, +300, +0},{+0, +200, +300, -100},{+0, +0, +0, -200},{+0, +0, +0, -300}};public MarioStateMachine() {this.score = 0;this.currentState = State.SMALL;}public void obtainMushRoom() {executeEvent(Event.GOT_MUSHROOM);}public void obtainCape() {executeEvent(Event.GOT_CAPE);}public void obtainFireFlower() {executeEvent(Event.GOT_FIRE);}public void meetMonster() {executeEvent(Event.MET_MONSTER);}private void executeEvent(Event event) {int stateValue = currentState.getValue();int eventValue = event.getValue();this.currentState = transitionTable[stateValue][eventValue];this.score += actionTable[stateValue][eventValue];}public int getScore() {return this.score;}public State getCurrentState() {return this.currentState;}}
状态机实现方式三:状态模式
public interface IMario { //所有状态类的接口State getName();//以下是定义的事件void obtainMushRoom();void obtainCape();void obtainFireFlower();void meetMonster();}public class SmallMario implements IMario {private MarioStateMachine stateMachine;public SmallMario(MarioStateMachine stateMachine) {this.stateMachine = stateMachine;}@Overridepublic State getName() {return State.SMALL;}@Overridepublic void obtainMushRoom() {stateMachine.setCurrentState(new SuperMario(stateMachine));stateMachine.setScore(stateMachine.getScore() + 100);}@Overridepublic void obtainCape() {stateMachine.setCurrentState(new CapeMario(stateMachine));stateMachine.setScore(stateMachine.getScore() + 200);}@Overridepublic void obtainFireFlower() {stateMachine.setCurrentState(new FireMario(stateMachine));stateMachine.setScore(stateMachine.getScore() + 300);}@Overridepublic void meetMonster() {// do nothing...}}public class SuperMario implements IMario {private MarioStateMachine stateMachine;public SuperMario(MarioStateMachine stateMachine) {this.stateMachine = stateMachine;}@Overridepublic State getName() {return State.SUPER;}@Overridepublic void obtainMushRoom() {// do nothing...}@Overridepublic void obtainCape() {stateMachine.setCurrentState(new CapeMario(stateMachine));stateMachine.setScore(stateMachine.getScore() + 200);}@Overridepublic void obtainFireFlower() {stateMachine.setCurrentState(new FireMario(stateMachine));stateMachine.setScore(stateMachine.getScore() + 300);}@Overridepublic void meetMonster() {stateMachine.setCurrentState(new SmallMario(stateMachine));stateMachine.setScore(stateMachine.getScore() - 100);}}// 省略CapeMario、FireMario类...public class MarioStateMachine {private int score;private IMario currentState; // 不再使用枚举来表示状态public MarioStateMachine() {this.score = 0;this.currentState = new SmallMario(this);}public void obtainMushRoom() {this.currentState.obtainMushRoom();}public void obtainCape() {this.currentState.obtainCape();}public void obtainFireFlower() {this.currentState.obtainFireFlower();}public void meetMonster() {this.currentState.meetMonster();}public int getScore() {return this.score;}public State getCurrentState() {return this.currentState.getName();}public void setScore(int score) {this.score = score;}public void setCurrentState(IMario currentState) {this.currentState = currentState;}}
实际上,上面的代码还可以继续优化,我们可以将状态类设计成单例,毕竟状态类中不包含任何成员变量。在这里,我们可以通过函数参数将 MarioStateMachine 传递进状态类。
public interface IMario {State getName();void obtainMushRoom(MarioStateMachine stateMachine);void obtainCape(MarioStateMachine stateMachine);void obtainFireFlower(MarioStateMachine stateMachine);void meetMonster(MarioStateMachine stateMachine);}public class SmallMario implements IMario {private static final SmallMario instance = new SmallMario();private SmallMario() {}public static SmallMario getInstance() {return instance;}@Overridepublic State getName() {return State.SMALL;}@Overridepublic void obtainMushRoom(MarioStateMachine stateMachine) {stateMachine.setCurrentState(SuperMario.getInstance());stateMachine.setScore(stateMachine.getScore() + 100);}@Overridepublic void obtainCape(MarioStateMachine stateMachine) {stateMachine.setCurrentState(CapeMario.getInstance());stateMachine.setScore(stateMachine.getScore() + 200);}@Overridepublic void obtainFireFlower(MarioStateMachine stateMachine) {stateMachine.setCurrentState(FireMario.getInstance());stateMachine.setScore(stateMachine.getScore() + 300);}@Overridepublic void meetMonster(MarioStateMachine stateMachine) {// do nothing...}}// 省略SuperMario、CapeMario、FireMario类...public class MarioStateMachine {private int score;private IMario currentState;public MarioStateMachine() {this.score = 0;this.currentState = SmallMario.getInstance();}public void obtainMushRoom() {this.currentState.obtainMushRoom(this);}public void obtainCape() {this.currentState.obtainCape(this);}public void obtainFireFlower() {this.currentState.obtainFireFlower(this);}public void meetMonster() {this.currentState.meetMonster(this);}public int getScore() {return this.score;}public State getCurrentState() {return this.currentState.getName();}public void setScore(int score) {this.score = score;}public void setCurrentState(IMario currentState) {this.currentState = currentState;}}
针对状态机,今天我们总结了三种实现方式。
- 第一种实现方式叫分支逻辑法。
利用 if-else 或者 switch-case 分支逻辑,参照状态转移图,将每一个状态转移原模原样地直译成代码。对于简单的状态机来说,这种实现方式最简单、最直接,是首选。
- 第二种实现方式叫查表法。
对于状态很多、状态转移比较复杂的状态机来说,查表法比较合适。通过二维数组来表示状态转移图,能极大地提高代码的可读性和可维护性。
- 第三种实现方式叫状态模式。
对于状态并不多、状态转移也比较简单,但事件触发执行的动作包含的业务逻辑可能比较复杂的状态机来说,我们首选这种实现方式。
像游戏这种比较复杂的状态机,包含的状态比较多,我优先推荐使用查表法,而状态模式会引入非常多的状态类,会导致代码比较难维护。相反,像电商下单、外卖下单这种类型的状态机,它们的状态并不多,状态转移也比较简单,但事件触发执行的动作包含的业务逻辑可能会比较复杂,所以,更加推荐使用状态模式来实现。
