一、概念
软件工程中,设计模式是指软件设计问题的推荐方案。
设计模式一般是描述如何组织代码和使用最佳实践来解决常见的设计问题。
设计模式是高层次的方案,与具体实现细节无关(如算法,数据结构,网页等)。
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
设计模式可以提高代码的可重用性和可读性,增强系统的可靠性和可维护性,解决一系列的复杂问题,提高协作效率。
二、设计模式分类
image.png经典的《设计模式》一书归纳出23种设计模式。
这23种模式又可归为,创建型、结构型和行为型3大类。
1.创建型模式
提供实例化的方法,为适合的状况提供相应的对象创建方法。
社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象的使用分开也就成为了必然趋势。
因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。
这里有5个具体的创建型模式,它们分别是:
工厂方法模式【Factory Method】
抽象工厂模式【Abstract Factory】
创建者模式【Builder】
原型模式【Prototype】
单例模式【Singleton】
2、结构型模式
通常用来处理实体之间的关系,使得这些实体能够更好地协同工作。
在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,
因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。
这里有7个具体的结构型模式可供研究,它们分别是:
外观模式【Facade】
适配器模式【Adapter】
代理模式【Proxy】
装饰模式【Decorator】
桥接模式【Bridge】
组合模式【Composite】
享元模式【Flyweight】
3、行为型模式
用于在不同的实体间进行通信,为实体之间的通信提供更容易,更灵活的通信方法。
在对象的创建和对象的结构问题都解决了之后,就剩下对象的行为问题了。
如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高。
这里有11个具体的行为型模式,它们分别是:
模板方法模式【Template Method】
观察者模式【Observer】
状态模式【State】
策略模式【Strategy】
职责链模式【Chain of Responsibility】
命令模式【Command】
访问者模式【Visitor】
调停者模式【Mediator】
备忘录模式【Memento】
迭代器模式【Iterator】
解释器模式【Interpreter】
三、设计模式六大原则
image.png1、单一原则(Single Responsibility Principle)
一个类只负责一项职责,尽量做到类只有一个行为原因引起变化;
业务对象(BO business object)、业务逻辑(BL business logic)拆分
2.里氏替换原则(LSP liskov substitution principle)
子类可以扩展父类的功能,但不能改变原有父类的功能;
目的:增强程序的健壮性。实际项目中,每个子类对应不同的业务含义,使父类作为参数,传递不同的子类完成不同的业务逻辑。
3.依赖倒置原则(dependence inversion principle)
面向接口编程;(通过接口作为参数实现应用场景)
依赖于抽象而不依赖于具体。
抽象就是接口或者抽象类,细节就是实现类
依赖倒置原则定义:
上层模块不应该依赖下层模块,两者应依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象接口负责定义public属性和方法,并且申明与其他对象依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑。
4.接口隔离(interface segregation principle)
建立单一接口;(扩展为类也是一种接口,一切皆接口)。
使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度。
降低依赖,降低耦合。
定义:
客户端不应该依赖它不需要的接口;类之间依赖关系应该建立在最小的接口上;
5.迪米特原则(law of demeter LOD)
最少知道原则,尽量降低类与类之间的耦合;
一个对象应该对其他对象有最少的了解,即一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6.开闭原则(open closed principle)
用抽象构建架构,用实现扩展原则;
开闭原则就是说对扩展开放,对修改关闭。
一个软件实体通过扩展来实现变化,而不是通过修改原来的代码来实现变化。实现一个热插拔的效果。
开闭原则是对软件实体的未来事件而制定的对现行开发设计进行约束的一个原则。
四、创建型模式实现
1、工厂模式【Factory Method】
意图:工厂模式包涵一个超类。这个超类提供一个抽象化的接口来创建一个特定类型的对象,而不是决定哪个对象可以被创建。
为了实现此方法,需要创建一个工厂类创建并返回。
当程序运行输入一个“类型”的时候,需要创建于此相应的对象。这就用到了工厂模式。在如此情形中,实现代码基于工厂模式,可以达到可扩展,可维护的代码。
当增加一个新的类型,不再需要修改已存在的类,只增加能够产生新类型的子类。
适用性:当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
当需要创建的对象不多且不会频繁增加时【可枚举的对象】
比如:a.多种数据库(MySQL/MongoDB)的实例
b.多种格式文件的解析器(XML/JSON)
c.根据不同环境加载配置文件【当输入“development”,则加载开发环境的配置文件,而输入“production”,则加载生产环境下的配置文件。】
2、抽象工厂模式【Abstract Factory】
抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。
3、创建者模式【Builder】
意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性:当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造的对象有不同的表示时。
当需要创建不同的产品时,只需要派生一个具体的创建者,重写相应的组件构建方法即可。
4、原型模式【Prototype】
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性:当我们已有一个对象,并希望创建该对象的一个完整副本时,原型模式就派上用场了。在我们知道对象的某些部分会被变更但又希望保持原有对象不变之时,通常需要对象的一个副本。在这样的案例中,重新创建原有对象是没有意义的。另一个案例是,当我们想复制一个复杂对象时,使用原型模式会很方便。对于复制复杂对象,我们可以将对象当作是从数据库中获取的,并引用其他一些也是从数据库中获取的对象。若通过多次重复查询数据来创建一个对象,则要做很多工作。在这种场景下使用原型模式要方便得多。
当要实例化的类是在运行时刻指定时,
通过动态装载;
或者为了避免创建一个与产品类层次平行的工厂类层次时;或者当一个类的实例只能有几个不同状态组合中的一种时。
建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
5、单例模式【Singleton】
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
某个服务器程序的配置信息存放在一个文件中,客户端通过一个AppConfig的类来读取配置文件的信息。
如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建AppConfig对象的实例。
这就导致系统中存在多个APPConfig的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。
事实上,类似APPConfig这样的类,我们希望在程序运行期间只存在一个实例对象。
五、结构型模式实现
其主要用来处理一个系统中不同实体(比如类和对象)之间关系,关注的是提供一种简单的对象组合方式来创造新的功能。
1、外观模式【Facade】
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
本质上,外观(Facade)是在已有的复杂系统之上实现的一个抽象层。
下图演示了外观的角色。从图中展示的类可知,仅Computer类需要暴露给客户端代码。客户端仅执行Computer的start()方法。所有其他复杂部件都由外观类Computer来维护。
image.png使用外观模式的最常见理由是为一个复杂系统提供单个简单的入口点。引入外观之后,客户端代码通过简单地调用一个方法/函数就能使用一个系统。同时内部系统并不会丢失任何功能,外观只是封装了内部系统。
当你致电一个银行或公司,通常是先被连线到客服部门,客服职员在你和业务部门及帮你解决具体问题的职员之间充当一个外观的角色。
也可以将汽车或摩托车的启动钥匙视为一个外观。外观是激活一个系统的便捷方式,系统的内部则非常复杂。
2、适配器模式【Adapter】
所谓适配器模式是指一种接口适配技术,它可通过某个类来使用另一个接口与之不兼容的类,运用此模式,两个类的接口都无需改动。
适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,比如在需要对早期代码复用一些功能等应用上很有实际价值。
适配器模式(Adapter Pattern):将一个类的接口转换成为客户希望的另外一个接口.Adapter Pattern使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.应用场景:系统数据和行为都正确,但接口不符合时,目的是使控制范围之外的一个原有对象与某个接口匹配,适配器模式主要应用于希望复用一些现存的类,但接口又与复用环境不一致的情况
image.png如果你有一部智能手机或者一台平板电脑,在想把它(比如,iPhone手机的闪电接口)连接到你的电脑时,就需要使用一个USB适配器。如果你从大多数欧洲国家到英国旅行,在为你的笔记本电脑充电时,就需要使用一个插头适配器。
什么时候使用Adapter模式
很多时候,我们并非从0开始编程,特别是当现有的类已经被充分测试过了,Bug很少,而且已经被用于其他软件之中时,我们更愿意将这些类作为组件重复利用。
Adapter模式会对现有的类进行适配,生成新的类。通过该模式可以很方便地创建我们需要的方法群。
3、代理模式【Proxy】
为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。
在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),
直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
image.png想在访问一个类时做一些控制。
即增加中间层实现与被代理类组合。
1、Windows 里面的快捷方式。
2、买火车票不一定在火车站买,也可以去代售点。
3、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
优点:
1、职责清晰。
2、高扩展性。
3、智能化。
缺点:
1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:
按职责来划分,通常有以下使用场景:
1、远程代理。
2、虚拟代理。
3、Copy-on-Write 代理。
4、保护(Protect or Access)代理。
5、Cache代理。
6、防火墙(Firewall)代理。
7、同步化(Synchronization)代理。
8、智能引用(Smart Reference)代理。
4、装饰模式【Decorator】
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
把每个要装饰的功能放在单独的类中,用这个类去包装所要装饰的对象,因此客户端可以有选择地、按顺序的使用装饰功能包装对象。
5、桥接模式【Bridge】
将抽象部分与实现部分分离,使它们都可以独立的变化。
桥接模式的核心意图就是把类的实现独立出来,让他们各自变化。这样使每种实现的变化不会影响其他实现,从而达到应对变化的目的
1.如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
2.设计要求实例化角色的任何改变不应当影响客户端,或者说实例化角色的改变对客户端是完全透明的。
3.一个构件有多于一个的抽象化角色和实例化角色,系统需要它们之间进行动态耦合。
4.虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
就拿汽车在路上行驶的来说。即有小汽车又有公共汽车,它们都能在市区中的公路上行驶,也能在高速公路上行驶。这你会发现,对于交通工具(汽车)有不同的类型,然而它们所行驶的环境(路)也在变化,在软件系统中就要适应两个方面的变化?怎样实现才能应对这种变化呢?概述:在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用Bridge模式。
6、组合模式【Composite】
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。
Composite模式采用 树性结构 来实现普遍存在的对象容器,从而将一对多的关系转化为一对一的关系,使得客户代码可以一致地(复用)处理对象和对象容器, 无需关心处理的是单个的对象,还是组合的对象容器。
客户代码与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能”应对变化“。
Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技术来改善效率。
image.png在需要体现部分与整体层次的结构时;
希望用户忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象时。
会员卡消费,首先:
1.我们的部件有,总店,分店,加盟店!
2.我们的部件共有的行为是:刷会员卡
3.部件之间的层次关系,也就是店面的层次关系是,总店下有分店、分店下可以拥有加盟店。
有了我们这几个必要条件后,要求就是目前店面搞活动当我在总店刷卡后,就可以累积相当于在所有下级店面刷卡的积分总额。
7、享元模式【Flyweight】
运用共享技术有效地支持大量细粒度的对象。
享元旨在优化性能和内存使用。内部状态:享元对象中不会随环境改变而改变的共享部分。比如围棋棋子的颜色。外部状态:随环境改变而改变、不可以共享的状态就是外部状态。比如围棋棋子的位置。
程序中使用了大量的对象,造成很大的存储开销。
如果删除对象的外部状态,可以用相对较少的共享对象取代很多组对象,就可以考虑使用享元模式。
假设我们正在设计一个性能关键的游戏,例如第一人称射击(First-Person Shooter,FPS)游戏。在FPS游戏中,玩家(士兵)共享一些状态,如外在表现和行为。例如,在《反恐精英》游戏中,同一团队(反恐精英或恐怖分子)的所有士兵看起来都是一样的(外在表现)。同一个游戏中,(两个团队的)所有士兵都有一些共同的动作,比如,跳起、低头等(行为)。这意味着我们可以创建一个享元来包含所有共同的数据。当然,士兵也有许多因人而异的可变数据,这些数据不是享元的一部分,比如,枪支、健康状况和地理位置等。
六、行为型模式实现
用于在不同的实体间进行通信,为实体之间的通信提供更容易,更灵活的通信方法。
1、模板方法模式【Template Method】
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
在多个算法或框架具有类似或相同的逻辑的时候,可以使用模板方法模式,以实现代码重用。
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。
最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
控制子类扩展。模板方法只在特定点调用“hook ”操作),这样就只允许在这些点进行扩展
某超类的子类中有公有的方法,并且逻辑基本相同,可以使用模板模式。必要时可以使用钩子方法约束其行为。
2、观察者模式【Observer】
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。[典型的发布订阅]
当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
市里新修了一个图书馆,现在招募一个图书管理员叫T,T知道图书馆里的图书更新和借阅等信息。现在有三个同学甲乙丙想去了解以后几个月的图书馆图书信息和借阅信息,于是它们去T那里注册登记。当图书馆图书更新后,T就给注册了的同学发送图书更新信息。三个月后,丙不需要知道图书更新信息了,于是就去T那儿注销了它的信息。所以,以后,只有甲乙会收到消息。几个月后,丁也去图书馆注册了信息,所以以后甲乙丁会收到图书更新信息。
3、状态模式【State】
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
在目前主流的RPG(Role Play Game,角色扮演游戏)中,使用状态模式可以对游戏角色进行控制,游戏角色的升级伴随着其状态的变化和行为的变化。对于游戏程序本身也可以通过状态模式进行总控,一个游戏活动包括开始、运行、结束等状态,通过对状态的控制可以控制系统的行为,决定游戏的各个方面,因此可以使用状态模式对整个游戏的架构进行设计与实现。
4、策略模式【Strategy】
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护的问题。
许多相关的类仅仅是行为有异。
“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句
商场搞活动,可以按正常收费、打折收费、返利收费等支付方式,通过 一个具体的策略类来控制支付方式(也就是不同的算法)
5、职责链模式【Chain of Responsibility】
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
image.png适用性:
有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
可处理一个请求的对象集合应被动态指定。
比如费用报销找上级领导审批,不同的级别可以审批不同的金额。这时候就可以使用职责链模式。
6、命令模式【Command】
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
1、命令设计模式帮助我们将一个操作(撤销、重做、复制、粘贴等)封装成一个对象。简而言之,这意味着创建一个类,包含实现该操作所需要的所有逻辑和方法。这样做的优势如下所述。
2、我们并不需要直接执行一个命令。命令可以按照希望执行。调用命令的对象与指导如何执行命令的对象解耦。调用者无需知道命令的任何实现细节。如果有意义,可以把多个命令组织起来,这样调用者能够按顺序执行它们。例如,在实现一个多层撤销命令时,这是很有用的。
3、事务型行为和日志记录:事务型行为和日志记录对于为变更记录一份持久化日志是很重要的。操作系统用它来从系统崩溃中恢复,关系型数据库用它来实现事务,文件系统用它来实现快照,而安装程序(向导程序)用它来恢复取消的安装。
4、宏:在这里,宏是指一个动作序列,可在任意时间点按要求进行录制和执行。流行的编辑器(比如,Emacs和Vim)都支持宏。
7、访问者模式【Visitor】
1)对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
2)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。
访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
安排不同年份的财务报表给不同的角色分析,这就是访问者模式的魅力;访问者模式的核心是在保持原有数据结构的基础上,实现多种数据的处理方法,该方法的角色就是访问者。
8、调停者模式【Mediator】
也叫中介者模式。
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解时可以使用中介者模式。
一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象时可以使用中介者模式。
想定制一个分布在多个类中的行为,而又不想生成太多的子类时可以使用中介者模式。
比如:
1、消息中间件。各个组件互相通信时,通过消息中间件来操作,不直接与组件联系。
2、电脑主板
电脑里面各个配件之间的交互,主要是通过主板来完成的。如果电脑里面没有了主板,那么各个配件之间就必须自行相互交互,以互相传送数据。而且由于各个配件的接口不同,相互之间交互时,还必须把数据接口进行转换才能匹配上。
image.png9、备忘录模式【Memento】
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
在备忘录模式中,如果要保存的状态多,可以创造一个备忘录管理者角色来管理备忘录。
适用性:必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
比如:
1、需要保存和恢复数据的相关状态场景。如保存游戏状态的场景:撤销场景,事务回滚等;
2、副本监控场景。备忘录可以当做一个临时的副本监控,实现非实时和准实时的监控。
10、迭代器模式【Iterator】
意图:提供一种方法顺序访问一个集合对象中各个元素, 而又不需暴露该对象的内部表示。
主要解决:不同的方式来遍历整个集合对象。
image.png适用性:访问一个聚合对象的内容而无需暴露它的内部表示。
支持对聚合对象的多种遍历。
为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。
比如:遍历一个集合。
11、解释器模式【Interpreter】
意图:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
image.png适用性:
当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
该文法简单。对于复杂的文法, 文法的类层次变得庞大而无法管理。此时使用语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
效率不是一个关键问题。最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
比如:对于一些固定文法构建一个解释句子的解释器。
编译器、运算表达式计算
转载于:https://baijiahao.baidu.com/s?id=1661505290125455712&wfr=spider&for=pc
网友评论