设计模式之行为型

前言

最近加班是真的很多,无法腾出大块时间来学习。设计模式又不想只更到一半半途而废,想了又想,决定精简,保证大家一看就懂(看完就忘…)。设计模式分创建型模式,结构型模式和行为型模式。到目前为止,创建型模式已经讲完,对于剩下的模式,会分成这两大块统一讲解。

行为型模式

行为型模式主要关注的点事类的动作,各个类之间相互的作用,将职责划分清楚,使我们的代码更加的清晰。

策略模式

定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。

策略模式是一个出现频率很高,但又很简单的模式。下面的场景是我们要出去旅游,但是可以选择出去旅游的交通方式,比如坐飞机,坐火车或者步行。废话不再多说,直接上码:
public interface Strategy {
    void travel();
}
public class Walk implements Strategy {
    @Override
    public void travel() {
        System.out.println("步行去旅行");
    }
}
public class Train implements Strategy {
    @Override
    public void travel() {
        System.out.println("坐火车去旅行");
    }
}
public class Airplane implements Strategy {
    @Override
    public void travel() {
        System.out.println("坐着飞机去旅行");
    }
}
public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public void execute(){
        strategy.travel();
    }
}
public class Client {
    public static void main(String[] args) {
        Context context = new Context(new Airplane());
        context.execute();
    }
}
console:
坐着飞机去旅行

策略模式的优点非常明显,在现有的系统中增加一个策略太容易,只要实现接口就可以了,其他都不用修改,类似于一个可以反复拆卸的插件,符合ocp原则。其缺点就是每个策略都是一个类,复用性很小,复杂的业务场景容易发生类数量爆炸,并且策略模式和迪米特法则是违背的,我们看下上面的clent场景类(相当于项目中的高层调用模块),我只是想使用一个策略,凭什么就要了解这个策略呢?那要封装类就没有意义了,这是策略模式的一个大缺点,所以策略模式很少单独出现,大多结合其他模式来弥补这个缺陷,如工厂方法或者代理模式。

观察者模式

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新

策略模式也叫发布订阅模式,其中最主要额角色名称就是subject被观察者和observer观察者。接下来会模拟一个场景,学校作为被观察者发布放假的消息,家长和学生作为观察者实现自己的逻辑:

/**
 * 主题(被观察者)需要实现的职责,就是可以动态的增加删除观察者。
 * 根据主题的状态通知所有的观察者Observer
 */
public abstract class Subject {
    private Vector<Observer> observerList = new Vector<>();

    private int status;

    public void addObserver(Observer observer){
        observerList.add(observer);
    }

    public void delObserver(Observer observer){
        observerList.remove(observer);
    }
    public void notifyAllObserver(){
        observerList.forEach(observer -> {
            observer.update();
        });
    }

}
public class SchoolSubject extends Subject {
    //学校宣布放七天假期
    public void haveSevenDaysHoliday(){
        System.out.println("学校:从今天开始放假,所有学生七天后返校");
        super.notifyAllObserver();
    }
}
public interface Observer {
    //观察者,被通知了实现其自己的逻辑
     void update() ;
}
public class Parent implements Observer {
    @Override
    public void update() {
        System.out.println("家长:这倒霉孩子怎么又放假了,坚决不能让他玩王者荣耀....");
    }
}
public class Student implements Observer {
    @Override
    public void update() {
        System.out.println("学生:哇哈哈,终于有时间打王者荣耀喽");
    }
}

public class Client {

    public static void main(String[] args) {
        SchoolSubject subject = new SchoolSubject();
        Student student = new Student();
        Parent parent = new Parent();
        subject.addObserver(student);
        subject.addObserver(parent);
        subject.haveSevenDaysHoliday();
    }

}
console:
学校:从今天开始放假,所有学生七天后返校
学生:哇哈哈,终于有时间打王者荣耀喽
家长:这倒霉孩子怎么又放假了,坚决不能让他玩王者荣耀....

虽说观察者和被观察者是耦合在一起的,但是不管是扩展增加观察者还是被观察者都非常容易。并且根据单一职责原则,每个类的职责都是唯一,需要一套机制将类串联起来形成一个真实的场景,就比如学校公布放假,孩子想着玩游戏,家长为了孩子的成绩禁止孩子玩游戏,然后因为学校放假我就不玩了(小学生,你懂得),这样就形成了一个触发机制。其缺点就是执行效率低下,需异步执行。从原理上看,我们常用的mq就是观察者模式的升级版。

责任链模式

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

责任链我们很容易想到链表结构,实际上责任链就是一种基于链表的处理方式。当一个请求过来,调用链表的头结点,处理之后再往后流转。
有这么一个场景:路飞饿了打开美团准备订外卖。选中吃的后,进行下单,首先校验是否在营业时间内,然后校验是否在配送范围内,然后校验是否有货等等,设定责任链都通过后,路飞才能订到饭。

//链表内结点的基类
public abstract class RuleHandler {

    protected RuleHandler successor;

    public abstract void echo(Context context);

    public void setSuccessor(RuleHandler successor) {
        this.successor = successor;
    }

    public RuleHandler getSuccessor() {
        return successor;
    }
}
//判断营业时间
public class TimeHandler extends RuleHandler {
    @Override
    public void echo(Context context) {
        //营业时间判断
        if (context.isTimeInRange()){
            if (this.getSuccessor()!=null){
                this.getSuccessor().echo(context);
            }
        }else {
            throw new RuntimeException("不在营业时间内");
        }
    }
}
//判断是否在配送范围内
public class AreaHanler extends RuleHandler {
    @Override
    public void echo(Context context) {
        if (context.isAreaInRange()){
            if (this.getSuccessor()!=null){
                this.getSuccessor().echo(context);
            }
        }else {
            throw  new RuntimeException("不在配送范围内");
        }
    }
}
//判断库存
public class StockHandler extends RuleHandler {
    @Override
    public void echo(Context context) {
        if (context.hasStock()){
            if (this.getSuccessor()!=null){
                this.getSuccessor().echo(context);
            }
        }else {
            throw new RuntimeException("挑选的商品已经卖完");
        }
    }
}

客户端调用逻辑:

public class Client {

    public static void main(String[] args) {
        RuleHandler timeHandler = new TimeHandler();
        RuleHandler areaHandler = new AreaHanler();
        RuleHandler stockHandler = new StockHandler();
        
        timeHandler.setSuccessor(areaHandler);
        areaHandler.setSuccessor(stockHandler);
        timeHandler.echo(new Context());
    }
    
}

代码非常简单,责任链模式的重点是在链上,由一条链去处理请求并返回相应的结果。它非常显著的优点就是将请求和处理分开,两者解耦,提高系统的灵活性。缺点就是链表遍历必须从链头到链尾,存在性能问题。采用了类似递归调用的方式,增大了读懂逻辑的难度

模板方法模式

在之前的博客里已经长篇大论过,故直接拿过来。
模板方法

状态模式

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

状态模式的核心是封装,通过状态的变更引起行为的变更。现在大家来思考一下,电脑有三种状态,分别为关机,已启动。

//代表环境,也就是状态的主体
public class Context {

    //所有的电脑状态
    public final static OpenState OPEN = new OpenState();
    public final static CloseState CLOSE = new CloseState();
    //电脑当前的状态
    private ComputerState currentState;

    public ComputerState getCurrentState() {
        return currentState;
    }

    public void setCurrentState(ComputerState currentState) {
        this.currentState = currentState;
        this.currentState.setContext(this);
    }

    public void openMachine() {
        this.currentState.openMachine();
    }

    public void closeMachine() {
        this.currentState.closeMachine();
    }
}
//状态基类,真实的电脑逻辑封装在了状态中
public abstract class ComputerState {

    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }
    public abstract void openMachine();

    public abstract void closeMachine();

}
public class OpenState extends ComputerState{
    @Override
    public void openMachine() {
        System.out.println("电脑开机...");
    }

    @Override
    public void closeMachine() {
        super.context.setCurrentState(Context.CLOSE);
        super.context.getCurrentState().closeMachine();
    }
}
public class CloseState extends ComputerState {
    @Override
    public void openMachine() {
        super.context.setCurrentState(Context.OPEN);
        super.context.getCurrentState().openMachine();
    }

    @Override
    public void closeMachine() {
        System.out.println("电脑关机...");
    }
}

客户端测试类:

public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        context.setCurrentState(Context.OPEN);
        context.openMachine();
        context.closeMachine();
    }
}

状态模式的优点有结构清晰,避免了各种条件的判断,省掉了swtich…case,if…else语句的使用,提升了代码的可读性。遵循了单一职责原则和开闭原则,每个状态都是一个子类,增加状态只需增加一个状态子类,修改状态,修改对应的子类就可以了。封装性非常好,客户端不需知道内部状态的转换以及相应的逻辑.其缺点就是状态子类会太多,并且我们可以将状态存储到数据库中,然后根据状态执行相应的操作,这也是一种不错的实现方式,具体如何使用看大家个人喜好了。

总结

本章的行为型模式总结了策略模式、观察者模式、责任链模式、模板方法模式和状态模式,其实不仅于此,还有备忘录模式和命令模式等,但因其使用场景有限,就不做一一探讨了,留给读者自己学习~.~

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000012923805
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞