Flutter 中的那些设计模式的写法(持续更新)
前言
我们都知道设计模式是相同的,同一种设计模式的理念不会因为语言不同而会有所改变,但是由于语法的差异,设计模式的写法也有所差异,本文会介绍一些flutter中常用设计模式的写法以及使用场景。
常见的设计模式有23种,按照类型可以细分为:
创建型模式
- 单例模式 (Singleton)
- 工厂方法模式 (Factory Method)
- 抽象工厂模式 (Abstract Factory)
- 建造者模式 (Builder)
- 原型模式 (Prototype)
结构型模式
- 适配器模式 (Adapter)
- 桥接模式 (Bridge)
- 组合模式 (Composite)
- 装饰模式 (Decorator)
- 外观模式 (Facade)
- 享元模式 (Flyweight)
- 代理模式 (Proxy)
行为型模式
- 责任链模式 (Chain of Responsibility)
- 命令模式 (Command)
- 解释器模式 (Interpreter)
- 迭代器模式 (Iterator)
- 中介者模式 (Mediator)
- 备忘录模式 (Memento)
- 观察者模式 (Observer)
- 状态模式 (State)
- 策略模式 (Strategy)
- 模板方法模式 (Template Method)
- 访问者模式 (Visitor)
单例模式
想必对于大部分的开发来说,这是咱们接触最多的设计模式之一了。
单例模式(Singleton Pattern)理念:
唯一实例: 单例模式保证了某个类只有一个实例存在。这意味着无论何时何地调用该类,只会返回同一个对象实例。
全局访问: 提供一个全局访问点来获取该唯一实例。通常通过一个静态方法或属性来实现。
延迟实例化: 单例模式通常支持延迟实例化,即在第一次请求时创建实例,以节省资源。
设计思路:
私有构造函数: 将类的构造函数设为私有,以防止外部通过普通构造方法创建实例。
静态实例: 使用静态变量存储类的唯一实例。
静态方法: 提供一个公共的静态方法或属性,返回该唯一实例。
代码实现:
1. 饿汉式单例
这种方式在类加载时就创建实例,线程安全,但如果实例初始化开销大且不一定会用到,会造成资源浪费。
class Singleton {//私有构造函数Singleton._privateConstructor();// 静态实例static final Singleton instance = Singleton._privateConstructor();// 内部方法void printHello() {print('Hello');}
}
//调用方式Singleton.instance.printHello();
2. 懒汉式单例
这种方式在第一次使用时才创建实例,延迟加载。
class SingletonLazy {//私有构造SingletonLazy._privateConstructor();//静态实例static SingletonLazy? _instance;//获取实列方法static SingletonLazy getInstance() {if (_instance == null) {_instance = SingletonLazy._privateConstructor();}return _instance!;}
}
也可以通过dart中的工厂类来创建懒汉式
class SingletonFactory {// 私有构造函数SingletonFactory._privateConstructor();// 静态变量来存储唯一实例static SingletonFactory? _instance;// 工厂构造函数factory SingletonFactory() {if (_instance == null) {_instance = SingletonFactory._privateConstructor();}return _instance!;}void someMethod() {print('This is a method in SingletonFactory');}
}
3.静态内部类
这种方式利用 Dart 的静态内部类特性,既实现了延迟加载,又保证了线程安全。
class Singleton {// 私有构造函数Singleton._privateConstructor();// 静态内部类static final Singleton instance = _SingletonHolder.instance;// 内部类static class _SingletonHolder {static final Singleton instance = Singleton._privateConstructor();}
}
4. 懒汉式双重检查
额~~~写java过来的人条件反射就是,单例要考虑线程安全,加锁。。。
其实在flutter中,由于Flutter是单线程模式的,很少会有多线程切换的使用场景,它的异步操作是通过Future来实现的,其内部也是通过堆栈来实现阻塞(await)和插件队列执行,理论上是线程安全的,除非你非得使用Isolates,但是Isolates一种多线程模型,每个 Isolate 拥有独立的内存空间,因此单例在不同的 Isolate 中不会共享。这意味着在 Dart 中,通常不需要担心传统意义上的多线程竞争条件。所以个人觉得,没有必要加锁考虑并发情况,非得写考虑 静态内部类
工厂设计模式
在 Flutter 中,工厂设计模式(Factory Pattern)是一种创建型设计模式,用于定义一个接口或抽象类以创建对象,但让子类决定要实例化的类。工厂模式可以将实例化对象的逻辑与使用对象的逻辑分离,提高代码的可扩展性和可维护性。
像支付(不同的支付类型)、日志、等都可以使用该模式
工厂模式的主要分类
- 简单工厂模式: 提供一个创建对象实例的方法,根据传入的参数决定创建哪种类型的对象。
- 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而无需明确指定具体类。
简单工厂模式
简单工厂模式通过一个静态方法创建对象实例。
//对象实例子
abstract class Shape {void draw();
}
//实现抽象方法,重写自己的实例子
class Circle implements Shape {void draw() {print("Drawing a Circle");}
}class Square implements Shape {void draw() {print("Drawing a Square");}
}class ShapeFactory {
// 创建静态方法,通过传入不同的类型来判断要执行的工厂类static Shape createShape(String type) {if (type == 'circle') {return Circle();} else if (type == 'square') {return Square();} else {throw Exception("Shape type $type not recognized");}}
}
// 调用实现
void main() {Shape circle = ShapeFactory.createShape('circle');circle.draw(); // 输出: Drawing a CircleShape square = ShapeFactory.createShape('square');square.draw(); // 输出: Drawing a Square
}
抽象工厂模式
抽象工厂模式用于创建相关或依赖对象的家族。
abstract class Button {void paint();
}class WindowsButton implements Button {void paint() {print("Rendering a button in Windows style");}
}class MacOSButton implements Button {void paint() {print("Rendering a button in MacOS style");}
}abstract class GUIFactory {Button createButton();
}class WindowsFactory implements GUIFactory { Button createButton() {return WindowsButton();}
}class MacOSFactory implements GUIFactory { Button createButton() {return MacOSButton();}
}void main() {GUIFactory factory = WindowsFactory();Button button = factory.createButton();button.paint(); // 输出: Rendering a button in Windows stylefactory = MacOSFactory();button = factory.createButton();button.paint(); // 输出: Rendering a button in MacOS style
}
策略设计模式
在 Flutter 中,策略模式(Strategy Pattern)是一种常用的设计模式,它允许你定义一系列算法,将每一个算法封装起来,并且使它们可以互相替换。而不用每次改动都去修改代码实现,策略模式可以让我们不用关注算法的具体实现是什么。
策略模式的结构
- 策略接口(Strategy Interface): 定义算法或行为的接口。
- 具体策略(Concrete Strategies): 实现策略接口的不同算法。
- 上下文(Context): 维护对策略对象的引用,并在需要时调用策略对象的方法。
我们以用算不同类型的折扣为例。策略模式可以帮助我们在不同的折扣计算方法之间切换。
- 定义策略接口
首先,定义一个策略接口,描述不同折扣算法的通用方法。
abstract class DiscountStrategy {
//计算折扣金额double calculateDiscount(double price);
}
2. 实现具体策略
为不同的折扣类型算法的具体的策略类。
//无折扣
class NoDiscountStrategy implements DiscountStrategy { double calculateDiscount(double price) {return 0.0;}
}
//具体折扣
class PercentageDiscountStrategy implements DiscountStrategy {final double percentage;PercentageDiscountStrategy(this.percentage); double calculateDiscount(double price) {return price * (percentage / 100);}
}
//全免
class FixedAmountDiscountStrategy implements DiscountStrategy {final double discountAmount;FixedAmountDiscountStrategy(this.discountAmount); double calculateDiscount(double price) {return discountAmount;}
}
3. 创建上下文
定义一个上下文类(Context),用于持有策略对象,并在需要时调用策略的方法。
class ShoppingCart {DiscountStrategy _discountStrategy;ShoppingCart(this._discountStrategy);void setDiscountStrategy(DiscountStrategy strategy) {_discountStrategy = strategy;}//计算打完折后的最终价格double calculateFinalPrice(double price) {double discount = _discountStrategy.calculateDiscount(price);return price - discount;}
}
4. 使用策略模式
现在可以在应用程序中使用策略模式,根据不同的需求动态选择折扣策略。
void main() {double itemPrice = 100.0;ShoppingCart cart = ShoppingCart(NoDiscountStrategy());print("Final price with no discount: ${cart.calculateFinalPrice(itemPrice)}");cart.setDiscountStrategy(PercentageDiscountStrategy(10));print("Final price with 10% discount: ${cart.calculateFinalPrice(itemPrice)}");cart.setDiscountStrategy(FixedAmountDiscountStrategy(15));print("Final price with $15 discount: ${cart.calculateFinalPrice(itemPrice)}");
}
建造者模式(Builder Pattern)
建造者模式(Builder Pattern)是一种创建型设计模式,它允许使用者一步步构建一个复杂对象。与直接使用构造函数或工厂模式不同,建造者模式提供了一种灵活的方式来创建对象,特别是当对象的创建过程涉及多个步骤或需要复杂配置时。
在 Flutter 中,建造者模式(链式调用)可以用于构建复杂的 UI 组件或对象配置,像UI组件的封装使用构建者模式是非常常用的。
建造者模式的结构
- 产品(Product): 需要构建的复杂对象。
- 建造者接口(Builder Interface): 定义创建产品对象的所有步骤。
- 具体建造者(Concrete Builder): 实现 Builder 接口,并提供创建产品的具体步骤。
- 指挥者(Director): 控制构建过程,按顺序调用建造者中的各个步骤。
下面以封装一个UI组件为例:
1. 定义产品
首先,定义需要创建的复杂对象(对象参数比较多,或者链路很长)。
class ComplexWidget {String? title;String? imageUrl;String? description; String toString() {return 'ComplexWidget(title: $title, imageUrl: $imageUrl, description: $description)';}
}
2. 定义建造者接口
定义一个接口,描述创建产品的步骤。
abstract class ComplexWidgetBuilder {void setTitle(String title);void setImageUrl(String imageUrl);void setDescription(String description);ComplexWidget getResult();
}
3. 实现具体建造者
实现建造者接口,提供具体的构建步骤。
class ConcreteComplexWidgetBuilder implements ComplexWidgetBuilder {final ComplexWidget _complexWidget = ComplexWidget();void setTitle(String title) {_complexWidget.title = title;}void setImageUrl(String imageUrl) {_complexWidget.imageUrl = imageUrl;}void setDescription(String description) {_complexWidget.description = description;} ComplexWidget getResult() {return _complexWidget;}
}
4. 定义指挥者
定义一个指挥者类,用于控制构建过程。
class Director {late ComplexWidgetBuilder _builder;void setBuilder(ComplexWidgetBuilder builder) {_builder = builder;}ComplexWidget buildCompleteWidget() {_builder.setTitle("Sample Title");_builder.setImageUrl("http://example.com/image.png");_builder.setDescription("This is a sample description.");return _builder.getResult();}
}
5. 使用建造者模式
在应用程序中使用建造者模式来创建复杂对象。
void main() {Director director = Director();ComplexWidgetBuilder builder = ConcreteComplexWidgetBuilder();director.setBuilder(builder);ComplexWidget widget = director.buildCompleteWidget();print(widget);// 输出: ComplexWidget(title: Sample Title, imageUrl: http://example.com/image.png, description: This is a sample description.)
}
未完待续
后面慢慢会把常用的设计模式都在这里做更新。。。
每个设计模式都有自己的优缺点,其实适合自己的才是最合理的,不要为了设计代码而设计代码,如果不理解业务场景强上设计模式,只会让代码写的更复杂
相关文章:

Flutter 中的那些设计模式的写法(持续更新)
前言 我们都知道设计模式是相同的,同一种设计模式的理念不会因为语言不同而会有所改变,但是由于语法的差异,设计模式的写法也有所差异,本文会介绍一些flutter中常用设计模式的写法以及使用场景。 常见的设计模式有23种࿰…...

【提效工具开发】Python功能模块执行和 SQL 执行 需求整理
需求梳理 背景 当前我们在IDE或MySQL查询工具中只能进行个人使用,缺乏共享功能,且在查询及数据统计上有一定的不便。为了改善这种情况,计划搭建一个Web平台,通过后台交互来提升效率。此平台需要兼容Python工具和SQL工具的管理、执…...

Linux系列-进程的状态
🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 操作系统就是计算机领域的哲学,是为了保证在所有情况下都适用,加载到内存叫做新建状态。 并行和并发 计算机同时进行多个任务,在用户感知的…...

SpringBoot项目中常用的一些注解
一、核心注解 SpringBootApplication 作用:标注一个主程序类,表明这是一个Spring Boot应用程序的入口。说明:这是一个复合注解,组合了Configuration、EnableAutoConfiguration和ComponentScan。 EnableAutoConfiguration 作用&…...

【网络】自定义协议——序列化和反序列化
> 作者:დ旧言~ > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:了解什么是序列化和分序列,并且自己能手撕网络版的计算器。 > 毒鸡汤:有些事情,总是不明白,所以我不…...

Pytorch如何精准记录函数运行时间
0. 引言 参考Pytorch官方文档对CUDA的描述,GPU的运算是异步执行的。一般来说,异步计算的效果对于调用者来说是不可见的,因为 每个设备按照排队的顺序执行操作Pytorch对于CPU和GPU的同步,GPU间的同步是自动执行的,不需…...

使用 Java 实现邮件发送功能
引言 1. JavaMail API 简介 2. 环境准备 2.1 Maven 依赖 2.2 Gradle 依赖 3. 发送简单文本邮件 4. 发送 HTML 邮件 5. 发送带附件的邮件 6. 注意事项 引言 在现代应用开发中,邮件发送功能是非常常见的需求,例如用户注册验证、密码重置、订单确认…...

html第一个网页
创建你的第一个HTML网页是一个激动人心的步骤。以下是创建一个简单网页的基本步骤和代码示例: 基础结构:所有的HTML文档都应该包含以下基本结构。 <!DOCTYPE html> <html> <head><title>我的第一个网页</title> </he…...

前后端交互接口(三)
前后端交互接口(三) 前言 前两集我们先做了前后端交互接口的约定以及浅浅的阅读了一些proto代码。那么这一集我们就来看看一些重要的proto代码,之后把protobuffer给引入我们的项目当中! gateway.proto 我们来看一眼我们的网关…...

华为Mate70前瞻,鸿蒙NEXT正式版蓄势待发,国产系统迎来关键一战
Mate 70系列要来了 上个月,vivo、小米、OPPO、荣耀等众多智能手机制造商纷纷发布了他们的年度旗舰产品,手机行业内竞争异常激烈。 同时,华为首席执行官余承东在其个人微博上透露,Mate 70系列将标志着华为Mate系列手机达到前所未有…...

【安卓13 源码】Input子系统(4)- InputReader 数据处理
1. 多指触控协议 多指触控协议有 2 种: > A类: 处理无关联的接触: 用于直接发送原始数据; > B类: 处理跟踪识别类的接触: 通过事件slot发送相关联的独立接触更新。 B协议可以使用一个ID来标识触点&…...

Xserver v1.4.2发布,支持自动重载 nginx 配置
Xserver——优雅、强大的 php 集成开发环境 本次更新为大家带来了更好的用户体验。 🎉 下载依赖组件时,显示进度条,展示下载进度。 🎉 保存站点信息和手动修改 vhost 配置文件之后,自动重载 nginx 配置 🐞…...

Java反射原理及其性能优化
目录 JVM是如何实现反射的反射的性能开销体现在哪里如何优化反射性能开销 1. JVM是如何实现反射的? 反射是Java语言中的一种强大功能,它允许程序在运行时动态地获取类的信息以及操作对象。下面是一个简单的示例,演示了如何使用反射调用方法ÿ…...

RabbitMQ 管理平台(控制中心)的介绍
文章目录 一、RabbitMQ 管理平台整体介绍二、Overview 总览三、Connections 连接四、Channels 通道五、Exchanges 交换机六、Queues 队列查看队列详细信息查看队列的消息内容 七、Admin 用户给用户分配虚拟主机 一、RabbitMQ 管理平台整体介绍 RabbitMQ 管理平台内有六个模块&…...

【SQL】在 SQL Server 中创建数据源是 MySQL 数据表的视图
背景:Windows系统已安装了mysql5.7和sqlServer数据库,现在需要在sqlServer创建视图或者查询来自mysql的数据,视图的数据来源mysql数据库。下面进行实现在sqlserver实现获取mysql数据表数据构建视图。 1、打开 ODBC 数据源管理器,…...

现代Web开发:Next.js 深度解析与最佳实践
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 现代Web开发:Next.js 深度解析与最佳实践 现代Web开发:Next.js 深度解析与最佳实践 现代Web开发…...

LeetCode题练习与总结:赎金信--383
一、题目描述 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以,返回 true ;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1࿱…...

eval: jdk1.8.0_431/jre/bin/java: Permission denied
当您在启动Tomcat或其他Java应用时遇到“Permission denied”错误,这通常表示当前用户没有执行指定Java可执行文件的权限。以下是解决这个问题的几种方法: 方法一:检查文件权限 查看文件权限: 使用ls -l命令查看Java可执行文件的…...

.Net IOC理解及代码实现
IOC理解 IoC(Inversion of Control):即控制反转,这是一种设计思想,指将对象的控制权交给IOC容器,由容器来实现对象的创建、管理,程序员只需要从容器获取想要的对象就可以了。DI(Dependency Injection),即依…...

履带机器人(一、STM32控制部分--标准库)
一、履带机器人整体逻辑框架 通过在PC端搭建上位机,使得在PC端可以给STM32发送控制指令并且接受STM32的状态信息。 通过RS485通信,使得STM32可以和电机进行通信,STM32发送启动、停止、转速、方向等指令,并接受电机返回的状态信息。 二、STM32逻辑框架 整体逻辑: 1、先…...

地理空间-Java实现航迹稀释
Java实现航迹点稀释算法(Douglas - Peucker算法)的示例代码,该算法可在保证航迹整体形状变化不大的情况下减少航迹点数量: import java.util.ArrayList; import java.util.List; class Point { double x; double y; public Point…...

qt QHttpMultiPart详解
1. 概述 QHttpMultiPart是Qt框架中用于处理HTTP多部分请求的类。它类似于RFC 2046中描述的MIME multipart消息,允许在单个HTTP请求中包含多个数据部分,如文件、文本等。这种多部分请求在上传文件或发送带有附件的邮件等场景中非常有用。QHttpMultiPart类…...

【测试】【Debug】vscode中同一个测试用例出现重复
这种是正常的情况 当下面又出现一个 类似python_test->文件夹名->test_good ->test_pad 同一个测试用例出现两次,名称都相同,显然是重复了。那么如何解决? 这种情况是因为在终端利用“pip install pytest”安装 之后,又…...

Mac上的免费压缩软件-FastZip使用体验实测
FastZip是Mac上的一款免费的压缩软件,分享一下我在日常使用中的体验 压缩格式支持7Z、Zip,解压支持7Z、ZIP、RAR、TAR、GZIP、BZIP2、XZ、LZIP、ACE、ISO、CAB、PAX、JAR、AR、CPIO等所有常见格式的解压 体验使用下来能满足我所有的压缩与解压的需求&a…...

Linux(CentOS)运行 jar 包
1、在本地终端运行,关闭终端,程序就会终止 java -jar tlias-0.0.1-SNAPSHOT.jar 发送请求,成功 关闭终端(程序也会终止) 发送请求,失败 2、在远程终端运行,关闭终端,程序就会终止 …...

基于YOLOv8 Web的安全帽佩戴识别检测系统的研究和设计,数据集+训练结果+Web源码
摘要 在工地,制造工厂,发电厂等地方,施工人佩戴安全帽能有效降低事故发生概率,在工业制造、发电等领域需要进行施工人员安全帽监测。目前大多数的 YOLO 模型还拘泥于公司、企业开发生产的具体产品中,大多数无编程基础…...

LabVIEW VISA通信常见问题
在工业自动化和测试测量等应用中,使用LabVIEW的VISA函数与设备进行通信时,若发送指令后未能接收数据,以下因素可能是原因: 设备未响应或响应延迟应用示例:例如,在控制测量仪器(如电压表…...

Node.js Stream(流)以及模块系统使用介绍 (基础介绍 五)
Stream(流) Stream 是 Node.js 中非常重要的一个模块,应用广泛。 Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出…...

嵌入式linux中设备树控制硬件的方法
大家好,今天主要给大家分享一下,如何使用linux系统下的设备树进行硬件控制方法。 第一:linux系统中设备树驱动LED原理 在linux系统中可以使用设备树向Linux内核传递相关的寄存器地址,linux驱动中使用OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关…...

定时器入门:Air780E定时器基础与进阶
今天我们学习的是Air780E定时器基础与进阶,让大家更深入的了解定时器。 一、定时器(timer)的概述 在Air780E模组搭载的LuatOS系统中,定时器(timer)是一项基础且关键的服务。它允许开发者在特定的时间点或周期性地执行代码段&…...