零基础设计模式——结构型模式 - 组合模式

news2025/6/1 0:57:51

第三部分:结构型模式 - 组合模式 (Composite Pattern)

在学习了桥接模式如何分离抽象和实现以应对多维度变化后,我们来探讨组合模式。组合模式允许你将对象组合成树形结构来表现“整体-部分”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

  • 核心思想:将对象组合成树状结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

组合模式 (Composite Pattern)

“将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象(叶子节点)和组合对象(容器节点)的使用具有一致性。” (Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.)

想象一下电脑文件系统的目录结构。一个目录可以包含文件(叶子节点),也可以包含其他目录(容器节点),这些子目录又可以包含文件或其他目录,形成一个树形结构。无论你操作的是一个文件还是一个目录(比如计算大小、显示名称),你可能希望用类似的方式来对待它们。

组合模式正是为了解决这类问题而设计的。

1. 目的 (Intent)

组合模式的主要目的:

  1. 表示部分-整体层次结构:清晰地表示对象之间的树形结构关系。
  2. 统一操作接口:使得客户端可以一致地处理单个对象(叶子)和对象的组合(容器/分支)。客户端不需要区分它正在处理的是一个叶子节点还是一个容器节点,从而简化了客户端代码。
  3. 递归组合:容器节点可以包含叶子节点,也可以包含其他容器节点,形成递归结构。

2. 生活中的例子 (Real-world Analogy)

  • 公司组织架构

    • 一个公司(Company - 根容器)由多个部门(Department - 容器)组成。
    • 每个部门可以包含员工(Employee - 叶子),也可以包含子部门(SubDepartment - 容器)。
    • 无论是计算整个公司的总薪资,还是某个部门的总薪资,或者某个员工的薪资,都可以通过一个统一的操作(如 getSalary())来递归地完成。
  • 图形用户界面 (GUI)

    • 一个窗口(Window - 根容器)可以包含面板(Panel - 容器)和各种控件如按钮(Button - 叶子)、文本框(TextBox - 叶子)。
    • 面板本身也可以包含其他面板或控件。
    • 当需要绘制整个窗口时,窗口会调用其子组件的绘制方法,子面板会再调用其子组件的绘制方法,直到叶子控件被绘制。这个过程对调用者(如窗口管理器)来说是统一的。
  • 菜单系统

    • 一个菜单栏(MenuBar - 根容器)包含多个菜单(Menu - 容器)。
    • 每个菜单可以包含菜单项(MenuItem - 叶子),也可以包含子菜单(SubMenu - 容器)。
    • 点击一个菜单项执行操作,点击一个子菜单则展开它。对用户来说,操作菜单和菜单项的方式是相似的。
  • 装配线上的部件

    • 一个复杂产品(如汽车 - 根容器)由许多主要部件(如引擎、底盘 - 容器)组成。
    • 每个主要部件又由更小的零件(如螺丝、活塞 - 叶子)或子部件(如变速箱 - 容器)组成。
    • 计算总成本或总重量时,可以递归地累加所有部分的成本或重量。

3. 结构 (Structure)

组合模式通常包含以下角色:

  1. Component (组件接口):为组合中的对象声明接口。它既可以代表叶子 (Leaf) 也可以代表容器 (Composite)。它通常声明了所有类共有的一系列操作,如 add(), remove(), getChild(), operation() 等。对于叶子节点,add(), remove(), getChild() 等管理子节点的操作可能没有意义,可以提供默认实现(如抛出异常或空操作)。
  2. Leaf (叶子节点):在组合中表示叶节点对象,叶节点没有子节点。它实现了 Component 接口中定义的操作。
  3. Composite (容器/组合节点):定义有子部件的那些部件的行为。存储子部件并在 Component 接口中实现与子部件相关的操作(如 add(), remove(), getChild())。Composite 的 operation() 方法通常会递归调用其子组件的 operation() 方法。
  4. Client (客户端):通过 Component 接口操纵组合部件的对象。
    在这里插入图片描述
    透明方式 vs 安全方式
  • 透明方式 (Transparent)Component 接口中声明所有管理子对象的方法(add, remove, getChild)。这样客户端可以统一对待所有组件,无需区分叶子和容器。但叶子节点需要对这些管理方法提供空实现或抛出异常,因为它们没有子节点。这是上图所示的方式。
  • 安全方式 (Safe):只在 Composite 类中声明管理子对象的方法。客户端在调用这些方法前需要判断对象类型是否为 Composite。这样叶子节点就不需要实现不相关的方法,更安全,但客户端处理起来稍微麻烦一些。

通常推荐透明方式,因为它简化了客户端代码,尽管可能牺牲一点类型安全(叶子节点可能会被错误地调用 add 方法)。

4. 适用场景 (When to Use)

  • 当你希望表示对象的部分-整体层次结构时。
  • 当你希望客户端代码可以统一处理组合结构中的单个对象和组合对象时,而无需关心其具体是叶子还是容器。
  • 当对象的结构是递归的,并且你想以统一的方式处理这种结构时。
  • 当你想让客户能够增加新的组件类型(叶子或容器)而无需修改现有使用该结构的代码时。

5. 优缺点 (Pros and Cons)

优点:

  1. 简化客户端代码:客户端可以一致地使用组合结构中的所有对象,无需编写特殊的代码来区分叶子和容器。
  2. 易于增加新类型的组件:可以很容易地增加新的 LeafComposite 类,它们都遵循 Component 接口,对现有结构和客户端代码影响小,符合开闭原则。
  3. 使层次结构更清晰:代码直接反映了对象的树形结构。

缺点:

  1. 使设计过于通用(透明方式下):如果采用透明方式,Component 接口需要包含所有可能的操作,包括那些只对 Composite 有意义的操作(如 add, remove)。这可能使得叶子节点的接口不太纯粹,因为它们不得不实现一些无用的方法。
  2. 难以限制组合中的组件类型:有时你可能希望一个 Composite 只能包含特定类型的 Component。标准组合模式很难直接实现这种约束,可能需要额外的类型检查或使用泛型(如果语言支持)。
  3. 如果管理子节点的操作过于复杂,Component接口会变得臃肿

6. 实现方式 (Implementations)

让我们以一个图形绘制系统为例,其中可以有简单的图形(如点、线、圆 - 叶子)和复杂的组合图形(可以包含其他简单或复杂图形 - 容器)。

组件接口 (Graphic - Component)
// graphic.go (Component)
package graphics

import "fmt"

// Graphic 组件接口
type Graphic interface {
	Draw() // 所有图形对象共有的操作:绘制
	Add(g Graphic) error    // 添加子图形 (对叶子节点无意义)
	Remove(g Graphic) error // 移除子图形 (对叶子节点无意义)
	GetChild(index int) (Graphic, error) // 获取子图形 (对叶子节点无意义)
	GetName() string // 获取图形名称
}
// Graphic.java (Component)
package com.example.graphics;

// 组件接口
public interface Graphic {
    void draw(); // 所有图形对象共有的操作:绘制
    String getName(); // 获取图形名称

    // 管理子图形的方法 (透明方式)
    // 对于叶子节点,这些方法通常会抛出UnsupportedOperationException或空实现
    default void add(Graphic graphic) {
        throw new UnsupportedOperationException("Cannot add to a leaf graphic.");
    }

    default void remove(Graphic graphic) {
        throw new UnsupportedOperationException("Cannot remove from a leaf graphic.");
    }

    default Graphic getChild(int index) {
        throw new UnsupportedOperationException("Leaf graphic has no children.");
    }
}
叶子节点 (Dot, Line, Circle - Leaf)
// dot.go (Leaf)
package graphics

import "fmt"

// Dot 叶子节点
type Dot struct {
	Name string
	X, Y int
}

func NewDot(name string, x, y int) *Dot {
	return &Dot{Name: name, X: x, Y: y}
}

func (d *Dot) GetName() string {
	return d.Name
}

func (d *Dot) Draw() {
	fmt.Printf("Drawing Dot '%s' at (%d, %d)\n", d.Name, d.X, d.Y)
}

func (d *Dot) Add(g Graphic) error {
	return fmt.Errorf("cannot add to a leaf graphic (Dot: %s)", d.Name)
}

func (d *Dot) Remove(g Graphic) error {
	return fmt.Errorf("cannot remove from a leaf graphic (Dot: %s)", d.Name)
}

func (d *Dot) GetChild(index int) (Graphic, error) {
	return nil, fmt.Errorf("leaf graphic (Dot: %s) has no children", d.Name)
}

// line.go (Leaf) - 类似 Dot,省略 Add, Remove, GetChild 的错误实现
type Line struct {
	Name       string
	StartX, StartY int
	EndX, EndY   int
}

func NewLine(name string, sx, sy, ex, ey int) *Line {
	return &Line{Name: name, StartX: sx, StartY: sy, EndX: ex, EndY: ey}
}

func (l *Line) GetName() string {
	return l.Name
}

func (l *Line) Draw() {
	fmt.Printf("Drawing Line '%s' from (%d,%d) to (%d,%d)\n", l.Name, l.StartX, l.StartY, l.EndX, l.EndY)
}

func (l *Line) Add(g Graphic) error { return fmt.Errorf("cannot add to Line") }
func (l *Line) Remove(g Graphic) error { return fmt.Errorf("cannot remove from Line") }
func (l *Line) GetChild(index int) (Graphic, error) { return nil, fmt.Errorf("Line has no children") }
// Dot.java (Leaf)
package com.example.graphics;

public class Dot implements Graphic {
    private String name;
    private int x, y;

    public Dot(String name, int x, int y) {
        this.name = name;
        this.x = x;
        this.y = y;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void draw() {
        System.out.printf("Drawing Dot '%s' at (%d, %d)%n", name, x, y);
    }
    // add, remove, getChild 使用接口的默认实现 (抛出UnsupportedOperationException)
}

// Line.java (Leaf)
package com.example.graphics;

public class Line implements Graphic {
    private String name;
    private int startX, startY, endX, endY;

    public Line(String name, int startX, int startY, int endX, int endY) {
        this.name = name;
        this.startX = startX;
        this.startY = startY;
        this.endX = endX;
        this.endY = endY;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void draw() {
        System.out.printf("Drawing Line '%s' from (%d,%d) to (%d,%d)%n", name, startX, startY, endX, endY);
    }
}
容器节点 (CompoundGraphic - Composite)
// compound_graphic.go (Composite)
package graphics

import (
	"fmt"
)

// CompoundGraphic 容器节点
type CompoundGraphic struct {
	Name     string
	children []Graphic
}

func NewCompoundGraphic(name string) *CompoundGraphic {
	return &CompoundGraphic{
		Name:     name,
		children: make([]Graphic, 0),
	}
}

func (cg *CompoundGraphic) GetName() string {
	return cg.Name
}

func (cg *CompoundGraphic) Draw() {
	fmt.Printf("Drawing CompoundGraphic '%s':\n", cg.Name)
	for _, child := range cg.children {
		fmt.Print("  ") // Indent for clarity
		child.Draw()
	}
	fmt.Printf("Finished Drawing CompoundGraphic '%s'\n", cg.Name)
}

func (cg *CompoundGraphic) Add(g Graphic) error {
	cg.children = append(cg.children, g)
	fmt.Printf("Added %s to %s\n", g.GetName(), cg.GetName())
	return nil
}

func (cg *CompoundGraphic) Remove(g Graphic) error {
	found := false
	newChildren := make([]Graphic, 0)
	for _, child := range cg.children {
		if child == g { // Pointer comparison for simplicity, or use unique ID
			found = true
			fmt.Printf("Removed %s from %s\n", g.GetName(), cg.GetName())
			continue
		}
		newChildren = append(newChildren, child)
	}
	cg.children = newChildren
	if !found {
		return fmt.Errorf("child graphic '%s' not found in '%s'", g.GetName(), cg.GetName())
	}
	return nil
}

func (cg *CompoundGraphic) GetChild(index int) (Graphic, error) {
	if index < 0 || index >= len(cg.children) {
		return nil, fmt.Errorf("index out of bounds for CompoundGraphic '%s'", cg.Name)
	}
	return cg.children[index], nil
}
// CompoundGraphic.java (Composite)
package com.example.graphics;

import java.util.ArrayList;
import java.util.List;

public class CompoundGraphic implements Graphic {
    private String name;
    private List<Graphic> children = new ArrayList<>();

    public CompoundGraphic(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void draw() {
        System.out.printf("Drawing CompoundGraphic '%s':%n", name);
        for (Graphic child : children) {
            System.out.print("  "); // Indent for clarity
            child.draw();
        }
        System.out.printf("Finished Drawing CompoundGraphic '%s'%n", name);
    }

    @Override
    public void add(Graphic graphic) {
        children.add(graphic);
        System.out.printf("Added %s to %s%n", graphic.getName(), this.name);
    }

    @Override
    public void remove(Graphic graphic) {
        if (children.remove(graphic)) {
            System.out.printf("Removed %s from %s%n", graphic.getName(), this.name);
        } else {
            System.out.printf("Graphic %s not found in %s for removal%n", graphic.getName(), this.name);
        }
    }

    @Override
    public Graphic getChild(int index) {
        if (index >= 0 && index < children.size()) {
            return children.get(index);
        }
        throw new IndexOutOfBoundsException("Index out of bounds for CompoundGraphic '" + name + "'");
    }
}
客户端使用
// main.go (示例用法)
/*
package main

import (
	"./graphics"
	"fmt"
)

func main() {
	// Create leaf objects
	dot1 := graphics.NewDot("Dot1", 10, 20)
	line1 := graphics.NewLine("Line1", 0, 0, 100, 100)
	dot2 := graphics.NewDot("Dot2", 50, 60)

	// Create a compound graphic
	compound1 := graphics.NewCompoundGraphic("Picture1")
	compound1.Add(dot1)
	compound1.Add(line1)

	// Create another compound graphic and add the first one to it
	mainPicture := graphics.NewCompoundGraphic("MainCanvas")
	mainPicture.Add(compound1)
	mainPicture.Add(dot2)

	fmt.Println("\n--- Drawing Main Canvas ---")
	mainPicture.Draw() // Client treats mainPicture (Composite) and dot1 (Leaf) uniformly via Graphic interface

	fmt.Println("\n--- Trying to add to a leaf (should fail) ---")
	err := dot1.Add(line1)
	if err != nil {
		fmt.Println("Error as expected:", err)
	}

	fmt.Println("\n--- Removing an element ---")
	compound1.Remove(dot1)
	fmt.Println("\n--- Drawing Main Canvas After Removal ---")
	mainPicture.Draw()

    // Accessing a child
    child, err := compound1.GetChild(0)
    if err == nil {
        fmt.Printf("\n--- Child 0 of Picture1 is: %s ---\n", child.GetName())
        child.Draw()
    } else {
        fmt.Println("Error getting child:", err)
    }
}
*/
// Main.java (示例用法)
/*
package com.example;

import com.example.graphics.*;

public class Main {
    public static void main(String[] args) {
        // Create leaf objects
        Graphic dot1 = new Dot("Dot1", 10, 20);
        Graphic line1 = new Line("Line1", 0, 0, 100, 100);
        Graphic dot2 = new Dot("Dot2", 50, 60);

        // Create a compound graphic
        CompoundGraphic compound1 = new CompoundGraphic("Picture1");
        compound1.add(dot1);
        compound1.add(line1);

        // Create another compound graphic and add the first one to it
        CompoundGraphic mainPicture = new CompoundGraphic("MainCanvas");
        mainPicture.add(compound1);
        mainPicture.add(dot2);

        System.out.println("\n--- Drawing Main Canvas ---");
        mainPicture.draw(); // Client treats mainPicture (Composite) and dot1 (Leaf) uniformly via Graphic interface

        System.out.println("\n--- Trying to add to a leaf (should throw exception) ---");
        try {
            dot1.add(line1);
        } catch (UnsupportedOperationException e) {
            System.out.println("Exception as expected: " + e.getMessage());
        }

        System.out.println("\n--- Removing an element ---");
        compound1.remove(dot1);
        System.out.println("\n--- Drawing Main Canvas After Removal ---");
        mainPicture.draw();

        // Accessing a child
        try {
            Graphic child = compound1.getChild(0);
            System.out.printf("%n--- Child 0 of Picture1 is: %s ---%n", child.getName());
            child.draw();
        } catch (IndexOutOfBoundsException e) {
            System.out.println("Error getting child: " + e.getMessage());
        }
    }
}
*/

7. 总结

组合模式通过引入一个共同的组件接口,使得客户端可以统一处理单个对象(叶子)和对象的组合(容器)。它非常适合用来表示具有“部分-整体”层次结构的对象,并能有效地简化客户端与这种复杂结构交互的方式。通过递归组合,可以构建出任意复杂的树形结构,同时保持操作的一致性。

记住它的核心:统一对待单个对象和组合对象,形成树形结构

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

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

相关文章

腾讯云国际站可靠性测试

在数字化转型加速的今天&#xff0c;企业对于云服务的依赖已从“可选”变为“必需”。无论是跨境电商的实时交易&#xff0c;还是跨国企业的数据协同&#xff0c;云服务的可靠性直接决定了业务连续性。作为中国领先的云服务提供商&#xff0c;腾讯云国际站&#xff08;Tencent …

自定义异常小练习

在开始之前,让我们高喊我们的口号&#xff1a; ​​​​​​​ 键盘敲烂,年薪百万&#xff01; 目录 键盘敲烂,年薪百万&#xff01; 异常综合练习&#xff1a; 自定义异常 异常综合练习&#xff1a; 自定义异常&#xff1a; 定义异常类写继承关系空参构造带参构造 自定…

SpringBoot整合MinIO实现文件上传

使用Spring Boot与JSP和MinIO&#xff08;一个开源对象存储系统&#xff0c;兼容Amazon S3&#xff09;进行集成&#xff0c;您可以创建一个Web应用来上传、存储和管理文件。以下是如何将Spring Boot、JSP和MinIO集成的基本步骤&#xff1a; 这个是minio正确启动界面 这个是min…

基于面向对象设计的C++日期推算引擎:精准高效的时间运算实现与运算重载工程化实践

前引&#xff1a; 在软件开发中&#xff0c;时间与日期的处理是基础但极具挑战性的任务。传统的手工日期运算逻辑往往面临闰年规则、月份天数动态变化、时区转换等复杂场景的容错难题&#xff0c;且代码冗余度高、可维护性差。本文将深入探讨如何利用C的面向对象特性与成员函数…

如何把 Microsoft Word 中所有的汉字字体替换为宋体?

Ctrl H &#xff0c;然后&#xff0c;点击更多&#xff0c;勾选使用通配符&#xff0c;查找内容中填入 [一-龥]{1,}&#xff0c; 这是 Word 通配符匹配汉字的经典写法&#xff08;匹配 Unicode 范围内的 CJK 汉字&#xff09;。 然后&#xff0c; “替换为”留空&#xff0c;点…

02. [Python+Golang+PHP]三数之和,多种语言实现最优解demo

一、问题描述&#xff1a;三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中…

倚光科技在二元衍射面加工技术上的革新:引领光学元件制造新方向​

倚光科技二元衍射面加工技术&#xff08;呈现出细腻的光碟反射纹路&#xff09; 在光学元件制造领域&#xff0c;二元衍射面的加工技术一直是行业发展的关键驱动力之一。其精准的光相位调制能力&#xff0c;在诸多前沿光学应用中扮演着不可或缺的角色。然而&#xff0c;长期以来…

驱动开发(2)|鲁班猫rk3568简单GPIO波形操控

上篇文章写了如何下载内核源码、编译源码的详细步骤&#xff0c;以及一个简单的官方demo编译&#xff0c;今天分享一下如何根据板子的引脚写自己控制GPIO进行高低电平反转。 想要控制GPIO之前要学会看自己的引脚分布图&#xff0c;我用的是鲁班猫RK3568&#xff0c;引脚分布图如…

《软件工程》第 3 章 -需求工程概论

在软件工程的开发流程中&#xff0c;需求工程是奠定项目成功基础的关键环节。它专注于获取、分析、定义和管理软件需求&#xff0c;确保开发出的软件能真正满足用户需求。接下来&#xff0c;我们将按照目录内容&#xff0c;结合 Java 代码和实际案例&#xff0c;深入讲解需求工…

VMware-MySQL主从

MySQL主从 服务器信息 服务器类型角色主机地址主机名称虚拟机master192.168.40.128test-1虚拟机slave192.168.40.129test-2 Master 配置&#xff08;192.168.40.128&#xff09; 删除自动生成的配置 /var/lib/mysql/auto.cnf [roottest-1 ~]# rm -rf /var/lib/mysql/auto.…

2023-ICLR-ReAct 首次结合Thought和Action提升大模型解决问题的能力

关于普林斯顿大学和Google Research, Brain Team合作的一篇文章, 在语言模型中协同Reasoning推理和Action行动。 论文地址&#xff1a;https://arxiv.org/abs/2210.03629 代码&#xff1a;https://github.com/ysymyth/ReAct.git 其他复现 langchain &#xff1a;https://pytho…

Rust 开发的一些GUI库

最近考虑用Rust干点什么&#xff0c;于是搜集了下资料——根据2025年最新调研结果和社区实践&#xff0c;Rust GUI库生态已形成多个成熟度不同的解决方案。以下是当前主流的GUI库分类及特点分析&#xff0c;结合跨平台支持、开发体验和实际应用场景进行综合评估&#xff1a; 一…

【第四十六周】文献阅读:从 RAG 到记忆:大型语言模型的非参数持续学习

目录 摘要Abstract从 RAG 到记忆&#xff1a;大型语言模型的非参数持续学习研究背景方法论1. 离线索引&#xff08;Offline Indexing&#xff09;2. 在线检索&#xff08;Online Retrieval&#xff09;具体细节 创新性实验结果局限性总结 摘要 本论文旨在解决当前检索增强生成…

从智能提效到产品赋能的架构实践

摘要 本文深入探讨了企业级系统从智能化提效阶段向产品赋能阶段演进的架构实践路径。通过分析传统架构的局限性,提出了以用户价值为导向的现代化架构设计理念,并结合实际案例展示了如何构建可扩展、高可用、智能化的产品架构体系。 1. 引言 在数字化转型的浪潮中,企业技术…

关于OT IIOT系统远程访问的零信任安全

什么是OT & IIOT&#xff1f;—— 工业领域的“操作基石”与“智能升级” 在工业数字化转型的浪潮中&#xff0c;OT&#xff08;运营技术&#xff09;与IIoT&#xff08;工业物联网&#xff09;是两个核心概念。前者是工业生产的“神经中枢”&#xff0c;后者是驱动智能升…

【Doris基础】Apache Doris vs 传统数据仓库:架构与性能的全面对比

目录 1 引言 1.1 传统数据仓库的发展 1.2 现代分析型数据库的崛起 2 核心架构对比 2.1 传统数据仓库的架构 2.2 Doris的架构设计 3 关键技术差异 3.1 存储引擎对比 3.2 查询执行对比 3.3 数据摄入方式对比 4 性能与扩展性对比 4.1 性能基准对比 4.2 扩展性对比 5…

【VScode】python初学者的有力工具

还记得23年11月&#xff0c;我还在欣喜Spyder像Rstudio一样方便。 但苦于打开软件打开太卡、太耗时&#xff08;初始化-再加载一些东西&#xff09;&#xff0c;一度耗费了我学习的热情。 就在24年5月份&#xff0c;别人推荐下发现了一个更加轻量级、方便又快速的ID&#xff0…

443端口:HTTPS通信的安全基石

在互联网通信中&#xff0c;端口是数据传输的虚拟通道&#xff0c;每个端口对应特定的服务或协议。其中&#xff0c;443端口 作为 HTTPS协议 的默认端口&#xff0c;在现代网络安全中扮演着至关重要的角色。 一、443端口的核心作用 HTTPS加密通信 443端口是HTTPS&#xff08;…

宝塔安装WordPress程序

宝塔安装WordPress程序 一、提前准备1&#xff0c;下载WordPress2&#xff0c;在宝塔创建站点 二、部署项目1&#xff0c;上传下载的wordpress压缩包至创建的项目根目录下并解压 三、wordpress安装1&#xff0c;在浏览器打开创建的网站2&#xff0c;开始按照流程安装配置数据库…

Agent 的7 中设计模式

这里写自定义目录标题 建立有效的Agent什么是Agent&#xff1f;何时&#xff08;以及何时不使用&#xff09;使用代理何时以及如何使用框架构建块、工作流和Agent构建模块&#xff1a;增强型LLM(The augmented LLM)工作流程&#xff1a;提示链接(Prompt chaining)工作流程&…