设计模式之行为型模式(共十一种)

一、概述

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。

行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

行为型模式是 GoF 中最为庞大的一类,它包含以下 11 种模式。

  • 模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
  • 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
  • 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
  • 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
  • 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
  • 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
  • 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
  • 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
  • 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
  • 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
  • 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

二、策略模式

2.1 概念

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
个人总结:统一接口下的一系列算法类(多种策略),用一个类将其封装起来,使它们(多种策略)可动态切换。和工厂模式的区别:工厂模式是创建型模式,是为了创建不同对象;而策略模式是行为模式,为了选择不同的行为。

2.2 示例

public class Strategy {
    public static void main(String[] args) {
        OperationStrategy operationStrategy = new OperationStrategy(new OperationAdd());
        operationStrategy.executeStrategy(15, 21);
    }
}

interface Operation {
    public void doOperation(int a, int b);
}

//策略1
class OperationAdd implements Operation {
    public void doOperation(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
    }
}

//策略2
class OperationMultiply implements Operation {
    public void doOperation(int a, int b) {
        System.out.println(a + "*" + b + "=" + (a * b));
    }
}

//封装一系列策略,可任意替换策略(实现同一个接口)
class OperationStrategy {
    private Operation operation;

    public OperationStrategy(Operation operation) {
        this.operation = operation;
    }

    //执行策略
    public void executeStrategy(int a, int b) {
        operation.doOperation(a, b);
    }
}

三、模板方法模式

3.1 概念

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
个人总结:将一些固定步骤、固定逻辑的方法封装成模板方法。调用模板方法即可完成那些特定的步骤。例如,spring中对Hibernate的事务管理,开启session、关闭session等固定步骤不需重复写,直接丢给一个实体保存。

3.2 示例

public class Template {
    public static void main(String[] args) {
        Game game = new FootballGame();
        game.play();
    }
}

abstract class Game {
    //步骤1,初始化游戏
    abstract void initialize();

    //步骤2,开始游戏
    abstract void startPlay();

    //步骤3,结束游戏
    abstract void endPlay();

    //主方法,模板方法,设置为final,在抽象类中实现
    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }
}

class FootballGame extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }
}

四、观察者模式

4.1 概念

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
个人总结:一个对象(被观察者)状态变化时,通知所有依赖于它的对象(观察者);这种依赖方式具有双向性:观察者指定被观察的对象,或者被观察对象添加观察者,下面例子采用后者方式

4.2 示例

public class Observer {
    public static void main(String[] args) {
        Subject subject = new Subject();
        subject.addSubjectObserver(new Observer1());
        subject.addSubjectObserver(new Observer2());
        subject.setState(1);
    }
}

class Subject {
    //一对多关系,多个该类的观察者
    private List<SubjectObserver> subjectObservers = new ArrayList<>();
    //状态(被观察),发生变化时通知所有观察者
    private int state;

    public void setState(int state) {
        this.state = state;
        //改变状态,通知所有观察者
        notifyAllSubjectObservers();
    }

    public void addSubjectObserver(SubjectObserver subjectObserver) {
        subjectObservers.add(subjectObserver);
    }

    //通知所有观察者
    public void notifyAllSubjectObservers() {
        for (SubjectObserver subjectObserver : subjectObservers) {
            subjectObserver.alert();
        }
    }
}

abstract class SubjectObserver {
    protected Subject subject;

    public abstract void alert();
}

//观察者1
class Observer1 extends SubjectObserver {
    @Override
    public void alert() {
        System.out.println("Observer1: subject is changed!");
    }
}

//观察者2
class Observer2 extends SubjectObserver {
    @Override
    public void alert() {
        System.out.println("Observer2: subject is changed!");
    }
}

五、迭代器模式

5.1 概念

提供一种方法顺序访问一个聚合对象中各个元素,而又无须暴露该对象的内部表示。
个人总结:Java中的iterator的简单实现原理。将聚合类中遍历各个元素的行为分离出来,封装成迭代器,让迭代器来处理遍历的任务;使简化聚合类,同时又不暴露聚合类的内部。

5.2 示例

public class IteratorDemo {
    public static void main(String[] args) {
        MyContainer myContainer = new MyContainer();
        Iterator iterator = myContainer.getIterator();
        while (iterator.hashNext())
            System.out.println(iterator.next());
    }
}

//迭代器接口
interface Iterator {
    public boolean hashNext();

    public Object next();
}

//容器接口
interface Container {
    public Iterator getIterator();
}

//自定义容器(聚合类)
class MyContainer implements Container {
    public String names[] = {"Robert", "John", "Julie", "Lora"};

    @Override
    public Iterator getIterator() {
        return new MyIterator();
    }

    //自定义迭代器,迭代器类定义为容器类的内部类
    private class MyIterator implements Iterator {
        int index = 0;

        //自定义遍历规则
        @Override
        public boolean hashNext() {
            if (index < names.length)
                return true;
            return false;
        }

        @Override
        public Object next() {
            if (this.hashNext())
                return names[index++];
            return null;
        }
    }
}

六、责任链模式

6.1 概念

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
个人总结:在Handler类里面聚合自己,形成一条Handler链(或树、环等),并且可以将请求往下一个Handler传递(只允许传给另一个,而不允许传给多个)。例子:Struts拦截器,Filter过滤器

6.2 示例

public class Chain_of_Responsibility {
    public static void main(String[] args) {
        ResponsibilityHandler handler1 = new ResponsibilityHandler("handler1");
        ResponsibilityHandler handler2 = new ResponsibilityHandler("handler2");
        ResponsibilityHandler handler3 = new ResponsibilityHandler("handler3");
        handler1.setResponsibilityHandler(handler2);
        handler2.setResponsibilityHandler(handler3);
        handler1.operator();//操作请求会沿着这条链传递下去
    }
}

//责任处理器/接收器
class ResponsibilityHandler {
    //聚合自己,构成一条责任链
    private ResponsibilityHandler responsibilityHandler = null;
    private String name;

    public ResponsibilityHandler(String name) {
        this.name = name;
    }

    public ResponsibilityHandler next() {
        return this.responsibilityHandler;
    }

    public void setResponsibilityHandler(ResponsibilityHandler responsibilityHandler) {
        this.responsibilityHandler = responsibilityHandler;
    }

    public void operator() {
        System.out.println(name + " is handler!");
        if (this.next() != null) {
            //将请求发送到下一个责任接收器
            next().operator();
        }
    }
}

七、命令模式

7.1 概念

将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
个人总结:三种角色(调用者→接受者→命令);解耦行为请求者和行为实现着,实现请求和执行分开;调用者选择命令发布,命令指定执行者。

7.2 示例

public class CommandDemo {
    public static void main(String[] args) {
        Receiver receiver = new Receiver("小明");
        //指定命令的执行者
        Command shootCommand = new ShootCommand(receiver);
        Command otherCOmmand = new OtherCommand(receiver);
        Invoker invoker = new Invoker();
        invoker.addCommands(shootCommand);
        invoker.addCommands(otherCOmmand);
        invoker.sendCommands();
    }
}

//命令
interface Command {
    public void execute();
}

//射击命令
class ShootCommand implements Command {
    private Receiver receiver;

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

    public void execute() {
        System.out.println("shootCommand is execute:");
        receiver.action();
    }
}

//其他命令
class OtherCommand implements Command {
    private Receiver receiver;

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

    public void execute() {
        System.out.println("otherCommand is execute:");
        receiver.action();
    }
}

//命令接受者(士兵)
class Receiver {
    public String name;

    public Receiver(String name) {
        this.name = name;
    }

    //行动,执行命令
    public void action() {
        System.out.println(name + " received the command!");
    }
}

//命令调用者(司令官)
class Invoker {
    private List<Command> commandList = new ArrayList<Command>();

    public void addCommands(Command command) {
        this.commandList.add(command);
    }

    //发出命令
    public void sendCommands() {
        for (Command command : commandList) {
            command.execute();
            System.out.println();
        }
        commandList.clear();
    }
}

八、备忘录模式

8.1 概念

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
个人总结:创建一个备忘录类,用来存储原始类的信息;同时创建备忘录仓库类,用来存储备忘录类,当然,原始类与备忘录类的对应关系要处理好。

8.2 示例

public class MementoDemo {
    public static void main(String[] args) {
        //待备份的类
        Originator originator = new Originator();
        originator.setState("123");
        System.out.println("初始化的状态为:" + originator.getState());
        MementoStorage mementoStorage = new MementoStorage();
        mementoStorage.add(originator.createMemento());
        originator.setState("321");
        System.out.println("修改后的状态为:" + originator.getState());
        originator.restoreMemento(mementoStorage.get(0));
        System.out.println("还原后的状态为:" + originator.getState());
    }
}

//备忘录类
class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return this.state;
    }
}

//备忘录类仓库,备忘录管理类
class MementoStorage {
    private List<Memento> mementoList = new ArrayList<Memento>();

    public void add(Memento state) {
        mementoList.add(state);
    }

    public Memento get(int index) {
        return mementoList.get(index);
    }
}

//原始类
class Originator {
    private String state;

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

    public String getState() {
        return this.state;
    }

    //创建备份
    public Memento createMemento() {
        //把需要备份的信息全部存储到备份类中。
        return new Memento(state);
    }

    //还原备份
    public void restoreMemento(Memento memento) {
        //把备份类中存储的信息还原
        state = memento.getState();
    }
}

九、状态模式

9.1 概念

允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
个人总结:对象具有多种状态,且每种状态具有特定的行为;应用场景: 行为随状态改变而改变的场景。代码形式似乎也和哪种设计模式相似,还是那句话,设计模式提倡的是思想,而不是形式。

9.2 示例

public class StateDemo {
    public static void main(String[] args) {
        QQContext context = new QQContext();
        //设置状态,不同的状态对应不同的行为
        context.setState(new OnlineState());
        context.getState().getMessage();
    }
}

interface State {
    public void getMessage();
}

//在线状态(状态对象)
class OnlineState implements State {
    //在线状态下的行为
    public void getMessage() {
        System.out.println("在线中,对好友可见!");
    }
}

//隐身状态(状态对象)
class StealthState implements State {
    //隐身状态下的行为
    public void getMessage() {
        System.out.println("隐身中,对好友不可见!");
    }
}

//QQ的登陆状态类
class QQContext {
    private State state;

    public State getState() {
        return state;
    }

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

十、访问者模式

10.1 概念

主要将数据结构与数据操作分离。
个人总结:在被访问的类里面加一个对外提供接待访问者的接口(如下面例子的accept()方法)。访问者封装了对被访问者结构的一些杂乱操作,避免这些操作"污染"被访问者,解耦结构与算法,同时具有优秀的扩展性。

10.2 示例

public class Visitor {
    public static void main(String[] args) {
        Computer computer = new Computer("myComputer");
        //computer接受computerVisitor的访问
        computer.accept(new ComputerVisitor());
    }
}

//被访问者
class Computer {
    private String computerName;

    public String getComputerName() {
        return computerName;
    }

    public Computer(String computerName) {
        this.computerName = computerName;
    }

    //提供接待访问者的接口
    public void accept(ComputerVisitor computerVisitor) {
        //访问者访问自身
        computerVisitor.visit(this);
    }
}

//访问者
class ComputerVisitor {
    //访问Computer类,将被访问者的引用传入访问者
    public void visit(Computer computer) {
        System.out.println("访问" + computer + "的name属性:" + computer.getComputerName());
    }
}

十一、中介者模式

11.1 概念

用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
个人总结:中介者对象,用来封装关联对象之间的交互操作,使关联对象之间耦合度松散;例如,MVC模式中"控制器"就是"模型"和"视图"的中介者;与适配器模式的区别:适配器模式为了桥接互不兼容的接口,中介者为了分离原始结构和交互行为。

11.2 示例

public class Mediator {
    public static void main(String[] args) {
        User1 user1 = new User1("小明");
        User2 user2 = new User2("小红");
        UserMediator userMediator = new UserMediator(user1, user2);
        userMediator.introduceYourselves();
    }
}

class User1 {
    private String name;

    public String getName() {
        return name;
    }

    public User1(String name) {
        this.name = name;
    }
}

class User2 {
    private String name;

    public String getName() {
        return name;
    }

    public User2(String name) {
        this.name = name;
    }
}

//中介者,用来封装User1与User2的交互操作
class UserMediator {
    private User1 user1;
    private User2 user2;

    //将User1与User2传入它们的中介者
    public UserMediator(User1 user1, User2 user2) {
        this.user1 = user1;
        this.user2 = user2;
    }

    public void introduceYourselves() {
        System.out.println("Hello " + user1.getName() + ",I'm " + user2.getName());
        System.out.println("Hi " + user2.getName() + ",My name is " + user1.getName());
    }
}

十二、解释器模式

12.1 概念

给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
个人总结:自己定义一种语言或表达式(附对应的解释器),用来表示一些复杂的频繁发生的行为。例如:正则表达式的解释,sql语句的解释。使用场景极少,会引起效率、性能、维护等问题。

12.2 示例

public class Interpreter {
    public static void main(String[] args) {
        Context context = new Context();
        //创建自定义变量
        Variable a = new Variable();
        Variable b = new Variable();
        //创建常量 3
        Constant c = new Constant(3);
        //给变量赋值
        context.put(a, 5).put(b, 1);
        //构建语法树(自定义表达式:a+b+3)
        Expression exp = new Add(new Add(a, b), c);
        //解释表达式a+b+3
        int result = exp.interpret(context);
        System.out.println("a+b+3 = " + result);
    }
}

//表达式(所有表达式角色继承该接口,自带解释器)
interface Expression {
    //解释器,解释角色所需参数存储在Context类中
    public int interpret(Context context);
}

//构件环境类,包含解释器之外的一些全局信息,一般是 HashMap。
class Context {
    private Map<Variable, Integer> valueMap = new HashMap<>();

    public Context put(Variable x, int y) {
        valueMap.put(x, y);
        return this;
    }

    public int get(Variable x) {
        int value = (Integer) valueMap.get(x);
        return value;
    }
}

//终结符表达式角色--常量
class Constant implements Expression {
    private int a;

    public Constant(int a) {
        this.a = a;
    }

    public int interpret(Context context) {
        return a;
    }
}

//终结符表达式角色--变量
class Variable implements Expression {
    public int interpret(Context context) {
        return context.get(this);
    }
}

//非终结符表达式角色--运算符(+)
class Add implements Expression {
    private Expression x, y;

    public Add(Expression x, Expression y) {
        this.x = x;
        this.y = y;
    }

    public int interpret(Context context) {
        return x.interpret(context) + y.interpret(context);
    }
}
posted @ 2025-04-12 20:01  夏尔_717  阅读(396)  评论(0)    收藏  举报