【设计模式——学习笔记】23种设计模式——策略模式Strategy(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录

  • 案例引入
    • 传统方案实现
      • 实现
      • 分析
  • 介绍
    • 基本介绍
    • 登场角色
  • 案例实现
    • 案例一
      • 类图
      • 实现
    • 案例二
      • 类图
      • 实现
      • 问答
  • 策略模式在JDK源码中的使用
  • 总结
  • 文章说明

案例引入

  • 有各种鸭子,比如野鸭、北京鸭、水鸭等。 鸭子有各种行为,比如走路、叫、飞行等。不同鸭子的行为可能略有不同。要求显示鸭子的信息

传统方案实现

不同的鸭子继承一个父类Duck,如果是相同的行为就继承,不同行为就重写方法

实现

【鸭子抽象类】

package com.atguigu.strategy;

public abstract class Duck {

   public Duck() {

   }

   /**
    * 显示鸭子信息
    */
   public abstract void display();

   /**
    * 叫法
    */
   public void quack() {
      System.out.println("鸭子嘎嘎叫~~");
   }

   /**
    * 游泳方法
    */
   public void swim() {
      System.out.println("鸭子会游泳~~");
   }

   /**
    * 飞翔方法
    */
   public void fly() {
      System.out.println("鸭子会飞翔~~~");
   }

}

【野鸭】

package com.atguigu.strategy;

public class WildDuck extends Duck {

   @Override
   public void display() {
      System.out.println(" 这是野鸭 ");
   }

}

【北京鸭】

package com.atguigu.strategy;

public class PekingDuck extends Duck {

   @Override
   public void display() {
      System.out.println("~~北京鸭~~~");
   }

   /**
    * 因为北京鸭不能飞翔,因此需要重写fly
    */
   @Override
   public void fly() {
      System.out.println("北京鸭不能飞翔");
   }

}

【玩具鸭】

package com.atguigu.strategy;

public class ToyDuck extends Duck {
    @Override
    public void display() {
        System.out.println("玩具鸭");
    }

    //-------需要重写父类的所有方法---------

    public void quack() {
        System.out.println("玩具鸭不能叫~~");
    }

    public void swim() {
        System.out.println("玩具鸭不会游泳~~");
    }

    public void fly() {
        System.out.println("玩具鸭不会飞翔~~~");
    }
}

分析

  • 缺点:因为继承了Duck,所有鸭子都有了会飞的方法,虽然可以通过覆盖fly方法来解决,但是,如果子类很多方法都不需要呢,如果每个都要去覆盖一下就很麻烦了
  • 改进:使用策略模式

介绍

基本介绍

  • 策略模式中,定义算法族,分别封装到不同的类中,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。使用策略模式可以整体地替换算法的实现部分,让我们可以轻松地以不同的算法去解决同一个问题
  • 该模式体现了几个设计原则:把变化的代码从不变的代码中分离出来;针对接口编程而不是具体类(定义了策略接口);多用组合/聚合,少用继承(客户通过组合方式使用策略)

登场角色

在这里插入图片描述

在这里插入图片描述

Context聚合了策略接口,后面需要使用到哪个具体策略的方法,就传入该具体策略的实例作为参数即可

  • Strategy(策略):Strategy角色负责定义实现策略所必需的接口(API)
  • ConcreteStrategy(具体的策略):ConcreteStrategy角色负责实现Strategy角色的接口(API),即负责实现具体的策略(战略、方向、方法和算法)
  • Context(上下文):负责使用Strategy角色。Context角色保存了ConcreteStrategy角色的实例,并使用ConcreteStrategy角色(即调用Strategy角色的接口)去实现需求

案例实现

案例一

类图

在这里插入图片描述

实现

【飞翔策略 FlyBehavior】

package com.atguigu.strategy.improve;

public interface FlyBehavior {

   /**
    * 让子类具体实现
    */
   void fly();
}

【叫策略 QuackBehavior】

package com.atguigu.strategy.improve;

public interface QuackBehavior {
   void quack();
}

【飞翔技术高超:GoodFlyBehavior】

package com.atguigu.strategy.improve;

public class GoodFlyBehavior implements FlyBehavior {

   @Override
   public void fly() {
      System.out.println(" 飞翔技术高超 ~~~");
   }

}

【飞翔技术一般:BadFlyBehavior 】

package com.atguigu.strategy.improve;

public class BadFlyBehavior implements FlyBehavior {

   @Override
   public void fly() {
      System.out.println(" 飞翔技术一般 ");
   }

}

【不会飞翔】

package com.atguigu.strategy.improve;

public class NoFlyBehavior implements FlyBehavior{

   @Override
   public void fly() {
      System.out.println(" 不会飞翔  ");
   }

}

【鸭子抽象类】

package com.atguigu.strategy.improve;

public abstract class Duck {

   /**
    * 策略接口 飞翔
    */
   FlyBehavior flyBehavior;
   /**
    * 策略接口 叫
    */
   QuackBehavior quackBehavior;

   public Duck() {

   }

   /**
    * 显示鸭子信息
    */
   public abstract void display();

   public void quack() {
      System.out.println("鸭子嘎嘎叫~~");
   }

   public void swim() {
      System.out.println("鸭子会游泳~~");
   }

   public void fly() {
      //改进
      if(flyBehavior != null) {
         flyBehavior.fly();
      }
   }

   public void setFlyBehavior(FlyBehavior flyBehavior) {
      this.flyBehavior = flyBehavior;
   }


   public void setQuackBehavior(QuackBehavior quackBehavior) {
      this.quackBehavior = quackBehavior;
   }



}

【野鸭】

package com.atguigu.strategy.improve;

public class WildDuck extends Duck {
   
   /**
    * 构造器,传入FlyBehavor 的对象
    */
   public  WildDuck() {
      // 野鸭飞翔技术较强
      flyBehavior = new GoodFlyBehavior();
   }


   @Override
   public void display() {
      System.out.println(" 这是野鸭 ");
   }

}

【北京鸭】

package com.atguigu.strategy.improve;

public class PekingDuck extends Duck {
    
    /**
     * 假如北京鸭可以飞翔,但是飞翔技术一般
     */
    public PekingDuck() {
        flyBehavior = new BadFlyBehavior();

    }

    @Override
    public void display() {
        System.out.println("~~北京鸭~~~");
    }

}

【玩具鸭】

package com.atguigu.strategy.improve;

public class ToyDuck extends Duck {
    public ToyDuck() {
        // 玩具鸭不会飞翔
        flyBehavior = new NoFlyBehavior();
    }

    @Override
    public void display() {
        System.out.println("玩具鸭");
    }

    /**
     * 需要重写父类的所有方法
     */
    public void quack() {
        System.out.println("玩具鸭不能叫~~");
    }

    public void swim() {
        System.out.println("玩具鸭不会游泳~~");
    }

}

【主类】

package com.atguigu.strategy.improve;

public class Client {

   public static void main(String[] args) {
      WildDuck wildDuck = new WildDuck();
      wildDuck.fly();

      ToyDuck toyDuck = new ToyDuck();
      toyDuck.fly();

      PekingDuck pekingDuck = new PekingDuck();
      pekingDuck.fly();
      
      //可以动态改变某个对象的行为, 将北京鸭改成不能飞
      pekingDuck.setFlyBehavior(new NoFlyBehavior());
      System.out.println("北京鸭的实际飞翔能力");
      pekingDuck.fly();
   }

}

【输出】

飞翔技术高超 ~~~
 不会飞翔  
 飞翔技术一般 
北京鸭的实际飞翔能力
 不会飞翔  

Process finished with exit code 0

尖叫策略和飞翔策略的实现方式一样,这里就不再实现了

案例二

类图

在这里插入图片描述

实现

【手势类:并不是策略模式的角色】

package com.atguigu.strategy.Sample;

/**
 * 手势
 */
public class Hand {
    /**
     * 表示石头的值
     */
    public static final int HANDVALUE_GUU = 0;
    /**
     * 表示剪刀的值
     */
    public static final int HANDVALUE_CHO = 1;
    /**
     * 表示布的值
     */
    public static final int HANDVALUE_PAA = 2;
    /**
     * 表示猜拳中3种手势的实例
     */
    public static final Hand[] hand = {
            new Hand(HANDVALUE_GUU),
            new Hand(HANDVALUE_CHO),
            new Hand(HANDVALUE_PAA),
    };
    /**
     * 表示猜拳中手势所对应的字符串
     */
    private static final String[] name = {
            "石头", "剪刀", "布",
    };
    /**
     * 表示猜拳中出的手势的值
     */
    private int handvalue;

    private Hand(int handvalue) {
        this.handvalue = handvalue;
    }

    /**
     * 根据手势的值获取其对应的实例,这是一种单例模式,每种手势只有一个实例
     *
     * @param handvalue
     * @return
     */
    public static Hand getHand(int handvalue) {
        return hand[handvalue];
    }

    /**
     * 如果this胜了h则返回true
     *
     * @param h
     * @return
     */
    public boolean isStrongerThan(Hand h) {
        return fight(h) == 1;
    }

    /**
     * 如果this输给了h则返回true
     *
     * @param h
     * @return
     */
    public boolean isWeakerThan(Hand h) {
        return fight(h) == -1;
    }

    /**
     * 计分:平0, 胜1, 负-1
     *
     * @param h
     * @return
     */
    private int fight(Hand h) {
        if (this == h) {
            return 0;
        } else if ((this.handvalue + 1) % 3 == h.handvalue) {
            // 当(this.handvalue + 1) % 3 == h.handvalue时,可能得手势组合如下
            // this是石头,h是剪刀
            // this是剪刀,h是布
            // this是布,h是石头
            return 1;
        } else {
            return -1;
        }
    }

    /**
     * 转换为手势值所对应的字符串
     *
     * @return
     */
    public String toString() {
        return name[handvalue];
    }
}

【策略接口】

package com.atguigu.strategy.Sample;

public interface Strategy {
    /**
     * 获取下一局要出的手势
     * @return
     */
    public abstract Hand nextHand();

    /**
     * 学习上一局的手势是否获胜了,获胜就传进来true,否则返回false
     * @param win
     */
    public abstract void study(boolean win);
}

【具体策略一】

package com.atguigu.strategy.Sample;

import java.util.Random;

/**
 * 该策略是:如果上一局赢了,这局的手势就和上一局的相同;如果上一局输了,就随机出
 */
public class WinningStrategy implements Strategy {
    private Random random;
    /**
     * 保存上一局是赢还是输了
     */
    private boolean won = false;
    /**
     * 保存上一局出的手势
     */
    private Hand prevHand;
    
    public WinningStrategy(int seed) {
        random = new Random(seed);
    }
    public Hand nextHand() {
        if (!won) {
            prevHand = Hand.getHand(random.nextInt(3));
        }
        return prevHand;
    }
    public void study(boolean win) {
        won = win;
    }
}

【具体策略二】

package com.atguigu.strategy.Sample;

import java.util.Random;

public class ProbStrategy implements Strategy {
    private Random random;
    private int prevHandValue = 0;
    private int currentHandValue = 0;
    /**
     * 过去的胜率:history[上一局出的手势][这一局所出的手势]
     * 假设上一局出的手势是石头:
     * history[0][0]:两局分别出了石头、石头的获胜次数
     * history[0][1]:两局分别出了石头、剪刀的获胜次数
     * history[0][2]:两局分别出了石头、布的获胜次数
     * 若history[0][0]=3;history[0][1]=5;history[0][2]=7
     * 下一把出什么?使用轮盘赌的方式,出石头的概率是3/15;出剪刀的概率是5/15;出布的概率是7/15
     */
    private int[][] history = {
        { 1, 1, 1, },
        { 1, 1, 1, },
        { 1, 1, 1, },
    };
    public ProbStrategy(int seed) {
        random = new Random(seed);
    }

    /**
     * 学习历史胜率,根据轮盘赌的方式来出下一个手势
     * @return
     */
    public Hand nextHand() {
        int bet = random.nextInt(getSum(currentHandValue));
        int handvalue = 0;
        if (bet < history[currentHandValue][0]) {
            handvalue = 0;
        } else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
            handvalue = 1;
        } else {
            handvalue = 2;
        }
        prevHandValue = currentHandValue;
        currentHandValue = handvalue;
        return Hand.getHand(handvalue);
    }

    /**
     * 获取第一把出hv,第二把出1、2、3的总次数
     * @param hv
     * @return
     */
    private int getSum(int hv) {
        int sum = 0;
        for (int i = 0; i < 3; i++) {
            sum += history[hv][i];
        }
        return sum;
    }

    /**
     * 学习经验,更新 history 表格
     * @param win
     */
    public void study(boolean win) {
 
        if (win) {
            history[prevHandValue][currentHandValue]++;
        } else {
            history[prevHandValue][(currentHandValue + 1) % 3]++;
            history[prevHandValue][(currentHandValue + 2) % 3]++;
        }
    }
}

【游戏选手类】

package com.atguigu.strategy.Sample;

/**
 * 玩猜拳游戏的选手类
 */
public class Player {
    private String name;
    /**
     * 记录选手要选用的策略
     */
    private Strategy strategy;

    /**
     * 赢的局数
     */
    private int wincount;
    /**
     * 输的局数
     */
    private int losecount;
    /**
     * 总局数
     */
    private int gamecount;

    /**
     * 传入选手的姓名和策略
     *
     * @param name
     * @param strategy
     */
    public Player(String name, Strategy strategy) {
        this.name = name;
        this.strategy = strategy;
    }

    /**
     * 策略决定下一局要出的手势
     *
     * @return
     */
    public Hand nextHand() {
        return strategy.nextHand();
    }

    /**
     * 猜拳胜利
     */
    public void win() {                 
        strategy.study(true);
        wincount++;
        gamecount++;
    }

    /**
     * 猜拳失败
     */
    public void lose() {                
        strategy.study(false);
        losecount++;
        gamecount++;
    }

    /**
     * 猜拳平局
     */
    public void even() {                
        gamecount++;
    }

    public String toString() {
        return "[" + name + ":" + gamecount + " games, " + wincount + " win, " + losecount + " lose" + "]";
    }
}

【主类】

package com.atguigu.strategy.Sample;

public class Main {
    public static void main(String[] args) {
        // 让选手分别使用两种策略来比试
        Player player1 = new Player("Taro", new WinningStrategy(314));
        Player player2 = new Player("Hana", new ProbStrategy(12));
        for (int i = 0; i < 10000; i++) {
            Hand nextHand1 = player1.nextHand();
            Hand nextHand2 = player2.nextHand();
            if (nextHand1.isStrongerThan(nextHand2)) {
//                System.out.println("Winner:" + player1);
                player1.win();
                player2.lose();
            } else if (nextHand2.isStrongerThan(nextHand1)) {
//                System.out.println("Winner:" + player2);
                player1.lose();
                player2.win();
            } else {
//                System.out.println("Even...");
                player1.even();
                player2.even();
            }
        }
        System.out.println("Total result:");
        System.out.println(player1.toString());
        System.out.println(player2.toString());
    }
}

【运行】

Total result:
[Taro:10000 games, 3107 win, 3617 lose]
[Hana:10000 games, 3617 win, 3107 lose]

Process finished with exit code 0

问答

如果需要增加一个随机出手势的策略,需要怎么实现

答:在nextHand方法中使用随机数即可,因为全部都是随机的,不需要学习之前的经验,因此study方法可以是空方法

在示例程序中,Hand类的fight方法负责判断平局。在进行判断时,它使用的表达式不是this.handValue == h.value,而是this==h,请问为什么可以这样写?

答:因为使用了单例模式,只有三个手势实例,如果两个手势的handValue相同,说明两个实例就是同一个实例

编写Winningstrategy类时,won 字段的定义不是private boolean won = false; 而是写成了如下这样private boolean won;虽然写法不同,但是两者的运行结果一样,为什么?

答:因为全局变量如果没有被赋值就会被自动初始化:boolean类型默认是false;数值类型默认是0;引用类型默认是null。注意,局部变量不会被自动初始化

策略模式在JDK源码中的使用

简单来说,就是在排序的时候,可以指定不同的排序策略

package com.atguigu.jdk;

import java.util.Arrays;
import java.util.Comparator;


public class Strategy {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //数组
        Integer[] data = {9, 1, 2, 8, 4, 3};
        // 实现降序排序,返回-1放左边,1放右边,0保持不变

        // 说明
        // 1. 实现了 Comparator 接口(策略接口) , 匿名类对象:new Comparator<Integer>(){..}
        // 2. 对象 new Comparator<Integer>(){..} 就是实现了 策略接口 的对象
        // 3. public int compare(Integer o1, Integer o2){} 指定具体的处理策略
        Comparator<Integer> comparator = new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                if (o1 > o2) {
                    return -1;
                } else {
                    return 1;
                }
            }
        };

        // sort源码说明 传入数字和一个排序策略
      /*
       * public static <T> void sort(T[] a, Comparator<? super T> c) {
              if (c == null) {
                  sort(a); //默认方法
              } else {
                  if (LegacyMergeSort.userRequested)
                      legacyMergeSort(a, c); //使用策略对象c
                  else
                     // 使用策略对象c
                      TimSort.sort(a, 0, a.length, c, null, 0, 0);
              }
          }
       */
        // 方式1
        Arrays.sort(data, comparator);
      // 降序排序
        System.out.println(Arrays.toString(data));

        //方式2- 同时lambda 表达式实现 策略模式
        Integer[] data2 = {19, 11, 12, 18, 14, 13};

        // 换一个排序策略
        Arrays.sort(data2, (var1, var2) -> {
            if (var1.compareTo(var2) > 0) {
                return -1;
            } else {
                return 1;
            }
        });

        System.out.println("data2=" + Arrays.toString(data2));

    }

}

总结

【说明】

  • 策略模式的关键是分析项目中变化部分与不变部分

【优点】

  • 策略模式的核心思想是:多用组合/聚合,少用继承;用行为类来组合,而不是行为的继承
  • 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略 (或者行为)即可,避免了使用多重判断语句 (if…else if…else)
  • 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy类中使得你可以独立于其他Context改变它,使它易于切换、易于理解、易于扩展
  • 程序运行过程中也可以切换策略:如果使用Strategy模式,在程序运行中也可以切换ConcreteStrategy角色。如在内存较少的环境使用一种算法,内存较多的环境使用另外一种算法

【缺点】

  • 每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大

【问答】

为什么需要特意编写Strategy角色

答:当我们想要通过改善算法来提高算法的处理速度时,如果使用了Strategy模式,就不必修改Strategy角色的接口(API)了,仅仅修改ConcreteStrategy角色即可。而且,使用委托这种弱关联关系可以很方便地整体替换算法,这样也更加方便算法的比较

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/75376.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[NLP]LLM 训练时GPU显存耗用量估计

以LLM中最常见的Adam fp16混合精度训练为例&#xff0c;分析其显存占用有以下四个部分&#xff1a; GPT-2含有1.5B个参数&#xff0c;如果用fp16格式&#xff0c;只需要1.5G*2Byte3GB显存, 但是模型状态实际上需要耗费1.5B*1624GB. 比如说有一个模型参数量是1M&#xff0c;在…

k8s 自身原理之 Service

好不容易&#xff0c;终于来到 k8s 自身的原理之 关于 Service 的一部分了 前面我们用 2 个简图展示了 pod 之间和 pod 与 node 之间是如何通信息的&#xff0c;且通信的数据包是不会经过 NAT 网络地址转换的 那么 Service 又是如何实现呢&#xff1f; Service 我们知道是用…

Flask-SQLAlchemy

认识Flask-SQLAlchemy Flask-SQLAlchemy 是一个为 Flask 应用增加 SQLAlchemy 支持的扩展。它致力于简化在 Flask 中 SQLAlchemy 的使用。SQLAlchemy 是目前python中最强大的 ORM框架, 功能全面, 使用简单。 ORM优缺点 优点 有语法提示, 省去自己拼写SQL&#xff0c;保证SQL…

Unity游戏源码分享-俄罗斯方块unity2017

Unity游戏源码分享-俄罗斯方块unity2017 工程地址&#xff1a; https://download.csdn.net/download/Highning0007/88204011

【Linux初阶】system V - 共享内存

文章目录 前言一、共享内存初识1.共享内存的原理2.理解共享内存3.共享的内存的概念 二、共享内存函数1.shmget函数2.shmat函数3.shmdt函数4.shmctl函数 三、共享内存的查看方法及其特征四、共享内存的代码实现五、共享内存优缺点分析1.共享内存的优点2.共享内存的缺点 六、共享…

【Nacos2.24持久化到Postgres数据库适配——详细版】

Nacos2.24持久化到Postgres数据库适配 前言步骤拉取源码添加依赖修改源码编译打包修改配置测试运行 参考 前言 公司基于springboot实现了一套单体框架&#xff0c;目前我负责搭建SpringCloud微服务框架&#xff0c;需要用到nacos&#xff0c;但是由于公司特殊性&#xff0c;na…

1022.从根到叶的二进制之和

目录 一、题目 二、代码 一、题目 二、代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nu…

玩转 VS code 之下载篇

VSCode 简介 Visual Studio Code (简称 VS Code / VSC)&#xff0c;是2015 年由微软公司推出的一款免费开源的现代化轻量级代码编辑器&#xff0c;支持几乎所有主流的开发语言的语法高亮、智能代码补全、GIT 等特性&#xff0c;支持插件扩展等等 可用于 Windows&#xff0c;ma…

等保测评标准和规范有哪些?

等保测评标准和规范的出现&#xff0c;为我国信息安全等级保护制度的建立和健全提供了重要的保障。 作为信息安全领域的重要评估标准&#xff0c;等保测评旨在通过对信息系统、网络安全设备和安全产品等的安全性能、安全功能、安全管理、安全控制和安全审计等方面的要求进行检查…

【golang】怎样判断一个变量的类型?

怎样判断一个变量的类型&#xff1f; package mainimport "fmt"var container []string{"zero", "one", "two"} func main() {container : map[int]string{0: "zero", 1: "one", 2: "two"}fmt.Printf…

AWS中Lambda集成SNS

1.创建Lambda 在Lambda中&#xff0c;创建名为AWSSNSDemo的函数 use strict console.log(loading function); var aws require(aws-sdk); var docClient new aws.DynamoDB.DocumentClient(); aws.config.regionap-southeast-1;exports.handler function(event,context,cal…

stop job is running for Advanced key-value store

今天虚拟机磁盘撑满了&#xff0c;本来还能凑合运行&#xff0c;结果重启了下&#xff0c;就报了这个 stop job is running for Advanced key-value store (1min 59s / no limit) 解决方式很简单&#xff0c; 1、虚拟机关电源&#xff0c;任务管理器&#xff0c;关闭VM&#x…

LeetCode_03Java_1572. 矩阵对角线元素的和

给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线的和为&#xff1a;1 5 9 3 7 2…

我们为什么需要API管理系统?

我们为什么需要API管理系统&#xff1f; 随着web技术的发展&#xff0c;前后端分离成为越来越多互联网公司构建应用的方式。前后端分离的优势是一套Api可被多个客户端复用&#xff0c;分工和协作被细化&#xff0c;大大提高了编码效率&#xff0c;但同时也带来一些“副作用”:…

前端接口修改工具 Requestly具体操作

更新于2023年8月12日18:17:56&#xff0c;插件版本可能会变&#xff0c;界面可能会有所变化 插件下载地址&#xff1a;https://chrome.google.com/webstore/detail/requestly-open-source-htt/mdnleldcmiljblolnjhpnblkcekpdkpa 注意&#xff0c;必须用谷歌浏览器&#xff0c;…

Python-OpenCV中的图像处理-模板匹配

Python-OpenCV中的图像处理-模板匹配 模板匹配单对象的模板匹配多对象的模板匹配 模板匹配 使用模板匹配可以在一幅图像中查找目标函数&#xff1a; cv2.matchTemplate()&#xff0c; cv2.minMaxLoc()模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。 OpenCV 为我们提…

强制Edge或Chrome使用独立显卡【WIN10】

现代浏览器通常将图形密集型任务卸载到 GPU&#xff0c;以改善你的网页浏览体验&#xff0c;从而释放 CPU 资源用于其他任务。 如果你的系统有多个 GPU&#xff0c;Windows 10 可以自动决定最适合 Microsoft Edge 自动使用的 GPU&#xff0c;但这并不一定意味着最强大的 GPU。 …

力扣初级算法(数组拆分)

力扣初级算法&#xff08;数组拆分&#xff09; 每日一算法&#xff1a; 力扣初级算法&#xff08;数组拆分&#xff09; 学习内容&#xff1a; 1.问题描述 给定长度为 2n 的整数数组 nums &#xff0c;你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) …

leetcode 6914. 翻倍以链表形式表示的数字

给你一个 非空 链表的头节点 head &#xff0c;表示一个不含前导零的非负数整数。 将链表 翻倍 后&#xff0c;返回头节点 head 。 示例 1&#xff1a; 输入&#xff1a;head [1,8,9] 输出&#xff1a;[3,7,8] 解释&#xff1a;上图中给出的链表&#xff0c;表示数字 189 。返…

ubuntu下FFmpeg安装和使用以及CMakeLists.txt模板

sudo apt install ffmpeg sudo apt-get install libavfilter-devcmakelist模板 CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(ffmpeg_demo)# 设置ffmpeg依赖库及头文件所在目录&#xff0c;并存进指定变量 set(ffmpeg_libs_DIR /usr/lib/x86_64-linux-gnu) …