1.策略模式

  • 策略模式(Strategy Pattern)
    • 定义⼀系列的算法,把它们⼀个个封装起来, 并且使它们 可相互替换
    • 淘宝天猫双⼗⼀,正在搞活动有打折的、有满减的、有返利的等等,这些算法只是⼀种策略,并且是随时都可能互相替换的, 我们就可以定义⼀组算法,将每个算法

都封装起来,并且使它们之间可以互换

  • 应⽤场景

    • ⽼王计划外出旅游,选择骑⾃⾏⻋、坐汽⻋、⻜机等, 每⼀种旅⾏⽅式都是⼀个策略
    • Java AWT中的LayoutManager,即布局管理器
    • 如果在⼀个系统⾥⾯有许多类,它们之间的区别仅在于它们的⾏为,那么可以使⽤策略模式
    • 不希望暴露复杂的、与算法有关的数据结构,那么可以 使⽤策略模式来封装算法
  • ⻆⾊

    • Context上下⽂:屏蔽⾼层模块对策略、算法的直接访问,封装可能存在的变化
    • Strategy策略⻆⾊:抽象策略⻆⾊,是对策略、算法家族的抽象,定义每个策略或算法必须具有的⽅法和属性
    • ConcreteStrategy具体策略⻆⾊:⽤于实现抽象策略中的操作,即实现具体的算法

image.png

  • 业务需求

    ⽼王⾯试进了⼤⼚,是电商项⽬的营销活动组,负责多个营销活 动,有折扣、优惠券抵扣、满减等,项⽬上线后,产品经理找茬,经常新增营销活动,导致代码改动多,加班严重搞的⽼王很 恼⽕。 他发现这些都是活动策略,商品的价格是根据不同的活动策略进 ⾏计算的,因此⽤策略设计模式进⾏了优化,后续新增策略后只 要简单配置就⾏了,不⽤⼤动⼲⼽

  1. public abstract class Strategy {
  2. /**
  3. * 根据简单订单对象,计算商品折扣后的价格
  4. * @param productOrder
  5. * @return
  6. */
  7. public abstract double computePrice(ProductOrder productOrder);
  8. }
  1. public class PromotionContext {
  2. private Strategy strategy;
  3. public PromotionContext(Strategy strategy){
  4. this.strategy = strategy;
  5. }
  6. /**
  7. * 根据策略计算最终的价格
  8. * @param productOrder
  9. * @return
  10. */
  11. public double executeStrategy(ProductOrder productOrder){
  12. return strategy.computePrice(productOrder);
  13. }
  14. }
  1. public class DiscountActivity extends Strategy{
  2. /**
  3. * 具体的折扣
  4. */
  5. private double rate;
  6. public DiscountActivity(double rate){
  7. this.rate = rate;
  8. }
  9. @Override
  10. public double computePrice(ProductOrder productOrder) {
  11. //一系列复杂的计算
  12. return productOrder.getOldPrice() * rate;
  13. }
  14. }
  1. public class NormalActivity extends Strategy{
  2. @Override
  3. public double computePrice(ProductOrder productOrder) {
  4. return productOrder.getOldPrice();
  5. }
  6. }
  1. public class VoucherActivity extends Strategy {
  2. /**
  3. * 传入优惠券
  4. */
  5. private double voucher;
  6. public VoucherActivity(double voucher){
  7. this.voucher = voucher;
  8. }
  9. @Override
  10. public double computePrice(ProductOrder productOrder) {
  11. if(productOrder.getOldPrice() > voucher){
  12. return productOrder.getOldPrice() - voucher;
  13. }else {
  14. return 0;
  15. }
  16. }
  17. }
  1. public class ProductOrder {
  2. private double oldPrice;
  3. private int userId;
  4. private int productId;
  5. public ProductOrder(double oldPrice, int userId, int productId){
  6. this.oldPrice = oldPrice;
  7. this.userId = userId;
  8. this.productId = productId;
  9. }
  10. //getter/setter
  11. }
  • 优点

    • 满⾜开闭原则,当增加新的具体策略时,不需要修改上下⽂类的代码,上下⽂就可以引⽤新的具体策略的实例
    • 避免使⽤多重条件判断,如果不⽤策略模式可能会使⽤ 多重条件语句不利于维护,和⼯⼚模式的搭配使⽤可以 很好地消除代码if-else的多层嵌套(⼯⼚模式主要是根 据参数,获取不同的策略)
  • 缺点

    • 策略类数量会增多,每个策略都是⼀个类,复⽤的可能 性很⼩
    • 对外暴露了类所有的⾏为和算法,⾏为过多导致策略类 膨胀
  • JDK源码的应⽤

    • Comparator 接⼝常⽤的 compare()⽅法,就是⼀个策略设计模式的应⽤,把 Comparator 作为参数使⽤⽣成不同的排序策略
      1. List<Student> list = new ArrayList<>();
      2. list.add(new Student("Anna", 15));
      3. list.add(new Student("⼩D", 18));
      4. list.add(new Student("⽼王", 20));
      5. // 对伙伴的集合按年龄进⾏排序
      6. Collections.sort(list, new Comparator<Student>() {
      7. @Override
      8. public int compare(Student s1, Student s2) {
      9. // 升序
      10. //return s1.getAge()-s2.getAge();
      11. // 降序
      12. // return s2.getAge()-s1.getAge();
      13. }
      14. });

2.模板方法模式

  • 模板⽅法模式Template Method

    • 定义⼀个操作中的算法⻣架,将算法的⼀些步骤延迟到⼦类中,使得⼦类可以不改变该算法结构的情况下重定义该算法的某些特定步骤,属于⾏为型模式
  • 应⽤场景

    • javaweb⾥⾯的Servlet,HttpService类提供了⼀个 service( )⽅法
    • 有多个⼦类共有逻辑相同的⽅法,可以考虑作为模板⽅法
    • 设计⼀个系统时知道了算法所需的关键步骤,且确定了这些步骤的执⾏顺序,但某些步骤的具体实现还未知, 可以延迟到⼦类进⾏完成
  1. /**
  2. * 抽象类(Abstract Class)
  3. */
  4. public abstract class AbstractClass {
  5. /**
  6. * 模版⽅法
  7. */
  8. public void templateMethod() {
  9. specificMethod();
  10. abstractMethod1();
  11. abstractMethod2();
  12. }
  13. /**
  14. *具体⽅法
  15. */
  16. public void specificMethod() {
  17. System.out.println("抽象类中的具体⽅法被调⽤");
  18. }
  19. // 抽象⽅法1
  20. public abstract void abstractMethod1();
  21. // 抽象⽅法2
  22. public abstract void abstractMethod2();
  23. }
  • ⻆⾊
    • 抽象模板(Abstract Template):

定义⼀个模板⽅法,这 个模板⽅法⼀般是⼀个具体⽅法,给出⼀个顶级算法⻣ 架,⽽逻辑⻣架的组成步骤在相应的抽象操作中,推迟到⼦类实现

  1. - 模板⽅法:定义了算法的⻣架,按某种顺序调⽤其包含的基本⽅法
  2. - 基本⽅法:是整个算法中的⼀个步骤
  3. - 抽象⽅法:在抽象类中申明,由具体⼦类实现。
  4. - 具体⽅法:在抽象类中已经实现,在具体⼦类中可以继承或重写它
  • 具体模板(Concrete Template):实现⽗类所定义的⼀个 或多个抽象⽅法,它们是⼀个顶级算法逻辑的组成步骤

image.png

⼩滴课堂-⽼王成功晋升为管理者,但是团队来了很多新兵,由于团队⽔平参差不⻬,经常有新项⽬进来,但整体流程很不规范。

⼀个项⽬的⽣命周期:需求评审-设计-开发-测试-上线-运维。整个周期⾥⾯,需求评审-设计是固定的操作,⽽其他步骤 则流程耗时等是根据项⽬来定的。

因此⽼王梳理了⼀个模板,来规范化项⽬,他只管核⼼步骤和项⽬⾥程碑产出的结果,具体的⼯时安排和开发就让团队成员去操作

编码实战

  1. public abstract class AbstractProjectManager {
  2. /**
  3. * 定义模板方法,声明final,防止子类覆盖他,更改顺序,流程是一样复用
  4. */
  5. public final void processProject(){
  6. review();
  7. degisn();
  8. coding();
  9. test();
  10. online();
  11. }
  12. /**
  13. * 各个项目都需要评审,具体方法
  14. */
  15. public void review(){
  16. System.out.println("项目需求评审");
  17. }
  18. /**
  19. * 各个项目都需要设计,具体方法
  20. */
  21. public void degisn(){
  22. System.out.println("UI UE进行设计");
  23. }
  24. /**
  25. * 抽象方法,由就具体子类进行实现,编码耗时不一样
  26. */
  27. public abstract void coding();
  28. /**
  29. * 抽象方法,由就具体子类进行实现,测试有多种,自动化测试、安全测试、压力测试、手工测试
  30. */
  31. public abstract void test();
  32. /**
  33. * 抽象方法,由就具体子类进行实现,上线有全量发布,灰度发布,停机发布
  34. */
  35. public abstract void online();
  36. }
  1. public class PayServiceProjectManager extends AbstractProjectManager {
  2. @Override
  3. public void coding() {
  4. System.out.println("开发耗时30天");
  5. }
  6. @Override
  7. public void test() {
  8. System.out.println("功能测试,安全测试,压力测试");
  9. }
  10. @Override
  11. public void online() {
  12. System.out.println("全量上线");
  13. }
  14. }
  1. public class UserServiceProjectManager extends AbstractProjectManager {
  2. @Override
  3. public void coding() {
  4. System.out.println("开发耗时10天");
  5. }
  6. @Override
  7. public void test() {
  8. System.out.println("功能测试,压力测试,还有手工测试");
  9. }
  10. @Override
  11. public void online() {
  12. System.out.println("灰度发布,全量上线");
  13. }
  14. }
  • 优点

    • 扩展性好,对不变的代码进⾏封装,对可变的进⾏扩展,符合 开闭原则
  • 提⾼代码复⽤性 将相同部分的代码放在抽象的⽗类中,将不同的代码放⼊不同的⼦类中

    • 通过⼀个⽗类调⽤其⼦类的操作,通过对⼦类的具体实 现扩展不同的⾏为,实现了反向控制
  • 缺点

    • 每⼀个不同的实现都需要⼀个⼦类来实现,导致类的个 数增加,会使系统变得复杂
  • 模板⽅法模式和建造者模式区别

    • 两者很⼤的交集,建造者模式⽐模板⽅法模式多了⼀个指挥类,该类体现的是模板⽅法模式中抽象类的固定算法的功能,是⼀个创建对象的固定算法

3.观察者设计模式

  • 观察者模式

    • 定义对象间⼀种⼀对多的依赖关系,使得每当⼀个对象改变状态,则所有依赖于它的对象都会得到通知并⾃动更新,也叫做发布订阅模式Publish/Subscribe,属于⾏为型模式
  • 应⽤场景

    • 消息通知⾥⾯:邮件通知、⼴播通知、微信朋友圈、微博私信等,就是监听观察事件
    • 当⼀个对象的改变需要同时改变其它对象,且它不知道 具体有多少对象有待改变的时候,考虑使⽤观察者模式
  • ⻆⾊

    • Subject主题:持有多个观察者对象的引⽤,抽象主题提供了⼀个接⼝可以增加和删除观察者对象;有⼀个观 察者数组,并实现增、删及通知操作
    • Observer抽象观察者:为具体观察者定义⼀个接⼝, 在得到主题的通知时更新⾃⼰
    • ConcreteSubject具体主题:将有关状态存⼊具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知
    • ConcreteObserver具体观察者:实现抽象观察者⻆⾊所要求的更新接⼝,以便使本身的状态与主题的状态保持⼀致
  • 优点

    • 降低了⽬标与观察者之间的耦合关系,⽬标与观察者之间建⽴了⼀套触发机制
    • 观察者和被观察者是抽象耦合的
  • 缺点

    • 观察者和观察⽬标之间有循环依赖的话,会触发它们之间进⾏循环调⽤,可能导致系统崩溃
    • ⼀个被观察者对象有很多的直接和间接的观察者的话, 将所有的观察者都通知到会花费很多时间

image.png

  1. public class Subject {
  2. private List<Observer> observerList = new ArrayList<>();
  3. /**
  4. * 新增观察者
  5. * @param observer
  6. */
  7. public void addObserver(Observer observer){
  8. this.observerList.add(observer);
  9. }
  10. /**
  11. *删除观察者
  12. * @param observer
  13. */
  14. public void deleteObserver(Observer observer){
  15. this.observerList.remove(observer);
  16. }
  17. public void notifyAllObserver(){
  18. for(Observer observer:this.observerList){
  19. observer.update();
  20. }
  21. }
  22. }
  1. public interface Observer {
  2. /**
  3. * 观察到消息后进行的操作,就是响应
  4. */
  5. void update();
  6. }
  1. public class BossConcreteSubject extends Subject {
  2. public void doSomething(){
  3. System.out.println("老板完成自己的工作");
  4. //还有其他操作
  5. System.out.println("视察公司工作情况");
  6. super.notifyAllObserver();
  7. }
  8. }
  1. public class AnnaConcreteObserver implements Observer {
  2. @Override
  3. public void update() {
  4. System.out.println("Anna小姐姐发现领导到来,暂停在线摸鱼,回归工作");
  5. }
  6. }
  1. public class LWConcreteObserver implements Observer {
  2. @Override
  3. public void update() {
  4. System.out.println("老王发现领导到来,暂停在线摸鱼,回归工作");
  5. }
  6. }

4.责任链设计模式

  • 责任链设计模式(Chain of Responsibility Pattern)

    • 客户端发出⼀个请求,链上的对象都有机会来处理这⼀请求,⽽客户端不需要知道谁是具体的处理对象
    • 让多个对象都有机会处理请求,避免请求的发送者和接收者之间的耦合关系,将这个对象连成⼀条调⽤链,并沿着这条链传递该请求,直到有⼀个对象处理它才终⽌
    • 有两个核⼼⾏为:⼀是处理请求,⼆是将请求传递到下⼀节点
  • 应⽤场景

    • Apache Tomcat 对 Encoding 编码处理的处理, SpringBoot⾥⾯的拦截器、过滤器链
    • 在请求处理者不明确的情况下向多个对象中的⼀个提交请求
    • 如果有多个对象可以处理同⼀个请求,但是具体由哪个对象处理是由运⾏时刻动态决定的,这种对象就可以使⽤职责链模式
  • ⻆⾊

    • Handler抽象处理者:定义了⼀个处理请求的接⼝
    • ConcreteHandler具体处理者: 处理所负责的请求,可 访问它的后续节点,如果可处理该请求就处理,否则就 将该请求转发给它的后续节点

image.png

  • 责任链设计模式案例实战
  • 业务需求

    ⻛控规则,就是对于每个场景,定义⼀些规则,来进⾏相应的控制,⽐如银⾏借款、⽀付宝提现、⼤额转账等 会触发不同的策 略。

    像互联⽹⾦融⾏业的话,除了公司内部政策,所处的外部环境经常发⽣变化,⽐如国家经常会出政策,这些都经常需要调整相应 的⻛控参数和⻛控级别。

    例⼦:⽀付宝转账,根据转账额度不同,会触发的⻛控级别不⼀ 样,1000元以下直接转,1千到1万需要⼿机号验证码,1万到以 上需要刷脸验证。

image.png

  1. public class Request {
  2. /**
  3. * 类别
  4. */
  5. private String requestType;
  6. /**
  7. * 金额
  8. */
  9. private int money;
  10. //getter setter
  11. }
  12. public enum RequestType {
  13. /**
  14. * 转账
  15. */
  16. TRANSFER,
  17. /**
  18. * 提现
  19. */
  20. CASH_OUT;
  21. }
  1. public abstract class RiskControlManager {
  2. protected String name;
  3. /**
  4. * 更严格的风控策略
  5. */
  6. protected RiskControlManager superior;
  7. public RiskControlManager(String name){
  8. this.name = name;
  9. }
  10. /**
  11. * 设置更严格的风控策略
  12. * @param superior
  13. */
  14. public void setSuperior(RiskControlManager superior){
  15. this.superior = superior;
  16. }
  17. /**
  18. * 处理请求
  19. * @param request
  20. */
  21. public abstract void handlerRequest(Request request);
  22. }
  1. public class FirstRiskControlManager extends RiskControlManager {
  2. public FirstRiskControlManager(String name) {
  3. super(name);
  4. }
  5. /**
  6. * 1000元以内可以直接处理
  7. * @param request
  8. */
  9. @Override
  10. public void handlerRequest(Request request) {
  11. if(RequestType.valueOf(request.getRequestType())!=null && request.getMoney()<=1000){
  12. System.out.println("普通操作,输入支付密码即可");
  13. System.out.println(name+":"+request.getRequestType() + ", 金额:"+request.getMoney() +" 处理完成");
  14. }else {
  15. //下个节点进行处理
  16. if(superior!=null){
  17. superior.handlerRequest(request);
  18. }
  19. }
  20. }
  21. }
  1. public class SecondRiskControlManager extends RiskControlManager {
  2. public SecondRiskControlManager(String name) {
  3. super(name);
  4. }
  5. /**
  6. * 处理 1千到1万之间
  7. * @param request
  8. */
  9. @Override
  10. public void handlerRequest(Request request) {
  11. if(RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>1000 && request.getMoney()<10000){
  12. System.out.println("稍大额操作,输入支付密码+短信验证码即可");
  13. System.out.println(name+":"+request.getRequestType() + ", 金额:"+request.getMoney() +" 处理完成");
  14. }else {
  15. //下个节点进行处理
  16. if(superior!=null){
  17. superior.handlerRequest(request);
  18. }
  19. }
  20. }
  21. }
  1. public class ThirdRiskControlManager extends RiskControlManager {
  2. public ThirdRiskControlManager(String name) {
  3. super(name);
  4. }
  5. @Override
  6. public void handlerRequest(Request request) {
  7. if(RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>10000){
  8. System.out.println("大额操作,输入支付密码+验证码+人脸识别 ");
  9. System.out.println(name+":"+request.getRequestType() + ", 金额:"+request.getMoney() +" 处理完成");
  10. }else {
  11. //下个节点进行处理
  12. if(superior!=null){
  13. superior.handlerRequest(request);
  14. }
  15. }
  16. }
  17. }

5.命令设计模式

  • 命令设计模式(Command Pattern)

    • 请求以命令的形式包裹在对象中,并传给调⽤对象。调 ⽤对象寻找可以处理该命令的对象,并把该命令传给相应的对象 执⾏命令,属于⾏为型模式
    • 命令模式是⼀种特殊的策略模式,体现的是多个策略执⾏的问题,⽽不是选择的问题
  • 应⽤场景

    • 只要是你认为是命令的地⽅,就可以采⽤命令模式
    • ⽇常每个界⾯、按钮、键盘 事件操作都是 命令设计模式
  • ⻆⾊

    • 抽象命令(Command):需要执⾏的所有命令都在这⾥声明
    • 具体命令(ConcreteCommand):定义⼀个接收者和⾏为之间的弱耦合,实现execute()⽅法,负责调⽤接收者的相应操作,execute()⽅法通常叫做执⾏⽅法。
    • 接受者(Receiver):负责具体实施和执⾏⼀个请求,⼲活的⻆⾊,命令传递到这⾥是应该被执⾏的,实施和执⾏请求的⽅法叫做⾏动⽅法
    • 请求者(Invoker):负责调⽤命令对象执⾏请求,相关的⽅法叫做⾏动⽅法
    • 客户端(Client):创建⼀个具体命令(ConcreteCommand)对象并确定其接收者。

image.png

  1. //接受者,命令执⾏者
  2. public class Receiver {
  3. public void doSomething() {
  4. System.out.println("Receiver---doSomething");
  5. }
  6. }
  1. //抽象命令
  2. public interface Command {
  3. /**
  4. 执⾏动作
  5. */
  6. void execute();
  7. }
  1. //具体命令
  2. public class ConcreteCommand implements Command{
  3. /**
  4. 对哪个receiver类进⾏命令处理
  5. */
  6. private Receiver receiver;
  7. public ConcreteCommand(Receiver receiver) {
  8. this.receiver = receiver;
  9. }
  10. /**
  11. 必须实现⼀个命令
  12. */
  13. @Override
  14. public void execute() {
  15. System.out.println("ConcreteCommand---execute");
  16. receiver.doSomething();
  17. }
  18. }
  1. //请求者
  2. public class Invoker {
  3. private Command command;
  4. public Invoker(Command command){
  5. this.command = command;
  6. }
  7. /*
  8. 执⾏命令
  9. */
  10. public void action(){
  11. this.command.execute();
  12. }
  13. }
  1. //使⽤
  2. public static void main(String[] args) {
  3. //创建接收者
  4. Receiver receiver = new Receiver();
  5. //创建命令对象,设定它的接收者
  6. Command command = new
  7. ConcreteCommand(receiver);
  8. //创建请求者,把命令对象设置进去
  9. Invoker invoker = new Invoker(command);
  10. //执⾏⽅法
  11. invoker.action();
  12. }
  • 命令设计模式之智能家居控制案例实战
  • 业务需求

⽼王-搬新家了,他想实现智能家居,开发⼀个app,可以控制家⾥的家电,⽐如控制空调的开关、加热、制冷 等功能
利⽤命令设计模式,帮⽼王完成这个需求,注意:动作请求者就 是⼿机app, 动作的执⾏者是家电的不同功能

  1. public class ConditionReceiver {
  2. public void on(){
  3. System.out.println("空调开启了");
  4. }
  5. public void off(){
  6. System.out.println("空调关闭了");
  7. }
  8. public void cool(){
  9. System.out.println("空调开始制冷");
  10. }
  11. public void warm(){
  12. System.out.println("空调开始制暖");
  13. }
  14. }
  1. public interface Command {
  2. /**
  3. * 执行动作
  4. */
  5. void execute();
  6. }
  1. public class OnCommand implements Command{
  2. /**
  3. * 对哪个receiver 进行命令处理
  4. */
  5. private ConditionReceiver receiver;
  6. public OnCommand(ConditionReceiver receiver){
  7. this.receiver = receiver;
  8. }
  9. /**
  10. * 必须实现一个命令的调用
  11. */
  12. @Override
  13. public void execute() {
  14. System.out.println("OnCommand -> execute");
  15. receiver.on();
  16. }
  17. }
  18. public class OffCommand implements Command{
  19. /**
  20. * 对哪个receiver 进行命令处理
  21. */
  22. private ConditionReceiver receiver;
  23. public OffCommand(ConditionReceiver receiver){
  24. this.receiver = receiver;
  25. }
  26. /**
  27. * 必须实现一个命令的调用
  28. */
  29. @Override
  30. public void execute() {
  31. System.out.println("OffCommand -> execute");
  32. receiver.off();
  33. }
  34. }
  35. public class WarmCommand implements Command{
  36. /**
  37. * 对哪个receiver 进行命令处理
  38. */
  39. private ConditionReceiver receiver;
  40. public WarmCommand(ConditionReceiver receiver){
  41. this.receiver = receiver;
  42. }
  43. /**
  44. * 必须实现一个命令的调用
  45. */
  46. @Override
  47. public void execute() {
  48. System.out.println("WarmCommand -> execute");
  49. receiver.warm();
  50. }
  51. }
  52. public class CoolCommand implements Command{
  53. /**
  54. * 对哪个receiver 进行命令处理
  55. */
  56. private ConditionReceiver receiver;
  57. public CoolCommand(ConditionReceiver receiver){
  58. this.receiver = receiver;
  59. }
  60. /**
  61. * 必须实现一个命令的调用
  62. */
  63. @Override
  64. public void execute() {
  65. System.out.println("CoolCommand -> execute");
  66. receiver.cool();
  67. }
  68. }
  1. public class AppInvoker {
  2. private Command onCommand;
  3. private Command offCommand;
  4. private Command coolCommand;
  5. private Command warmCommand;
  6. public void setOnCommand(Command onCommand) {
  7. this.onCommand = onCommand;
  8. }
  9. public void setOffCommand(Command offCommand) {
  10. this.offCommand = offCommand;
  11. }
  12. public void setCoolCommand(Command coolCommand) {
  13. this.coolCommand = coolCommand;
  14. }
  15. public void setWarmCommand(Command warmCommand) {
  16. this.warmCommand = warmCommand;
  17. }
  18. /**
  19. * 开机
  20. */
  21. public void on(){
  22. onCommand.execute();
  23. }
  24. /**
  25. * 关机
  26. */
  27. public void off(){
  28. offCommand.execute();
  29. }
  30. public void warm(){
  31. warmCommand.execute();
  32. }
  33. public void cool(){
  34. coolCommand.execute();
  35. }
  36. }