java

设计模式之三:行为型模式

2019-06-11  本文已影响0人  suxin1932
行为模式涉及怎样合理地设计对象之间的交互通信,
以及怎样合理为对象分配职责,让设计富有弹性,易维护和易复用。

#(1)责任链模式:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
#(2)命令模式:
将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化;
对请求排队或记录请求日志,以及支持可撤销的操作。
#(3)解释器模式:
给定一个语言,定义它文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
#(4)迭代器模式:
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
#(5)中介者模式:
用一个中介对象来封装一系列的对象交互。
中介者使各对象不需要显示地相互引用,从而使其耦合松散,
而且可以独立地改变它们之间的交互。
#(6)备忘录模式:
在不破坏封装性的情况下,捕获一个对象的内部状态,
并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。
#(7)观察者模式:
定义对象间的一种一对多的依赖关系,
当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并被自动更新。
#(8)状态模式:
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
#(9)策略模式:
定义一系列算法,把它们一个个封装起来,并且可使它们可以相互替换。
策略模式使算法可独立于使用它的客户而变化。
#(10)模板方法模式:
定义一个操作中算法的骨架,而将一些步骤延迟到子类中。
模板方法使子类可以不改变一个算法的结构即可定义该算法的某些特定步骤。
#(11)访问者模式:
表示一个作用于某对象结构中的各个元素的操作。
它可以在不改变各个元素的类的前提下定义作用于这些元素的新操作。

1.责任链模式(chain)

#概念
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

责任链模式是使用多个对象处理用户请求的成熟模式,
责任链模式的关键是将用户的请求分派给许多对象,这些对象被组织成一个责任链,
即每个对象含有后继对象的引用,
并要求责任链上的每个对象,如果能处理用户的请求,就做出处理,不再将用户的请求传递给责任链上的下一个对象;
如果不能处理用户的请求,就必须将用户的请求传递给责任链上的下一个对象。

#责任链模式的优点
(1)责任链中的对象只和自己的后继是低耦合关系,和其他对象毫无关联。
(2)当在处理者中分配职责时,责任链给应用程序更多的灵活性。
(3)应用程序可以动态地增加、删除处理者或重新指派处理者的职责。
(4)应用程序可以动态地增加、删除处理者或重新指派处理者的职责。
(5)应用程序可以动态地改变处理者之间的先后顺序。
(6)使用责任链的用户不必知道处理者的信息,用户不会知道到底是哪个对象处理了它的请求。

#适合使用责任链模式的情景
(1)有许多对象可以处理用户的请求,希望程序在运行期间自动确定处理用户的那个对象。
(2)希望用户不必明确指定接受者的情况下,向多个接受者的一个提交请求。
(3)程序希望动态制定可处理用户请求的对象集合。

#责任链模式的结构总包括两种角色:
(1)处理者(Handler):
处理者是一个接口,负责规定具体处理者处理用户请求的方法,以及具体处理者设置后继对象的方法。
(2)具体处理者(ConcreteHandler):
具体处理者是实现处理者接口的类的实例。
具体处理者通过调用处理者接口规定的方法处理用户的请求,
即在接到用户的请求后,处理者将调用接口规定的方法,
在执行该方法的过程中,如果发现能处理用户的请求,就处理有关数据,
否则就反馈无法处理的信息给用户,然后将用户的请求传递给自己的后继对象。

#具体示例:
1.servlet中的Filter
public final class ApplicationFilterChain implements FilterChai{...}
2.Dubbo中的Filter
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {...}
3.Mybatis中的Plugin
public class Plugin implements InvocationHandler{...}
4.java中的异常处理机制
责任链示例.png
package com.zy.designPattern.chain.leave;
/**
 * 员工请假,每个领导的权限不一样
 * 比如小组长只能批1天假,经历能批3天的假,董事长能批15天的假等等。
 * 1:"请假人" 为发送者,发送请假申请。
 * 2:"虚线里面的各个领导"是接收者。
 * 3:而"虚线里面的各个领导"又组成了一个"领导链"(因为请假需要各级领导签字审批)
 * 4:而 "请假人"和"领导链"之间是又是解耦的,因为“请假人”并不知道“领导链”中的具体结构,请假审批到哪一步等等,"请假人"也并不需要关心。
 */
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 发起者(请假人)
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LeaveRequest {
    private String name; // 请假人
    private int days; // 请假天数
    private String reason; // 请假原因
}
package com.zy.designPattern.chain.leave;
import lombok.Data;
/**
 * 接收者(领导链)
 */
@Data
public abstract class Boss {
    protected String bossName;
    protected Boss nextBoss;
    public Boss(String bossName) {
        this.bossName = bossName;
    }
    public abstract void handler(LeaveRequest request);
}
package com.zy.designPattern.chain.leave;

/**
 * 组长继承领导抽象类,组长的职能是批一天以内的假
 */
public class TeamLeader extends Boss {
    public TeamLeader(String name) {
        super(name);
    }
    @Override
    public void handler(LeaveRequest request) {
        if (request.getDays() <= 1) {
            System.out.println(String.format("%s同意了%s的请假,请假时间为%s天, 请假原因:%s",
                    this.getBossName(), request.getName(), request.getDays(), request.getReason()));
        } else {
            if (this.getNextBoss() != null) {
                this.getNextBoss().handler(request);
            } else {
                System.out.println(String.format("%s不能批", this.getBossName()));
            }
        }
    }
}
package com.zy.designPattern.chain.leave;

public class ManagerLeader extends Boss{
    public ManagerLeader(String bossName) {
        super(bossName);
    }
    @Override
    public void handler(LeaveRequest request) {
        if (request.getDays() <= 3) {
            System.out.println(String.format("%s同意了%s的请假,请假时间为%s天, 请假原因:%s",
                    this.getBossName(), request.getName(), request.getDays(), request.getReason()));
        } else {
            if (this.getNextBoss() != null) {
                this.getNextBoss().handler(request);
            } else {
                System.out.println(String.format("%s不能批", this.getBossName()));
            }
        }
    }
}
package com.zy.designPattern.chain.leave;

import org.junit.Test;
public class ChainTest {
    @Test
    public void fn01() {
        Boss teamLeader = new TeamLeader("teamLeader");
        Boss managerLeader = new ManagerLeader("managerLeader");
        teamLeader.setNextBoss(managerLeader);
        // 请假: 从小到大
        LeaveRequest request = new LeaveRequest("tom", 2, "有事了");
        teamLeader.handler(request);
    }
}

2.命令模式(command)

#概念
在许多设计中,经常涉及到一个对象请求另一个对象调用其方法达到某种目的。
如果请求者不希望或无法直接和被请求者打交道,即不希望或无法含有被请求者的引用,那么就可以使用命令模式。

命令模式的核心就是使用命令对象来封装方法调用,即将请求者的请求:
“接收者调用方法”封装到命令对象的一个方法中,
这样一来,当一个对象请求另一个对象调用方法来完成某项任务时,
只需和命令对象打交道,即让命令对象调用封装了“请求”的那个方法即可。

命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系:
1.更方便的对命令进行扩展
2.对多个命令的统一控制(这种控制包括但不限于:队列、撤销/恢复、记录日志等等)
3. 命令模式是对功能方法的抽象,并不是对对象的抽象。
4. 命令模式是将功能提升到对象来操作,以便对多个功能进行一系列的处理以及封装。

#适用场景:
1. 命令的发送者和命令执行者有不同的生命周期。命令发送了并不是立即执行。
2. 命令需要进行各种管理逻辑。
3. 需要支持撤消\重做操作

#经典的命令模式包括4个角色:
Command:定义命令的统一接口
ConcreteCommand:Command接口的实现者,用来执行具体的命令,某些情况下可以直接用来充当Receiver。
Receiver:命令的实际执行者
Invoker:命令的请求者,是命令模式中最重要的角色。这个角色用来对各个命令进行控制。

#应用示例:
spring JdbcTemplate

代码示例

命令者模式.png
package com.zy.designPattern.command;

import lombok.Setter;
/*
 * 发号施令者
 * 
 */
public class Invoker {

    @Setter
    private Command command;

    public void run() {
        command.execute();
    }

}
package com.zy.designPattern.command;

/*
 * 最终执行命令者
 * 
 */
public class Consumer {

    public void fn01() {
        System.out.println("--->执行了方法fn01()");
    }

    public void fn02() {
        System.out.println("--->执行了方法fn02()");
    }

}
package com.zy.designPattern.command;
/*
 * 抽象传递命令者
 * 
 */
public interface Command {
    void execute();
}
package com.zy.designPattern.command;
/*
 * 传递命令者1
 * 
 */
public class Command01 implements Command {

    private Consumer consumer;

    public Command01(Consumer consumer) {
        this.consumer = consumer;
    }

    @Override
    public void execute() {
        this.consumer.fn01();
    }
}
package com.zy.designPattern.command;
/*
 * 传递命令者2
 * 
 */
public class Command02 implements Command {

    private Consumer consumer;

    public Command02(Consumer consumer) {
        this.consumer = consumer;
    }

    @Override
    public void execute() {
        this.consumer.fn02();
    }

}
package com.zy.designPattern.command;

public class CommandTest {

    public static void main(String[] args) {
        Invoker invoker = new Invoker();
        Consumer consumer = new Consumer();

        Command01 command01 = new Command01(consumer);
        invoker.setCommand(command01);
        invoker.run();

        System.out.println("--------");

        Command02 command02 = new Command02(consumer);
        invoker.setCommand(command02);
        invoker.run();
    }
}

https://www.jianshu.com/p/b8ff58f91fd5

5.中介者模式(mediator)

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

如果两个类不必彼此通信,那么这两个类就不应当发生直接关联的关系。
如果其中一个类需要调用另一个类中的方法,我们可以通过第三方来转发这个调用。

通过中介者模式,我们可以将复杂关系的网状结构变成结构简单的以中介者为核心的星形结构,每个对象不再和它与之关联的对象直接发生相互作用,而是通过中介者对象来另一个对象发生相互作用。

但是就是因为中介者对象封装了对象之间的关联关系,导致中介者对象变得比较庞大,"系统难以维护"。
它需要知道每个对象和他们之间的交互细节,如果它出问题,将会导致整个系统都会出问题。
"所以它比较容易应用也很容易误用。"
故当系统中出现了“多对多”交互复杂的关系群时,先判断设计是否合理, 再考虑选用何种设计模式。

#主要包含如下几个角色:
>>Mediator: 
抽象中介者。定义了同事对象到中介者对象之间的接口。
>>ConcreteMediator: 
具体中介者。实现抽象中介者的方法,它需要知道所有的具体同事类,
同时需要从具体的同事类那里接收信息,并且向具体的同事类发送信息。
>>Colleague: 
抽象同事类。
>>ConcreteColleague: 
具体同事类。每个具体同事类都只需要知道自己的行为即可,但是他们都需要认识中介者。

#应用示例:
SpringMVC中的Controller是Model和View的中介者
中介者模式.png

以某聊天软件XX群聊为例

图片.png
package com.zy.designPattern.mediator;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

/**
 * 用户抽象类
 */
public abstract class User {

    protected Mediator mediator;
    @Getter
    @Setter
    private String name;

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

    // 群发消息
    abstract void sendMsg2All(String msg);

    // 私发消息
    abstract void sendMsg2Person(String msg, String name);

    // 接收消息
    abstract void receiveMsg(String msg);

    // 加入聊天室
    abstract void join();

    // 离开聊天室
    abstract void leave();

}
package com.zy.designPattern.mediator;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 中介者抽象类
 */
public abstract class Mediator {

    // 所有聊天室的用户
    List<User> list = new CopyOnWriteArrayList<>();

    // 群发消息
    abstract void sendMsg2All(String msg);

    // 私发消息
    abstract void sendMsg2Person(String msg, String name);

    // 加入聊天室
    abstract void join(User user);

    // 离开聊天室
    abstract void leave(User user);
}
package com.zy.designPattern.mediator;

import java.util.Optional;
/*
 * 具体中介者: 聊天平台
 * 
 */
public class WechatPlatform extends Mediator {
    @Override
    void sendMsg2All(String msg) {
        for (User user : list) {
            user.receiveMsg(msg);
        }
    }

    @Override
    void sendMsg2Person(String msg, String name) {
        for (User user : list) {
            if (user.getName().equals(name)) {
                user.receiveMsg(msg);
            }
        }
    }

    @Override
    void join(User user) {
        list.add(user);
    }

    @Override
    void leave(User user) {
        Optional.ofNullable(user).ifPresent(user1 -> list.remove(user1));
    }

}
package com.zy.designPattern.mediator;
/*
 * 具体用户类
 * 
 */
public class WechatUser extends User {

    public WechatUser(Mediator mediator, String name) {
        super(mediator, name);
    }

    @Override
    void sendMsg2All(String msg) {
        mediator.sendMsg2All(msg);
    }

    @Override
    void sendMsg2Person(String msg, String name) {
        mediator.sendMsg2Person(msg, name);
    }

    @Override
    void receiveMsg(String msg) {
        System.out.println(this.getName() + "收到消息--->" + msg);
    }

    @Override
    void join() {
        mediator.join(this);
    }

    @Override
    void leave() {
        mediator.leave(this);
    }
}
package com.zy.designPattern.mediator;

public class MediatorTest {

    public static void main(String[] args) {
        Mediator wechatPlatform = new WechatPlatform();
        WechatUser user01 = new WechatUser(wechatPlatform, "user01");
        WechatUser user02 = new WechatUser(wechatPlatform, "user02");
        WechatUser user03 = new WechatUser(wechatPlatform, "user03");
        user01.join();
        user02.join();
        user03.join();
        System.out.println("user01-->群发消息---------------");
        user01.sendMsg2All("hello, i am user01");
        System.out.println("user01给user02私发消息-------------");
        user01.sendMsg2Person("你好么, user02", user02.getName());
        System.out.println("user01离开聊天室---------------");
        user01.leave();
    }
}

https://blog.csdn.net/cangchen/article/details/44948565

6.备忘录模式(memento)

#概念
备忘录模式用于保存和恢复对象的状态。

#与"原型模式"的区别
原型模式保存的是当前对象的所有状态信息,恢复的时候会生成与保存的对象完全相同的另外一个实例;
而备忘录模式保存的是我们关心的在恢复时需要的对象的部分状态信息,相当于快照。

#应用场景
1、需要保存/恢复数据的相关状态场景。 如快照机制, ctrl+z撤销机制
2、提供一个可回滚的操作。 

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

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

#应用示例
(1) 文字或者图像编辑软件的撤销(Ctrl+Z)的功能,
当软件关闭再打开时不能再进行撤销操作,也就是说不能再回到关闭软件前的状态,
在编辑文件的同时可以保存一些内部状态,这些状态在软件关闭时从内存销毁,
当然这些状态的保存也不是无限的,很多软件只提供有限次的撤销操作。
(2) 数据库管理系统DBMS所提供的事务管理应用了备忘录模式,当数据库某事务中一条数据操作语句执行失败时,
整个事务将进行回滚操作,系统回到事务执行之前的状态。
(3)后悔药
(4)打游戏时的存档

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

#备忘录模式包含如下角色:
Originator: 原发器
Memento: 备忘录
Caretaker: 负责人

代码示例

备忘录模式.png
package com.zy.designPattern.memento;

import lombok.Getter;

/**
 * 备忘录
 */
public class Memento {

    @Getter
    private String state;

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

}
package com.zy.designPattern.memento;

import lombok.Getter;
import lombok.Setter;

/**
 * 原发器
 */
public class Originator {

    @Setter
    @Getter
    private String state;

    public Memento saveState2Mento() {
        return new Memento(state);
    }

    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}
package com.zy.designPattern.memento;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CareTaker {

    private List<Memento> mementos = new CopyOnWriteArrayList<>();

    public void add(Memento memento) {
        mementos.add(memento);
    }

    public Memento get(int index) {
        return mementos.get(index);
    }
}
package com.zy.designPattern.memento;

public class MementoTest {

    public static void main(String[] args) {
        CareTaker careTaker = new CareTaker();

        Originator originator = new Originator();
        originator.setState("1");
        careTaker.add(originator.saveState2Mento());
        originator.setState("2");
        careTaker.add(originator.saveState2Mento());
        originator.setState("3");
        careTaker.add(originator.saveState2Mento());

        System.out.println("current state-->" + originator.getState());
        originator.getStateFromMemento(careTaker.get(0));
        System.out.println("first saved state -->" + originator.getState());
        originator.getStateFromMemento(careTaker.get(1));
        System.out.println("second saved state -->" + originator.getState());
    }
}

7.观察者模式

#概念
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。
比如,当一个对象被修改时,则会自动通知它的依赖对象。

#意图:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

#主要解决:
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

#何时使用:
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

#如何解决:
使用面向对象技术,可以将这种依赖关系弱化。

#观察者模式的结构中包含四种角色:
(1)主题(Subject):
主题是一个接口,该接口规定了具体主题需要实现的方法,
比如,添加、删除观察者以及通知观察者更新数据的方法。
(2)观察者(Observer):
观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
(3)具体主题(ConcreteSubject):
具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。
具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
(4)具体观察者(ConcreteObserver):
具体观察者是实现观察者接口类的一个实例。
具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,
使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。

#关键代码:
在抽象类里有一个 ArrayList 存放观察者们。

#优点: 
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。

#缺点: 
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。 

#注意事项: 
1、JAVA 中已经有了对观察者模式的支持类。 
2、避免循环引用。 
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。 
观察者模式.png

代码实现

观察者模式.png
package com.zy.designPattern.observer.version2;

/**
 * 观察者抽象类
 */
public abstract class Observer {

    protected Subject subject;

    public abstract void update();
}
package com.zy.designPattern.observer.version2;


import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 主题类: 即被观察者
 * 用于解决一对多的关系问题
 */
public class Subject {

    private List<Observer> observers = new CopyOnWriteArrayList<>();

    private int state;

    public int getState() {
        return state;
    }

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

    public void subscribe(Observer observer) {
        observers.add(observer);
    }

    public void notifyAllObserver() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}
package com.zy.designPattern.observer.version2;

/**
 * 具体观察者1
 */
public class Observer01 extends Observer {

    public Observer01(Subject subject) {
        this.subject = subject;
        this.subject.subscribe(this);
    }

    @Override
    public void update() {
        System.out.println("observer01--->" + subject.getState());
    }

}
package com.zy.designPattern.observer.version2;

/**
 * 观察者2
 */
public class Observer02 extends Observer {
    public Observer02(Subject subject) {
        this.subject = subject;
        this.subject.subscribe(this);
    }

    @Override
    public void update() {
        System.out.println("observer02---->" + subject.getState());
    }
}
package com.zy.designPattern.observer.version2;

public class ObserverTest {

    public static void main(String[] args) {
        Subject subject = new Subject();
        Observer01 observer01 = new Observer01(subject);
        Observer02 observer02 = new Observer02(subject);

        subject.setState(2);
        System.out.println("--------");
        subject.setState(5);
    }
}

8.状态模式

#意图:
允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

#主要解决:
对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

#何时使用:
代码中包含大量与对象状态有关的条件语句。

#如何解决:
将各种具体的状态类抽象出来。

#关键代码:
通常命令模式的接口中只有一个方法。
而状态模式的接口中有一个或者多个方法。
而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。
也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。
状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。

#应用实例: 
1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。 
2、曾侯乙编钟中,'钟是抽象接口','钟A'等是具体状态,'曾侯乙编钟'是具体环境(Context)。

#优点: 
1、封装了转换规则。
2、枚举可能的状态,在枚举状态之前需要确定状态种类。 
3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 
4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 
5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

#缺点: 
1、状态模式的使用必然会增加系统类和对象的个数。 
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 
3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,
增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

#使用场景: 
1、行为随状态改变而改变的场景。 
2、条件、分支语句的代替者。

#注意事项:
在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。

代码实现

假设要模拟一个ATM机,有以下需求:
#取款,验证卡密,吐出现钞,结束服务
#若卡密验证失败或者余额不足,则直接弹出卡片,结束本次服务
#机器内无存钞,显示No Cash,并向银行发送无钞信息
#机器故障,显示No Service,并向维修人员发送维修请求
状态模式.png
package com.zy.designPattern.state;

/**
 * 定义ATM状态
 */
public interface ATMState {
    /**
     * 插卡
     */
    void insertCard();
    /**
     * 验证密码
     */
    void verifyPwd();
    /**
     * 取款
     */
    void getCash();
    /**
     * 显示余额
     */
    void queryBalance();
    /**
     * 退卡
     */
    void exitCard();
}
package com.zy.designPattern.state;

import lombok.Data;

/**
 * ATM
 */
@Data
public class ATM {

    /**
     * 所有状态
     */
    private ATMState readyState; // 就绪状态
    private ATMState noCashState; // 无现金状态
    private ATMState noServiceState; // 服务异常状态
    private ATMState currentState; // 当前状态
    private int totalAmount; // 机内现钞数量

    /**
     * 插卡
     */
    public void insertCard() {
        currentState.insertCard();
    }
    /**
     * 验证密码
     */
    public void verifyPwd() {
        currentState.verifyPwd();
    }
    /**
     * 取款
     */
    public void getCash() {
        currentState.getCash();
    }
    /**
     * 显示余额
     */
    public void queryBalance() {
        currentState.queryBalance();
    }
    /**
     * 退卡
     */
    public void exitCard() {
        currentState.exitCard();
    }

    /*测试用的临时变量*/
    private String pwd;//密码
    private int balance;//余额
    private int amount;//取款金额

    public ATM(int totalAmount, int balance, int amount, String pwd) throws Exception{
        //初始化所有状态
        readyState = new ReadyState(this);
        noCashState = new NoCashState(this);
        noServiceState = new NoServiceState(this);

        if(totalAmount > 0){
            currentState = readyState;
        }
        else if(totalAmount == 0){
            currentState = noCashState;
        }
        else{
            throw new Exception();
        }

        //初始化测试数据
        this.totalAmount = totalAmount;
        this.balance = balance;
        this.amount = amount;
        this.pwd = pwd;
    }

}
package com.zy.designPattern.state;

/**
 * ATM的就绪状态
 */
public class ReadyState implements ATMState {
    //保留状态持有者的引用,以便对其进行操作
    private ATM atm;

    public ReadyState(ATM atm) {
        this.atm = atm;
    }

    @Override
    public void insertCard() {
        System.out.println("插卡完成");
    }

    @Override
    public void verifyPwd() {
        System.out.println("密码已提交");
        if ("123456".equals(atm.getPwd())) {
            System.out.println("密码正确");
        } else {
            System.out.println("密码错误");
        }
    }

    @Override
    public void getCash() {
        if (atm.getTotalAmount() >= atm.getAmount() && atm.getBalance() >= atm.getAmount()) {
            // 更新账户余额
            atm.setBalance(atm.getBalance() - atm.getAmount());
            // 更新机器内现钞总数
            atm.setTotalAmount(atm.getTotalAmount() - atm.getAmount());
            System.out.println("取出现金-->" + atm.getAmount());
            System.out.println("取款完成");
            // 弹出卡片
            exitCard();
            // 检查卡内余钞
            if (atm.getTotalAmount() == 0) {
                atm.setCurrentState(atm.getNoCashState());
                System.out.println("发送无钞票信息给银行");
            }
        } else {
            System.out.println("余额不足, 取款失败");
            // 退卡
            exitCard();
        }
    }

    @Override
    public void queryBalance() {
        System.out.println("余额是:--->" + atm.getBalance());
    }

    @Override
    public void exitCard() {
        System.out.println("卡已退出");
    }
}
package com.zy.designPattern.state;

public class NoCashState implements ATMState {
    //保留状态持有者的引用,以便对其进行操作
    private ATM atm;

    public NoCashState(ATM atm) {
        this.atm = atm;
    }

    @Override
    public void insertCard() {
        System.out.println("插卡完成");
    }

    @Override
    public void verifyPwd() {
        System.out.println("密码已提交");
        if ("123456".equals(atm.getPwd())) {
            System.out.println("密码正确");
        } else {
            System.out.println("密码错误");
        }
    }

    @Override
    public void getCash() {
        System.out.println("取款失败, ATM里无钞票");
    }

    @Override
    public void queryBalance() {
        System.out.println("余额是:--->" + atm.getBalance());
    }

    @Override
    public void exitCard() {
        System.out.println("卡已退出");
    }
}
package com.zy.designPattern.state;

public class NoServiceState implements ATMState {
    //保留状态持有者的引用,以便对其进行操作
    private ATM atm;

    public NoServiceState(ATM atm) {
        this.atm = atm;
    }

    @Override
    public void insertCard() {
        System.out.println("插卡失败,机器发生了故障");
    }

    @Override
    public void verifyPwd() {
        System.out.println("密码提交失败,机器发生了故障");
    }

    @Override
    public void getCash() {
        System.out.println("取款失败, 机器故障");
    }

    @Override
    public void queryBalance() {
        System.out.println("余额查询失败,机器发生了故障");
    }

    @Override
    public void exitCard() {
        System.out.println("取卡失败,机器发生了故障");
    }
}
package com.zy.designPattern.state;

public class StateTest {

    public static void main(String[] args) throws Exception {
        test(1000, 500, 200, "123456");
    }

    private static void test(int totalAmount, int balance, int amount, String pwd) throws Exception {
        ATM atm = new ATM(totalAmount, balance, amount, pwd);
        // 输出初始状态
        System.out.println(atm.toString());
        atm.insertCard();
        atm.verifyPwd();
        atm.getCash();
        // 输出结束状态
        System.out.println(atm.toString());
    }
}

9.策略模式

方法是类中最重要的组成部分,一个方法的方法体由一系列语句构成,也就是说一个方法的方法体是一个算法。
在某些设计中,一个类的设计人员经常可能涉及这样的问题:
由于用户需求的变化,导致经常需要修改类中某个方法的方法体,
即需要不断地变化算法。在这样的情况下可以考虑使用策略模式。

策略模式是处理算法不同变体的一种成熟模式,策略模式通过接口或抽象类封装算法的标识,
即在接口中定义一个抽象方法,实现该接口的类将实现接口中的抽象方法。
策略模式把针对一个算法标识的一系列具体算法分别封装在不同的类中,使得各个类给出的具体算法可以相互替换。

在策略模式中,封装算法标识的接口称作策略,实现该接口的类称作具体策略。
策略模式的结构包括三种角色:
(1)策略(Strategy):
策略是一个接口,该接口定义若干个算法标识,即定义了若干个抽象方法。
(2)具体策略(ConcreteStrategy):
具体策略是实现策略接口的类。
具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体算法。
(3)上下文(Context):
上下文是依赖于策略接口的类,即上下文包含有策略声明的变量。
上下文中提供了一个方法,该方法委托策略变量调用具体策略所实现的策略接口中的方法。

以求均值为例

行为型模式--策略模式.png
package com.zy.designPattern.strategy;

/**
 * 策略接口
 */
public interface ComputeStrategy {

    /**
     * 策略抽象方法
     * @param scores
     * @return
     */
    double computeScore(double[] scores);
}
package com.zy.designPattern.strategy;

import java.util.Arrays;

/**
 * 具体策略类:
 *   计算平均值: 去头去尾
 */
public class ComputeStrategyAvgFreeHeadAndTail implements ComputeStrategy {

    @Override
    public double computeScore(double[] scores) {
        if (scores == null || scores.length < 3) {
            return 0;
        }
        double sum = 0;
        Arrays.sort(scores);
        for (int i = 1; i < scores.length - 1; i++) {
            sum += scores[i];
        }
        return sum / (scores.length - 2);
    }
}
package com.zy.designPattern.strategy;

/**
 * 具体策略类:
 *   计算平均值
 */
public class ComputeStrategyAvg implements ComputeStrategy {

    @Override
    public double computeScore(double[] scores) {
        if (scores == null) {
            return 0;
        }
        double sum = 0;
        for (int i = 0; i < scores.length; i ++) {
            sum += scores[i];
        }
        return sum / scores.length;
    }

}
package com.zy.designPattern.strategy;

/**
 * 上下文类包含策略声明的变量,此变量可用于保存具体策略的引用
 */
public class SubjectAvgScore {

    private ComputeStrategy computeStrategy;

    public void setComputeStrategy(ComputeStrategy computeStrategy) {
        this.computeStrategy = computeStrategy;
    }

    public double getAvgScore(double[] scores) {
        if (computeStrategy != null) {
            return computeStrategy.computeScore(scores);
        }
        return 0;
    }
}
package com.zy.designPattern.strategy;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

public class StrategyTest {

    public static void main(String[] args) {
        SubjectAvgScore subjectAvgScore = new SubjectAvgScore();
        ComputeStrategyAvg computeStrategyAvg = new ComputeStrategyAvg();
        double[] zhangsanScores = {1, 2, 3, 4, 8};
        Stu zhangsan = new Stu();
        zhangsan.setName("zhangsan");
        subjectAvgScore.setComputeStrategy(computeStrategyAvg);
        System.out.println(zhangsan.getName() + "---->" + subjectAvgScore.getAvgScore(zhangsanScores));
        ComputeStrategyAvgFreeHeadAndTail computeStrategyAvgFreeHeadAndTail = new ComputeStrategyAvgFreeHeadAndTail();
        double[] lisiScores = {1, 2, 3, 4, 8};
        Stu lisi = new Stu();
        lisi.setName("lisi");
        subjectAvgScore.setComputeStrategy(computeStrategyAvgFreeHeadAndTail);
        System.out.println(lisi.getName() + "----->" + subjectAvgScore.getAvgScore(lisiScores));
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class Stu {
        private String name;
        private double score;
    }

}

10.模板模式

#意图:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

#主要解决:
一些方法通用,却在每一个子类都重新写了这一方法。

#何时使用:
有一些通用的方法。

#如何解决:
将这些通用算法抽象出来。

#关键代码:
在抽象类实现,其他步骤在子类实现。

#应用实例: 
1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 
2、比如各种流程固定的操作等
3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,
比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。

#优点: 
1、封装不变部分,扩展可变部分。 
2、提取公共代码,便于维护。 
3、行为由父类控制,子类实现。

#缺点:
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

#使用场景: 
1、有多个子类共有的方法,且逻辑相同。 
2、重要的、复杂的方法,可以考虑作为模板方法。

#注意事项:
为防止恶意操作,一般模板方法都加上 final 关键词。

代码参考

package com.zy.designPattern.template;

/**
 * 模板抽象类, 模板方法是抽象方法
 */
public abstract class CookieTemplate {

    protected abstract void firstStep();

    protected abstract void secondStep();

    protected abstract void thirdStep();

    public final void cook() {
        firstStep();
        secondStep();
        thirdStep();
    }
}
package com.zy.designPattern.template;

/**
 * 基于模板的具体做饭方法
 */
public class Cooker01 extends CookieTemplate {
    @Override
    protected void firstStep() {
        System.out.println("买菜----->");
    }

    @Override
    protected void secondStep() {
        System.out.println("洗菜----->");
    }

    @Override
    protected void thirdStep() {
        System.out.println("做菜----->");
    }
}
package com.zy.designPattern.template;

public class TemplateTest {

    public static void main(String[] args) {
        Cooker01 cooker01 = new Cooker01();
        cooker01.cook();
    }
}
上一篇下一篇

猜你喜欢

热点阅读