• 23 种设计模式之工厂模式


    23 种设计模式之工厂模式



    一、简单(静态)工厂模式

    1. 认识

    • 一个工厂类根据传入的参量决定创建出那一种产品类的实例。因为逻辑实现简单,所以称为简单工厂模式,也因为工厂中的方法一般设置为静态,所以也称为静态工厂,它不属于23种模式。
    • 简单工厂模式专门定义一个工厂类来负责创建其他类的实例,被创建的实例通常都具有共同的父类,在工厂类中,可以根据参数的不同返回不同类的实例。升级版本简单工厂模式,通过反射根据类的全路径名生成对象。
    • 简单工厂模式就是将这部分创建对象语句分离出来,由工厂类来封装实例化对象的行为,修改时只需要修改类中的操作代码,使用时调用该类不需要考虑实例化对象的行为,使得后期代码维护升级更简单方便,有利于代码的可修改性与可读性。
    • 如果增加新的产品的话,需要修改工厂类的判断逻辑,违背开闭原则。

    2. UML图

    简单介绍一下UML:

    泛化: 继承 带三角箭头的实线,箭头指向类

    实现: 实现 带三角箭头的虚线,箭头指向接口

    依赖: new A的对象当作方法参数传递进来作为B类的局部变量 带箭头的虚线,指向被使用者

    关联: 一个类作为另一个类的成员变量 带普通箭头的实心线,指向被拥有者

    聚合: new A的对象当作方法参数传递进来作为B类的成部变量 带空心菱形的实心线,菱形指向整体

    组合: new A的对象当作构造方法参数传递进来作为B类的成部变量或者A类作为B类成 员变量并已经new A(A类和B类具有相同的生命周期) 带实心菱形的实线,菱形指向整体 总结:各种关系的强弱顺序:泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖

    区分:

    1. 如果B类作为了A类的成员变量(has的关系),则一般是A类与B类是关联(A类与B类平级)、聚合(A类是整体,B类是部分)、组合的关系(A类是整体,B类是部分,且A类B类有相同的生命周期,)根据上下文语意区分:聚合(B类即便不在A类中也可以单独存在),组合(B类不在A类中就无法单独存在)。
    2. 如果B类作为了A类的局部变量(use的关系),方法的形参,或者对静态方法的调用一般是依赖关系。

    在这里插入图片描述
    UML说明: 苹果手机和红米手机继承了手机这个抽象类,工厂类里根据客户端传入的参数生成相应的对象,如,客户说要红米,工厂给客户一个红米手机,客户说要苹果,工厂给客户一个苹果手机。

    简单工厂有三个对象:

    • 抽象产品类:提供抽象方法供具体产品类实现
    • 具体产品类:提供具体的产品
    • 工厂:根据内部逻辑返回相应的产品

    3. 代码实现

    1. 抽象产品类Phone 这里可以是类,也可以是接口或者抽象类,千万不要思维定式。我比较喜欢面向接口编程,所以我这里用了接口。
    public interface Phone {
         void produce();
    }
    
    • 1
    • 2
    • 3

    具体产品类

    public class ApplePhoneImpl implements Phone{
        @Override
        public void produce() {
            System.out.println("生产苹果手机");
        }
    }
    public class RedmiPhoneImpl implements Phone{
        @Override
        public void produce() {
            System.out.println("生产了红米手机");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    工厂类

    public class Factory {
        
        public Phone getPhone(String type){
            Phone phone = null;
            if("红米".equals(type)){
                phone = new RedmiPhoneImpl();
            }else if("苹果".equals(type)){
                phone = new ApplePhoneImpl();
            }//.....
            return phone;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    测试

    	@Test
        public void test1(){
            Factory factory = new Factory();
            
            Phone redmiPhone = factory.getPhone("红米");
            System.out.println(redmiPhone);
            redmiPhone.produce();
    
            Phone applePhone = factory.getPhone("苹果");
            System.out.println(applePhone);
            applePhone.produce();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运行结果如下:

    在这里插入图片描述

    4. 总结

    • 优点: 只需要传入一个正确的参数,就可以获取你所需要的对象而无需知道其创建对象的细节
    • 缺点: 扩展性差,当增加新的产品需要修改工厂类的判断逻辑,违背开闭原则,如我想要买一个华为手机的话,除了新增华为手机这个产品类,还需要修改工厂中的逻辑

    5. 升级版本

    通过反射创建对象,以改进了之前提到的缺点(增加新的产品需要修改工厂类的判断逻辑),现在增加新的具体产品的时候不需要修改工厂中的代码。满足了开闭原则。

    工厂类代码如下

    public class FactoryPlus {
        public Phone getPhone(Class clazz) throws Exception {
            return (Phone) Class.forName(clazz.getName()).newInstance();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试代码如下:

    @Test
    public void test2() throws Exception {
            FactoryPlus factory = new FactoryPlus();
    
            Phone redmiPhone = factory.getPhone(RedmiPhoneImpl.class);
            System.out.println(redmiPhone);
            redmiPhone.produce();
    
            Phone applePhone = factory.getPhone(ApplePhoneImpl.class);
            System.out.println(applePhone);
            applePhone.produce();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运行结果如下:

    在这里插入图片描述

    工厂类中的方法逻辑,是利用反射机制生成对象返回,好处是增加一种产品时,不需要修改工厂类中的代码。满足了开闭原则。缺点:这种写法粗看牛逼,细想之下,不谈reflection的效率还有以下问题:个人觉得不好,因为Class.forName(clz.getName()).newInstance()调用的是无参构造函数生成对象,它和newObject()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂,没有实际意义。2 不同的产品需要不同额外参数的时候 不支持。

    6. 再升级(重要)

    工厂类

    public class FactoryPlusPlus {
    
        /**
         * 熟悉吧!!!spring ioc 就是通过将下面的这句话配置在配置文件中,再利用反射创建对象,
         * 这就是spring ioc的原理:工厂+配置文件+反射!!以达到彻底解耦的目的**/
        private static String className="com.wander.design.simplefactory.product.ApplePhoneImpl";
    
        public static Phone getPhone() throws Exception {
            return (Phone) Class.forName(className).newInstance();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试代码

    @Test
    public void test3() throws Exception {
            Phone phone = FactoryPlusPlus.getPhone();
            phone.produce();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    说明

    spring ioc容器的原理就是这种方式:工厂+配置文件+反射,spring通过读取配置文件(),获取到className再利用反射机制Class.forName(className).newInstance()得到对象赋值给配置文件里bean标签的id属性的值,就是工厂生成的对象名。

    优点: 就是满足OCP原则,在不修改源代码的前提下切换底层的实现,达到解耦的目的!

    7. 开发常用版本:多方法工厂

    使用以上两种方法的工厂,都有两个缺点:一是不同的产品需要不同额外参数的时候不支持。二是如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。

    而多方法的工厂模式为不同产品,提供不同的生产方法,使用时 需要哪种产品就调用该种产品的方法,使用方便、容错率高。

    工厂类代码如下:

    public class FactoryMoreMethod {
    
        public static Phone getApple(){
            return new ApplePhoneImpl();
        }
    
        public static Phone getRedmi(){
            return new RedmiPhoneImpl();
        }
    
        /**新增华为手机产品,只需要在工厂中增加一个静态方法即可,不需要修改原有的方法**/
        public static Phone getHonor(){
            return new HonorPhoneImpl();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    测试代码

    	@Test
        public void test3() throws Exception {
            Phone apple = FactoryMoreMethod.getApple();
            apple.produce();
    
            Phone redmi = FactoryMoreMethod.getRedmi();
            redmi.produce();
    
            Phone honor = FactoryMoreMethod.getHonor();
            honor.produce();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    应用场景

    查看java源码:java.util.concurrent.Executors类便是一个生成Executor 的工厂 ,其采用的便是 多方法静态工厂模式:例如ThreadPoolExecutor类构造方法有5个参数,其中三个参数写法固定,前两个参数可配置,如下写。

    public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    又如JDK想增加创建ForkJoinPool类的方法了,只想配置parallelism参数,便在类里增加一个如下的方法:

    public static ExecutorService newWorkStealingPool(int parallelism) {
            return new ForkJoinPool
                (parallelism,
                 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                 null, true);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    总结:多方法工厂的优势,方便创建同种类型的复杂参数对象。

    8. 应用场景

    • 在任何需要生成复杂对象的地方,都可以使用工厂方法模式。直接用new可以完成的不需要用工厂模式个人理解,重点就是这个复杂(构造函数有很多参数)和 是否可以 直接用new。
    • 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。(对于升级后的简单工厂模式只知道类名即可)
    • 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。(对于升级后的简单工厂模式已解决这个问题,符合开闭原则)
    • 简单工厂在源码中的使用–Calendar:
    Calendar cal = Calendar.getInstance(zone.toTimeZone(), locale);
    public static Calendar getInstance(TimeZone zone, Locale aLocale) {
          return createCalendar(zone, aLocale);
      }
    
      private static Calendar createCalendar(TimeZone zone, Locale aLocale) {
          '部分删减'
          Calendar cal = null;
    
          if (aLocale.hasExtensions()) {
              String caltype = aLocale.getUnicodeLocaleType("ca");
              if (caltype != null) {
                  switch (caltype) {
                  case "buddhist":
                  cal = new BuddhistCalendar(zone, aLocale);
                      break;
                  case "japanese":
                      cal = new JapaneseImperialCalendar(zone, aLocale);
                      break;
                  case "Gregory":
                      cal = new GregorianCalendar(zone, aLocale);
                      break;
                  }
              }
          }
          if (cal == null) {
              if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                  cal = new BuddhistCalendar(zone, aLocale);
              } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                         && aLocale.getCountry() == "JP") {
                  cal = new JapaneseImperialCalendar(zone, aLocale);
              } else {
                  cal = new GregorianCalendar(zone, aLocale);
              }
          }
          return cal;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    回到顶部


    二、工厂方法模式

    1. 认识

    • 一句话来说就是,定义一个创建对象的接口,让子类决定实例化那个类。因为当需要增加一个新的产品时,我们需要增加一个具体的产品类和与之对应的具体子工厂,然后在具体子工厂方法中进行对象实例化,所以称为工厂方法模式。
    • 具体来说就是定义一个用于创建对象的工厂接口,但让实现这个工厂接口的子类来决定实例化哪个具体产品类,工厂方法让类的实例化推迟到子类中进行。
    • 工厂方法模式非常符合“开闭原则”,当需要增加一个新的产品时,我们只需要增加一个具体的产品类和与之对应的具体工厂即可,无须修改原有系统。同时在工厂方法模式中用户只需要知道生产产品的具体工厂即可,无须关系产品的创建过程,甚至连具体的产品类名称都不需要知道。
    • 虽然他很好的符合了“开闭原则”,但是由于每新增一个新产品时就需要增加两个类,这样势必会导致系统的复杂度增加。

    2. UML类图

    UML说明: 苹果手机和红米手机实现了手机这个抽象类,苹果工厂和红米工厂实现了抽象工厂,苹果工厂当然要生产(依赖)苹果手机,红米工厂当然要生产(依赖)红米。客户要买苹果手机要去问苹果工厂要苹果手机,客户要买红米手机当然要去问红米工厂要红米手机。

    工厂方法有四个对象:

    • 抽象产品类:提供抽象方法供具体产品类实现
    • 具体产品类:提供具体的产品
    • 抽象工厂:提供抽象方法供具体工厂实现
    • 具体工厂:提供具体的工厂

    3. 代码实现

    (1)抽象产品类和简单工厂的抽象产品类一样

    (2)具体产品类和简单工厂的具体产品类一样

    (3)抽象工厂

    public interface Factory {
        Phone getPhone();
    }
    
    • 1
    • 2
    • 3

    具体工厂

    public class AppleFactoryImpl implements Factory{
    
        @Override
        public Phone getPhone() {
            return new ApplePhoneImpl();
        }
    }
    
    public class RedmiFactoryImpl implements Factory{
        @Override
        public Phone getPhone() {
            return new RedmiPhoneImpl();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    测试代码

    	@Test
    	public void test1(){
    
            Factory applePhoneFactory = new AppleFactoryImpl();
            Factory redmiPhoneFactory = new RedmiFactoryImpl();
    
            Phone applePhone = applePhoneFactory.getPhone();
            Phone redmiPhone = redmiPhoneFactory.getPhone();
    
            System.out.println(applePhone);
            System.out.println(redmiPhone);
    
            applePhone.produce();
            redmiPhone.produce();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4. 总结

    优点:

    ①用户只需要关心所需产品的对应工厂,无需关心细节

    ②完全支持开闭原则,提高可扩展性。所谓的开闭原则就是对扩展开放,对修改关闭,再说白点就是实现工厂方法以后要进行扩展时不需要修改原有代码,只需要增加一个工厂实现类和产品实现类就可以。这样的好处可以降低因为修改代码引进错误的风险。

    缺点:

    ①每加入一种产品,会创建一个具体工厂类和具体产品类,因此,类的个数容易过多,增加复杂度。

    ②抽象工厂和抽象产品增加了系统的抽象性和理解难度

    5. 工厂方法与简单工厂的区别

    ①可以看出,工厂方法模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。

    ②工厂方法使一个产品类的实例化延迟到其具体工厂子类.

    ③工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改已有的类。

    ④而简单工厂需要修改工厂类的方法,多方法静态工厂模式需要增加一个静态方法。

    缺点:引入抽象工厂层后,每次新增一个具体产品类,也要同时新增一个具体工厂类,所以我更青睐多方法静态工厂,每次新增一个具体产品类,工厂只需要新增一个静态方法。

    6. 应用场景

    (1)客户端不知道它所需要的对象的类。(需要知道所需的对象的类使用升级版简单工厂模式,需要知道所需的参数的类使用简单工厂模式)。

    (2)抽象工厂类通过其子类来指定创建哪个对象。

    (3)简单工厂在源码中的使用–Collection:Collection(抽象工厂):

    public interface Collection<E> extends Iterable<E> {
     Iterator<E> iterator();
    }
    
    • 1
    • 2
    • 3

    ArrayList(具体工厂):

    public class ArrayList<E>{
        public Iterator<E> iterator() {
            return new Itr();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Iterator(抽象产品):

    public interface Iterator<E> {
     boolean hasNext();
    }
    
    • 1
    • 2
    • 3

    Itr(具体产品):

    private class Itr implements Iterator<E> {
     int cursor; // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
    
        public boolean hasNext() {
                return cursor != size;
        }
        '省略代码...'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    回到顶部


    三、抽象工厂模式

    1.认识

    ①一句话来说就是,创建相关或依赖对象的家族,而无需明确指定具体类。因为我们可以定义具体产品类实现不止一个抽象工厂接口,一个工厂也可以生成不止一个产品类,是三个模式中较为抽象,并具一般性的模式。我们在使用中要注意使用抽象工厂模式的条件。

    ②所谓抽象工厂模式就是提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。他允许客户端使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。它的优点是隔离了具体类的生成,使得客户端不需要知道什么被创建了,而缺点就在于新增新的行为会比较麻烦,因为当添加一个新的产品对象时,需要更改接口及其下所有子类。

    2. UML类图

    在这里插入图片描述

    UML说明: 具体的苹果手机产品和具体的红米手机产品实现了手机产品抽象类,具体的苹果充电器产品和具体的红米充电器产品实现了充电器产品抽象类。具体的苹果工厂和具体的红米工厂实现了手机抽象工厂,然后苹果工厂生产苹果手机和苹果充电器,红米工厂生成红米手机和红米充电器。客户想要苹果手机和苹果充电器就要向苹果工厂要产品(对象),客户想要红米手机和红米充电器就要向红米工厂要产品(对象)。

    工厂方法有四个对象:

    • 抽象产品类:为每种具体产品声明接口,如图中Phone手机抽象类和Charger充电器抽象类
    • 具体产品类:定义了工厂生产的具体产品对象,实现抽象产品接口声明的业务方法,如图中ApplePhoneImpl、RedmiPhoneImpl,AppleChargerImpl,RedmiChargerImpl
    • 抽象工厂:它声明了一组用于创建一种产品的方法,每一个方法对应一种产品,如上述类图中的Factory就定义了两个方法,分别创建Phone和Charger
    • 具体工厂:它实现了在抽象工厂中定义的创建产品的方法,生产一组具体产品,这组产品构件成了一个产品种类,每一个产品都位于某个产品等级结构中,如上述类图中的AppleFactoryImpl和RedmiFactoryImpl

    3. 代码实现

    抽象的产品

    public interface Phone {
        void produce();
    }
    
    public interface Charger {
        void produce();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    具体的产品

    1. 苹果具体的产品
    public class AppleChargerImpl implements Charger{
    
        @Override
        public void produce() {
            System.out.println("生产苹果充电器");
        }
    }
    
    public class ApplePhoneImpl implements Phone {
        @Override
        public void produce() {
            System.out.println("生产苹果手机");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 红米具体的产品
    public class RedmiChargerImpl implements Charger{
        @Override
        public void produce() {
            System.out.println("生产红米充电器");
        }
    }
    
    
    public class RedmiPhoneImpl implements Phone {
        @Override
        public void produce() {
            System.out.println("生产了红米手机");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 抽象工厂
    public interface Factory {
        Phone getPhone();
    
        Charger getCharger();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 具体的工厂
    public class AppleFactoryImpl implements Factory {
    
        @Override
        public Phone getPhone() {
            return new ApplePhoneImpl();
        }
    
        @Override
        public Charger getCharger() {
            return new AppleChargerImpl();
        }
    }
    
    public class RedmiFactoryImpl implements Factory {
        
        @Override
        public Phone getPhone() {
            return new RedmiPhoneImpl();
        }
    
        @Override
        public Charger getCharger() {
            return new RedmiChargerImpl();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    1. 测试代码
    	@Test
        public void test1(){
            Factory appleFactory = new AppleFactoryImpl();
            Phone applePhone = appleFactory.getPhone();
            Charger appleCharger = appleFactory.getCharger();
            System.out.println(appleFactory);
            applePhone.produce();
            appleCharger.produce();
    
            Factory redmiFactory = new RedmiFactoryImpl();
            Phone redmiPhone = redmiFactory.getPhone();
            Charger redmiCharger = redmiFactory.getCharger();
            System.out.println(redmiFactory);
            redmiPhone.produce();
            redmiCharger.produce();
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果

    在这里插入图片描述

    4. 总结

    优点:

    • 具体产品在应用层代码隔离,无须关系创建细节
    • 将一个系列的产品统一到一起创建
    • 对于增加新的产品族(一个具体工厂就是一个产品族),抽象工厂模式很好地支持了“开闭原则”,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。

    缺点:

    • 规定了所有可能被创建的产品集合,产品族扩展新的产品(工厂中添加新的方法)困难。如果产品族扩展新的产品,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了“开闭原则”。
    • 增加了系统的抽象性和理解难度

    5. 应用场景

    抽象工厂在实际的开发中运用并不多,主要是在开发工程中很少会出现多个产品种类的情况,大部分情况使用以上两种工厂模式即可解决

    6. 个人总结

    一句话总结工厂模式:方便创建 同种产品类型的 复杂参数 对象工厂模式重点就是适用于 构建同产品类型(同一个接口 基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。(如果构建某个对象很复杂,需要很多参数,但这些参数大部分都是“不固定”的,应该使用建造者Builder模式)

    回到顶部


  • 相关阅读:
    全网显示 IP 归属地,这背后的技术你知道吗?
    vue的配置环境变量
    2022牛客暑期多校训练营1 个人题解
    正则表达式
    IPv6与VoIP——配置Cisco CME实现VoIP实验
    【华为上机真题 2022】停车场车辆统计
    弘辽科技:淘宝直通车销量怎么是0?0销量怎么起步
    求推荐几款http可视化调试工具?
    【Git】bad signature 0x00000000 index file corrupt. fatal: index file corrupt
    再服务器上配置其他版本的DGL
  • 原文地址:https://blog.csdn.net/weixin_47410172/article/details/126580354