Golang 设计模式

文章目录

  • 创建型模式
    • 简单工厂模式
      • 图形接口
      • 具体图形类:圆形
      • 具体图形类:矩形
      • 工厂类定义
      • 使用简单工厂模式
    • 抽象工厂模式
      • 1. 定义产品接口
      • 2. 定义具体产品实现类
      • 3. 定义抽象工厂接口
      • 4. 定义具体工厂实现类
      • 5. 使用抽象工厂创建对象并使用产品
    • 创建者模式
      • 1. 定义产品结构体
      • 2. 定义创建者接口和具体创建者结构体
      • 3. 定义指挥者结构体
      • 4. 使用创建者模式构建对象
    • 原型模式
      • 定义原型接口和实现类:
      • 使用原型模式创建对象:
    • 单例模式
    • 懒汉式单例(线程不安全)
      • 懒汉式单例(线程安全)
      • 饿汉式单例
  • 结构性模式
    • 外观行模式
      • DVD 播放器接口和实现
      • 投影仪接口和实现
      • 音响系统接口和实现
      • 外观类实现
      • 使用外观模式

创建型模式

简单工厂模式

简单工厂模式是一种创建型设计模式。它定义了一个工厂类,这个工厂类负责创建产品对象。客户端(使用产品的代码)只需要向工厂类请求所需的产品,而不需要知道产品是如何创建的,这样就将产品的创建和使用分离,使得代码的结构更加清晰。
假设我们要创建一个图形绘制工具,首先定义一个图形接口,然后有不同的图形类实现这个接口。

图形接口

type Shape interface {Draw()
}

具体图形类:圆形

type Circle struct{}func (c *Circle) Draw() {println("Drawing a circle")
}

具体图形类:矩形

type Rectangle struct{}func (r *Rectangle) Draw() {println("Drawing a rectangle")
}

工厂类定义

接着创建简单工厂类,用于生产不同的图形。

  type ShapeFactory struct{}func (f *ShapeFactory) CreateShape(shapeType string) Shape {if shapeType == "circle" {return &Circle{}} else if shapeType == "rectangle" {return &Rectangle{}}return nil}

使用简单工厂模式

在主函数或者其他客户端代码中,可以这样使用工厂类来获取产品(图形)并使用。

  func main() {factory := &ShapeFactory{}circle := factory.CreateShape("circle")circle.Draw()rectangle := factory.CreateShape("rectangle")rectangle.Draw()}

抽象工厂模式

抽象工厂方法模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离,使得代码更具灵活性和可维护性。其核心在于有一个抽象工厂接口,该接口定义了创建一系列相关产品对象的抽象方法,然后由具体的工厂类来实现这些抽象方法,创建具体的产品对象。

1. 定义产品接口

首先,定义一系列相关的产品接口,比如这里假设有两种产品:ProductA和ProductB。

// ProductA接口,代表产品A的抽象
type ProductA interface {Use()
}// ProductB接口,代表产品B的抽象
type ProductB interface {Use()
}

接口中定义了Use方法,用于表示产品的使用行为,具体的产品实现类需要实现这个方法。

2. 定义具体产品实现类

接着,创建具体的产品实现类,分别实现ProductA和ProductB接口。

// ConcreteProductA1是ProductA接口的一个具体实现
type ConcreteProductA1 struct{}func (p *ConcreteProductA1) Use() {println("Using ConcreteProductA1")
}// ConcreteProductA2是ProductA接口的另一个具体实现
type ConcreteProductA2 struct{}func (p *ConcreteProductA2) Use() {println("Using ConcreteProductA2")
}// ConcreteProductB1是ProductB接口的一个具体实现
type ConcreteProductB1 struct{}func (p *ConcreteProductB1) Use() {println("Using ConcreteProductB1")
}// ConcreteProductB2是ProductB接口的另一个具体实现
type ConcreteProductB2 struct{}func (p *ConcreteProductB2) Use() {println("Using ConcreteProductB2")
}

这些具体产品类实现了各自接口的Use方法,定义了具体的使用行为,比如打印出不同的使用提示信息。

3. 定义抽象工厂接口

然后,定义抽象工厂接口,它声明了创建ProductA和ProductB的抽象方法。

// AbstractFactory接口,定义创建产品的抽象方法
type AbstractFactory interface {CreateProductA() ProductACreateProductB() ProductB
}

4. 定义具体工厂实现类

再创建具体的工厂实现类,实现抽象工厂接口,根据具体工厂的逻辑来创建对应的具体产品。

// ConcreteFactory1是AbstractFactory接口的一个具体实现,用于创建特定组合的产品
type ConcreteFactory1 struct{}func (f *ConcreteFactory1) CreateProductA() ProductA {return &ConcreteProductA1{}
}func (f *ConcreteFactory1) CreateProductB() ProductB {return &ConcreteProductB1{}
}// ConcreteFactory2是AbstractFactory接口的另一个具体实现,用于创建另一种组合的产品
type ConcreteFactory2 struct{}func (f *ConcreteFactory2) CreateProductA() ProductA {return &ConcreteProductA2{}
}func (f *ConcreteFactory2) CreateProductB() ProductB {return &ConcreteProductB2{}
}

在这些具体工厂类中,实现了CreateProductA和CreateProductB方法,分别返回对应的具体产品实例。

5. 使用抽象工厂创建对象并使用产品

func main() {// 使用ConcreteFactory1创建产品factory1 := &ConcreteFactory1{}productA1 := factory1.CreateProductA()productB1 := factory1.CreateProductB()productA1.Use()productB1.Use()// 使用ConcreteFactory2创建产品factory2 := &ConcreteFactory2{}productA2 := factory2.CreateProductA()productB2 := factory2.CreateProductB()productA2.Use()productB2.Use()
}

在main函数中,首先实例化了不同的具体工厂(这里是ConcreteFactory1和ConcreteFactory2),然后通过这些工厂分别创建对应的产品实例,并调用产品的Use方法来展示其使用行为。

优点

  • 解耦对象创建和使用:使得代码中创建对象的部分和使用对象的部分相互独立,便于维护和扩展。例如,当需要添加新的具体产品或者改变产品的创建逻辑时,只需要在对应的工厂类和产品类中修改,而不会影响到使用这些产品的其他代码。
  • 便于代码的复用和替换:可以方便地替换具体的工厂实现类,从而改变创建的产品组合,在不同的应用场景下复用相同的产品接口和使用逻辑。
    缺点
  • 增加代码复杂度:由于引入了抽象工厂、具体工厂、产品接口以及多个具体产品类等多层结构,代码的整体复杂度会有所增加,对于简单的应用场景可能显得有些 “大材小用”。
  • 不利于理解:对于不熟悉设计模式的开发者来说,初次接触抽象工厂方法模式时,理解其结构和运行流程可能需要花费一些时间。

创建者模式

创建者模式是一种创建型设计模式,它将一个复杂对象的构建过程和它的表示分离,使得同样的构建过程可以创建不同的表示。该模式适用于创建对象的过程较为复杂,需要多个步骤来完成,并且不同的配置或步骤组合能够生成不同形态的对象的场景。

1. 定义产品结构体

首先,定义要构建的复杂产品对象,这里以一个简单的 Computer(电脑)结构体为例,它包含多个部件属性。

type Computer struct {CPU    stringMemory stringStorage stringGPU    string
}

2. 定义创建者接口和具体创建者结构体

接着,创建创建者接口,它声明了构建产品各个部件以及获取最终产品的抽象方法。然后创建具体的创建者结构体,实现这些抽象方法来定义具体的构建逻辑。

// Builder接口,定义构建电脑各部件及获取最终电脑的方法
type Builder interface {BuildCPU()BuildMemory()BuildStorage()BuildGPU()GetComputer() *Computer
}// GamingComputerBuilder是实现Builder接口的具体创建者结构体,用于构建游戏电脑
type GamingComputerBuilder struct {computer Computer
}func (b *GamingComputerBuilder) BuildCPU() {b.computer.CPU = "Intel Core i9"
}func (b *GamingComputerBuilder) BuildMemory() {b.computer.Memory = "32GB DDR5"
}func (b *GamingComputerBuilder) BuildStorage() {b.computer.Storage = "1TB NVMe SSD"
}func (b *GamingComputerBuilder) BuildGPU() {b.computer.GPU = "NVIDIA RTX 4090"
}func (b *GamingComputerBuilder) GetComputer() *Computer {return &b.computer
}// OfficeComputerBuilder是实现Builder接口的另一个具体创建者结构体,用于构建办公电脑
type OfficeComputerBuilder struct {computer Computer
}func (b *OfficeComputerBuilder) BuildCPU() {b.computer.CPU = "Intel Core i5"
}func (b *OfficeComputerBuilder) BuildMemory() {b.computer.Memory = "16GB DDR4"
}func (b *OfficeComputerBuilder) BuildStorage() {b.computer.Storage = "512GB SATA SSD"
}func (b *OfficeComputerBuilder) BuildGPU() {b.computer.GPU = "Integrated Graphics"
}func (b *OfficeComputerBuilder) GetComputer() *Computer {return &b.computer
}

在上述代码中,GamingComputerBuilder 按照游戏电脑的配置来构建各个部件,而 OfficeComputerBuilder 则按照办公电脑的配置进行构建,它们都实现了 Builder 接口的抽象方法,最后通过 GetComputer 方法返回构建好的电脑对象。

3. 定义指挥者结构体

指挥者结构体用于控制构建过程,它接收一个创建者对象,并按照特定顺序调用创建者的构建方法来完成产品的构建。

// Director结构体,用于协调构建过程
type Director struct {builder Builder
}func (d *Director) Construct() *Computer {d.builder.BuildCPU()d.builder.BuildMemory()d.builder.BuildStorage()d.builder.BuildGPU()return d.builder.GetComputer()
}

4. 使用创建者模式构建对象

最后,在 main 函数中展示如何使用创建者模式来构建不同类型的电脑对象。

func main() {// 创建游戏电脑gamingBuilder := &GamingComputerBuilder{}gamingDirector := &Director{builder: gamingBuilder}gamingComputer := gamingDirector.Construct()println("Gaming Computer Configuration:")println("CPU:", gamingComputer.CPU)println("Memory:", gamingComputer.Memory)println("Storage:", gamingComputer.Storage)println("GPU:", gamingComputer.GPU)// 创建办公电脑officeBuilder := &OfficeComputerBuilder{}officeDirector := &Director{builder: officeBuilder}officeComputer := officeDirector.Construct()println("\nOffice Computer Configuration:")println("CPU:", officeComputer.CPU)println("Memory:", officeComputer.Memory)println("Storage:", officeComputer.Storage)println("GPU:", officeComputer.GPU)
}

在 main 函数中,先分别实例化了游戏电脑和办公电脑对应的创建者和指挥者,然后通过指挥者调用构建过程来获取构建好的电脑对象,并打印出它们的配置信息。
优点

  • 解耦构建过程和产品表示:使得构建复杂对象的代码与对象本身的结构和表示分离,方便对构建过程进行修改和扩展,而不影响产品的使用逻辑。
  • 便于精细化控制构建过程:可以通过指挥者精确地控制构建步骤的顺序,并且能够根据不同需求灵活地使用不同的创建者来构建多种形态的产品。
  • 提高代码可读性和可维护性:将复杂的构建逻辑封装在创建者和指挥者中,使得代码结构更加清晰,易于理解和维护,尤其是在构建过程涉及多个步骤和多种配置的情况下。
    缺点
  • 增加代码复杂度:引入了创建者、指挥者等额外的结构体和接口,相比简单直接创建对象的方式,代码结构变得更复杂,对于简单的创建对象场景可能会显得有些繁琐。
  • 需要额外的代码管理:要维护创建者、指挥者以及它们与产品之间的关系,需要更多的代码来进行管理和协调,增加了一定的开发成本。

原型模式

原型模式是一种创建型设计模式。它的核心思想是通过复制一个已经存在的对象(原型)来创建新的对象,而不是通过传统的使用构造函数来初始化对象。这就好比是克隆,以一个原始对象为蓝本,快速制造出与其相似的新对象。

定义原型接口和实现类:

首先定义一个原型接口,这个接口有一个方法用于克隆对象。假设我们要创建一个图形的原型模式,接口如下:

type Shape interface {Clone() Shape
}

然后创建具体的图形类实现这个接口。以矩形(Rectangle)为例:

type Rectangle struct {width  float64height float64
}
func (r *Rectangle) Clone() Shape {return &Rectangle{width:  r.width,height: r.height,}
}

使用原型模式创建对象:

在主函数或者其他需要创建对象的地方,可以这样使用原型模式。

func main() {originalRectangle := &Rectangle{width: 10, height: 20}clonedRectangle := originalRectangle.Clone().(*Rectangle)// 验证克隆后的对象是否和原对象相同(除了内存地址)if originalRectangle.width == clonedRectangle.width && originalRectangle.height == clonedRectangle.height {println("克隆成功")}
}

单例模式

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。就好像在一个应用程序中,某些资源(如数据库连接池、配置管理器等)只需要一个实例存在,以避免资源浪费或数据不一致等问题。

懒汉式单例(线程不安全)

这种方式是在第一次调用获取实例的方法时才创建实例。

type Singleton struct{}var instance *Singletonfunc GetInstance() *Singleton {if instance == nil {instance = &Singleton{}}return instance
}

懒汉式单例(线程安全)

为了解决上述问题,可以使用互斥锁来保证在多线程环境下的安全。

import ("sync"
)type Singleton struct{}var (instance *Singletonmutex    sync.Mutex
)func GetInstance() *Singleton {mutex.Lock()defer mutex.Unlock()if instance == nil {instance = &Singleton{}}return instance
}

这里使用了sync.Mutex互斥锁,在获取实例的方法中,先加锁,然后判断实例是否为nil,如果是则创建实例,最后解锁。这样就保证了在多线程环境下只有一个实例被创建。

饿汉式单例

这种方式是在程序启动时就创建好实例,而不是等到第一次调用获取实例的方法时。

type Singleton struct{}var instance = &Singleton{}func GetInstance() *Singleton {return instance
}

结构性模式

外观行模式

外观模式(Facade Pattern)是一种结构型设计模式。它为复杂的子系统提供了一个统一的、简化的接口,隐藏了子系统的复杂性和内部实现细节。就好像给一个复杂的机器(子系统)安装了一个简单的控制面板(外观类),用户通过操作这个控制面板就能轻松地使用机器,而无需了解机器内部复杂的结构和运作原理。

假设场景:家庭影院系统
家庭影院系统通常包括多个复杂的子系统,如 DVD 播放器、投影仪、音响系统等。首先定义这些子系统的接口和实现。

DVD 播放器接口和实现

type DVDPlayer interface {On()Off()Play(movie string)Stop()
}type dvdPlayer struct{}func (d *dvdPlayer) On() {println("DVD player is on")
}func (d *dvdPlayer) Off() {println("DVD player is off")
}func (d *dvdPlayer) Play(movie string) {println("Playing movie:", movie)
}func (d *dvdPlayer) Stop() {println("DVD player stopped")
}

投影仪接口和实现

type Projector interface {On()Off()Project(image string)
}type projector struct{}func (p *projector) On() {println("Projector is on")
}func (p *projector) Off() {println("Projector is off")
}func (p *projector) Project(image string) {println("Projecting image:", image)
}

音响系统接口和实现

type SoundSystem interface {On()Off()SetVolume(volume int)
}type soundSystem struct{}func (s *soundSystem) On() {println("Sound system is on")
}func (s *soundSystem) Off() {println("Sound system is off")
}func (s *soundSystem) SetVolume(volume int) {println("Setting sound volume to:", volume)
}

外观类实现

现在创建外观类来提供家庭影院系统的简单统一接口。

type HomeTheaterFacade struct {dvdPlayer  DVDPlayerprojector  ProjectorsoundSystem SoundSystem
}func NewHomeTheaterFacade() *HomeTheaterFacade {return &HomeTheaterFacade{dvdPlayer:  &dvdPlayer{},projector:  &projector{},soundSystem: &soundSystem{},}
}func (h *HomeTheaterFacade) WatchMovie(movie string) {h.dvdPlayer.On()h.projector.On()h.soundSystem.On()h.soundSystem.SetVolume(10)h.dvdPlayer.Play(movie)h.projector.Project("Movie Image")
}func (h *HomeTheaterFacade) EndMovie() {h.dvdPlayer.Stop()h.dvdPlayer.Off()h.projector.Off()h.soundSystem.Off()
}

使用外观模式

在主函数中,可以这样使用外观类来操作家庭影院系统。

  func main() {homeTheater := NewHomeTheaterFacade()homeTheater.WatchMovie("Avatar")homeTheater.EndMovie()}

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

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

相关文章

PyTorch框架——基于深度学习YOLOv5神经网络水果蔬菜检测识别系统

基于深度学习YOLOv5神经网络水果蔬菜检测识别系统,其能识别的水果蔬菜有15种,# 水果的种类 names: [黑葡萄, 绿葡萄, 樱桃, 西瓜, 龙眼, 香蕉, 芒果, 菠萝, 柚子, 草莓, 苹果, 柑橘, 火龙果, 梨子, 花生, 黄瓜, 土豆, 大蒜, 茄子, 白萝卜, 辣椒, 胡萝卜,…

Mac玩Steam游戏秘籍!

Mac玩Steam游戏秘籍! 大家好!最近有不少朋友在用MacBook玩Steam游戏时遇到不支持mac的问题。别担心,我来教你如何用第三方工具Crossover来畅玩这些不支持的游戏,简单又实用! 第一步:下载Crossover 首先&…

【网络篇】IP知识

IPv4首部与IPv6首部 IPv4相对于IPv6的好处: 1.IPv6可自动配置,即使没有DHCP服务器也可以实现自动分配IP地址,实现即插即用。 2.IPv6包首部长度采用固定40字节,删除了选项字段,以及首部校验和,简化了首部…

我的年度总结

这一年的人生起伏:从曙光到低谷再到新的曙光 其实本来没打算做年度总结的,无聊打开了帅帅的视频,结合自己最近经历的,打算简单聊下。因为原本打算做的内容会是一篇比较丧、低能量者的呻吟。 实习生与创业公司的零到一 第一段工…

Vue脚手架开发 Vue2基础 VueRouter的基本使用 vue-router路由案例

vue-router路由 Vue脚手架开发,创建项目:https://blog.csdn.net/c_s_d_n_2009/article/details/144973766 Vue Router,Vue Router | Vue.js 的官方路由,Vue.js 的官方路由,为 Vue.js 提供富有表现力、可配置的、方便…

Windows远程桌面网关出现重大漏洞

微软披露了其Windows远程桌面网关(RD Gateway)中的一个重大漏洞,该漏洞可能允许攻击者利用竞争条件,导致拒绝服务(DoS)攻击。该漏洞被标识为CVE-2025-21225,已在2025年1月的补丁星期二更新中得到…

c语言----------内存管理

内存管理 目录 一。作用域1.1 局部变量1.2 静态(static)局部变量1.3 全局变量1.4 静态(static)全局变量1.5 extern全局变量声明1.6 全局函数和静态函数1.7 总结 二。内存布局2.1 内存分区2.2 存储类型总结2.3内存操作函数1) memset()2) memcpy()3) memmove()4) memcmp() 2.4 堆…

【2024年华为OD机试】 (C卷,100分)- 堆栈中的剩余数字(Java JS PythonC/C++)

一、问题描述 题目描述 向一个空栈中依次存入正整数&#xff0c;假设入栈元素 n(1<n<2^31-1)按顺序依次为 nx…n4、 n3、n2、 n1, 每当元素入栈时&#xff0c;如果 n1n2…ny(y 的范围[2,x]&#xff0c; 1<x<1000)&#xff0c;则 n1~ny 全部元素出栈&#xff0c;重…

Java安全—SPEL表达式XXESSTI模板注入JDBCMyBatis注入

前言 之前我们讲过SpringBoot中的MyBatis注入和模板注入的原理&#xff0c;那么今天我们就讲一下利用以及发现。 这里推荐两个专门研究java漏洞的靶场&#xff0c;本次也是根据这两个靶场来分析代码&#xff0c;两个靶场都是差不多的。 https://github.com/bewhale/JavaSec …

51单片机入门基础

目录 一、基础知识储备 &#xff08;一&#xff09;了解51单片机的基本概念 &#xff08;二&#xff09;掌握数字电路基础 &#xff08;三&#xff09;学习C语言编程基础 二、开发环境搭建 &#xff08;一&#xff09;硬件准备 &#xff08;二&#xff09;软件准备 三、…

基于Java的百度AOI数据解析与转换的实现方法

目录 前言 一、AOI数据结构简介 1、官网的实例接口 2、响应参数介绍 二、Java对AOI数据的解析 1、数据解析流程图 2、数据解析实现 3、AOI数据解析成果 三、总结 前言 在当今信息化社会&#xff0c;地理信息数据在城市规划、交通管理、商业选址等领域扮演着越来越重要的…

【WEB】网络传输中的信息安全 - 加密、签名、数字证书与HTTPS

文章目录 1. 概述2. 网络传输安全2.1.什么是中间人攻击2.2. 加密和签名2.2.1.加密算法2.2.2.摘要2.2.3.签名 2.3.数字证书2.3.1.证书的使用2.3.2.根证书2.3.3.证书链 2.4.HTTPS 1. 概述 本篇主要是讲解讲一些安全相关的基本知识&#xff08;如加密、签名、证书等&#xff09;&…

shell练习2

需求&#xff1a;判断192.168.1.0/24网络中&#xff0c;当前在线的ip有哪些&#xff0c;并编写脚本打印出来。 #!/bin/bashnmap -sn 192.168.1.0/24 | grep Nmap scan report for | awk {print $5} 注意&#xff1a;当运行 bash ip.sh 时出现 nmap: command not found 的错误…

【运维自动化-作业平台】魔法变量到底如何使用之主机列表类型

蓝鲸作业平台&#xff0c;以下简称作业平台或JOB平台 魔法变量&#xff1a;JOB平台执行引擎提供的特有的变量能力用法 脚本中使用&#xff0c;并且需要事先声明&#xff1a;job_import {{变量名}} 声明后&#xff0c;同样是使用 dollar 符 大括号&#xff1a;${变量名}来取值…

活动预告 | CCF开源发展委员会开源供应链安全技术研讨会(2025第一期)——“大模型时代的开源供应链安全风控技术”...

点击蓝字 关注我们 CCF Opensource Development Committee CCF开源发展委员会开源供应链安全工作组&#xff08;以下简称CCF-ODC-OSS&#xff09;将于1月17日下午在北京黄大年茶思屋举行2025年第一期开源供应链安全技术研讨会&#xff0c;此次研讨会主题为“大模型时代的开源供…

XML序列化和反序列化的学习

1、基本介绍 在工作中&#xff0c;经常为了调通上游接口&#xff0c;从而对请求第三方的参数进行XML序列化&#xff0c;这里常使用的方式就是使用JAVA扩展包中的相关注解和类来实现xml的序列化和反序列化。 2、自定义工具类 import javax.xml.bind.JAXBContext; import javax.x…

基于php求职招聘系统设计

基于php求职招聘系统设计 摘要 随着社会信息化时代的到来&#xff0c;如今人们社会的生活节奏普遍加快&#xff0c;人们对于工作效率的要求也越来越高&#xff0c;企业 举办招聘会耗时耗财&#xff0c;个人参加招聘会漫无目的寻找不到“方向”&#xff0c;网络搜索工作量目的…

SDK调用文心一言如何接入,文心一言API接入教程

一、前期准备 注册百度智能云账号&#xff1a; 前往百度智能云官网注册一个账号。这是接入文心一言API的基础。 了解API接口&#xff1a; 在百度智能云开放平台中&#xff0c;找到文心一言API的详情页&#xff0c;了解提供的API接口类型&#xff08;如云端API、移动端API、离线…

【机器学习】数据拟合-最小二乘法(Least Squares Method)

最小二乘法&#xff08;Least Squares Method&#xff09; 最小二乘法是一种广泛使用的数据拟合方法&#xff0c;用于在统计学和数学中找到最佳拟合曲线或模型&#xff0c;使得观测数据点与模型预测值之间的误差平方和最小化。以下是详细介绍&#xff1a; 基本概念 假设有一组…

Flutter 多终端测试 自定义启动画面​​​​​​​ 更换小图标和应用名称

多终端测试 flutter devices flutter run -d emulator-5554 flutter run -d emulator-5556 自定义启动画面 之前&#xff1a; 进入assert 3x 生成 1x 2x dart run flutter_native_splash:create dart run flutter_native_splash:remove 现在&#xff08;flutter_nativ…