设计模式下篇-行为型

行为型主要解决类或对象之间交互的经典结构。行为型的设计模式有观察者模式,模板模式,策略模式,职责链模式,状态模式,迭代器模式,访问者模式,备忘录模式,命令模式,解释器模式和中介模式。

接下来以3个W和1个H来学习下这十一种设计模式

十一种模式介绍

观察者模式-Observer

什么是观察者模式

在对象之间定义一个一对多的依赖,当一个对象(被观察者)状态改变的时候,所有依赖的对象(观察者)都会自动收到通知。

为什么使用观察者模式

降低被观察者和观察者的耦合性,提高易用性。

如何使用观察者模式

被观察者,该被观察者有一组注册的观察者,当被观察者改变时,通知每一个观察者

// 观察者
public interface Observer {
    
    void update(Observable o, Object arg);
}

// 被观察者的封装类
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;
    
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    
    // something
    
    public void notifyObservers(Object arg) {
        
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    // something
    
    protected synchronized void setChanged() {
        changed = true;
    }

}

// 被观察者
public class MyObservable extends Observable {

    public synchronized void setChanged() {
        super.setChanged();
    }
}

// 调用类
public class Main {

    public static void main(String[] args) {
        // java.util.Observable
        MyObservable observable = new MyObservable();
        observable.addObserver(new java.util.Observer() {
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("A"+arg.toString());
                o.deleteObserver(this);
            }
        });

        observable.addObserver(new java.util.Observer() {
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("B"+arg.toString());
            }
        });

        observable.setChanged();
        observable.notifyObservers("message1");
        observable.setChanged();
        observable.notifyObservers("message2");

    }
}

// 打印如下:
Bmessage1
Amessage1
Bmessage2

额外学习

在jdk9,Observable被废弃,使用java.beans包下的PropertyChangeEvent和PropertyChangeListener来代替Observer和Observable的功能。

使用观察者模式实现简单的EventBus,git仓库:https://github.com/CNXMBuyu/design-pattern-study.git

  • 改进方案-消息队列

设计模式核心是对程序进行解耦,在观察者模式中被观察者需要添加观察者,且当发起通知时要遍历所有的观察者。为了使解耦的更加彻底,就出现了消息队列。

模板模式-Template

什么是模板模式

将算法整体架构定义好,让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。

为什么使用模板模式

代码复用

如何使用模板模式

定义算法整体架构(抽象类),将需要子类来实现的方法定义为抽象方法。

public abstract class Player {

    public void initPlayer() {
        // 保存玩家信息
        savePlayer();
        // 初始化玩家数据,因为每款游戏都不同,所以需要有子类具体实现
        initData();
    }

    protected void savePlayer() {
        System.out.println("已保存玩家信息");
    }

    /**
     * 初始化玩家数据
     */
    protected abstract void initData();

}

public class GamePlayer extends Player {

    @Override
    protected void initData() {
        System.out.println("初始化游戏玩家");
    }
}

相似功能:回调

A类事先注册某个函数F到B类,A类在调用B类的P函数的时候,B类反过来调用A类注册给它的F函数。

public class Player {

    public void initPlayer(PlayerCallback playerCallback){
        // 保存玩家信息
        savePlayer();
        // 初始化玩家数据,因为每款游戏都不同,所以需要有子类具体实现
        playerCallback.initData();
    }

    protected void savePlayer(){
        System.out.println("已保存玩家信息");
    }
}

public interface PlayerCallback {

    /**
     * 初始化玩家数据
     */
    void initData();
}

// 调用类
public class Main {

    public static void main(String[] args) {
        Player player = new Player();
        player.initPlayer(new PlayerCallback() {
            @Override
            public void initData() {
                System.out.println("初始化游戏玩家数据");
            }
        });
    }
}

回调函数的经典案例:spring的JdbcTemplate

模板模板是使用继承来实现,而回调函数是使用组合来实现。

策略模式-Strategy

什么是策略模式

策略模式就是定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。

为什么使用策略模式

解耦策略的定义、创建、使用这三部分

如何使用策略模式

分为定义、创建、使用三个步骤。


// 以下为策略的定义

// 策略接口
public interface Cache {

    void addCache(String key, String content);

    String getCache(String key);
}


// 策略的实现者
public class MemoryCache implements Cache {

    private ConcurrentMap<String, String> cache = new ConcurrentHashMap<>();

    @Override
    public void addCache(String key, String content) {
        cache.put(key, content);
        System.out.println("memory add cache; key:" + key + ", content:" + content);
    }

    @Override
    public String getCache(String key) {
        System.out.println("memory get key:" + key + ", content:" + cache.get(key));
        return cache.get(key);
    }
}

// 策略的实现者
public class RedisCache implements Cache {

    @Override
    public void addCache(String key, String content) {
        System.out.println("redis add cache; key:" + key + ", content:" + content);
    }

    @Override
    public String getCache(String key) {
        String content = "redis get key:" + key;
        System.out.println(content);
        return content;
    }
}

// 策略的创建
public class CacheFactory {

    public Cache getCache(String type){
        String redisType = "redis";
        if(redisType.equals(type)){
            return RedisCacheSingleton.INSTANCE;
        } else {
            return MemoryCacheSingleton.INSTANCE;
        }
    }

    /**
     * 使用内部类的单例
     */
    private static class RedisCacheSingleton{
        private static RedisCache INSTANCE = new RedisCache();
    }

    /**
     * 使用内部类的单例
     */
    private static class MemoryCacheSingleton{
        private static MemoryCache INSTANCE = new MemoryCache();
    }
}

// 策略的使用
public class Main {

    public static void main(String[] args) {
        Cache redisCache = new CacheFactory().getCache("redis");
        redisCache.addCache("a", "a content");
        redisCache.getCache("a");

        Cache memoryCache = new CacheFactory().getCache("memory");
        memoryCache.addCache("a", "a content");
        memoryCache.getCache("a");
    }
}

职责链模式-Chain

什么是职责链模式

职责链模式是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

为什么使用职责链模式

将请求的发送和接收解耦。

如何使用职责链模式

public abstract class AbstractScript{

    /**
     * 执行初始化
     */
    public abstract void doInit();
}

public class ScriptA extends  AbstractScript {

    @Override
    public void doInit() {
        System.out.println(this.getClass().getName());
    }
}

public class ScriptB extends  AbstractScript {

    @Override
    public void doInit() {
        System.out.println(this.getClass().getName());
    }
}


// 这里对职责器模式进行了一个小的修改,职责器模式定义“直到链上的某个接收对象能够处理它为止”,而我们处理它之后依然没有停止,知道执行结束
public class ScriptChain {

    public void init(List<AbstractScript> initScriptList) {
        // 排序
        Collections.sort(initScriptList);
        // 遍历执行所有脚本
        for (AbstractScript script : initScriptList) {
            if (script.filter(predicate -> {
                return true;
            })) {
                script.doInit();
            }
        }
    }
}

状态模式-State

什么是状态模式

状态机又叫有限状态机,由3个部分组成:状态、事件、动作。其中,事件也称为转移条件。事件触发状态的转移及动作的执行。

为什么使用状态模式

当状态条状触发的动作比较复杂时,使用状态模式,可以提高代码的可维护性。

如何使用状态模式

订单有5个状态,分别为待付款,已关闭,已付款,已退款,已完成。事件有同意和拒绝。示例代码简单没有动作。

public class Order {

    private OrderStateEnum status =  OrderStateEnum.unpaid;

    public OrderStateEnum getStatus() {
        return status;
    }

    public void setStatus(OrderStateEnum status) {
        this.status = status;
    }
}

public enum OrderStateEnum {

    unpaid, closed, paid, refund, finish
}


// 状态接口
public interface OrderState {

    default void agree(OrderStateMachine orderStateMachine){
        // nothing
    }

    default void reject(OrderStateMachine orderStateMachine){
        // nothing
    }
}

// 待付款的状态改变
public class UnpaidOrderState implements OrderState {

    @Override
    public void agree(OrderStateMachine orderStateMachine) {
        // 处理已付款的相关事项
        // .....
        // 状态调整
        orderStateMachine.setState(new PaidOrderState());
    }

    @Override
    public void reject(OrderStateMachine orderStateMachine) {
        orderStateMachine.setState(new ClosedOrderState());
    }
}

// 已付款的状态改变
public class PaidOrderState implements OrderState {

    @Override
    public void agree(OrderStateMachine orderStateMachine) {
        orderStateMachine.setState(new FinishOrderState());
    }

    @Override
    public void reject(OrderStateMachine orderStateMachine) {
            orderStateMachine.setState(new RefundedOrderState());
    }
}

// 已退款-最终状态,没有任何操作
public class RefundedOrderState implements OrderState {}

// 已关闭-最终状态,没有任何操作
public class ClosedOrderState implements OrderState {}

// 已完成-最终状态,没有任何操作
public class FinishOrderState implements OrderState {}

// 状态机
public class OrderStateMachine {

    private OrderState state;

    private static Map<OrderStateEnum, OrderState> orderStateMap = new HashMap<>(10);

    static{
        orderStateMap.put(OrderStateEnum.unpaid, new UnpaidOrderState());
        orderStateMap.put(OrderStateEnum.closed, new ClosedOrderState());
        orderStateMap.put(OrderStateEnum.paid, new UnpaidOrderState());
        orderStateMap.put(OrderStateEnum.refund, new RefundedOrderState());
        orderStateMap.put(OrderStateEnum.finish, new FinishOrderState());
    }

    public OrderStateMachine(Order order){
        state = orderStateMap.get(order.getStatus());
    }

    public void agree(){
        state.agree(this);
    }

    public void reject(){
        state.reject(this);
    }

    public OrderState getState() {
        return state;
    }

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

// 调用代码
public class Main {

    public static void main(String[] args) {
        Order order = new Order();
        OrderStateMachine orderStateMachine = new OrderStateMachine(order);
        orderStateMachine.agree();
        System.out.println(orderStateMachine.getState().toString());
        orderStateMachine.agree();
        System.out.println(orderStateMachine.getState().toString());
    }
}

// 打印日志如下:
cn.hgy.state.PaidOrderState@136432db
cn.hgy.state.FinishOrderState@7382f612

迭代器模式-Iterator

什么是迭代器模式

迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。

为什么使用迭代器模式

  • 迭代器模式封装集合内部的复杂数据结构,开发者不需要了解如何遍历,直接使用容器提供的迭代器即可;
  • 迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一;
  • 迭代器模式让添加新的遍历算法更加容易,更符合开闭原则。除此之外,因为迭代器都实现自相同的接口,在开发中,基于接口而非实现编程,替换迭代器也变得更加容易。

如何使用迭代器模式

定义迭代器接口和具体的实现。

public interface Iterator<E> {
    
    boolean hasNext();
    
    E next();
}

访问者模式-Visitor

什么是访问者模式

访问者模式允许一个或者多个操作应用到一组对象上。

为什么使用访问者模式

解耦操作和对象本身,保持类职责单一、满足开闭原则以及应对代码的复杂性。

如何使用访问者模式

public abstract class AbstractFile {

    public abstract void read(IRead read);
}

public class Doc extends AbstractFile {
    @Override
    public void read(IRead read) {
        read.read(this);
    }
}

public class Pdf extends  AbstractFile {
    @Override
    public void read(IRead read) {
        read.read(this);
    }
}

public interface IRead {

    void read(Doc doc);

    void read(Pdf pdf);
}

public class ReadImpl implements IRead {
    @Override
    public void read(Doc doc) {
        System.out.println("doc");
    }

    @Override
    public void read(Pdf pdf) {
        System.out.println("pdf");
    }
}

public class Main {

    public static void main(String[] args) {
        List<AbstractFile> fileList = new ArrayList<>();
        fileList.add(new Doc());
        fileList.add(new Pdf());
        IRead read = new ReadImpl();
        fileList.forEach(abstractFile -> {
            // 编译会失败,所以改用abstractFile.read(read);
            // read.read(abstractFile);
            abstractFile.read(read);
        });
    }
}

备忘录模式-Memento

什么是备忘录模式

备忘录模式也叫快照模式,具体来说,就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。

命令模式-Command

什么是命令模式

命令模式将请求封装为一个对象,这样可以使用不同的请求参数化其他对象,并且能够支持请求的排队执行、记录日志、撤销等附加功能。

为什么使用命令模式

解耦行为请求者与行为实现者。

在大部分编程语言中,函数是没法作为参数传递给其他函数的,也没法赋值给变量。借助命令模式,我们将函数封装成对象,这样就可以实现把函数像对象一样使用。

如何使用命令模式


public interface Command {
    
    void execute();
}

// 灯
public class Light {

    public void on() {
        System.out.println("灯亮了");
    }
    
    public void off() {
        System.out.println("灯暗了");
    }
}

// 开灯的命令
public class LightOnCommand implements Command {

    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }
}

// 关灯的命令
public class LightOffCommand implements Command {

    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }
}

// 使用类
public class Main {

    public static void main(String[] args) {
        Queue<Command> request = new LinkedList<>();
        Light light = new Light();
        request.add(new LightOnCommand(light));
        request.add(new LightOffCommand(light));

        request.forEach(command -> {
            command.execute();
        });
    }
}

解释器模式-Interpreter

什么是解释器模式

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。

中介模式-Mediator

什么是中介模式

中介模式定义了一个单独的对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。

总结

观察者模式

观察者将自己注册到被观察者中,当被观察者发生改变时,将改变推送给每一个被观察中。

可以通过框架或者反射解决注册到被观察者中,或者使用消息队列解耦观察者和被观察者;可以通过异步执行观察者函数。

模板模式

定义一套流程,将其中部分功能放在子类实现。

模板模式需要继承实现,可以通过回调来实现类似功能,解决继承问题。

策略模式

定义一组功能相似的算法实现,可以互相替换。

职责链模式

职责链模式是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。

状态模式

适用于状态并不多、状态转移也比较简单,但事件触发执行的动作包含的业务逻辑可能比较复杂的状态机。

迭代器模式

迭代器模式用来遍历集合对象。解耦容器代码和遍历代码。

访问者模式

访问者模式允许一个或者多个操作应用到一组对象上。

备忘录模式

备忘录模式也叫快照模式,具体来说,就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。

命令模式

命令模式将请求封装为一个对象,这样可以使用不同的请求参数化其他对象,并且能够支持请求的排队执行、记录日志、撤销等附加功能。

解释器模式

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法

中介模式

中介模式定义了一个单独的对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。

关注我

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

瑾析编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值