23种设计模式——行为型模式(Behavioral Pattern)(上半部)

行为型模式(Behavioral Pattern)——上半部

这些模式关注对象之间的通信和交互,旨在解决对象之间的责任分配和算法的封装。

1.责任链模式(Chain of Responsibility Pattern)

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

介绍

**意图:**避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

**主要解决:**职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用:处理消息的时候以过滤很多道。

**如何解决:**拦截的类都实现统一接口。

**关键代码:**Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例: 1、JS 中的事件冒泡。 2、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

优点:

  1. 降低耦合度。它将请求的发送者和接收者解耦。
  2. 简化了对象。使得对象不需要知道链的结构。
  3. 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
  4. 增加新的请求处理类很方便

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。

**注意事项:**在 JAVA WEB 中遇到很多应用。

实现

我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。

23种设计模式——行为型模式(Behavioral Pattern)(上半部)_第1张图片

抽象类 AbstractLogger,带有详细的日志记录级别

public abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;

    protected int level;
    protected AbstractLogger nextLogger;
    public void setNextLogger(AbstractLogger nextLogger){
        this.nextLogger = nextLogger;
    }
    public void logMessage(int level,String message){
        if(this.level<=level){
            write(message);
        }
        if(nextLogger!=null){
            nextLogger.logMessage(level,message);
        }
    }
    public abstract void write(String message);
}

创建三种类型的记录器ConsoleLogger ErrorLogger FileLogger,都扩展了 AbstractLogger

public class ConsoleLogger extends AbstractLogger{
    public ConsoleLogger(int level) {
        this.level = level;
    }
    @Override
    public void write(String message) {
        System.out.println("ConsoleLogger ::"+message);
    }
}

class ErrorLogger extends AbstractLogger{
    public ErrorLogger(int level){
        this.level = level;
    }
    @Override
    public void write(String message) {
        System.out.println("ErrorLogger ::"+message);
    }
}

class FileLogger extends AbstractLogger{
    public FileLogger(int level){
        this.level = level;
    }
    @Override
    public void write(String message) {
        System.out.println("FileLogger ::"+message);
    }
}

创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。Client 调用该责任链

public class Client{
    public static void main(String[] args) {
        AbstractLogger chain = Client.getChain();
        chain.logMessage(1,"处理一级日志");
        chain.logMessage(2,"处理二级日志");
        chain.logMessage(3,"处理三级日志");

//        ConsoleLogger ::处理一级日志
//        FileLogger ::处理二级日志
//        ConsoleLogger ::处理二级日志
//        ErrorLogger ::处理三级日志
//        FileLogger ::处理三级日志
//        ConsoleLogger ::处理三级日志
    }

    ///获取责任链的方法
    private static AbstractLogger getChain(){
        ErrorLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
        FileLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        ConsoleLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);

        return errorLogger;
    }
}








命令模式(Command Pattern)

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

介绍

**意图:**将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

**主要解决:**在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

**何时使用:**在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

**如何解决:**通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。

**关键代码:**定义三个角色:

  1. received 真正的命令执行对象
  2. Command
  3. invoker 使用命令对象的入口

**应用实例:**struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。

优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。

**缺点:**使用命令模式可能会导致某些系统有过多的具体命令类。

**使用场景:**认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。

命令模式结构示意图:

23种设计模式——行为型模式(Behavioral Pattern)(上半部)_第2张图片

实现

我们首先创建作为命令的接口 Order,然后创建作为请求的 Stock 类。实体命令类 BuyStockSellStock,实现了 Order 接口,将执行实际的命令处理。创建作为调用对象的类 Broker,它接受订单并能下订单。

Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。Client 类使用 Broker 类来演示命令模式。

23种设计模式——行为型模式(Behavioral Pattern)(上半部)_第3张图片

创建一个请求类 Stock

public class Stock {
    private String name = "A股";
    private int quantity = 100;
    public void buy(){
        System.out.println("买了name:"+name+"数量:"+quantity);
    }
    public void sell(){
        System.out.println("卖了name:"+name+"数量:"+quantity);
    }
}

创建一个命令接口 Order 和其具体子类 SellStock BuyStock

public interface Order {
    void execute();
}
class SellStock implements Order{
    private Stock stock;
    SellStock(Stock stock){
        this.stock = stock;
    }
    @Override
    public void execute() {
        stock.sell();
    }
}
class BuyStock implements Order{
    private Stock stock;
    BuyStock(Stock stock){
        this.stock = stock;
    }
    @Override
    public void execute() {
        stock.buy();
    }
}

创建命令调用类 Receiver

public class Receiver {
    private List<Order> orders = new ArrayList<>();
    public void takeOrder(Order order){
        orders.add(order);
    }
    public void executeAllOrder(){
        for(Order order : orders){
            order.execute();
        }
        orders.clear();
    }
}

Client使用 Receiver类来接受并执行命令

public class Client {
    public static void main(String[] args) {
        Stock stock = new Stock();

        SellStock sellStock = new SellStock(stock);
        BuyStock buyStock = new BuyStock(stock);

        Receiver receiver = new Receiver();
        receiver.takeOrder(sellStock);
        receiver.takeOrder(buyStock);

        receiver.executeAllOrder();

    }
}








解释器模式(Interpreter Pattern)

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

介绍

**意图:**给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

**主要解决:**对于一些固定文法构建一个解释句子的解释器。

**何时使用:**如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

**如何解决:**构建语法树,定义终结符与非终结符。

**关键代码:**构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。

**应用实例:**编译器、运算表达式计算。

优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。

缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。

使用场景: 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。

**注意事项:**可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。

实现

我们将创建一个接口 Expression 和实现了 Expression 接口的实体类。定义作为上下文中主要解释器的 TerminalExpression 类。其他的类 OrExpressionAndExpression 用于创建组合式表达式。

23种设计模式——行为型模式(Behavioral Pattern)(上半部)_第4张图片

创建一个接口 Expression 和实现了 Expression 接口的实体类

public interface Expression {
    boolean interpret(String context);
}
class TerminalExpression implements Expression{
    private String data;

    TerminalExpression(String data){
        this.data = data;
    }
    @Override
    public boolean interpret(String context) {
        if(context.contains(data)){
            return true;
        }
        return false;
    }
}

class AndExpression implements Expression{
    private Expression expression1 = null;
    private Expression expression2 = null;
    AndExpression(Expression expression1,Expression expression2){
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
    @Override
    public boolean interpret(String context) {
        return expression1.interpret(context) && expression2.interpret(context);
    }
}

class OrExpression implements Expression{
    private Expression expression1 = null;
    private Expression expression2 = null;
    OrExpression(Expression expression1,Expression expression2){
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
    @Override
    public boolean interpret(String context) {
        return expression1.interpret(context) || expression2.interpret(context);
    }
}

Client我们的演示类使用 Expression 类创建规则和演示表达式的解析

public class Client {
    public static void main(String[] args) {
        TerminalExpression mike = new TerminalExpression("mike");
        TerminalExpression tom = new TerminalExpression("tom");
        AndExpression mikeAndTom = new AndExpression(mike, tom);

        TerminalExpression mary = new TerminalExpression("mary");
        TerminalExpression Amy = new TerminalExpression("Amy");
        OrExpression maryOrAmy = new OrExpression(mary, Amy);

        System.out.println(mikeAndTom.interpret("mike and tom"));
        System.out.println(maryOrAmy.interpret("mary or Amy"));

    }
}








迭代器模式(Iterator Pattern)

迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

介绍

意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

**主要解决:**不同的方式来遍历整个整合对象。

**何时使用:**遍历一个聚合对象。

**如何解决:**把在元素之间游走的责任交给迭代器,而不是聚合对象。

**关键代码:**定义接口:hasNext, next。

**应用实例:**JAVA 中的 iterator。

优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

**缺点:**由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。

**注意事项:**迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

实现

我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。

Client,我们的演示类使用实体类 NamesRepository 来打印 NamesRepository 中存储为集合的 Names

23种设计模式——行为型模式(Behavioral Pattern)(上半部)_第5张图片

terator接口

public interface Iterator {
    public boolean hasNext();
    public Object next();
}

实现了 Container 接口的实体类将负责实现 Iterator 接口

public interface Container {
    public Iterator getIterator();
}
class NameRepository implements Container{
    String[] names = new String[]{"mike","mary","tom"};
    private Iterator iterator;

    @Override
    public Iterator getIterator() {
        if(iterator==null){
            iterator =  new NameIterator();
        }
        return iterator;
    }

    private class NameIterator implements Iterator{
        private int index ;
        @Override
        public boolean hasNext() {
            return index<names.length?true:false;
        }
        @Override
        public Object next() {
            return this.hasNext()?names[index++]:null;
        }
    }
}

Client 调用 iterator

public class Client {
    public static void main(String[] args) {
        NameRepository nameRepository = new NameRepository();
        while(nameRepository.getIterator().hasNext()){
            System.out.println("name:"+nameRepository.getIterator().next());

        }
    }
}








中介者模式(Mediator Pattern)

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

介绍

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

**主要解决:**对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

**何时使用:**多个类相互耦合,形成了网状结构。

**如何解决:**将上述网状结构分离为星型结构

**关键代码:**对象 Colleague 之间的通信封装到一个类中单独处理。

**应用实例: MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。

**缺点:**中介者会庞大,变得复杂难以维护。

使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

实现

我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoomUserUser 对象使用 ChatRoom 方法来分享他们的消息。

Client,我们的演示类使用 User 对象来显示他们之间的通信。

23种设计模式——行为型模式(Behavioral Pattern)(上半部)_第6张图片

ChatRoom 聊天室向所有的用户显示消息

public class ChatRoom {
    public static void sendMassage(User user,String message){
        System.out.println(user.getName()+":"+message);
    }
}

User 对象使用 ChatRoom 方法来分享他们的消息

public class User {
    private String name;

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

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void sentMassage(String message){
        ChatRoom.sendMassage(this,message);
    }
}

Client演示类使用 User 对象来显示他们之间的通信

public class Client {
    public static void main(String[] args) {
        User mike = new User("mike");
        User tom = new User("tom");

        mike.sentMassage("I am mike");
        tom.sentMassage("I am tom");
    }
}








备忘录模式(Memento Pattern)

备忘录模式(Memento Pattern)保存一个对象的某个状态,120对象。备忘录模式属于行为型模式。

介绍

**意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

**主要解决:**所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

**何时使用:**很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。

**如何解决:**通过一个备忘录类专门存储对象状态。

**关键代码:**客户不与备忘录类耦合,与备忘录管理类耦合。

应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctrl + z。 4、IE 中的后退。 5、数据库的事务管理。

优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。

**缺点:**消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

使用场景: 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。

注意事项: 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。

实现

备忘录模式使用三个类 MementoOriginatorCareTaker。Memento 包含了要被恢复的对象的状态。Originator 创建并在 Memento 对象中存储状态。Caretaker 对象负责从 Memento 中恢复对象的状态。

Client类使用 CareTakerOriginator 对象来显示对象的状态恢复。

23种设计模式——行为型模式(Behavioral Pattern)(上半部)_第7张图片

Memento 包含了要被恢复的对象的状态

public class Memento {
    private String state;

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

    public String getState() {
        return state;
    }
}

Originator 创建并在 Memento 对象中存储状态

public class Originator {
    private String state;

    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public Memento setStateToMemento(){
        return new Memento(state);
    }
    public void getStateFromMemento(Memento memento){
        state = memento.getState();
    }

}

Caretaker 对象负责从 Memento 中恢复对象的状态

public class CareTaker {
    private static List<Memento> mementoList = new ArrayList<>();

    public static void setMementoList(Memento memento) {
        mementoList.add(memento);
    }
    public static Memento getMementoList(int index) {
        return mementoList.get(index);
    }
}

Client类使用 CareTakerOriginator 对象来显示对象的状态恢复

public class Client {
    public static void main(String[] args) {
        Originator originator1 = new Originator();
        originator1.setState("状态一");
        CareTaker.setMementoList(originator1.setStateToMemento());

        originator1.setState("状态二");
        CareTaker.setMementoList(originator1.setStateToMemento());

        originator1.setState("状态三");
        CareTaker.setMementoList(originator1.setStateToMemento());

        originator1.getStateFromMemento(CareTaker.getMementoList(0));
        System.out.println(originator1.getState());//        状态一

        originator1.getStateFromMemento(CareTaker.getMementoList(1));
        System.out.println(originator1.getState());//        状态二

        originator1.getStateFromMemento(CareTaker.getMementoList(2));
        System.out.println(originator1.getState());//        状态三
    }
}


你可能感兴趣的:(java设计模式,设计模式)