模板方法(TemplateMethod)

模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

应用场景

例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。

ES6

  1. class AbstractClass {
  2. templateMethod() {
  3. this.specificMethod();
  4. this.abstractMethod1();
  5. this.abstractMethod2();
  6. }
  7. specificMethod() {
  8. console.log('抽象类 - 具体方法被调用力');
  9. }
  10. abstractMethod1() {
  11. }
  12. abstractMethod2() {
  13. }
  14. }
  15. class ConcreteClass extends AbstractClass {
  16. constructor() {
  17. super();
  18. }
  19. abstractMethod1() {
  20. console.log('被调用1');
  21. }
  22. abstractMethod2() {
  23. console.log('被调用2');
  24. }
  25. }
  26. const concreteClassTest = new ConcreteClass();
  27. concreteClassTest.templateMethod();

Typesript

  1. abstract class AbstractClass {
  2. templateMethod() {
  3. this.specificMethod();
  4. this.abstractMethod1();
  5. this.abstractMethod2();
  6. }
  7. specificMethod() {
  8. console.log('抽象类 - 具体方法被调用力');
  9. }
  10. abstractMethod1() {
  11. }
  12. abstractMethod2() {
  13. }
  14. }
  15. class ConcreteClass extends AbstractClass {
  16. constructor() {
  17. super();
  18. }
  19. abstractMethod1() {
  20. console.log('被调用1');
  21. }
  22. abstractMethod2() {
  23. console.log('被调用2');
  24. }
  25. }
  26. const concreteClassTest = new ConcreteClass();
  27. concreteClassTest.templateMethod();

该模式的主要优点如下。

  1. 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  2. 它在父类中提取了公共的部分代码,便于代码复用。
  3. 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

该模式的主要缺点如下。

  1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
  2. 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

    策略(Strategy)

    模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。

应用场景

当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。
如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。

ES6

  1. class ConcreteStrategyA {
  2. strategyMethod() {
  3. console.log('具体策略A')
  4. }
  5. }
  6. class ConcreteStrategyB {
  7. strategyMethod() {
  8. console.log('具体策略B')
  9. }
  10. }
  11. class Context {
  12. setStrategy(strateg) {
  13. this.strateg =strateg;
  14. }
  15. strategyMethod() {
  16. this.strateg.strategyMethod();
  17. }
  18. }
  19. const contextTest = new Context();
  20. const strategya = new ConcreteStrategyA();
  21. const strategyb = new ConcreteStrategyB();
  22. contextTest.setStrategy(strategya);
  23. contextTest.strategyMethod();
  24. contextTest.setStrategy(strategyb);
  25. contextTest.strategyMethod();

Typescript

  1. interface strategyF {
  2. strategyMethod(): void;
  3. }
  4. class ConcreteStrategyA implements strategyF{
  5. strategyMethod() {
  6. console.log('具体策略A')
  7. }
  8. }
  9. class ConcreteStrategyB implements strategyF{
  10. strategyMethod() {
  11. console.log('具体策略B')
  12. }
  13. }
  14. class Context {
  15. strateg!: strategyF;
  16. setStrategy(strateg:strategyF) {
  17. this.strateg =strateg;
  18. }
  19. strategyMethod() {
  20. this.strateg.strategyMethod();
  21. }
  22. }
  23. const contextTest = new Context();
  24. const strategya = new ConcreteStrategyA();
  25. const strategyb = new ConcreteStrategyB();
  26. contextTest.setStrategy(strategya);
  27. contextTest.strategyMethod();
  28. contextTest.setStrategy(strategyb);
  29. contextTest.strategyMethod();

策略模式的主要优点如下。

  1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
  2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

其主要缺点如下。

  1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  2. 策略模式造成很多的策略类。

    命令(Command)

    模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。

应用场景

在软件开发系统中,常常出现“方法的请求者”与“方法的实现者”之间存在紧密的耦合关系。这不利于软件功能的扩展与维护。例如,想对行为进行“撤销、重做、记录”等处理都很不方便,因此“如何将方法的请求者与方法的实现者解耦?”变得很重要,命令模式能很好地解决这个问题。

ES6

  1. class Invoker {
  2. constructor(cmd){
  3. this.cmd = cmd;
  4. }
  5. call() {
  6. console.log('请求者 - 调用者');
  7. this.cmd.execute();
  8. }
  9. }
  10. class ConcreteCommand {
  11. constructor() {
  12. this.receiver=new Receiver();
  13. }
  14. execute() {
  15. console.log('执行了命令');
  16. this.receiver.action();
  17. }
  18. }
  19. class Receiver {
  20. action(){
  21. console.log('接收者');
  22. }
  23. }
  24. const cmd = new ConcreteCommand();
  25. const invokerTest = new Invoker(cmd);
  26. invokerTest.call();

Typescirt

  1. class Invoker {
  2. cmd:ConcreteCommand
  3. constructor(cmd:ConcreteCommand){
  4. this.cmd = cmd;
  5. }
  6. call() {
  7. console.log('请求者 - 调用者');
  8. this.cmd.execute();
  9. }
  10. }
  11. interface command {
  12. execute(): void;
  13. }
  14. class ConcreteCommand implements command{
  15. receiver:Receiver
  16. constructor() {
  17. this.receiver=new Receiver();
  18. }
  19. execute() {
  20. console.log('执行了命令');
  21. this.receiver.action();
  22. }
  23. }
  24. class Receiver {
  25. action(){
  26. console.log('接收者');
  27. }
  28. }
  29. const cmd = new ConcreteCommand();
  30. const invokerTest = new Invoker(cmd);
  31. invokerTest.call();

命令模式的主要优点如下。

  1. 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
  2. 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

其缺点是:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。

职责链(Chain of Responsibility)

模式:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

应用场景

一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;

ES6

  1. class Handler {
  2. next = null;
  3. setNext(handle) {
  4. this.next = handle;
  5. }
  6. getNext() {
  7. return this.next;
  8. }
  9. handleRequest() {}
  10. }
  11. class ConcreteHandler1 extends Handler{
  12. handleRequest(info) {
  13. if(info == 'one') {
  14. console.log('负责第一个请求');
  15. } else {
  16. if(this.getNext()) {
  17. this.getNext().handleRequest(info);
  18. } else {
  19. console.log('没有处理请求的');
  20. }
  21. }
  22. }
  23. }
  24. class ConcreteHandler2 extends Handler{
  25. handleRequest(info) {
  26. if(info == 'two') {
  27. console.log('负责第二个请求');
  28. } else {
  29. if(this.getNext()) {
  30. this.getNext().handleRequest(info);
  31. } else {
  32. console.log('没有处理请求的');
  33. }
  34. }
  35. }
  36. }
  37. const concreteHandler1 = new ConcreteHandler1();
  38. const concreteHandler2 = new ConcreteHandler2();
  39. concreteHandler1.setNext(concreteHandler2);
  40. concreteHandler1.handleRequest('one');
  41. concreteHandler1.handleRequest('two');

Typescript

  1. abstract class Handler {
  2. next: any= null;
  3. setNext<T>(handle:T) {
  4. this.next = handle;
  5. }
  6. getNext() {
  7. return this.next;
  8. }
  9. abstract handleRequest(info:string):void;
  10. }
  11. class ConcreteHandler1 extends Handler{
  12. handleRequest(info:string) {
  13. if(info == 'one') {
  14. console.log('负责第一个请求');
  15. } else {
  16. if(this.getNext()) {
  17. this.getNext().handleRequest(info);
  18. } else {
  19. console.log('没有处理请求的');
  20. }
  21. }
  22. }
  23. }
  24. class ConcreteHandler2 extends Handler{
  25. handleRequest(info:string) {
  26. if(info == 'two') {
  27. console.log('负责第二个请求');
  28. } else {
  29. if(this.getNext()) {
  30. this.getNext().handleRequest(info);
  31. } else {
  32. console.log('没有处理请求的');
  33. }
  34. }
  35. }
  36. }
  37. const concreteHandler1 = new ConcreteHandler1();
  38. const concreteHandler2 = new ConcreteHandler2();
  39. concreteHandler1.setNext<ConcreteHandler2>(concreteHandler2);
  40. concreteHandler1.handleRequest('one');
  41. concreteHandler1.handleRequest('two');

其主要优点如下。

  1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

其主要缺点如下。

  1. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

    状态(State)

    模式:允许一个对象在其内部状态发生改变时改变其行为能力。

应用场景

当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。如人的情绪有高兴的时候和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。

ES6

  1. class Context{
  2. state = new ConcreteStateA();
  3. setState(obj) {
  4. this.state = obj;
  5. }
  6. handle() {
  7. this.state.handle(this);
  8. }
  9. }
  10. class ConcreteStateA {
  11. handle(context) {
  12. console.log('执行了a');
  13. context.setState(new ConcreteStateB());
  14. }
  15. }
  16. class ConcreteStateB {
  17. handle(context) {
  18. console.log('执行了b');
  19. context.setState(new ConcreteStateA());
  20. }
  21. }
  22. const context = new Context();
  23. context.handle();
  24. context.handle();
  25. context.handle();
  26. context.handle();

Typescirp

  1. abstract class ContextBase {
  2. abstract handle(): void;
  3. }
  4. class Context extends ContextBase{
  5. state:any = new ConcreteStateA();
  6. setState<T>(obj:T) {
  7. this.state = obj;
  8. }
  9. handle() {
  10. this.state.handle(this);
  11. }
  12. }
  13. class ConcreteStateA {
  14. handle(context:Context) {
  15. console.log('执行了a');
  16. context.setState<ConcreteStateB>(new ConcreteStateB());
  17. }
  18. }
  19. class ConcreteStateB {
  20. handle(context:Context) {
  21. console.log('执行了b');
  22. context.setState<ConcreteStateA>(new ConcreteStateA());
  23. }
  24. }
  25. const context = new Context();
  26. context.handle();
  27. context.handle();
  28. context.handle();
  29. context.handle();

其主要优点如下。

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

状态模式的主要缺点如下。

  1. 状态模式的使用必然会增加系统的类与对象的个数。
  2. 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混

    观察者(Observer)

    模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。

应用场景

许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;

ES6

  1. class ConcreteSubject {
  2. constructor() {
  3. this.list = [];
  4. }
  5. on(observable) {
  6. this.list.push(observable);
  7. }
  8. emit() {
  9. for(let i =0;i<this.list.length;i++) {
  10. this.list[i].response();
  11. }
  12. }
  13. }
  14. class ConcreteObserver1 {
  15. response() {
  16. console.log('ConcreteObserver1');
  17. }
  18. }
  19. class ConcreteObserver2 {
  20. response() {
  21. console.log('ConcreteObserver1');
  22. }
  23. }
  24. const concreteSubject = new ConcreteSubject();
  25. const concreteObserver1 = new ConcreteObserver1();
  26. const concreteObserver2 = new ConcreteObserver2();
  27. concreteSubject.on(concreteObserver1);
  28. concreteSubject.on(concreteObserver2);
  29. concreteSubject.emit();

Typescript

  1. interface concreteObserver {
  2. response(): void;
  3. }
  4. class ConcreteSubject {
  5. list:concreteObserver[]
  6. constructor() {
  7. this.list = [];
  8. }
  9. on(observable:concreteObserver) {
  10. this.list.push(observable);
  11. }
  12. emit() {
  13. for(let i =0;i<this.list.length;i++) {
  14. this.list[i].response();
  15. }
  16. }
  17. }
  18. class ConcreteObserver1 implements concreteObserver {
  19. response() {
  20. console.log('ConcreteObserver1');
  21. }
  22. }
  23. class ConcreteObserver2 implements concreteObserver {
  24. response() {
  25. console.log('ConcreteObserver1');
  26. }
  27. }
  28. const concreteSubject = new ConcreteSubject();
  29. const concreteObserver1 = new ConcreteObserver1();
  30. const concreteObserver2 = new ConcreteObserver2();
  31. concreteSubject.on(concreteObserver1);
  32. concreteSubject.on(concreteObserver2);
  33. concreteSubject.emit();

其主要优点如下。

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

它的主要缺点如下。

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

    中介者(Mediator)

    模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。

应用场景

在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。“每个人必须记住所有朋友电话”的问题,只要在网上建立一个每个朋友都可以访问的“通信录”就解决了。

ES6

  1. class ConcreteMediator {
  2. constructor() {
  3. this.list = [];
  4. }
  5. add(concrete) {
  6. concrete.setParent(this);
  7. this.list.push(concrete);
  8. }
  9. relay(colleague) {
  10. for(let i = 0;i < this.list.length;i ++) {
  11. if(!(this.list[i] === colleague)) {
  12. this.list[i].receive();
  13. }
  14. }
  15. }
  16. }
  17. class ConcreteColleague1 {
  18. setParent(concrete) {
  19. this.mediator = concrete;
  20. }
  21. receive() {
  22. console.log('收到请求1');
  23. }
  24. send() {
  25. console.log('ConcreteColleague1');
  26. this.mediator.relay(this);
  27. }
  28. }
  29. class ConcreteColleague2 {
  30. setParent(concrete) {
  31. this.mediator = concrete;
  32. }
  33. receive() {
  34. console.log('收到请求2');
  35. }
  36. send() {
  37. console.log('ConcreteColleague2');
  38. this.mediator.relay(this);
  39. }
  40. }
  41. const concreteMediator1 = new ConcreteMediator();
  42. const concrete1 = new ConcreteColleague1();
  43. const concrete2 = new ConcreteColleague2();
  44. concreteMediator1.add(concrete1);
  45. concreteMediator1.add(concrete2);
  46. concrete2.send();
  47. concrete1.send();

Typescirpt

  1. interface concreteColleague {
  2. setParent(concrete: any): void;
  3. receive(): void;
  4. send(): void;
  5. }
  6. interface concreteMediator {
  7. add(concrete:concreteColleague): void;
  8. relay(colleague: concreteColleague): void;
  9. }
  10. class ConcreteMediator {
  11. list: concreteColleague[];
  12. constructor() {
  13. this.list = [];
  14. }
  15. add(concrete:concreteColleague) {
  16. concrete.setParent(this);
  17. this.list.push(concrete);
  18. }
  19. relay(colleague:concreteColleague) {
  20. for(let i = 0;i < this.list.length;i ++) {
  21. if(!(this.list[i] === colleague)) {
  22. this.list[i].receive();
  23. }
  24. }
  25. }
  26. }
  27. abstract class ConcreteColleagueAbstract {
  28. mediator!: concreteMediator;
  29. abstract setParent(concrete: concreteMediator): void
  30. abstract receive(): void
  31. abstract send():void
  32. }
  33. class ConcreteColleague1 extends ConcreteColleagueAbstract{
  34. setParent(concrete:concreteMediator) {
  35. this.mediator = concrete;
  36. }
  37. receive() {
  38. console.log('收到请求1');
  39. }
  40. send() {
  41. console.log('ConcreteColleague1');
  42. this.mediator.relay(this);
  43. }
  44. }
  45. class ConcreteColleague2 extends ConcreteColleagueAbstract{
  46. setParent(concrete:concreteMediator) {
  47. this.mediator = concrete;
  48. }
  49. receive() {
  50. console.log('收到请求2');
  51. }
  52. send() {
  53. console.log('ConcreteColleague2');
  54. this.mediator.relay(this);
  55. }
  56. }
  57. const concreteMediator1 = new ConcreteMediator();
  58. const concrete1 = new ConcreteColleague1();
  59. const concrete2 = new ConcreteColleague2();
  60. concreteMediator1.add(concrete1);
  61. concreteMediator1.add(concrete2);
  62. concrete2.send();
  63. concrete1.send();

其主要优点如下:

  1. 降低了对象之间的耦合性,使得对象易于独立地被复用。
  2. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

其主要缺点是:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。

迭代器(Iterator)

模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

应用场景

在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如“数据结构”中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”。
迭代器模式”能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”。

ES6

  1. class ConcreteAggregate {
  2. constructor() {
  3. this.list = [];
  4. }
  5. add(obj) {
  6. this.list.push(obj);
  7. }
  8. remove(index) {
  9. this.list.slice(index,1);
  10. }
  11. }
  12. class ConcreteIterator {
  13. constructor(target) {
  14. this.target = target.list;
  15. this.listLength = target.list.length;
  16. this.indexList = 0;
  17. }
  18. hasNext() {
  19. return this.listLength > this.indexList?true:false;
  20. }
  21. first() {
  22. console.log(this.target[0]);
  23. }
  24. next() {
  25. this.indexList ++;
  26. if(this.hasNext()) {
  27. console.log(this.target[this.indexList]);
  28. } else {
  29. console.log('到了最大了');
  30. }
  31. }
  32. }
  33. const concreteAggregateTest = new ConcreteAggregate();
  34. concreteAggregateTest.add('1');
  35. concreteAggregateTest.add('2');
  36. concreteAggregateTest.add('3');
  37. const concreteIteratorTest = new ConcreteIterator(concreteAggregateTest);
  38. concreteIteratorTest.first();
  39. while (concreteIteratorTest.hasNext()) {
  40. concreteIteratorTest.next();
  41. }

Typescipt

  1. interface concreteAggregate {
  2. list:string[]
  3. add(obj: string): void;
  4. remove(index:number): void;
  5. }
  6. class ConcreteAggregate implements concreteAggregate{
  7. list: string[];
  8. constructor() {
  9. this.list = [];
  10. }
  11. add(obj:string) {
  12. this.list.push(obj);
  13. }
  14. remove(index:number) {
  15. this.list.slice(index,1);
  16. }
  17. }
  18. interface concreteIterator{
  19. hasNext(): boolean;
  20. first(): void;
  21. next(): void;
  22. }
  23. class ConcreteIterator implements concreteIterator{
  24. target: string[];
  25. listLength: number;
  26. indexList: number;
  27. constructor(target:concreteAggregate) {
  28. this.target = target.list;
  29. this.listLength = target.list.length;
  30. this.indexList = 0;
  31. }
  32. hasNext() {
  33. return this.listLength > this.indexList?true:false;
  34. }
  35. first() {
  36. console.log(this.target[0]);
  37. }
  38. next() {
  39. this.indexList ++;
  40. if(this.hasNext()) {
  41. console.log(this.target[this.indexList]);
  42. } else {
  43. console.log('到了最大了');
  44. }
  45. }
  46. }
  47. const concreteAggregateTest = new ConcreteAggregate();
  48. concreteAggregateTest.add('1');
  49. concreteAggregateTest.add('2');
  50. concreteAggregateTest.add('3');
  51. const concreteIteratorTest = new ConcreteIterator(concreteAggregateTest);
  52. concreteIteratorTest.first();
  53. while (concreteIteratorTest.hasNext()) {
  54. concreteIteratorTest.next();
  55. }

主要优点如下。

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

其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。

访问者(Visitor)

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

应用场景

有些集合对象中存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式。例如,公园中存在多个景点,也存在多个游客,不同的游客对同一个景点的评价可能不同。

ES6

  1. class ObjectStructure {
  2. constructor() {
  3. this.list = [];
  4. }
  5. add(obj) {
  6. this.list.push(obj);
  7. }
  8. accept(visitor){
  9. this.list.forEach((item,index) => {
  10. item.accept(visitor);
  11. });
  12. }
  13. }
  14. class ConcreteElementA {
  15. accept(visitor) {
  16. visitor.visit(this);
  17. }
  18. operation(){
  19. console.log('具体元素A的操作');
  20. }
  21. }
  22. class ConcreteElementB {
  23. accept(visitor) {
  24. visitor.visit(this);
  25. }
  26. operation(){
  27. console.log('具体元素B的操作');
  28. }
  29. }
  30. class ConcreteVisitorA {
  31. visit(element) {
  32. console.log('具体访问者A');
  33. element.operation();
  34. }
  35. }
  36. class ConcreteVisitorB {
  37. visit(element) {
  38. console.log('具体访问者B');
  39. element.operation();
  40. }
  41. }
  42. const objectStructureTest = new ObjectStructure();
  43. objectStructureTest.add(new ConcreteElementA());
  44. objectStructureTest.add(new ConcreteElementB());
  45. objectStructureTest.accept(new ConcreteVisitorA());
  46. objectStructureTest.accept(new ConcreteVisitorB());

Typescript

  1. interface visitor{
  2. visit(element:eElements): void;
  3. }
  4. interface objectStructure{
  5. add(obj:eElements): void;
  6. accept(visitor:visitor): void;
  7. }
  8. interface eElements{
  9. accept(visitor:visitor): void;
  10. operation(): void;
  11. }
  12. class ObjectStructure implements objectStructure{
  13. list: object[];
  14. constructor() {
  15. this.list = [];
  16. }
  17. add(obj: eElements) {
  18. this.list.push(obj);
  19. }
  20. accept(visitor:visitor){
  21. this.list.forEach((item:any,index) => {
  22. item.accept(visitor);
  23. });
  24. }
  25. }
  26. class ConcreteElementA implements eElements{
  27. accept(visitor:visitor) {
  28. visitor.visit(this);
  29. }
  30. operation(){
  31. console.log('具体元素A的操作');
  32. }
  33. }
  34. class ConcreteElementB implements eElements{
  35. accept(visitor:visitor) {
  36. visitor.visit(this);
  37. }
  38. operation(){
  39. console.log('具体元素B的操作');
  40. }
  41. }
  42. class ConcreteVisitorA {
  43. visit(element:eElements) {
  44. console.log('具体访问者A');
  45. element.operation();
  46. }
  47. }
  48. class ConcreteVisitorB {
  49. visit(element:eElements) {
  50. console.log('具体访问者B');
  51. element.operation();
  52. }
  53. }
  54. const objectStructureTest = new ObjectStructure();
  55. objectStructureTest.add(new ConcreteElementA());
  56. objectStructureTest.add(new ConcreteElementB());
  57. objectStructureTest.accept(new ConcreteVisitorA());
  58. objectStructureTest.accept(new ConcreteVisitorB());

主要优点如下:

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

主要缺点如下:

  1. 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
  2. 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
  3. 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类

    备忘录(Memento)

    模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。

ES6

  1. class Originator {
  2. setState(state){
  3. this.state = state;
  4. }
  5. getState() {
  6. return this.state;
  7. }
  8. restoreMemento(state) {
  9. this.setState(state);
  10. }
  11. }
  12. class Caretaker {
  13. setMemento(state){
  14. this.state = state;
  15. }
  16. getMemento() {
  17. return this.state;
  18. }
  19. }
  20. const originator = new Originator();
  21. const caretaker = new Caretaker();
  22. originator.setState('1');
  23. console.log(originator.getState());
  24. caretaker.setMemento(originator.getState());
  25. originator.setState('2');
  26. console.log(originator.getState());
  27. originator.restoreMemento(caretaker.getMemento());
  28. console.log(originator.getState());

Typescripit

  1. interface originator{
  2. setState(state: string): void;
  3. getState(state: string): string;
  4. restoreMemento(state:string): void;
  5. }
  6. class Originator implements originator{
  7. state!: string;
  8. setState(state:string){
  9. this.state = state;
  10. }
  11. getState() {
  12. return this.state;
  13. }
  14. restoreMemento(state:string) {
  15. this.setState(state);
  16. }
  17. }
  18. interface caretaker{
  19. setMemento(state: string): void;
  20. getMemento(): string;
  21. }
  22. class Caretaker {
  23. state!: string;
  24. setMemento(state:string){
  25. this.state = state;
  26. }
  27. getMemento() {
  28. return this.state;
  29. }
  30. }
  31. const originator = new Originator();
  32. const caretaker = new Caretaker();
  33. originator.setState('1');
  34. console.log(originator.getState());
  35. caretaker.setMemento(originator.getState());
  36. originator.setState('2');
  37. console.log(originator.getState());
  38. originator.restoreMemento(caretaker.getMemento());
  39. console.log(originator.getState());

主要优点如下。

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

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

解释器(Interpreter)

模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器