【设计模式】 - 行为型模式(Behavioral Patterns)

  • 行为型模式关注点:怎样运行对象/类?,“所以我们关注下类/对象的运行时流程控制”
  • 行为型模式用于描述程序在运行时复杂的流程控制。
  • 描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
  • 行为型模式分为类行为模式对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

模板方法(Template)

在模板模式中,一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,
但调用将以抽象类中定义的方式进行。

角色

  • 抽象类/抽象模板(Abstract Class)
  • 具体子类/具体实现(Concrete Class)
    在这里插入图片描述

实例

算法模板

public abstract class CookTemplate {
	// 模板算法
    void cook() {
        heating();
        addFood();
        addSalt();
        stirfry();
        end();
    }
    abstract void heating();

    abstract void addFood();

    abstract void addSalt();

    abstract void stirfry();

    abstract void end();
}

自定义实现

public class AutoCookMachine extends CookTemplate {

    @Override
    void heating() {
        System.out.println("加热处理");
    }

    @Override
    void addFood() {
        System.out.println("添加食物");
    }

    @Override
    void addSalt() {
        System.out.println("加盐");
    }

    @Override
    void stirfry() {
        System.out.println("开始炒制");
    }

    @Override
    void end() {
        System.out.println("炒至完成");
    }
}

调用展示略

应用场景

  • Spring的继承体系
  • JdbcTemplate、RedisTemplate的再拓展

策略模式(Strategy)

定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

角色

  • 抽象策略(Strategy)类:公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
  • 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
    在这里插入图片描述

实现

游戏策略

public abstract class GameStrategy {
    public abstract void regimentalWarfare();
}

游戏策略实现

public class UziStrategy extends GameStrategy {
    @Override
    public void regimentalWarfare() {
        System.out.println("生死看淡,不服就干");
    }
}

环境类

public class Context {

    private GameStrategy gameStrategy;

    public void setGameStrategy(GameStrategy gameStrategy) {
        this.gameStrategy = gameStrategy;
    }

    void startGame() {
        System.out.println("进行游戏对战");
        gameStrategy.regimentalWarfare();
    }
}

调用展示略

应用场景

  • Spring的 InstantiationStrategy
  • 线程池拒绝策略
  • 可定义策略来避免使用多重条件语句,如 if…else 语句、switch…case 语句

状态模式(State)

对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

角色

  • 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
    在这里插入图片描述

实现

团队抽象状态

public interface TeamState {
    void playGame();
	// 模拟状态流转
    TeamState changeState();
}

团队具体状态

// 势均力敌状态
public class MatchState implements TeamState {
   
    @Override
    public void playGame() {
        System.out.println("势均力敌五五开");
    }
    
    @Override
    public TeamState changeState() {
        return new EatBeefNoodlesState();
    } 
}

// 饿肚子状态
public class EatBeefNoodlesState implements TeamState {
    @Override
    public void playGame() {
        System.out.println("饿了,状态不行,吃点再战");
    }

    @Override
    public TeamState changeState() {
        return new VocationState();
    }
}

// 职业状态
public class VocationState implements TeamState {
    @Override
    public void playGame() {
        System.out.println("职业状态稳胜");
    }

    @Override
    public TeamState changeState() {
        return new VocationState();
    }
}

环境类

public class SKT {

    private TeamState state;

    public void setState(TeamState state) {
        this.state = state;
    }

    void startGame() {
        state.playGame();
    }
}

启动测试

public class StateStart {
    public static void main(String[] args) {
        SKT skt = new SKT();
        MatchState matchState = new MatchState();
        skt.setState(matchState);
        skt.startGame();

        // 模拟状态流转
        TeamState eatState = matchState.changeState();
        skt.setState(eatState);
        skt.startGame();

        TeamState vocationState = eatState.changeState();
        skt.setState(vocationState);
        skt.startGame();
    }
}
势均力敌五五开
饿了,状态不行,吃点再战
职业状态稳胜

应用场景

  • 流程框架与状态机

状态模式更关注做什么,策略模式更关注怎么做


中介者模式(Mediator)

用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,减少对象间混乱的依赖关系,从而使其耦合松散,而且可以独立地改变它们之间的交互。

在这里插入图片描述

角色

  • Mediator: 抽象中介者
  • ConcreteMediator: 具体中介者
  • Colleague: 抽象同事类
  • ConcreteColleague: 具体同事类

实现

需求:协调多个飞机之间的沟通

首先,我们需要定义一个中介者接口:

public interface TrafficMediator {
    void register(Aircraft aircraft);
    void send(String message, Aircraft aircraft);
}

实现接口,这里我们使用一个TrafficTower类作为中介者:

public class TrafficTower implements TrafficMediator {

    private List<Aircraft> aircrafts;

    public TrafficTower() {
        aircrafts = new ArrayList<>();
    }

    @Override
    public void register(Aircraft aircraft) {
        aircrafts.add(aircraft);
    }

    @Override
    public void send(String message, Aircraft sender) {
        for (Aircraft aircraft : aircrafts) {
            if (aircraft != sender) {
                aircraft.receive(message);
            }
        }
    }
}

这里的TrafficTower类实现了TrafficMediator接口,并且维护了一个Aircraft对象列表。当一个Aircraft对象注册到TrafficTower中时,TrafficTower将其添加到这个列表中。当TrafficTower收到一条消息时,它会遍历这个列表,向除了发送者之外的其他Aircraft对象发送消息。

定义一个Aircraft接口和两个实现类:

public interface Aircraft {
    void send(String message);
    void receive(String message);
}

public class Boeing747 implements Aircraft {

    private TrafficMediator mediator;

    public Boeing747(TrafficMediator mediator) {
        this.mediator = mediator;
        mediator.register(this);
    }

    @Override
    public void send(String message) {
        mediator.send(message, this);
    }

    @Override
    public void receive(String message) {
        System.out.println("Boeing747 received message: " + message);
    }
}

public class AirbusA380 implements Aircraft {

    private TrafficMediator mediator;

    public AirbusA380(TrafficMediator mediator) {
        this.mediator = mediator;
        mediator.register(this);
    }

    @Override
    public void send(String message) {
        mediator.send(message, this);
    }

    @Override
    public void receive(String message) {
        System.out.println("AirbusA380 received message: " + message);
    }
}

这里的Boeing747和AirbusA380类都实现了Aircraft接口,并且在构造函数中注册自己到TrafficTower中。当它们需要发送一条消息时,它们会调用TrafficTower的send方法,并且将自己作为发送者传入。

测试类:

public class Main {

    public static void main(String[] args) {
        TrafficTower trafficTower = new TrafficTower();
        Aircraft boeing = new Boeing747(trafficTower);
        Aircraft airbus = new AirbusA380(trafficTower);

        boeing.send("Hello, this is Boeing747.");
        airbus.send("Hi, this is AirbusA380.");
    }
}
AirbusA380 received message: Hello, this is Boeing747.
Boeing747 received message: Hi, this is AirbusA380.

可以看到,当一架飞机发送一条消息时,TrafficTower会将这条消息转发给其他所有的飞机。
通过中介者模式,我们可以实现对象之间的解耦,使得这些对象可以独立地进行修改和扩展。

应用场景

  • SpringMVC 的 DispatcherServlet是一个中介者,他会提取Controller、Model、View来进行调用。而无需controller直接调用view之类的渲染方法
  • 分布式系统中的网关
  • 迪米特法则的一个典型应用

观察者模式(Observer)

定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式或从属者(Dependents)模式。

角色

  • Subject: 目标
  • ConcreteSubject: 具体目标
  • Observer: 观察者
  • ConcreteObserver: 具体观察者
    在这里插入图片描述

实现

定义抽象主播和抽象粉丝类

// 主播类
public abstract class Tiktoker {

    List<Fans> fansList = new ArrayList<>();

    //粉丝增加
    public void addFans(Fans fans) {
        fansList.add(fans);
    }

    // 通知粉丝消息
    public abstract void notifyFans(String msg);

    // 粉丝减少
    public void fewerFans(Fans fans) {
        fansList.remove(fans);
    }
}

// 粉丝类
public abstract class Fans {

    List<Tiktoker> tiktokerList = new ArrayList<>();

    // 关注主播
    public void follow(Tiktoker tiktoker) {
        tiktokerList.add(tiktoker);
        tiktoker.addFans(this);
    }

    //取消关注
    public void Unfollow(Tiktoker tiktoker) {
        tiktokerList.remove(tiktoker);
        tiktoker.fewerFans(this);
    }
	
	// 接收主播消息
    public abstract void acceptMsg(String msg);
}

粉丝实现

public class PeopleFans extends Fans {
    @Override
    public void acceptMsg(String msg) {
        System.out.println("真实粉丝收到消息:" + msg);
    }
}


public class RobotFans extends Fans {
    @Override
    public void acceptMsg(String msg) {
        System.out.println("机器人水军收到消息,不予理会");
    }
}

主播实现,有自己的卖货和结束卖货方法

public class MMTiktoker extends Tiktoker {

    public void startSell(String msg) {
        for (Fans fans : this.fansList) {
            fans.acceptMsg(msg);
        }
    }

    public void endSell(String msg) {
        for (Fans fans : this.fansList) {
            fans.acceptMsg(msg);
        }
    }

    @Override
    public void notifyFans(String msg) {
        for (Fans fans : fansList) {
            fans.acceptMsg(msg);
        }
    }
}

测试类并输出

public class ObserverStart {
    public static void main(String[] args) {

        MMTiktoker tiktoker = new MMTiktoker();

        // 僵尸粉
        RobotFans robotFans1 = new RobotFans();
        RobotFans robotFans2 = new RobotFans();
        tiktoker.addFans(robotFans1);
        tiktoker.addFans(robotFans2);

        //开始卖货
        tiktoker.startSell("新主播福利,只要998,速来!");

        System.out.println("-----------------卖货途中,真实账号成为主播粉丝---------------");

        PeopleFans peopleFans1 = new PeopleFans();
        PeopleFans peopleFans2 = new PeopleFans();
        peopleFans1.follow(tiktoker);
        peopleFans2.follow(tiktoker);
        tiktoker.endSell("很高兴认识大家,下次再见");

        System.out.println("-----------------再次卖货,有人觉得主播不会擦边,取消关注---------------");
        peopleFans1.Unfollow(tiktoker);
        tiktoker.startSell("新主播福利,只要9.98,速来!");
    }
}
机器人水军收到消息,不予理会
机器人水军收到消息,不予理会
-----------------卖货途中,真实账号成为主播粉丝---------------
机器人水军收到消息,不予理会
机器人水军收到消息,不予理会
真实粉丝收到消息:很高兴认识大家,下次再见
真实粉丝收到消息:很高兴认识大家,下次再见
-----------------再次卖货,有人觉得主播不会擦边,取消关注---------------
机器人水军收到消息,不予理会
机器人水军收到消息,不予理会
真实粉丝收到消息:新主播福利,只要9.98,速来!

应用场景

  • Spring事件机制
  • vue双向绑定
  • 响应式编程核心思想

总体来说,观察者模式适用于一对多的对象交互场景,中介者模式适用于多个对象相互交互、需要解耦的场景。在具体应用时,需要根据实际情况选择合适的模式。


备忘录模式(Memento)

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

角色

  • 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  • 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  • 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
    在这里插入图片描述

实现

将发起人需要记录的内部状态抽取出来形成备忘录

public class GameRecord {
    // 存档标识id
    private Integer id;

    private Integer hp;
    private Integer mp;
    private Integer coin;
}

发起人

public class Hanleng {

    private Integer hp;
    private Integer mp;
    private Integer coin;

    GameServer server = new GameServer();


    //玩游戏 各属性值变化
    void playGame() throws InvocationTargetException, IllegalAccessException {
        int i = new Random().nextInt();
        this.hp = i;
        this.coin = i;
        this.mp = i;
        System.out.println("玩游戏,各个属性值:" + i);
    }

    //存档
    void saveGameRecord() throws InvocationTargetException, IllegalAccessException {
        GameRecord record = new GameRecord();
        BeanUtils.copyProperties(record, this);
        record.setId(111);
        server.add(record);
    }

    // 获得存档信息
    void getFromMemento() {
        GameRecord gameRecord = server.getRecords(111);
        System.out.println("获取存档信息:" + gameRecord.toString());
    }
}

管理者管理备忘录,维护备忘录集合。

public class GameServer {

    // 存档集合
    private Map<Integer, GameRecord> records = new HashMap<>();

    void add(GameRecord record) {
        records.put(record.getId(), record);
    }

    GameRecord getRecords(Integer id) {
        return records.get(id);
    }
}

启动测试

public class MementoStart {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        Hanleng hanleng = new Hanleng();
        hanleng.playGame();
        hanleng.saveGameRecord();
        hanleng.playGame();
        hanleng.playGame();
        hanleng.getFromMemento();
    }
}
玩游戏,各个属性值:909935452
玩游戏,各个属性值:2128808327
玩游戏,各个属性值:-873916245
获取存档信息:GameRecord(id=111, hp=909935452, mp=909935452, coin=909935452)

应用场景

  • 游戏存档
  • 数据库保存点事务(savepoint)
  • session活化钝化
  • 浏览器历史记录
  • 撤销和恢复操作

解释器模式(Interpreter)

给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

角色

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
    在这里插入图片描述

实现 - 景区免门票

需求:景区为报答灾时援助之情,要求江苏、浙江、上海来玩人员免票。对医生、火警免票。以上要求满足其义即可。

定义相关接口和实现

// 抽象表达式 - 身份信息验证
public interface IDCardExpression {
    Boolean interpret(String info);
}

// 实现一 终结表达式 (忽略lombok注解)
public class TerminalExpression implements IDCardExpression {
	// 传入的免票条件
    private Set<String> condition;
	// 解析依据
    private String split;

    @Override
    public Boolean interpret(String info) {
        String[] strings = info.split(split);
        for (String string : strings) {
            if (condition.contains(string)) {
                return true;
            }
        }
        return false;
    }
}

// 实现二 非终结表达式 (忽略lombok注解)
public class AndExpression implements IDCardExpression {

    private TerminalExpression city;
    private TerminalExpression occupation;

    @Override
    public Boolean interpret(String info) {
        return city.interpret(info) || occupation.interpret(info);
    }
}

环境角色:风景区

public class Area {
    private Set<String> cities = new HashSet<String>() {{
        add("江苏");
        add("浙江");
        add("上海");
    }};

    private Set<String> occupations = new HashSet<String>() {{
        add("医生");
        add("火警");
    }};

    // 读卡器获取身份信息
    private IDCardExpression cardExpression;

    //获取票价
    public void getTicketPrice(String info) {
    	// 按照规则的终结表达式对象
        TerminalExpression city = new TerminalExpression(cities, "-");
        TerminalExpression occupation = new TerminalExpression(occupations, ":");	
        // 非中介表达式构造 调用判断
        cardExpression = new AndExpression(city, occupation);
        if (cardExpression.interpret(info)) {
            System.out.println("符合免费免费标准");
        } else {
            System.out.println("收取票价两元");
        }
    }
}

测试并输出

public class InterpreterStart {
    public static void main(String[] args) {
        Area area = new Area();
        String info1 = "上海-张文宏:程序员";
        String info2 = "河北-张海风:程序员";
        String info3 = "河南-张乐嘉:医生";
        area.getTicketPrice(info1);
        area.getTicketPrice(info2);
        area.getTicketPrice(info3);
    }
}
符合免费免费标准
收取票价两元
符合免费免费标准

实现 - 计算器

假设我们有一个简单的文法,用于解析并计算简单的算术表达式。我们的文法规则如下:

  • 表达式可以是单个数字或由运算符连接的两个表达式。
  • 支持的运算符包括加法(+)和减法(-)。
// 抽象表达式接口
interface Expression {
    int interpret();
}

// 数字表达式类
class NumberExpression implements Expression {
    private int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    public int interpret() {
        return number;
    }
}

// 运算符表达式类
class OperatorExpression implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;
    private char operator;

    public OperatorExpression(Expression leftExpression, Expression rightExpression, char operator) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
        this.operator = operator;
    }

    public int interpret() {
        switch (operator) {
            case '+':
                return leftExpression.interpret() + rightExpression.interpret();
            case '-':
                return leftExpression.interpret() - rightExpression.interpret();
            default:
                throw new UnsupportedOperationException("Unsupported operator: " + operator);
        }
    }
}

// 解释器类
class Interpreter {
    private Expression expression;

    public Interpreter(Expression expression) {
        this.expression = expression;
    }

    public int interpret() {
        return expression.interpret();
    }
}

// 测试 输出
public class Main {
    public static void main(String[] args) {
        // 构建表达式:1 + 2 - 3
        Expression expression = new OperatorExpression(
            new OperatorExpression(
                new NumberExpression(1),
                new NumberExpression(2),
                '+'
            ),
            new NumberExpression(3),
            '-'
        );
        Interpreter interpreter = new Interpreter(expression);
        System.out.println("Result: " + interpreter.interpret());  // 输出:Result: 0
    }
}

应用场景

  • Spring的表达式解析:#{}
  • Thymeleaf等模板引擎的语法解析
  • 编译原理

命令模式(Command)

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

角色

  • 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  • 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  • 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
    在这里插入图片描述

实现

定义命令接收者和其功能

public class Receiver {
    void online() {
        System.out.println("收到命令,去学习");
    }

    void travel() {
        System.out.println("收到命令,去旅游");
    }
}

抽象命令和其实现

public interface Command {
    void execute();
}

// 实现1 旅游
public class WuHanTravelCommand implements Command {
    private Receiver receiver;

    public WuHanTravelCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        System.out.println("武汉旅游");
        receiver.travel();
    }
}

// 实现2 在线学习
public class OnlineCommand implements Command {

    private Receiver receiver;

    public OnlineCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        System.out.println("在线学习");
        receiver.online();
    }
}

定义命令发起人,封装命令

public class Invoker {

    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

    void call() {
        command.execute();
    }
}

测试输出

public class CommandStart {
    public static void main(String[] args) {
        Invoker invoker = new Invoker(new OnlineCommand(new Receiver()));
        invoker.call();
    }
}
在线学习
收到命令,去学习

应用场景

  • mvc就是典型的命令模式
  • 当系统需要执行一组操作时,命令模式可以定义宏命令(一个命令组合了多个命令)来实现该功能。
  • 结合备忘录模式还可以实现命令的撤销和恢复

迭代器模式(Iterator)

提供一个对象(迭代器)来顺序访问聚合对象(迭代数据)中的一系列数据,而不暴露聚合对象的内部表示。

角色

  • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  • 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  • 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
    在这里插入图片描述

实现

抽象美(渣)男类和实现

public abstract class BeautifulMan {

    public abstract void likeYou(String name);

    public abstract void sayBye(String name);

    public abstract LoveIterator getLoveIterator();
}

// 实现
public class BMan extends BeautifulMan {

    private List<String> girlFriends;

    public BMan(List<String> girlFriends) {
        this.girlFriends = girlFriends;
    }

    @Override
    public void likeYou(String name) {
        girlFriends.add(name);
    }

    @Override
    public void sayBye(String name) {
        girlFriends.remove(name);
    }

    @Override
    public LoveIterator getLoveIterator() {
        return new BManLoverIterator(girlFriends);
    }
}

恋史迭代器实现

public abstract class LoveIterator {
    public abstract String firstLove();

    public abstract String nextLove();

    public abstract Boolean hasNext();
}

// 实现
public class BManLoverIterator extends LoveIterator {
    private List<String> girlFriends;
    private int index = 0;

    public BManLoverIterator(List<String> girlFriends) {
        this.girlFriends = girlFriends;
    }

    @Override
    public String firstLove() {
        return CollectionUtils.isNotEmpty(girlFriends) ? girlFriends.get(0) : null;
    }

    @Override
    public String nextLove() {
        return girlFriends.get(index);
    }

    @Override
    public Boolean hasNext() {
        return ++index < girlFriends.size();
    }
}

测试输出

public class IteratorStart {
    public static void main(String[] args) {
        List<String> girls = new ArrayList<String>() {{
            add("Amelia");
            add("Olivia");
            add("Ava");
        }};
        BMan man = new BMan(girls);
        LoveIterator iterator = man.getLoveIterator();
        System.out.println("初恋:" + iterator.firstLove());
        System.out.println("初恋之后还有吗?    " + (iterator.hasNext() ? "有" : "没有"));
        System.out.println("之后的是:" + iterator.nextLove());
    }
}
初恋:Amelia
初恋之后还有吗?    有
之后的是:Olivia

应用场景

  • jdk容器接口的Iterator定义
  • 现实开发中,我们几乎无需编写迭代器,基本数据结构链表、树、图的迭代器已经都有了。除非要重写迭代逻辑。

访问者模式(Visitor)

将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离。

是行为类模式中最复杂的一种模式。

角色

  • 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
  • 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
  • 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
  • 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
  • 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
    在这里插入图片描述

实现

定义抽象元素和实现

// 抽象元素
public abstract class RobotHardware {

    public String command;

    public RobotHardware(String command) {
        this.command = command;
    }

    public abstract void work();

    // 元素需要接受访问者的访问
    public abstract void accept(Vistor vistor);

}

// cpu实现
public class CPU extends RobotHardware {

    public CPU(String common) {
        super(common);
    }

    @Override
    public void work() {
        System.out.println("CPU工作:" + this.command);
    }
	
    @Override
    public void accept(Vistor vistor) {
    	// 访问者要有修改元素的方法
        vistor.vistorCPU(this);
    }
}

// 硬盘实现
public class Disk extends RobotHardware {

    public Disk(String common) {
        super(common);
    }

    @Override
    public void work() {
        System.out.println("硬盘开始工作:" + this.command);
    }

    @Override
    public void accept(Vistor vistor) {
    	// 访问者要有修改元素的方法
        vistor.vistorDisk(this);
    }
}

抽象访问者及实现

public abstract class Vistor {
    public abstract void vistorDisk(Disk disk);
    public abstract void vistorCPU(CPU cpu);
}

// 实现
public class UpdatePackage extends Vistor {

    private String common;

    public UpdatePackage(String common) {
        this.common = common;
    }

    @Override
    public void vistorDisk(Disk disk) {
        disk.command += ">>>>" + common;
    }

    @Override
    public void vistorCPU(CPU cpu) {
        cpu.command += ">>>>" + common;
    }
}

实体类的结构

public class XiaoAi {
    // 出厂属性
    private CPU cpu = new CPU("嘶嘶嘶嘶");
    private Disk disk = new Disk("呜呜呜呜");

    // 回答问题的功能
    void answerQuestion() {
        cpu.work();
        disk.work();
        System.out.println("---小爱回答完毕!---");
    }

    // 小爱升级接收升级包 (接受访问者的访问)
    void accept(Vistor vistor) {
        // 访问者模式
        this.cpu.accept(vistor);
        this.disk.accept(vistor);
    }
}

测试输出

public class VisitorStart {
    public static void main(String[] args) {
        // 刚上市的小爱
        XiaoAi xiaoAi = new XiaoAi();
        xiaoAi.answerQuestion();

        // 小爱升级
        UpdatePackage updatePackage = new UpdatePackage("联网!");
        xiaoAi.accept(updatePackage);
        xiaoAi.answerQuestion();
    }
}
CPU工作:嘶嘶嘶嘶
硬盘开始工作:呜呜呜呜
---小爱回答完毕!---
CPU工作:嘶嘶嘶嘶>>>>联网!
硬盘开始工作:呜呜呜呜>>>>联网!
---小爱回答完毕!---

应用场景

  • Spring反射工具中的MethodVisitor
  • 应用于对象结构相对稳定,但其操作算法经常变化的程序

缺点:

  • 在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”
  • 违反依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类
  • 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性

职责链模式(Chain of Responsibility)

为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

角色

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

实现

模拟 Filter 链

定义自定义的Filter接口
Request &Response 为自定义类,里面未作操作,所以结构省略

public interface Filter {
    void doFilter(Request request, Response response, FilterChain filterChain);
}

定义三个接口实现

// 实现1
public class AFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        System.out.println("---A前---");
        filterChain.doFilter(request, response, filterChain);
        System.out.println("---A后---");
    }
}

//实现2
public class BFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        System.out.println("---B前---");
        filterChain.doFilter(request, response, filterChain);
        System.out.println("---B后---");
    }
}

//实现3
public class CFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        System.out.println("---C前---");
        filterChain.doFilter(request, response, filterChain);
        System.out.println("---C后---");
    }
}

定义维护链的类并实现Filter接口

@Data
public class FilterChain implements Filter {

    List<Filter> filterList = new ArrayList<>();

    // 游标
    int index = 0;

    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        if (index < filterList.size()) {
            Filter filter = filterList.get(index);
            index++;
            filter.doFilter(request, response, filterChain);
        }
    }
}

测试输出

public class ChainOfResponsibilityStart {
    public static void main(String[] args) {

        List<Filter> filterList = new ArrayList<Filter>() {{
            add(new AFilter());
            add(new BFilter());
            add(new CFilter());
        }};

        FilterChain chain = new FilterChain();
        chain.setFilterList(filterList);
        chain.doFilter(new Request(), new Response(), chain);
    }
}
---A前---
---B前---
---C前---
---C后---
---B后---
---A后---

应用场景

  • Tomcat的Pipeline、Valve
  • Filter链
  • Aop责任链
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总在寒冷清秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值