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

news2024/11/23 8:15:04

文章目录

  • 案例引入
    • 传统方案实现
      • 实现
      • 分析
  • 介绍
    • 基本介绍
    • 登场角色
  • 案例实现
    • 案例一
      • 类图
      • 实现
    • 案例二
      • 类图
      • 实现
      • 问答
  • 策略模式在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),进而综合两者的内容,让知识点更加全面

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

跨境电商ERP源码选择指南:如何挑选最适合您的方案

在如今充满机遇的跨境电商领域&#xff0c;选择适合自己的ERP源码方案至关重要。然而&#xff0c;众多选择使得挑选变得棘手。作为跨境电商ERP源码领域的专家&#xff0c;我将揭示7个权威建议&#xff0c;帮助您在海量方案中快速、准确地找到最适合您的ERP源码。让我们一起深入…

元宇宙赛道加速破圈 和数软件抓住“元宇宙游戏”发展新风口

当下海外游戏市场仍然具备较大的增长空间。据机构预测&#xff0c;至2025年全球移动游戏市场规模将达1606亿美元&#xff0c;对应2020-2025年复合增长率11&#xff05;。与此同时&#xff0c;随着元宇宙概念持续升温&#xff0c;国内外多家互联网巨头纷纷入场。行业分析平台New…

Android布局【GridLayout】

文章目录 GridLayout概述常见属性子控件属性项目结构主要代码 GridLayout概述 GridLayout也名网格布局,该布局与TableLayout类似&#xff0c;但与其相比&#xff0c;GridLayout会更加的灵活&#xff0c;比如 TableLayout不能将两行进行一个合并&#xff0c;只能将两列进行一个…

新能源汽车电控系统

新能源汽车电控系统主要分为&#xff1a;三电系统电控系统、高压系统电控系统、低压系统电控系统 三电系统电控系统 包括整车控制器、电池管理系统、驱动电机控制器等。 整车控制器VCU 整车控制器作为电动汽车中央控制单元&#xff0c;是整个控制系统的核心&#xff0c;也是…

C#_编码奥秘

什么是编码&#xff1f; 将信息&#xff08;文字、图像、声音、视频、代码&#xff09;使用特定的符号组合表示出来的过程。 任何信息载体都是编码&#xff0c;编码是 信息通信 与 信息处理 技术的基础。 编码与信息通信 长亮三下&#xff08;S&#xff09;&#xff0c;短亮三…

中小企业选择CRM系统需要有哪些功能?

对于中小企业来说&#xff0c;选择一个合适的CRM系统是非常重要的&#xff0c;一款好用的CRM可以帮助企业提高业务效率&#xff0c;获得更多收益。那么&#xff0c;中小企业CRM系统的主要特点有哪些呢&#xff1f;下面我们从四个方面来说说。 1、功能&#xff1a; 功能是指CR…

秦岭地形图、水系图、全景图

来源&#xff1a;头条留白sy&#xff0c;星球研究所等&#xff0c;转自&#xff1a;地理科学研究苑

Python数学函数、字符串和对象

学习目标&#xff1a; 使用math模块中的函数解决数学问题表示和处理字符串和字符使用ASCII和Unicode对字符编码使用ord函数获取一个字符的数值编码以及使用chr函数将一个数值编码转换成一个字符使用转义序列表示特殊字符调用带参数end的print函数使用str函数将数字转换成字符串…

小调查:你的流量卡是在线上买的还是在线下买的?

可能大家都知道&#xff0c;现在不管是线上还是线下都可以办理流量卡&#xff0c;线上的流量卡资费便宜一些&#xff0c;线下的流量卡功能更多一些&#xff0c;那么你是在线上购买的流量卡&#xff0c;还是在线下给我们的流量卡呢&#xff1f; ​ 都知道一分钱一分货&#xff…

电脑屏幕闪烁?别慌!解决方法在这!

“我新买了一台电脑&#xff0c;还没用几天呢&#xff0c;就出现了电脑屏幕闪烁的情况&#xff0c;这让我感到很烦躁。有什么方法可以解决电脑屏幕闪烁的问题呢&#xff1f;” 使用电脑的过程中&#xff0c;我们不难发现电脑屏幕有时候会出现闪烁的情况&#xff0c;这会导致使用…

试卷扫描成电子版方法分享,这个方法不要错过

很多时候&#xff0c;为了方便传输我们需要将试卷扫描成电子版进行存档&#xff0c;以备不时之需。很多小伙伴如果遇到试卷需要扫描转成电子版可能就不知道该如何操作了&#xff0c;其实试卷扫描是一项非常重要的工作&#xff0c;因此需要注意一些方法和细节。以下是试卷扫描成…

一个基于SpringBoot+Vue前后端分离高校心理健康系统详细设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

库存管理系统哪个好?亿发云南省大型智能WMS仓储信息解决方案,免费更新

在当今激烈的仓储物流竞争中&#xff0c;企业的成功关键在于加强作业效率和降低话费。随着科技的不断更新&#xff0c;传统仓储方式已逐渐被智能仓储管理系统&#xff08;WMS&#xff09;取代&#xff0c;并逐渐成为行业趋势。大数据时代下&#xff0c;引进行之有效的智能管理系…

Windows系统Git安装教程(详细Git安装过程)

获取Git安装程序 到Git官网下载&#xff0c;网站地址&#xff1a;https://git-scm.com/downloads&#xff0c;如下图&#xff1a; 因为我们是用Windows系统上的浏览器访问的&#xff0c;Git官网自动之别到了我使用的操作系统&#xff0c;所以右侧直接显示下载使用Windows系统的…

容器云平台监控告警体系—— Prometheus发送告警机制

1、概述 在Prometheus的架构中告警被划分为两个部分&#xff0c;在Prometheus Server中定义告警规则以及产生告警&#xff0c;Alertmanager组件则用于处理这些由Prometheus产生的告警。本文主要讲解Prometheus发送告警机制也就是在Prometheus Server中定义告警规则和产生告警部…

HTML5+CSS3自用笔记

助解&#xff1a;解析编译&#xff0c;加载运行 浏览器的渲染过程 JS加载执行 普通js/sync&#xff1a;阻塞 DOM加载解析 async&#xff1a;下载完就执行&#xff0c;无依赖 <script type"text/javascript" src"x.min.js" async"async"&g…

HoudiniVex笔记_P23_SDFBasics有向距离场

原视频&#xff1a;https://www.youtube.com/playlist?listPLzRzqTjuGIDhiXsP0hN3qBxAZ6lkVfGDI Bili&#xff1a;Houdini最强VEX算法教程 - VEX for Algorithmic Design_哔哩哔哩_bilibili Houdini版本&#xff1a;19.5 1、什么是SDF Houdini支持两种体积类型&#xff0c;…

c++11 标准模板(STL)(std::basic_stringbuf)(三)

定义于头文件 <sstream> template< class CharT, class Traits std::char_traits<CharT>, class Allocator std::allocator<CharT> > class basic_stringbuf : public std::basic_streambuf<CharT, Traits> std::basic_stringbuf…

location rewrite

Nginx location 匹配的规则和优先级 Nginx常用的变量 rewrite: 重定向功能 Location 匹配 URI URI&#xff1a;统一资源的表示符&#xff0c;是一种字符串标识&#xff0c;用于标识抽象或者物理资源 先来巩固一些与location结合使用的正则表达式 正则表达式&#xff1a;匹…

【833. 字符串中的查找与替换】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 你会得到一个字符串 s (索引从 0 开始)&#xff0c;你必须对它执行 k 个替换操作。替换操作以三个长度均为 k 的并行数组给出&#xff1a;indices, sources, targets。 要完成第 i 个替换操作: 检查…