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

介绍

  • 建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同属性的对象
  • 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,不需要知道内部的具体构建细节(如用户在选购汽车的时候,只需要选择好方向盘、轮胎、发动机类型,不需要知道零件是怎么制造出来的)

出场角色

在这里插入图片描述

  • Product(产品):一个具体的产品对象
  • Builder (抽象建造者): 创建指定roduct对象的各个部件的接口/抽象类,用于将建造流程整理出来,但是不在乎建造细节,即只声明抽象方法
  • ConcreteBuilder (具体建造者): 实现抽象建造者的接口
  • Director(指挥者/监工): 使用Builder接口来生成示例。它主要是用于创建一个复杂的对象。它主要有两个作用,一是隔离客户与对象的生产过程,二是控制产品对象的生产过程。
  • Client(使用者):让Director干活,使用建造者模式

案例

盖房子需求

房子有多种类型,比如普通房,高楼,别墅,建房子的过程包括打桩砌墙封顶多个步骤,不同类型的房子建造的流程差不多,但是里面的方法有所差异

原始实现

在这里插入图片描述

【抽象房子类】

package com.atguigu.builder;

public abstract class AbstractHouse {

   /**
    * 打地基
    */
   public abstract void buildBasic();

   /**
    * 砌墙
    */
   public abstract void buildWalls();

   /**
    * 封顶
    */
   public abstract void roofed();

   public void build() {
      buildBasic();
      buildWalls();
      roofed();
   }

}

【普通房子】

package com.atguigu.builder;

public class CommonHouse extends AbstractHouse {

   @Override
   public void buildBasic() {
      System.out.println(" 普通房子打地基 ");
   }

   @Override
   public void buildWalls() {
      System.out.println(" 普通房子砌墙 ");
   }

   @Override
   public void roofed() {
      System.out.println(" 普通房子封顶 ");
   }

}

【高楼】

package com.atguigu.builder;

public class HignBuilding extends AbstractHouse {

   @Override
   public void buildBasic() {
      System.out.println(" 高楼打地基 ");
   }

   @Override
   public void buildWalls() {
      System.out.println(" 高楼房子砌墙 ");
   }

   @Override
   public void roofed() {
      System.out.println(" 高楼房子封顶 ");
   }

}

【主类】

package com.atguigu.builder;

public class Client {

   public static void main(String[] args) {
      CommonHouse commonHouse = new CommonHouse();
      commonHouse.build();
      System.out.println("-------------------------------------------------------------------------------------------------------");
      HignBuilding highBuilding = new HignBuilding();
      highBuilding.build();
   }

}

【运行】

 普通房子打地基 
 普通房子砌墙 
 普通房子封顶 
-------------------------------------------------------------------------------------------------------
 高楼打地基 
 高楼房子砌墙 
 高楼房子封顶 

Process finished with exit code 0

分析

  • 优点:比较好理解,简单易操作
  • 缺点:程序结构过于简单,没有设计缓存层对象,程序的扩展和维护不好。这种设计方案,把产品(即:房子) 和 创建产品的过程(即:建房子流程) 封装在一起,耦合性增强了
  • 改进:使用建造者模式,将产品和产品建造过程解耦

建造者模式

在这里插入图片描述

【抽象房子】

package com.atguigu.builder.improve;

/**
 * 产品->Product
 */
public class House {
    private String basic;
    private String wall;
    private String roofed;

    public String getBasic() {
        return basic;
    }

    public void setBasic(String basic) {
        this.basic = basic;
    }

    public String getWall() {
        return wall;
    }

    public void setWall(String wall) {
        this.wall = wall;
    }

    public String getRoofed() {
        return roofed;
    }

    public void setRoofed(String roofed) {
        this.roofed = roofed;
    }

    public House(String basic, String wall, String roofed) {
        this.basic = basic;
        this.wall = wall;
        this.roofed = roofed;
    }

    public House() {
    }

    @Override
    public String toString() {
        return "House{" +
                "basic='" + basic + '\'' +
                ", wall='" + wall + '\'' +
                ", roofed='" + roofed + '\'' +
                '}';
    }
}

【抽象建造者】

package com.atguigu.builder.improve;


/**
 * 抽象的建造者
 */
public abstract class HouseBuilder {
   /**
    * 组合House
    */
   protected House house = new House();

   //-------------------------将建造的流程写好--------------------------

   /**
    * 打地基
    */
   public abstract void buildBasic();

   /**
    * 砌墙
    */
   public abstract void buildWalls();

   /**
    * 封顶
    */
   public abstract void roofed();

   /**
    * 建造好房子后将产品(房子) 返回
    * @return
    */
   public House buildHouse() {
      return house;
   }

}

【具体建造者:普通房】

package com.atguigu.builder.improve;

/**
 * 具体建造者
 */
public class CommonHouse extends HouseBuilder {

   @Override
   public void buildBasic() {
      System.out.println(" 普通房子打地基5米 ");
      super.house.setBasic("地基5米");
   }

   @Override
   public void buildWalls() {
      System.out.println(" 普通房子砌墙10cm ");
      super.house.setWall("墙10cm");
   }

   @Override
   public void roofed() {
      System.out.println(" 普通房子屋顶 ");
      super.house.setRoofed("普通房子屋顶");
   }

}

【具体建造者:高楼】

package com.atguigu.builder.improve;

/**
 * 具体建造者
 */
public class HighBuilding extends HouseBuilder {

   @Override
   public void buildBasic() {
      System.out.println(" 高楼的打地基100米 ");
      super.house.setBasic("地基100米");
   }

   @Override
   public void buildWalls() {
      System.out.println(" 高楼的砌墙20cm ");
      super.house.setWall("墙20cm");
   }

   @Override
   public void roofed() {
      System.out.println(" 高楼的透明屋顶 ");
      super.house.setRoofed("透明屋顶");
   }

}

【指挥者】

package com.atguigu.builder.improve;

/**
 * 指挥者,调用制作方法,返回产品
 */
public class HouseDirector {
   /**
    * 聚合
    */
   HouseBuilder houseBuilder = null;

   /**
    * 方式一:构造器传入 houseBuilder
    * @param houseBuilder
    */
   public HouseDirector(HouseBuilder houseBuilder) {
      this.houseBuilder = houseBuilder;
   }

   /**
    * 方式二:通过setter 传入 houseBuilder
    * @param houseBuilder
    */
   public void setHouseBuilder(HouseBuilder houseBuilder) {
      this.houseBuilder = houseBuilder;
   }

   /**
    * 指挥者统一管理建造房子的流程
    * @return
    */
   public House constructHouse() {
      houseBuilder.buildBasic();
      houseBuilder.buildWalls();
      houseBuilder.roofed();
      return houseBuilder.buildHouse();
   }


}

【客户端】

package com.atguigu.builder.improve;

public class Client {
    public static void main(String[] args) {

        ///盖普通房子
        //准备创建房子的指挥者
        HouseDirector houseDirector = new HouseDirector(new CommonHouse());
        //完成盖房子,返回产品(普通房子)
        House commonHouse = houseDirector.constructHouse();
        System.out.println("普通房子:" + commonHouse.toString());

        System.out.println("---------------------------------------------------------------------------------------------");
        ///盖高楼
        //重置建造者,改成修高楼
        houseDirector.setHouseBuilder(new HighBuilding());
        //完成盖房子,返回产品(高楼)
        House highBuilding = houseDirector.constructHouse();
        System.out.println("高楼:" + highBuilding.toString());
    }
}

【运行】

 普通房子打地基5米 
 普通房子砌墙10cm 
 普通房子屋顶 
普通房子:House{basic='地基5米', wall='墙10cm', roofed='普通房子屋顶'}
---------------------------------------------------------------------------------------------
 高楼的打地基100米 
 高楼的砌墙20cm 
 高楼的透明屋顶 
高楼:House{basic='地基100米', wall='墙20cm', roofed='透明屋顶'}

Process finished with exit code 0

文档编写

在这里插入图片描述

【抽象建造者】

package com.atguigu.builder.Sample;

public abstract class Builder {
    /**
     * 编写标题
     * @param title
     */
    public abstract void makeTitle(String title);

    /**
     * 编写字符串
     * @param str
     */
    public abstract void makeString(String str);

    /**
     * 编写条目
     * @param items
     */
    public abstract void makeItems(String[] items);

    /**
     * 完成文档编写
     */
    public abstract void close();
}

【具体建造者一】

package com.atguigu.builder.Sample;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class HTMLBuilder extends Builder {
    /**
     * 文件名
     */
    private String filename;
    /**
     * 用于编写文件的PrintWriter
     */
    private PrintWriter writer;

    /**
     * HTML文件的标题
     *
     * @param title
     */
    public void makeTitle(String title) {
        // 将标题作为文件名
        filename = title + ".html";
        try {
            // 生成 PrintWriter
            writer = new PrintWriter(new FileWriter(filename));
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 输出标题
        writer.println("<html><head><title>" + title + "</title></head><body>");
        writer.println("<h1>" + title + "</h1>");
    }

    /**
     * HTML文件中的字符串
     *
     * @param str
     */
    public void makeString(String str) {
        // 用<p>标签输出
        writer.println("<p>" + str + "</p>");
    }

    /**
     * HTML文件中的条目
     *
     * @param items
     */
    public void makeItems(String[] items) {
        // 用<ul>和<li>输出
        writer.println("<ul>");
        for (int i = 0; i < items.length; i++) {
            writer.println("<li>" + items[i] + "</li>");
        }
        writer.println("</ul>");
    }

    /**
     * 完成文档
     */
    public void close() {
        // 关闭标签
        writer.println("</body></html>");
        // 关闭文件
        writer.close();
    }

    public String getResult() {
        // 编写完成的文档
        return filename;
    }
}

【具体建造者二】

package com.atguigu.builder.Sample;

public class TextBuilder extends Builder {
    /**
     * 文档内容保存在该字段中
     */
    private StringBuffer buffer = new StringBuffer();

    /**
     * 纯文本的标题
     *
     * @param title
     */
    public void makeTitle(String title) {
        // 装饰线
        buffer.append("==============================\n");
        // 为标题添加『』
        buffer.append("『" + title + "』\n");
        // 换行
        buffer.append("\n");
    }

    /**
     * 纯文本的字符串
     *
     * @param str
     */
    public void makeString(String str) {
        // 为字符串添加■
        buffer.append('■' + str + "\n");
        // 换行
        buffer.append("\n");
    }

    /**
     * 纯文本的条目
     *
     * @param items
     */
    public void makeItems(String[] items) {
        for (int i = 0; i < items.length; i++) {
            // 为条目添加・
            buffer.append(" ・" + items[i] + "\n");
        }
        // 换行
        buffer.append("\n");
    }

    /**
     * 完成文档
     */
    public void close() {
        // 装饰线
        buffer.append("==============================\n");
    }

    /**
     * 完成的文档
     * @return
     */
    public String getResult() {
        // 将StringBuffer变换为String
        return buffer.toString();
    }
}

【指挥者】

package com.atguigu.builder.Sample;

/**
 * 由于TextBuilder和HTMLBuilder都是Builder的子类,因此Director 仅仅使用Builder的方法即可编写文档。
 * Director并不关心实际编写文档的到底是TextBuilder还是HTMBuilder
 */
public class Director {
    private Builder builder;

    /**
     * 实际上不会将 Builder类的实例作为参数传递给 Director类。这是因为 Builder 类是抽象类,是无法生成其实例的。
     * 实际上传递给 Director 类的是 Builder类的子类
     * @param builder
     */
    public Director(Builder builder) {
        this.builder = builder;
    }

    /**
     * 编写文档
     */
    public void construct() {
        // 标题
        builder.makeTitle("Greeting");
        // 字符串
        builder.makeString("从早上至下午");
        // 条目
        builder.makeItems(new String[]{
                "早上好。",
                "下午好。",
        });
        // 其他字符串
        builder.makeString("晚上");
        // 其他条目
        builder.makeItems(new String[]{
                "晚上好。",
                "晚安。",
                "再见。",
        });
        // 完成文档
        builder.close();
    }
}

【测试】

package com.atguigu.builder.Sample;

/**
 * Builder模式测试
 */
public class Main {
    public static void main(String[] args) {
        String type = "plain";
        if (type.equals("plain")) {
            TextBuilder textbuilder = new TextBuilder();
            Director director = new Director(textbuilder);
            director.construct();
            String result = textbuilder.getResult();
            System.out.println(result);
        } else if (type.equals("html")) {
            HTMLBuilder htmlbuilder = new HTMLBuilder();
            Director director = new Director(htmlbuilder);
            director.construct();
            String filename = htmlbuilder.getResult();
            System.out.println(filename + "文件编写完成。");
        } else {
            usage();
            System.exit(0);
        }
    }

    public static void usage() {
        System.out.println("Usage: java Main plain      编写纯文本文档");
        System.out.println("Usage: java Main html       编写HTML文档");
    }
}

【运行】

type = “plain”

==============================
『Greeting』

■从早上至下午

 ・早上好。
 ・下午好。

■晚上

 ・晚上好。
 ・晚安。
 ・再见。

==============================


Process finished with exit code 0

type = “html”

<html>
<head><title>Greeting</title></head>
<body>
<h1>Greeting</h1>
<p>从早上至下午</p>
<ul>
    <li>早上好。</li>
    <li>下午好。</li>
</ul>
<p>晚上</p>
<ul>
    <li>晚上好。</li>
    <li>晚安。</li>
    <li>再见。</li>
</ul>
</body>
</html>

【分析】

  • Main类并不知道Builder类,它只是调用了Direct 类的construct 方法这样,Director类就会开始工作(Main类对此一无所知),并完成文档的编写
  • Director类知道Builder类,它调用Builder类的方法来编写文档,但它并不知道它所使用的类到底是 TextBuilder类、HTMLBuilder类还是其他 Builder类的子类,而且也没有必要知道,因为Director类只使用Builder类的方法
  • 不论是将 TextBuilder 的实例传递给 Director,还是将HTMLBuilder类的实例传递给 Director,它都可以正常工作,原因正是Director类不知道Builder类的具体的子类。正是因为不知道才能够替换,正是因为可以替换,组件才具有高价值。作为设计人员,我们必须时刻关注这种“可替换性”

拓展一

【要求】

请修改 Builder类TetBuilder类和HTMLBuilder类,确保“在调用makeString 方法、makeItems 方法和close方法之前必须且只能调用一次makeTitle方法”

【Builder 类】

public abstract class Builder {
    private boolean initialized = false;
    public void makeTitle(String title) {
        if (!initialized) {
            buildTitle(title);
            initialized = true;
        }
    }
    public void makeString(String str) {
        if (initialized) {
            buildString(str);
        }
    }
    public void makeItems(String[] items) {
        if (initialized) {
            buildItems(items);
        }
    }
    public void close() {
        if (initialized) {
            buildDone();
        }
    }
    protected abstract void buildTitle(String title);
    protected abstract void buildString(String str);
    protected abstract void buildItems(String[] items);
    protected abstract void buildDone();
}

【其他两个类】

其他两个类的方法内容还是一样的,修改一下方法名,将抽象Builder的方法实现即可

问题

问:编写的文档被保存在了buffer字段中,但buffer字段并非是String类型的,而是StringBuffer类型的,请问是为什么呢?如果使用了 string 类型会有什么问题呢?

答:使用String类型保存文档会存在效率问题,因为String类型是不可变的,每次对String进行修改都会生成一个新的String对象,而StringBuffer类型是可变的,可以进行高效的字符串拼接和修改。因此,为了保证文档的高效修改和拼接,使用StringBuffer类型更加合适

JDK源码中使用建造者模式

在这里插入图片描述
在这里插入图片描述

  • Appendable 接口定义了多个append方法(抽象方法),即Appendable 为抽象建造者,定义了抽象方法
  • AbstractStringBuilder 实现了 Appendable 接口方法,这里的AbstractStringBuilder 已经是建造者,只是不能实例化
  • StringBuilder 即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成,而StringBuilder 继承了AbstractStringBuilder

在这里插入图片描述

【总结】

  • JDK中使用设计模式的方式不一定和标准的一样(设计模式可能是更后面提出来的),但是核心思想大同小异

注意事项

  • 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
  • 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程系统扩展方便,符合“开闭原则“
  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式

【抽象工厂模式vs建造者模式】

  • 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品: 具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可
  • 建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品
  • 两种模式都用来生成复杂的实例

文章说明

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

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

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

相关文章

C 程序 运算符

文章目录 1、算术运算符2、关系运算符3、逻辑运算符4、位运算符5、赋值运算符6、杂项运算符 ↦ sizeof & 三元7、运算符优先级 1、算术运算符 #include <stdio.h>int main() {int a 21;int b 10;int c ;c a b;printf("Line 1 - c 的值是 %d\n", c );c …

【mac系统】mac系统调整妙控鼠标速度

当下环境&#xff1a; mac系统版本&#xff0c;其他系统应该也可以&#xff0c;大家可以自行试下&#xff1a; 鼠标 mac妙控鼠标&#xff0c;型号A1657 问题描述&#xff1a; 通过mac系统自带的鼠标速度调节按钮&#xff0c;调到最大后还是感觉移动速度哦过慢 问题解决&…

Cesium态势标绘专题-矩形(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

网站实现下载apk安装包

目录 1、背景说明2、效果图3、具体实现3.1 界面代码3.2 js代码 4、说明4.1 存在异常4.2 解决方案 5、参考资料 1、背景说明 有时需要将写好的apk安装包在局域网内部进行发布&#xff0c;具体实现非常简单&#xff0c;如下所示 2、效果图 进入到网站后&#xff0c;点击下载按…

STM32MP157驱动开发——按键驱动(tasklet)

文章目录 “tasklet”机制&#xff1a;内核函数定义 tasklet使能/ 禁止 tasklet调度 tasklet删除 tasklet tasklet软中断方式的按键驱动程序(stm32mp157)tasklet使用方法&#xff1a;button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “tasklet”机制&#xff1a; …

Stable Diffusion服务环境搭建(远程服务版)

Stable Diffusion服务环境搭建&#xff08;远程服务版&#xff09; Stable Diffusion是什么 Stable diffusion是一个基于Latent Diffusion Models&#xff08;潜在扩散模型&#xff0c;LDMs&#xff09;的文图生成&#xff08;text-to-image&#xff09;模型。具体来说&#…

学生公寓报修管理系统的设计与实现(论文+源码)_kaic

摘 要 随着科技的发展&#xff0c;信息化的管理手段早以在人们生活的各个方面取代了传统的管理手段&#xff0c;以先进管理理念为基础的现代化信息管理系统已经成为了许多机构的必备工具。在如今大学的校园里&#xff0c;有着许许多多的信息化管理系统&#xff0c;如图书管理系…

前端面试题汇总大全!

文章目录 前端面试题汇总大全&#xff08;含答案超详细&#xff09;-- 持续更新一、HTML 篇1.xhtml和html有什么区别2.行内元素有哪些&#xff1f;块级元素有哪些&#xff1f; 空(void)元素有那些&#xff1f;行内元素和块级元素有什么区别3. 简述一下你对 HTML 语义化的理解&a…

【数学建模】为什么存在最优策略?

一、说明 在进行优化回归过程&#xff0c;首先要看看是否存在最优策略&#xff1f; 在有限马尔可夫决策过程 &#xff08;MDP&#xff09; 中&#xff0c;最优策略被定义为同时最大化所有状态值的策略。换句话说&#xff0c;如果存在最优策略&#xff0c;则最大化状态 s 值的策…

组件化开发复习

1.vue的根组件使用 // 1.创建appconst app Vue.createApp({// data: option apidata() {return {message: "Hello Vue",counter: 0,counter2: 0,content: ""}},watch: {content(newValue) {console.log("content:", newValue)}}}) createApp 函…

【升职加薪秘籍】我在服务监控方面的实践(3)-机器监控

大家好,我是蓝胖子&#xff0c;关于性能分析的视频和文章我也大大小小出了有一二十篇了&#xff0c;算是已经有了一个系列&#xff0c;之前的代码已经上传到github.com/HobbyBear/performance-analyze&#xff0c;接下来这段时间我将在之前内容的基础上&#xff0c;结合自己在公…

C语言---动态内存管理

C语言—动态内存管理 文章目录 C语言---动态内存管理1. 为什么要进行动态内存分配1.1 动态内存管理所在的区域 2. 动态内存函数的介绍2.1 malloc2.1.1 malloc语法2.1.2 malloc具体实例 2.2 free2.2.1 free语法2.2.2 free具体实例 2.3 calloc2.3.1 calloc语法2.3.2 calloc具体实…

42. 接雨水

题目介绍 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3…

OpenShift 4 - 可观测性之用 OpenTelemetry+Jaeger 实现 Distributed Tracing

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在支持 OpenShift 4.13 的环境中验证 文章目录 技术架构部署 Distributed Tracing 运行环境安装测试应用并进行观测跟踪参考 说明&#xff1a; 本文使用的测试应用采用的是 “手动 Instrumentation” 方式在…

Linux系列---【CentOS 7通过MSTSC连接远程桌面】

安装对应的yum源 yum list lightdm xorgxrdp xrdp 可以看到这些软件都在epel中&#xff0c;如果没有的话&#xff0c;请先安装对应的yum源。命令如下&#xff1a; yum install -y epel-release 确认yum源没有问题之后&#xff0c;我们就可以进行安装了。 安装lightdm xorgxrdp…

【C语言】嵌入式C语言项目管理利器:深入理解Makefile的应用与实践

目录 一、makedile的概述 1、案例引入 2、makefile 3、Makefile优点 二、makefile的语法规则 1、语法规则 2、简单实战 三、makefile的变量 1、自定义变量 2、系统环境变量 3、预定义变量 4、高级makefile 一、makedile的概述 1、案例引入 gcc a.c b.c c.c ‐o …

EasyExcel写出包含多个sheet页的Excel

EasyExcel导出包含多个sheet页的Excel 1.引入依赖 引入如下的EasyExcel的依赖&#xff0c;或直接下载jar包 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></depende…

RT-Thread快速入门-定时器管理

1时钟节拍 任何操作系统都需要提供一个时钟节拍&#xff0c;以供系统处理所有和时间有关的事件&#xff0c;如延时、线程的时间片轮转调度以及定时器超时等。时钟节拍&#xff08;OS Tick&#xff09;是操作系统中最小的时间单位。 时钟节拍是特定的周期性中断&#xff0c;这…

Spring Boot 整合 分布式搜索引擎 Elastic Search 实现 搜索、分页与结果过滤

文章目录 ⛄引言一、酒店搜索和分页⛅需求分析⚡源码编写 二、酒店结果过滤⌚需求分析⏰修改搜索业务 ✅效果图⛵小结 ⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据…

【java实习评审】对热门小说更新时的聚集访问流量进行性能优化优化,有较好的设计

大家好&#xff0c;本篇文章分享一下【校招VIP】免费商业项目“推推”第一期书籍详情模块java同学的文档周最佳作品。该同学来自西安建筑科技大学软件工程专业。 本项目亮点难点&#xff1a;1 热门书籍在更新点的访问压力&#xff0c;2 书籍更新通知的及时性和有效性&#xff…