设计模式之观察者、中介者、迭代器、访问者、备忘录、解释器模式

前言

这是设计模式的最后一章,包含了剩余的 行为型模式 中的 观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式

一、观察者模式

在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。

在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

1.1、实现方式

1.1.1、观察者模式角色

1.1.2、代码实现

案例:订阅文章的案例,当有文章更新时推送给所有的订阅者,其中订阅者就是观察者文章是否有新的推送

// 抽象主题类
public interface Subject {

    // 添加订阅者
    void attach(Observver observver);

    // 删除订阅者
    void detach(Observver observver);

    // 通知订阅者
    void notify(String msg);
}

// 抽象订阅者
public interface Observver {

    void update(String msg);
}

// 具体主题类
public class SubscriptionSubject implements Subject{

    private List weixinUserList = new ArrayList<>();

    @Override
    public void attach(Observver observver) {
        weixinUserList.add(observver);
    }

    @Override
    public void detach(Observver observver) {
        weixinUserList.remove(observver);
    }

    @Override
    public void notify(String msg) {
        // 遍历通知每一个订阅者
        for (Observver observver : weixinUserList) {
            observver.update(msg);
        }
    }
}

// 具体订阅类
public class WeiXinUser implements Observver{

    private String name;

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

    @Override
    public void update(String msg) {
        System.out.println(name + "-" + msg);
    }
}

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

        subject.attach(new WeiXinUser("孙悟空"));
        subject.attach(new WeiXinUser("猪悟能"));
        subject.attach(new WeiXinUser("沙悟净"));

        // 发布消息
        subject.notify("您订阅的文章更新了");
    }
}

1.2、观察者模式优缺点

优点

缺点

1.3、应用场景

  1. 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  2. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  3. 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
  4. 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。

二、中介者模式

在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。例如,每个人必须记住他(她)所有朋友的电话;而且,朋友中如果有人的电话修改了,他(她)必须让其他所有的朋友一起修改,这叫作“牵一发而动全身”,非常复杂。

如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。如前面所说的“每个人必须记住所有朋友电话”的问题,只要在网上建立一个每个朋友都可以访问的“通信录”就解决了。这样的例子还有很多,例如,你刚刚参加工作想租房,可以找“房屋中介”;或者,自己刚刚到一个陌生城市找工作,可以找“人才交流中心”帮忙。

在软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者;还有大家常用的 QQ 聊天程序的“中介者”是 QQ 服务器。所有这些,都可以采用“中介者模式”来实现,它将大大降低对象之间的耦合性,提高系统的灵活性。

中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

2.1、实现方式

2.1.1、中介者模式角色

2.1.2、代码实现

案例:租房例子,租客到中介那里找房子,中介有业主托管的房子

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

    public abstract void constact(String msg,Person person);
}

// 抽象同事类
public abstract class Person {

    protected String name;
    protected Mediator mediator;

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

// 具体的同事角色类(租房客)
public class Tanant extends Person{

    public Tanant(String name, Mediator mediator) {
        super(name, mediator);
    }
    // 沟通类
    public void constact(String msg) {
        mediator.constact(msg,this);
    }

    // 获取信息
    public void getMsg(String msg) {
        System.out.println("租房者" + name + "获取到的信息是:" + msg);
    }
}

// 具体同事类 房东类
public class HouseOwner extends Person{

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

    // 沟通类
    public void constact(String msg) {
        mediator.constact(msg,this);
    }

    // 获取信息
    public void getMsg(String msg) {
        System.out.println("房东" + name + "获取到的信息是:" + msg);
    }
}

// 具体中介者
public class MediatorStructure extends Mediator{

    // 房东
    private HouseOwner houseOwner;
    // 租房客
    private Tanant tanant;

    public HouseOwner getHouseOwner() {
        return houseOwner;
    }

    public void setHouseOwner(HouseOwner houseOwner) {
        this.houseOwner = houseOwner;
    }

    public Tanant getTanant() {
        return tanant;
    }

    public void setTanant(Tanant tanant) {
        this.tanant = tanant;
    }

    @Override
    public void constact(String msg, Person person) {
        // 判断如果是房东
        if(person == houseOwner) {
            houseOwner.getMsg(msg);
        }else {
            tanant.getMsg(msg);
        }
    }
}

// 测试类
public class Client {
    public static void main(String[] args) {
        // 创建中介者
        MediatorStructure structure = new MediatorStructure();

        Tanant tanant = new Tanant("小刚",structure);
        HouseOwner houseOwner = new HouseOwner("王刚", structure);

        structure.setHouseOwner(houseOwner);
        structure.setTanant(tanant);

        tanant.constact("我要租房");
        houseOwner.constact("我这有房");
    }
}

2.2、中介者模式优缺点

优缺点

优点

缺点

2.3、应用场景

三、迭代器模式

在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如 数据结构 中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”。

既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:

  1. 暴露了聚合类的内部表示,使其数据不安全;
  2. 增加了客户的负担。

“迭代器模式”能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”,如 Java 中的 Collection、List、Set、Map 等都包含了迭代器。

迭代器模式在生活中应用的比较广泛,比如:物流系统中的传送带,不管传送的是什么物品,都会被打包成一个个箱子,并且有一个统一的二维码。这样我们不需要关心箱子里是什么,在分发时只需要一个个检查发送的目的地即可。再比如,我们平时乘坐交通工具,都是统一刷卡或者刷脸进站,而不需要关心是男性还是女性、是残疾人还是正常人等信息。

迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

3.1、实现方式

3.1.1、迭代器模式角色

3.1.2、代码实现

案例:自己实现一个迭代器,来遍历结合

public class Student {
    private String name;
    private String num;

    public Student(String name, String num) {
        this.name = name;
        this.num = num;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNum() {
        return num;
    }

    public void setNum(String num) {
        this.num = num;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", num='" + num + ''' +
                '}';
    }
}

// 抽象迭代器
public interface StudentIterator{

    // 判断是否还有元素
    boolean hasNext();

    // 获取下一个元素
    Student next();
}

// 具体迭代器
public class StudentIteratorImpl implements StudentIterator{

    private List list;
    private int position = 0;

    public StudentIteratorImpl(List list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        return position < list.size();
    }

    @Override
    public Student next() {
        // 从集合中获取自指定位置数据
        Student student = list.get(position);
        position++;
        return student;
    }
}

// 抽象聚合角色接口
public interface StudentAggregate {
    // 添加学生
    void addStudent(Student student);

    // 删除学生
    void removeStudent(Student student);

    // 获取迭代对象功能
    StudentIterator getStudentIterator();
}

public class StudentAggregateImpl implements StudentAggregate{

    private List list = new ArrayList<>();

    @Override
    public void addStudent(Student student) {
        list.add(student);
    }

    @Override
    public void removeStudent(Student student) {
        list.remove(student);
    }

    @Override
    public StudentIterator getStudentIterator() {
        return new StudentIteratorImpl(list);
    }
}

public class Client {
    public static void main(String[] args) {
        // 创建聚合对象
        StudentAggregateImpl aggregate = new StudentAggregateImpl();

        // 添加元素
        aggregate.addStudent(new Student("张三","001"));
        aggregate.addStudent(new Student("李四","002"));
        aggregate.addStudent(new Student("王五","003"));
        aggregate.addStudent(new Student("赵六","004"));
        aggregate.addStudent(new Student("武七","005"));

        // 调用迭代器对象
        StudentIterator iterator = aggregate.getStudentIterator();
        while (iterator.hasNext()) {
            Student next = iterator.next();
            System.out.println(next);
        }
    }
}

3.2、迭代器模式优缺点

优点

缺点

3.3、应用场景

四、访问者模式

在现实生活中,有些集合对象存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式。例如,公园中存在多个景点,也存在多个游客,不同的游客对同一个景点的评价可能不同;医院医生开的处方单中包含多种药元素,査看它的划价员和药房工作人员对它的处理方式也不同,划价员根据处方单上面的药品名和数量进行划价,药房工作人员根据处方单的内容进行抓药。

这样的例子还有很多,例如,电影或电视剧中的人物角色,不同的观众对他们的评价也不同;还有顾客在商场购物时放在“购物车”中的商品,顾客主要关心所选商品的性价比,而收银员关心的是商品的价格和数量。

这些被处理的数据元素相对稳定而访问方式多种多样的 数据结构 ,如果用“访问者模式”来处理比较方便。访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性。

访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

4.1、实现方式

4.1.1、访问者模式角色

4.1.2、代码实现

案例:通过主人给宠物喂食

public interface Person {

    // 喂猫
    void feed(Cat cat);
    // 喂狗
    void feed(Dog dog);

}

// 抽象元素角色
public interface Animal {

    // 接受访问者访问
    void accept(Person person);
}

public class Cat implements Animal{

    @Override
    public void accept(Person person) {
        person.feed(this);
        System.out.println("好好吃,喵喵喵!");
    }
}

public class Dog implements Animal{
    @Override
    public void accept(Person person) {
        // 喂食
        person.feed(this);
        System.out.println("好好吃,汪汪汪!");
    }
}

public class Owner implements Person{
    @Override
    public void feed(Cat cat) {
        System.out.println("主任喂猫");
    }

    @Override
    public void feed(Dog dog) {
        System.out.println("主任喂狗");
    }
}

public class Someone implements Person{
    @Override
    public void feed(Cat cat) {
        System.out.println("别人喂猫");
    }

    @Override
    public void feed(Dog dog) {
        System.out.println("别人喂狗");
    }
}

public class Home {

    private List nodeList = new ArrayList<>();

    // 添加元素功能
    public void add(Animal animal) {
        nodeList.add(animal);
    }

    public void action(Person person) {
        for (Animal animal : nodeList) {
            animal.accept(person);
        }
    }
}

public class Client {
    public static void main(String[] args) {
        // 创建Home对象
        Home home = new Home();

        // 创建被访问者
        Dog dog = new Dog();
        Cat cat = new Cat();

        home.add(dog);
        home.add(cat);

        // 创建主任喂食
        Owner owner = new Owner();
        home.action(owner);
    }
}

4.2、访问者模式优缺点

优点

缺点

4.3、应用场景

五、备忘录模式

每个人都有犯错误的时候,都希望有种“后悔药”能弥补自己的过失,让自己重新开始,但现实是残酷的。在计算机应用中,客户同样会常常犯错误,能否提供“后悔药”给他们呢?当然是可以的,而且是有必要的。这个功能由“备忘录模式”来实现。

其实很多应用软件都提供了这项功能,如 Word、记事本、Photoshop、Eclipse 等软件在编辑时按 Ctrl+Z 组合键时能撤销当前操作,使文档恢复到之前的状态;还有在 IE 中的后退键、数据库事务管理中的回滚操作、玩游戏时的中间结果存档功能、数据库与操作系统的备份操作、棋类游戏中的悔棋功能等都属于这类。

备忘录模式能记录一个对象的内部状态,当用户后悔时能撤销当前操作,使数据恢复到它原先的状态。

备忘录(Memento)模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

5.1、实现方式

5.1.1、备忘录模式角色

5.1.2、白箱备忘录

public class GameRole {

    // 生命力
    private int vit;
    // 攻击力
    private int atk;
    // 防御力
    private int def;

    // 初始化内部状态
    public void initState() {
        this.vit = 100;
        this.atk = 100;
        this.def = 100;
    }

    // 战斗
    public void fight() {
        this.vit = 0;
        this.atk = 0;
        this.def = 0;
    }

    // 保存角色状态
    public RoleStateMemento saveState() {
        return new RoleStateMemento(vit,atk,def);
    }

    // 恢复角色信息
    public void recoverState(RoleStateMemento roleStateMemento) {
        // 将备忘录中的数据赋值给当前对象
        this.vit = roleStateMemento.getVit();
        this.atk = roleStateMemento.getAtk();
        this.def = roleStateMemento.getDef();
    }

    // 展示状态功能
    public void stateDisplay() {
        System.out.println("角色生命力:" + vit);
        System.out.println("角色攻击力:" + atk);
        System.out.println("角色防御力:" + def);
    }

    public int getVit() {
        return vit;
    }

    public void setVit(int vit) {
        this.vit = vit;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }
}

// 备忘录角色类
public class RoleStateMemento {

    // 生命力
    private int vit;
    // 攻击力
    private int atk;
    // 防御力
    private int def;

    public RoleStateMemento() {
    }

    public RoleStateMemento(int vit, int atk, int def) {
        this.vit = vit;
        this.atk = atk;
        this.def = def;
    }



    public int getVit() {
        return vit;
    }

    public void setVit(int vit) {
        this.vit = vit;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }
}

// 备忘录管理对象
public class RoleStateCaretaker {

    private RoleStateMemento roleStateMemento;

    public RoleStateMemento getRoleStateMemento() {
        return roleStateMemento;
    }

    public void setRoleStateMemento(RoleStateMemento roleStateMemento) {
        this.roleStateMemento = roleStateMemento;
    }
}

public class Client {
    public static void main(String[] args) {
        System.out.println("-------------------大战BOSS之前-------------------");
        // 创建
        GameRole gameRole = new GameRole();
        gameRole.initState();
        gameRole.stateDisplay();
        // 将游戏角色进行备份
        RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
        roleStateCaretaker.setRoleStateMemento(gameRole.saveState());

        System.out.println("-------------------大战BOSS之后-------------------");
        // 损耗严重
        gameRole.fight();
        gameRole.stateDisplay();

        System.out.println("-------------------恢复之前的状态-------------------");
        gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
        gameRole.stateDisplay();
    }
}

5.1.3、黑箱备忘录

将备忘录类当做成员内部类定义在角色类中

public class GameRole {

    // 生命力
    private int vit;
    // 攻击力
    private int atk;
    // 防御力
    private int def;

    // 初始化内部状态
    public void initState() {
        this.vit = 100;
        this.atk = 100;
        this.def = 100;
    }

    // 战斗
    public void fight() {
        this.vit = 0;
        this.atk = 0;
        this.def = 0;
    }

    // 保存角色状态
    public Memento saveState() {
        return new RoleStateMemento(vit,atk,def);
    }

    // 恢复角色信息
    public void recoverState(Memento memento) {
        RoleStateMemento roleStateMemento = (RoleStateMemento)memento;
        // 将备忘录中的数据赋值给当前对象
        this.vit = roleStateMemento.getVit();
        this.atk = roleStateMemento.getAtk();
        this.def = roleStateMemento.getDef();
    }

    // 展示状态功能
    public void stateDisplay() {
        System.out.println("角色生命力:" + vit);
        System.out.println("角色攻击力:" + atk);
        System.out.println("角色防御力:" + def);
    }

    public int getVit() {
        return vit;
    }

    public void setVit(int vit) {
        this.vit = vit;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }
	
    // 私有成员内部类
    private class RoleStateMemento implements Memento {
        // 生命力
        private int vit;
        // 攻击力
        private int atk;
        // 防御力
        private int def;

        public RoleStateMemento(int vit, int atk, int def) {
            this.vit = vit;
            this.atk = atk;
            this.def = def;
        }

        public RoleStateMemento() {
        }

        public int getVit() {
            return vit;
        }

        public void setVit(int vit) {
            this.vit = vit;
        }

        public int getAtk() {
            return atk;
        }

        public void setAtk(int atk) {
            this.atk = atk;
        }

        public int getDef() {
            return def;
        }

        public void setDef(int def) {
            this.def = def;
        }
    }
}

// 备忘录接口
public interface Memento {
}


// 备忘录管理对象
public class RoleStateCaretaker {

    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

public class Client {
    public static void main(String[] args) {
        System.out.println("-------------------大战BOSS之前-------------------");
        // 创建
        GameRole gameRole = new GameRole();
        gameRole.initState();
        gameRole.stateDisplay();
        // 将游戏角色进行备份
        RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
        roleStateCaretaker.setMemento(gameRole.saveState());

        System.out.println("-------------------大战BOSS之后-------------------");
        // 损耗严重
        gameRole.fight();
        gameRole.stateDisplay();

        System.out.println("-------------------恢复之前的状态-------------------");
        gameRole.recoverState(roleStateCaretaker.getMemento());
        gameRole.stateDisplay();
    }
}

5.2、备忘录模式优缺点

优点

缺点

5.3、应用场景

六、解释器模式

在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子,这样就可以用“编译原理”中的解释器模式来实现了。

虽然使用解释器模式的实例不是很多,但对于满足以上特点,且对运行效率要求不是很高的应用实例,如果用解释器模式来实现,其效果是非常好的,本文将介绍其工作原理与使用方法。

解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。

6.1、实现方式

6.1.1、解释器模式角色

6.1.2、代码实现

案例:通过解释器模式实现一个计算器

// 抽象类表达式,通过HashMap键值对,可以获取到变量的值
public abstract class Expression {

    /**
     * 表达式:a+b-c
     * 解释公式和数值,key 就是公式(表达式),参数[a,b,c]
     * value:就是具体的值
     * HashMap:{a:10,b:20,c:30}
     * @param var
     * @return
     */
    public abstract int interpreter(HashMap var);
}

/**
 * 抽象运算符号解释器,每个运算符号都只和自己左右两个数字有关系
 * 但左右两个数字有可能也是一个解析的结果,无论哪种类型,都是Expression类的实现类
 * @Date 2022/1/25 20:27
 * @Author 不夜
 */
public class SymbolExpression extends Expression{

    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    // 因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
    @Override
    public int interpreter(HashMap var) {
        return 0;
    }
}

// 变量解析器
public class VarExpression extends Expression{

    // key:a,b,c
    private String key;

    public VarExpression(String key) {
        this.key = key;
    }

    /**
     * var:就是HashMap
     * 根据变量名称,返回对应的值
     * @param var
     * @return
     */
    @Override
    public int interpreter(HashMap var) {
        return var.get(this.key);
    }
}

public class SubExpression extends SymbolExpression{

    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(HashMap var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
}

// 加法解释器
public class AddExpression extends SymbolExpression{

    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }

    /**
     * 处理相加
     * @param var
     * @return
     */
    @Override
    public int interpreter(HashMap var) {

        return super.left.interpreter(var) + super.right.interpreter(var);
    }
}

public class Calculator {

    // 定义表达式
    private Expression expression;

    /**
     *
     * @param expStr: a+b
     */
    public Calculator(String expStr) {
        // 安排运算先后顺序
        Stack stack = new Stack<>();
        // 表达式拆分成字符数组 [a,+,b]
        char[] charArray = expStr.toCharArray();

        Expression left = null;
        Expression right = null;
        // 遍历我们的字符数组,即遍历[a,+,b]
        // 针对不同情况,做处理
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
                // 如果是 + 号,就从栈中取出left  a
                // 再将,right取出  b
                case '+':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new AddExpression(left,right));
                    break;
                case '-':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left,right));
                    break;
                default:
                    // 如果是一个Var 就创建一个 VarExpression 对象,并push到stack中
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
                    break;
            }
        }
        // 遍历完整个 charArray后,stack 就得到最后Expression
        this.expression = stack.pop();
    }

    public int run(HashMap var) {
        // 最后将不表达式 a+b和var 合并
        // 然后传递给expression的interpreter解释执行
        return this.expression.interpreter(var);
    }
}

public class Client {
    public static void main(String[] args) throws IOException {
        String expStr = getExpStr();
        HashMap var = getValue(expStr);
        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
    }

    private static HashMap getValue(String expStr) throws IOException {

        HashMap map = new HashMap<>();
        for (char ch : expStr.toCharArray()) {
            if(ch != '+' && ch != '-') {
                if(!map.containsKey(String.valueOf(ch))) {
                    System.out.println("请输入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch),Integer.valueOf(in));
                }
            }
        }
        return map;

    }

    private static String getExpStr() throws IOException {
        System.out.println("请输入表达式");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }

}

6.2、解释器模式优缺点

优缺点

优点

缺点

6.3、应用场景

七、设计模式总结

分类

设计模式

简述

一句话归纳

目的

生活案例

创建型设计模式 (简单来说就是用来创建对象的)

工厂模式(Factory Pattern)

不同条件下创建不同实例

产品标准化,生产更高效

封装创建细节

实体工厂

单例模式(Singleton Pattern)

保证一个类仅有一个实例,并且提供一个全局访问点

世上只有一个我

保证独一无二

CEO


原型模式(Prototype Pattern)

通过拷贝原型创建新的对象

拔一根猴毛,吹出千万个

高效创建对象

克隆


建造者模式(Builder Pattern)

用来创建复杂的复合对象

高配中配和低配,想选哪配就哪配

开放个性配置步骤

选配


结构型设计模式 (关注类和对象的组合)

代理模式(Proxy Pattern)

为其他对象提供一种代理以控制对这个对象的访问

没有资源没时间,得找别人来帮忙

增强职责

媒婆

外观模式(Facade Pattern)

对外提供一个统一的接口用来访问子系统

打开一扇门,通向全世界

统一访问入口

前台


装饰器模式(Decorator Pattern)

为对象添加新功能

他大舅他二舅都是他舅

灵活扩展、同宗同源

煎饼


享元模式(Flyweight Pattern)

使用对象池来减少重复对象的创建

优化资源配置,减少重复浪费

共享资源池

全国社保联网


组合模式(Composite Pattern)

将整体与局部(树形结构)进行递归组合,让客户端能够以一种的方式对其进行处理

人在一起叫团伙,心在一起叫团队

统一整体和个体

组织架构树


适配器模式(Adapter Pattern)

将原来不兼容的两个类融合在一起

万能充电器

兼容转换

电源适配


桥接模式(Bridge Pattern)

将两个能够独立变化的部分分离开来

约定优于配置

不允许用继承


行为型设计模式 (关注对象之间的通信)

模板模式(Template Pattern)

定义一套流程模板,根据需要实现模板中的操作

流程全部标准化,需要微调请覆盖

逻辑复用

把大象装进冰箱

策略模式(Strategy Pattern)

封装不同的算法,算法之间能互相替换

条条大道通罗马,具体哪条你来定

把选择权交给用户

选择支付方式


责任链模式(Chain of Responsibility Pattern)

拦截的类都实现统一接口,每个接收者都包含对下一个接收者的引用。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

各人自扫门前雪,莫管他们瓦上霜

解耦处理逻辑

踢皮球


迭代器模式(Iterator Pattern)

提供一种方法顺序访问一个聚合对象中的各个元素

流水线上坐一天,每个包裹扫一遍

统一对集合的访问方式

逐个检票进站


命令模式(Command Pattern)

将请求封装成命令,并记录下来,能够撤销与重做

运筹帷幄之中,决胜千里之外

解耦请求和处理

遥控器


状态模式(State Pattern)

根据不同的状态做出不同的行为

状态驱动行为,行为决定状态

绑定状态和行为

订单状态跟踪


备忘录模式(Memento Pattern)

保存对象的状态,在需要时进行恢复

失足不成千古恨,想重来时就重来

备份、后悔机制

草稿箱


中介者模式(Mediator Pattern)

将对象之间的通信关联关系封装到一个中介类中单独处理,从而使其耦合松散

联系方式我给你,怎么搞定我不管

统一管理网状资源

朋友圈


解释器模式(Interpreter Pattern)

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

我想说”方言“,一切解释权都归我

实现特定语法解析

摩斯密码


观察者模式(Observer Pattern)

状态发生改变时通知观察者,一对多的关系

到点就通知我

解耦观察者与被观察者

闹钟


访问者模式(Visitor Pattern)

稳定数据结构,定义新的操作行为

横看成岭侧成峰,远近高低各不同

解耦数据结构和数据操作

KPI考核



委派模式(Delegate Pattern)

允许对象组合实现与继承相同的代码重用,负责任务的调用和分配

这个需求很简单,怎么实现我不管

只对结果负责

授权委托书


总结

通过7篇文章,通过理论加代码介绍了7种设计原则 和 23种设计模式,希望可以对读者朋友有所帮助,那么设计模式这块就暂时结束,但我们的编码和学习之路还远没有结束。您如果有任何问题可以评论区留言沟通。

展开阅读全文

页面更新:2024-04-26

标签:观察者   备忘录   访问者   模式   遍历   抽象   元素   角色   对象   中介   状态

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top