详细整理全23种设计模式-行为型模式篇2(模板方法、状态、命令、中介者)

这一篇主要讲行为型模式的中间四种
在这里插入图片描述

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

模版方法模式

模板方法模式常用于含有继承结构的代码中

先定义一个抽象类,apply() 是抽象方法,子类必须实现它。可以随意指定多少抽象方法,抽象类指定n多个方法,至于怎么做都是由子类去实现

public abstract class AbstractTemplate {
    // 这就是模板方法
    public void templateMethod() {
        init();
        apply(); // 这个是重点
        end(); // 可以作为钩子方法
    }

    protected void init() {
        System.out.println("init 抽象层已经实现,子类也可以选择覆写");
    }

    // 留给子类实现
    protected abstract void apply();

    protected void end() {
    }
}

实现类

public class ConcreteTemplate extends AbstractTemplate {
    public void apply() {
        System.out.println("子类实现抽象方法 apply");
    }

    public void end() {
        System.out.println("我们可以把 method3 当做钩子方法来使用,需要的时候覆写就可以了");
    }
}

使用

public static void main(String[] args) {
    AbstractTemplate t = new ConcreteTemplate();
    // 调用模板方法
    t.templateMethod();
}

优点:

  1. 封装不变部分,扩展可变部分。不变部分的方法封装到父类中实现,把可变部分由子类继承实现,便于子类继续扩展。
  2. 在父类中提取了公共的部分代码,便于代码复用。
  3. 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

缺点:

  1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,间接地增加了系统实现的复杂度。
  2. 由于继承关系自身的缺点,如果父类添加新的抽象方法,所有子类都要改一遍。

模板方法在spring中还有其余的相似名字,如:JdbcTemplateRedisTemplate

设计模式之模板设计模式-以spring的各种template为例

JdbcTemplate是不同dao框架(mybatis/hibernate)的封装
RestTemplate是不同http框架(jdk api/httpclient/okhttp)的封装

Slf4j是对不同日志框架(logback/log4j等)的封装,但它是外观(门面)模式。之前文章介绍过。

  • 模板定义父子逻辑,用子类实现抽象类的抽象方法,实现不同的子类
  • 门面是给多个对象提供单一创建方式

更通用点的例子,核心点就在templateMethod()方法,子类实现一系列抽象方法

public class TemplateMethodPattern {
    public static void main(String[] args) {
        AbstractClass tm = new ConcreteClass();
        tm.templateMethod();
    }
}

//抽象类
abstract class AbstractClass {
    //模板方法
    public void templateMethod() {
        specificMethod();
        abstractMethod1();
        abstractMethod2();
    }

    //具体方法
    public void specificMethod() {
        System.out.println("抽象类中的具体方法被调用...");
    }

    //抽象方法1
    public abstract void abstractMethod1();

    //抽象方法2
    public abstract void abstractMethod2();
}

//具体子类
class ConcreteClass extends AbstractClass {
    public void abstractMethod1() {
        System.out.println("抽象方法1的实现被调用...");
    }

    public void abstractMethod2() {
        System.out.println("抽象方法2的实现被调用...");
    }
}

状态模式

举个例子,商品库存中心的减库存和补库存

定义状态接口

public interface State {
    public void doAction(Context context);
}

减库存的实现类

public class DeductState implements State {

    public void doAction(Context context) {
        System.out.println("商品卖出,准备减库存");
        context.setState(this);

        //... 执行减库存的具体操作
    }

    public String toString() {
        return "Deduct State";
    }
}

补库存的实现类

public class RevertState implements State {

    public void doAction(Context context) {
        System.out.println("给此商品补库存");
        context.setState(this);

        //... 执行加库存的具体操作
    }

    public String toString() {
        return "Revert State";
    }
}

环境类Context,这里是作为商品存在的

public class Context {
    private State state;
    private String name;
    public Context(String name) {
        this.name = name;
    }

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

使用

public static void main(String[] args) {
        // 我们需要操作的是 iPhone X
        Context context = new Context("iPhone X");

        // 补库存
        State revertState = new RevertState();
        revertState.doAction(context);

        // 减库存
        State deductState = new DeductState();
        deductState.doAction(context);

        // 如果需要我们可以获取当前的状态
        // context.getState().toString();
}

核心就在State类中的doAction方法中的context.setState(this),把当前State类存到商品中

应用场景:

  • 上面的商品例子,对象需要有明确的状态时,可以考虑使用
  • 对象的行为取决于它的状态,并且必须在运行时根据状态改变它的行为时,可以考虑使用。(如启用/禁用的切换)

再举一个例子,状态模式一共就只有两个角色:环境类Context和状态类State。核心在环境类的Handle()方法中调用状态类的Handle()方法,做context.setState()存放不同的State实现类

public class StatePatternClient {
    public static void main(String[] args) {
        Context context = new Context();    //创建环境      
        context.Handle();    //处理请求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}

//环境类
class Context {
    private State state;

    //定义环境类的初始状态
    public Context() {
        this.state = new ConcreteStateA();
    }

    //设置新状态
    public void setState(State state) {
        this.state = state;
    }

    //读取状态
    public State getState() {
        return (state);
    }

    //对请求做处理
    public void Handle() {
        state.Handle(this);
    }
}

//抽象状态类
abstract class State {
    public abstract void Handle(Context context);
}

//具体状态A类
class ConcreteStateA extends State {
    public void Handle(Context context) {
        System.out.println("当前状态是 A.");
        context.setState(new ConcreteStateB());
    }
}

//具体状态B类
class ConcreteStateB extends State {
    public void Handle(Context context) {
        System.out.println("当前状态是 B.");
        context.setState(new ConcreteStateA());
    }
}

运行结果如下:

当前状态是 A.
当前状态是 B.
当前状态是 A.
当前状态是 B.

命令模式

一般代码中方法的请求者和实现者之间经常存在紧密的耦合关系,这不利于扩展和维护,为了使其解耦,可以使用命令模式

该模式有三个角色,调用者,命令类和接收者。

public class CommandPattern {
    public static void main(String[] args) {
        Command cmd = new ConcreteCommand();
        Invoker ir = new Invoker(cmd);
        System.out.println("客户访问调用者的call()方法...");
        ir.call();
    }
}
//调用者
class Invoker {
    private Command command;
    public Invoker(Command command) {
        this.command = command;
    }
    public void setCommand(Command command) {
        this.command = command;
    }
    public void call() {
        System.out.println("调用者执行命令command...");
        command.execute();
    }
}
//抽象命令
interface Command {
    public abstract void execute();
}
//具体命令
class ConcreteCommand implements Command {
    private Receiver receiver;
    ConcreteCommand() {
        receiver = new Receiver();
    }
    public void execute() {
        receiver.action();
    }
}
//接收者
class Receiver {
    public void action() {
        System.out.println("接收者的action()方法被调用...");
    }
}

调用者中传入命令类,命令类跑接收者方法。命令类作为中间商,负责解耦
在这里插入图片描述

优点:

  1. 通过引入中间件(抽象接口)降低系统耦合度。
  2. 扩展性良好,增删非常方便。采用命令模式增删不会影响其他类,且满足“开闭原则”。
  3. 可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
  5. 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。

缺点:

  1. 可能产生大量命令类。因为每一个具体操作都需要设计具体命令类,这会增加系统的复杂性。

中介者模式

前面说的命令模式是降低调用者和接收者之间的耦合,而中介者是降低对象和对象之间的耦合。

该模式有两个角色,中介者和对象类(该例子里是同事类)和前面命令模式一样,为了降低耦合就需要中间对象,中介者就是为了降低同事类之间的耦合而存在的。

import java.util.*;

public class MediatorPattern {
    public static void main(String[] args) {
        Mediator md = new ConcreteMediator();
        Colleague c1, c2;
        c1 = new ConcreteColleague1();
        c2 = new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("-------------");
        c2.send();
    }
}
//抽象中介者
abstract class Mediator {
    public abstract void register(Colleague colleague);
    public abstract void relay(Colleague cl); //转发
}
//具体中介者
class ConcreteMediator extends Mediator {
    private List<Colleague> colleagues = new ArrayList<Colleague>();
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMedium(this);
        }
    }
    public void relay(Colleague cl) {
        for (Colleague ob : colleagues) {
            if (!ob.equals(cl)) {
                ((Colleague) ob).receive();
            }
        }
    }
}
//抽象同事类
abstract class Colleague {
    protected Mediator mediator;
    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void receive();
    public abstract void send();
}
//具体同事类
class ConcreteColleague1 extends Colleague {
    public void receive() {
        System.out.println("具体同事类1收到请求。");
    }
    public void send() {
        System.out.println("具体同事类1发出请求。");
        mediator.relay(this); //请中介者转发
    }
}
//具体同事类
class ConcreteColleague2 extends Colleague {
    public void receive() {
        System.out.println("具体同事类2收到请求。");
    }
    public void send() {
        System.out.println("具体同事类2发出请求。");
        mediator.relay(this); //请中介者转发
    }
}

本例子模拟了同事之间的发送和接受消息,先注入中介者,可以发现中介者的代码里用了list.add()去注册,并且将中介者set到同事类中,然后send的时候做中介者的转发,调用所有非当前同事的receice方法

运行结果如下:

具体同事类1发出请求。
具体同事类2收到请求。
-------------
具体同事类2发出请求。
具体同事类1收到请求。

结构如图
在这里插入图片描述
优点:

  • 中介者模式会将对象之间的复杂关系由“网状结构”改为“星形结构”,将大大降低它们之间的“耦合性”,“中介者”就是作为星形中间那个点

使用场景:

  • 在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者

参考:
行为型模式应用实验
25000 字详解 23 种设计模式(多图 + 代码)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值