23种设计模式细解(下)

23种设计模式细解(下)

十七、状态模式(详解版)

17.1 定义

​ 对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

17.2 优点

  1. 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  2. 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  3. 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

17.3 缺点

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

17.4 模式的结构

​ 状态模式包含以下主要角色。

  1. 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
  2. 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
  3. 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

17.5 状态模式的应用场景

​ 通常在以下情况下可以考虑使用状态模式。

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

17.6 状态模式与责任链模式的区别

  1. 状态模式和责任链模式都能消除 if-else 分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用;
  2. 从定义来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象间的改变;
  3. 从代码实现上来看,两者最大的区别就是状态模式的各个状态对象知道自己要进入的下一个状态对象,而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责。

17.7 状态模式与策略模式的区别

​ 状态模式和策略模式的 UML 类图架构几乎完全一样,但两者的应用场景是不一样的。策略模式的多种算法行为择其一都能满足,彼此之间是独立的,用户可自行更换策略算法,而状态模式的各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户无法指定状态,只能设置初始状态。

十八、观察者模式(Observer模式)详解

18.1 定义

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

18.2 优点

  1. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
  2. 目标与观察者之间建立了一套触发机制。

18.3 缺点

  1. 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
  2. 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

18.4 模式结构

  1. 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
  2. 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
  3. 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  4. 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

18.5 应用场景

​ 在软件系统中,当系统一方行为依赖另一方行为的变动时,可使用观察者模式松耦合联动双方,使得一方的变动可以通知到感兴趣的另一方对象,从而让另一方对象对此做出响应。

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

18.6 模式扩展

​ 在 Java中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

  1. Observable 类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。

  2. Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。

18.7 观察者模式在spring源码中的应用

Spring 中实现的观察者模式包含三部分:Event 事件(相当于消息)、Listener 监听者(相当于观察者)、Publisher 发送者(相当于被观察者)。我们通过一个例子来看下,Spring 提供的观察者模式是怎么使用的。代码如下所示:

// Event事件
public class DemoEvent extends ApplicationEvent {
  private String message;


  public DemoEvent(Object source, String message) {
    super(source);
  }


  public String getMessage() {
    return this.message;
  }
}
// Listener监听者
@Component
public class DemoListener implements ApplicationListener<DemoEvent> {
  @Override
  public void onApplicationEvent(DemoEvent demoEvent) {
    String message = demoEvent.getMessage();
    System.out.println(message);
  }
}


// Publisher发送者
@Component
public class DemoPublisher {
  @Autowired
  private ApplicationContext applicationContext;
  public void publishEvent(DemoEvent demoEvent) {
    this.applicationContext.publishEvent(demoEvent);
  }

Spring 事件发布实现非常简单,只需要三个步骤:

​ 定义一个继承ApplicationEvent事件;
​ 定义一个实现ApplicationListener的监听器;
​ 定义一个发送者,调用ApplicationContext直接发布。
所以实现起来非常简单。

在前面将到的观察者模式,我们提到,观察者需要事先注册到被观察者,那么在Spring的实现中,观察者注册到了哪里?又是如何注册的呢?

虽然我们是使用applicationContext 发布事件,底层是使用ApplicationEventMulticaster者类来实现的,通过multicastEvent() 这个消息发送函数。

ApplicationEventMulticaster的初始化:

AnnotationConfigApplicationContext的refresh()方法

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      //1:准备刷新上下文环境
      prepareRefresh();


      //2:获取告诉子类初始化Bean工厂  不同工厂不同实现
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();


      //3:对bean工厂进行填充属性
      prepareBeanFactory(beanFactory);


      try {
         // 第四:留个子类去实现该接口
         postProcessBeanFactory(beanFactory);


         // 调用我们的bean工厂的后置处理器. 1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用
         invokeBeanFactoryPostProcessors(beanFactory);


         // 注册我们bean的后置处理器
         registerBeanPostProcessors(beanFactory);


         // 初始化国际化资源处理器.
         initMessageSource();


         // 创建事件多播器
         initApplicationEventMulticaster();


         // 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
         onRefresh();


         //把我们的事件监听器注册到多播器上
         registerListeners();


         // 实例化我们剩余的单实例bean.
         finishBeanFactoryInitialization(beanFactory);


         // 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
         finishRefresh();
      }


      catch (BeansException ex) {
      }


      finally {
  

在 initApplicationEventMulticaster()方法中初始化多播器。

protected void initApplicationEventMulticaster() {
   //获取我们的bean工厂对象
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   //判断容器中是没有有我们的applicationEventMulticaster 应用多播器组件
   if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      //直接显示的调用我们的getBean获取出来赋值给我们的applicationContext对象
      this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
   }
   //容器中没有的话
   else {
      //spring ioc显示的new 一个SimpleApplicationEventMulticaster对象保存在applicatoinContext对象中
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      //并且注入到容器中
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);


   }
}

那么什么时候注册的呢?

//把我们的事件监听器注册到多播器上
registerListeners();

注册的时候,获取实现ApplicaitonListener接口的类,然后加载到监听器集合中;

protected void registerListeners() {
   //获取容器中所有的监听器对象
   // 这个时候正常流程是不会有监听器的
   // (因为监听器不会在这之前注册,在initApplicationEventMulticaster后在registerListeners之前,只有一个可能在:在onRefresh里面注册了监听器)
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      //把监听器挨个的注册到我们的多播器上去
      getApplicationEventMulticaster().addApplicationListener(listener);
   }
   //获取bean定义中的监听器对象
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   //把监听器的名称注册到我们的多播器上
   for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }
   //在这里获取我们的早期事件
   Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
   // 在这里赋null。  也就是值此之后都将没有早期事件了
   this.earlyApplicationEvents = null;
   if (earlyEventsToProcess != null) {
      //通过多播器进行播发早期事件
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
         getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}

​ 事件的发布:

​ 通过 SimpleApplicationEventMulticaster 的 multicastEvent方法发布,发布的时候是可以支持同步或者异步。

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   //从多播器中获取出所有的监听器
   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      //判断多播器中是否支持异步多播的
      Executor executor = getTaskExecutor();
      if (executor != null) {
         //异步播发事件
         executor.execute(() -> invokeListener(listener, event));
      }
      else {//同步播发
         invokeListener(listener, event);
      }
   }
}

​ 借助 Spring 提供的观察者模式的骨架代码,如果我们要在 Spring 下实现某个事件的发送和监听,只需要做很少的工作,定义事件、定义监听器、往 ApplicationContext 中发送事件就可以了,剩下的工作都由 Spring 框架来完成。实际上,这也体现了 Spring 框架的扩展性,也就是在不需要修改任何代码的情况下,扩展新的事件和监听。

十九、中介者模式(详解版)

19.1 定义

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

19.2 优点

  1. 类之间各司其职,符合迪米特法则。
  2. 降低了对象之间的耦合性,使得对象易于独立地被复用。
  3. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

19.3 缺点

​ 中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。

19.4 模式结构

  1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
  2. 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
  3. 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
  4. 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

19.5 应用场景

  • 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
  • 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

二十、迭代器模式(详解版)

20.1 定义

​ 提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式。

20.2 优点

  1. 访问一个聚合对象的内容而无须暴露它的内部表示。
  2. 遍历任务交由迭代器完成,这简化了聚合类。
  3. 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
  4. 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
  5. 封装性良好,为遍历不同的聚合结构提供一个统一的接口。

20.3 缺点

​ 增加了类的个数,这在一定程度上增加了系统的复杂性。

20.4 模式结构

  1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

20.5 应用场景

  1. 当需要为聚合对象提供多种遍历方式时。
  2. 当需要为遍历不同的聚合结构提供一个统一的接口时。
  3. 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。

20.6 迭代器模式在JDK源码中的应用

​ 在JDK中最能体现迭代器模式的地方莫过于JDK中的容器类了,首先有一个Iterator接口,该接口包含了迭代过程中需要用到的几个方法,最重要的两个方法是hasNext()和next();简化之后的代码如下所示:

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

​ 以ArrayList为例,看一下它的继承层次:ArrayList实现了List接口,List接口继承了Collection接口,Collection接口继承了Iterable接口,Iterable接口中包含了一个iterator()方法,因此到ArrayList中就需要实现该iterator方法,该方法的实现很简单,就是返回一个实现了Iterator接口的迭代器实例。ArrayList中迭代器的实现采用的是内部类的形式,由于内部类可以直接访问外部类的成员变量,所以该迭代器内部类可以很方便地实现Iterator接口中的hasNext()和next()方法,ArrayList中的迭代器内部类名字是Itr,简化代码如下所示:

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    public boolean hasNext() {
        return cursor != size;
    }
    public E next() {
        Object[] elementData = ArrayList.this.elementData;
        return (E) elementData[cursor++];
    }
}

​ 这是ArrayList中的一个内部类,该类实现了Iterator接口,在实现hasNext()和next()方法的时候可以很方便地访问ArrayList的成员变量size和elementData数组。

​ ArrayList还有一个public成员方法iterator()该方法就可以直接返回一个该内部类迭代器的实例,其代码如下

    public Iterator<E> iterator() {
        return new Itr();
    }

​ 得到迭代器实例之后就可以用该迭代器实例对集合进行迭代。

​ 迭代器模式让我们在遍历集合元素的时候无需了解集合内部的实现形式,比如说有另外一种集合类LinkedList,它的底层是用链表实现的,只要它也同样提供一个返回实现了Iterator接口的迭代器实例的方法,我们就可以用统一的方式对集合进行遍历。

二十一、访问者模式

21.1 定义

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

21.2 优点

  1. 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  2. 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
  3. 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
  4. 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

21.3 缺点

1.增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”;

2.破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性;

3.违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

21.4 模式结构实现

​ 访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类。

1.抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
2.具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
3.抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
4.具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
5.对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

21.5 应用场景

​ 当系统中存在类型数量稳定(固定)的一类数据结构时,可以使用访问者模式方便地实现对该类型所有数据结构的不同操作,而又不会对数据产生任何副作用(脏数据)。

​ 简而言之,就是当对集合中的不同类型数据(类型数量稳定)进行多种操作时,使用访问者模式。

​ 通常在以下情况可以考虑使用访问者(Visitor)模式。
​ 1.对象结构相对稳定,但其操作算法经常变化的程序。
​ 2.对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
​ 3.对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。

21.6 访问者模式在spring源码中的应用

在 Spring IoC 中,BeanDefinition 用来存储 Spring Bean 的定义信息,比如属性值、构造方法参数或者更具体的实现。Spring 解析完配置后,会生成 BeanDefinition 并且记录下来。下次 getBean 获取 Bean 时,会通过 BeanDefinition 来实例化具体的 Bean 对象。

BeanDefinition 是一个接口,即一个抽象的定义,实际使用的是其实现类,如 ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition 等。

Spring 中的 BeanDefinitionVisitor 类主要用于访问 BeanDefinition,解析属性或者构造方法里面的占位符,并把解析结果更新到 BeanDefinition 中。这里应用的就是访问者模式。抽象元素为 BeanDefinition,具体元素有 RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition 等。

因为没有对访问者进行扩展,所以这里只有一个具体访问者 BeanDefinitionVisitor,没有再抽出一层抽象访问者。

BeanDefinitionVisitor 类的源码如下。

public class BeanDefinitionVisitor {
    @Nullable
    private StringValueResolver valueResolver;

    public BeanDefinitionVisitor(StringValueResolver valueResolver) {
        Assert.notNull(valueResolver, "StringValueResolver must not be null");
        this.valueResolver = valueResolver;
    }

    protected BeanDefinitionVisitor() {
    }

    public void visitBeanDefinition(BeanDefinition beanDefinition) {
        visitParentName(beanDefinition);
        visitBeanClassName(beanDefinition);
        visitFactoryBeanName(beanDefinition);
        visitFactoryMethodName(beanDefinition);
        visitScope(beanDefinition);         
        if (beanDefinition.hasPropertyValues()) {
             visitPropertyValues(beanDefinition.getPropertyValues());
        }
        if (beanDefinition.hasConstructorArgumentValues()) {
            ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
            visitIndexedArgumentValues(cas.getIndexedArgumentValues());
            visitGenericArgumentValues(cas.getGenericArgumentValues());
        }
    }
    protected void visitParentName(BeanDefinition beanDefinition) {
        String parentName = beanDefinition.getParentName();
        if (parentName != null) {
            String resolvedName = resolveStringValue(parentName);
            if (!parentName.equals(resolvedName)) {
                beanDefinition.setParentName(resolvedName);
            }
        }
    }
    ...
} 

以上没有使用双重分派模式,直接调用 visit 进行元素的访问。visitBeanDefinition() 方法分别实现了不同的 visit 来对相同的数据进行不同的处理。我们看到,在 visitBeanDefinition() 方法中,访问了其它数据,比如父类的名字、自己的类名、在 IoC 容器中的名称等各种信息。

二十二、备忘录模式

22.1定义

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

22.2 优点

  1. 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
  2. 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
  3. 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

22.3 缺点

​ 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

22.4 模式结构实现

  1. 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  2. 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  3. 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

由于 JDK、Spring、Mybatis中很少有备忘录模式,所以该设计模式不做典型应用源码分析。

二十三、解释器模式

23.1定义

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

23.2 优点

  1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

23.3 缺点

  1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
  2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
  3. 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

23.4 模式的结构

  1. 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  2. 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  3. 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  4. 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  5. 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值