23种设计模式的Flutter实现第一篇创建型模式(一)

目录

一、设计模式的分类

1.创建型模式

2.结构型模式

3.行为型模式

二、使用Flutter实现创建型设计模式

1.单例模式(Singleton)

1.概念

2.实现步骤

1.使用Dart的工厂构造函数

2.示例代码

3.使用单例模式

4.异步实现方式

2.工厂方法模式(Factory Method)

 1.概念

2.实现步骤        

1.定义日志记录器的抽象接口

2.定义具体的日志记录器

3.创建日志记录器的工厂类

4.使用工厂方法创建日志记录器

3.抽象工厂模式(Abstract Factory)

 1.概念

2.实现步骤

1.定义Alert抽象类

2.创建具体的Alert子类

3.创建AlertFactory工厂类

4.使用AlertFactory展示不同的Alert

4.建造者模式(Builder)

 1.概念

2.实现步骤

1.定义UserProfile类

2.创建UserProfileBuilder类

3.使用UserProfileBuilder创建UserProfile对象

5.原型模式(Prototype)

 1.概念

2.实现步骤

1.定义Shape抽象类

2.创建具体的Shape子类

3.使使用克隆创建新对象


        设计模式是软件开发中解决特定问题的通用解决方案。在代码中使用设计模式可以帮助我们更好地组织代码、提高复用性、增强扩展性。今天我们就来深入了解四人帮(GoF)提出的23种经典设计模式,这些模式被广泛应用于现代软件开发中,适合解决常见的设计难题。

一、设计模式的分类

        GOF把设计模式分成三大类,分别是创建型模式、结构性模式、行为型模式。

1.创建型模式

        创建型模式关注对象的实例化过程,通过封装实例化逻辑,使代码更灵活、解耦。

        创建型模式分为单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。

2.结构型模式

        结构型模式关注类和对象的组合,以实现更大的功能和灵活的结构。

        结构性模式分为适配器模式(Adapter)、桥接模式(Bridge)、组合模式(Composite)

、装饰器模式(Decorator)、 外观模式(Facade)、 享元模式(Flyweight)、 代理模式(Proxy)

3.行为型模式

        行为型模式关注对象间的交互和职责分配,提高对象之间的协作效率。

        行为型设计模式分为责任链模式(Chain of Responsibility)、命令模式(Command)、 解释器模式(Interpreter)、迭代器模式(Iterator)、中介者模式(Mediator)、 备忘录模式(Memento)、 观察者模式(Observer)、 状态模式(State)、 策略模式(Strategy)、 模板方法模式(Template Method)、 访问者模式(Visitor)

二、使用Flutter实现创建型设计模式

1.单例模式(Singleton)

1.概念

        在Flutter中,单例模式是一种常见的设计模式,可以确保一个类只有一个实例,且为全局共享。这对于需要全局管理的对象(如数据库连接、网络请求、状态管理等)非常有用。下面,我们就来实现一个典型的单例模式。

2.实现步骤

1.使用Dart的工厂构造函数

        在 Dart 中,我们可以通过 factory 构造函数来实现单例模式。factory 构造函数可以控制实例的创建,并确保只创建一个实例。

2.示例代码

        我们以 DatabaseService 类为例,假设它用于管理数据库连接。以下代码展示了如何实现单例模式:

class DatabaseService {// 私有的静态变量,存储唯一实例static final DatabaseService _instance = DatabaseService._internal();// 私有的命名构造函数,用于初始化实例DatabaseService._internal();// 工厂构造函数,用于返回唯一实例factory DatabaseService() {return _instance;}// 示例方法void connect() {print('Connecting to the database...');}
}
3.使用单例模式

        创建 DatabaseService 的实例时,无论调用多少次 DatabaseService(),都将返回相同的实例 _instance。

void main() {var db1 = DatabaseService();var db2 = DatabaseService();// 验证是否为相同实例if (identical(db1, db2)) {print('Both instances are identical.');}// 使用单例方法db1.connect();
}

        在上面的代码中,调用调用 DatabaseService() 将始终返回同一实例。

4.异步实现方式

        如果您的类需要异步初始化(例如网络请求或数据库初始化),可以使用 Future 返回一个 singleton 实例。

class DatabaseService {static DatabaseService? _instance;DatabaseService._internal();static Future<DatabaseService> getInstance() async {if (_instance == null) {_instance = DatabaseService._internal();// 模拟异步操作await Future.delayed(Duration(seconds: 1));print('Database initialized');}return _instance!;}
}

        使用异步单例:

void main() async {var db = await DatabaseService.getInstance();db.connect();
}

        这样,就实现了一个简单的单例模式。使用单例模式有助于在全局范围内共享实例,避免创建多个不必要的对象。

2.工厂方法模式(Factory Method)

 1.概念

        在Flutter中,工厂方法模式用于创建对象,同时将对象的创建逻辑与具体的实例分离。使用这种模式可以让客户端代码更灵活地处理不同类型的对象。下面是如何在Flutter中使用工厂方法模式的示例。

        我们以一个日志记录器为例,看一下工厂方法模式的实现步骤。

2.实现步骤        

        假设我们要实现一个日志记录器 Logger,可以根据不同的日志级别(如 Debug、Error)创建不同类型的日志对象。我们将使用工厂方法模式来创建这些不同的日志对象,而不直接暴露具体的类。

1.定义日志记录器的抽象接口

        首先,我们定义一个 Logger 抽象类,声明一个 log 方法。

abstract class Logger {void log(String message);
}
2.定义具体的日志记录器

        然后,我们创建几个实现 Logger 接口的具体日志记录器类,比如 DebugLogger 和 ErrorLogger。

class DebugLogger implements Logger {@overridevoid log(String message) {print('DEBUG: $message');}
}class ErrorLogger implements Logger {@overridevoid log(String message) {print('ERROR: $message');}
}
3.创建日志记录器的工厂类

        接下来,我们创建一个 LoggerFactory 类,用于根据不同的日志级别来返回对应的日志记录器。

enum LoggerType { debug, error }class LoggerFactory {// 工厂方法,根据 LoggerType 返回对应的 Logger 实例static Logger createLogger(LoggerType type) {switch (type) {case LoggerType.debug:return DebugLogger();case LoggerType.error:return ErrorLogger();default:throw Exception('Unsupported Logger Type');}}
}

        在 createLogger 工厂方法中,传入一个 LoggerType 枚举值,根据该值返回相应的日志记录器实例。

4.使用工厂方法创建日志记录器

        客户端代码可以使用 LoggerFactory 来创建不同的日志记录器,而无需直接实例化具体类:

void main() {Logger debugLogger = LoggerFactory.createLogger(LoggerType.debug);debugLogger.log('This is a debug message.');Logger errorLogger = LoggerFactory.createLogger(LoggerType.error);errorLogger.log('This is an error message.');
}

        在上面的例子中,LoggerFactory 类通过 createLogger 方法返回 Logger 的不同实现。客户端代码可以根据 LoggerType 创建不同的日志记录器,而不必知道每个记录器的具体实现细节。

3.抽象工厂模式(Abstract Factory)

 1.概念

        在Flutter中,工厂模式是一种非常有用的设计模式,可以用于在不暴露对象创建逻辑的前提下,根据条件创建和返回不同的对象。工厂模式与工厂方法模式略有不同,它更关注创建复杂对象,而不仅仅是通过继承来决定具体实例的生成。下面是如何在Flutter中使用工厂模式的示例。

        我们以一个消息提示框为例,看一下抽象工厂模式的实现。

2.实现步骤

        假设我们想要实现一个 AlertFactory 类,用于根据不同的需求来创建不同样式的提示框组件,例如:信息提示警告提示、和错误提示

1.定义Alert抽象类

        首先,我们定义一个 Alert 抽象类,该类声明了一个 showAlert 方法,用于展示提示框。

import 'package:flutter/material.dart';abstract class Alert {void showAlert(BuildContext context);
}
2.创建具体的Alert子类

        接下来,我们创建几个 Alert 的具体实现类,比如 InfoAlert、WarningAlert 和 ErrorAlert,它们各自定义了不同的提示框样式。

class InfoAlert implements Alert {@overridevoid showAlert(BuildContext context) {showDialog(context: context,builder: (context) => AlertDialog(title: Text('Info'),content: Text('This is an informational message.'),actions: [TextButton(onPressed: () => Navigator.pop(context),child: Text('OK'),),],),);}
}class WarningAlert implements Alert {@overridevoid showAlert(BuildContext context) {showDialog(context: context,builder: (context) => AlertDialog(title: Text('Warning'),content: Text('This is a warning message.'),actions: [TextButton(onPressed: () => Navigator.pop(context),child: Text('OK'),),],),);}
}class ErrorAlert implements Alert {@overridevoid showAlert(BuildContext context) {showDialog(context: context,builder: (context) => AlertDialog(title: Text('Error'),content: Text('This is an error message.'),actions: [TextButton(onPressed: () => Navigator.pop(context),child: Text('OK'),),],),);}
}
3.创建AlertFactory工厂类

        现在,我们创建一个 AlertFactory 类,根据传入的 AlertType 枚举值返回不同类型的提示框。

enum AlertType { info, warning, error }class AlertFactory {static Alert createAlert(AlertType type) {switch (type) {case AlertType.info:return InfoAlert();case AlertType.warning:return WarningAlert();case AlertType.error:return ErrorAlert();default:throw Exception('Unsupported Alert Type');}}
}

        在 createAlert 方法中,我们使用 switch 语句来判断 AlertType,并返回相应的 Alert 实现类实例。

4.使用AlertFactory展示不同的Alert

        在客户端代码中,我们可以使用 AlertFactory 来创建不同的提示框,而不需要直接实例化具体的 Alert 类。

void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('Factory Pattern Demo')),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [ElevatedButton(onPressed: () {Alert alert = AlertFactory.createAlert(AlertType.info);alert.showAlert(context);},child: Text('Show Info Alert'),),ElevatedButton(onPressed: () {Alert alert = AlertFactory.createAlert(AlertType.warning);alert.showAlert(context);},child: Text('Show Warning Alert'),),ElevatedButton(onPressed: () {Alert alert = AlertFactory.createAlert(AlertType.error);alert.showAlert(context);},child: Text('Show Error Alert'),),],),),),);}
}

        在上述实例中,AlertFactory 类的 createAlert 方法根据传入的 AlertType 返回不同的 Alert 实例,showAlert 方法在 Alert 子类中定义了各自的提示框样式,在客户端代码中,可以通过调用 AlertFactory.createAlert 方法来获取相应的提示框实例并展示提示。

4.建造者模式(Builder)

 1.概念

        在Flutter中,建造者模式(Builder Pattern)是一种创建型设计模式,它允许一步一步地创建复杂对象的不同表示方式。通过这种模式,可以更灵活地构建不同配置的对象,尤其是在需要逐步设置对象的属性或处理多个可选属性时非常有用。

        以下是一个使用建造者模式的示例,在这个示例中,我们将实现一个自定义的 UserProfile 类,它具有多个属性(如名字、年龄、头像、简介等)。我们会使用建造者模式来逐步构建这个类的对象。

2.实现步骤

        我们将创建一个 UserProfile 类,并通过建造者模式来灵活地设置不同属性以创建完整的用户简介对象。

1.定义UserProfile类

        首先,定义 UserProfile 类,并将其设置为不可变的类,同时使其属性通过 UserProfileBuilder 类来设置。

class UserProfile {final String name;final int age;final String? avatarUrl;final String? bio;// 构造函数私有化,确保只能通过 Builder 创建UserProfile._({required this.name,required this.age,this.avatarUrl,this.bio,});@overrideString toString() {return 'UserProfile(name: $name, age: $age, avatarUrl: $avatarUrl, bio: $bio)';}
}
2.创建UserProfileBuilder类

       接下来,创建 UserProfileBuilder 类,用于逐步构建 UserProfile 对象。

class UserProfileBuilder {// 定义用户属性,并提供默认值String? _name;int? _age;String? _avatarUrl;String? _bio;// 设置用户名UserProfileBuilder setName(String name) {_name = name;return this;}// 设置用户年龄UserProfileBuilder setAge(int age) {_age = age;return this;}// 设置用户头像URLUserProfileBuilder setAvatarUrl(String avatarUrl) {_avatarUrl = avatarUrl;return this;}// 设置用户简介UserProfileBuilder setBio(String bio) {_bio = bio;return this;}// 构建UserProfile对象UserProfile build() {// 确保必须属性不为空if (_name == null || _age == null) {throw Exception("Name and age must not be null");}return UserProfile._(name: _name!,age: _age!,avatarUrl: _avatarUrl,bio: _bio,);}
}
3.使用UserProfileBuilder创建UserProfile对象

        在客户端代码中,可以使用 UserProfileBuilder 来构建一个用户简介对象,设置不同的属性,然后调用 build() 方法生成最终的 UserProfile 实例。

void main() {// 使用建造者模式创建UserProfileUserProfile user = UserProfileBuilder().setName("Alice").setAge(25).setAvatarUrl("https://example.com/avatar.jpg").setBio("Flutter developer and open-source enthusiast.").build();print(user);// 还可以创建一个更简略的UserProfile对象UserProfile minimalUser = UserProfileBuilder().setName("Bob").setAge(30).build();print(minimalUser);
}

        在上面的实例代码中:    

        serProfile:这是一个不可变的类,属性设置为 final,并且构造函数为私有,以确保只能通过 UserProfileBuilder 创建实例。

         UserProfileBuilder:提供设置属性的链式方法(如 setName、setAge 等),每个方法都返回 this,以支持链式调用。build() 方法用于生成最终的 UserProfile 对象。

        构建用户简介:通过 UserProfileBuilder 的链式方法,可以灵活地设置所需的属性,最终通过 build() 方法生成 UserProfile。

5.原型模式(Prototype)

 1.概念

        在Flutter中,原型模式(Prototype Pattern)是一种创建型设计模式,用于通过克隆已有的对象来创建新对象,而不是直接实例化类。它适用于创建开销较大的对象,或在需要多个相似对象的场景下使用。

2.实现步骤

        下面是一个使用原型模式的示例,在这个示例中,我们将实现一个可克隆的 Shape 类,用于创建不同形状的对象(例如矩形和圆形),并通过克隆现有对象来生成新对象。

        在 Dart 中,我们可以通过 factory 构造函数来实现单例模式。factory 构造函数可以控制实例的创建,并确保只创建一个实例。

        假设我们有一个 Shape 抽象类,并通过原型模式来克隆出不同类型的形状对象。

1.定义Shape抽象类

        首先,我们定义一个 Shape 抽象类,并添加一个 clone 方法,用于返回当前对象的克隆。

abstract class Shape {String color;Shape(this.color);// 定义克隆方法Shape clone();// 描绘形状的方法void draw();
}
2.创建具体的Shape子类

        接下来,创建两个 Shape 的具体实现类:Rectangle 和 Circle,并在 clone 方法中实现对象的克隆逻辑。

class Rectangle extends Shape {double width;double height;Rectangle(String color, this.width, this.height) : super(color);@overrideShape clone() {return Rectangle(color, width, height);}@overridevoid draw() {print("Drawing Rectangle: color=$color, width=$width, height=$height");}
}class Circle extends Shape {double radius;Circle(String color, this.radius) : super(color);@overrideShape clone() {return Circle(color, radius);}@overridevoid draw() {print("Drawing Circle: color=$color, radius=$radius");}
}
3.使使用克隆创建新对象

        在客户端代码中,我们可以通过调用 clone 方法来克隆现有的对象,而无需直接创建新实例。

void main() {// 创建一个矩形原型对象Rectangle rectangle = Rectangle("blue", 10.0, 20.0);rectangle.draw();// 克隆矩形对象Rectangle clonedRectangle = rectangle.clone() as Rectangle;clonedRectangle.color = "red"; // 修改克隆对象的颜色clonedRectangle.draw();// 创建一个圆形原型对象Circle circle = Circle("green", 15.0);circle.draw();// 克隆圆形对象Circle clonedCircle = circle.clone() as Circle;clonedCircle.color = "yellow"; // 修改克隆对象的颜色clonedCircle.draw();
}

        在这个示例中,我们使用了原型模式的克隆特性,通过 clone 方法可以方便地创建相似的对象。原型模式在需要生成相同或相似配置的对象时非常实用。

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

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

相关文章

LeetCode105.从前序与中序遍历构造二叉树

题目要求 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 提示: 1 < preorder.length < 3000inorder.length preorder.length-3000 < pr…

【问卷调研】HarmonyOS SDK开发者社区用户需求有奖调研

问卷请点击&#xff1a;HarmonyOS SDK开发者社区用户需求有奖调研

IOT物联网低代码可视化大屏解决方案汇总

目录 参考来源云服务商阿里云物联网平台产品主页产品文档 开源项目DGIOT | 轻量级工业物联网开源平台项目特点项目地址开源许可 IoTGateway | 基于.NET6的跨平台工业物联网网关项目特点项目地址开源许可 IoTSharp | 基于.Net Core开源的物联网基础平台项目特点项目地址开源许可…

如何在Mac上切换到JDK 17开发环境

在本文中&#xff0c;我将为您介绍如何在Mac上切换到JDK 17&#xff0c;包括下载和安装JDK 17、设置环境变量、在IntelliJ IDEA中配置项目、修改Maven编译配置&#xff0c;并最终使用mvn clean install重新编译项目。通过这个流程&#xff0c;您可以顺利地将开发环境升级到JDK …

玩转ChatGPT:文献阅读 v2.0

一、写在前面 好久不更新咯。 因为最近ChatGPT更新了不少功能&#xff08;水一篇刷存在感&#xff09;&#xff1a; 上线ChatGPT-4o模型&#xff0c;说推理能力还不错&#xff1b;上线联网功能&#xff0c;类似Kimi那种。 所以呢&#xff0c;用它来读文献就挺舒服的了。例如…

自动驾驶系列—从数据采集到存储:解密自动驾驶传感器数据采集盒子的关键技术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

HarmonyOS本地存储-Preferences(用户首选项)的使用

一&#xff0c;用户首选项简述 ohos.data.preferences (用户首选项) 用户首选项为应用提供Key-Value键值型的数据处理能力&#xff0c;支持应用持久化轻量级数据&#xff0c;并对其修改和查询。 数据存储形式为键值对&#xff0c;键的类型为字符串型&#xff0c;值的存储数据…

SpringCloud 微服务消息队列灰度方案 (RocketMQ 4.x)

目录 背景遇到的问题 RocketMQ 基础基础消息模型扩展后的消息模型部署模型相关概念点 方案对比影子Topic的方案Tag的方案UserProperty的方案影子Group的方案灰度分区的方案方案对比 灰度分区方案设计适配只有部分灰度的情况所做的功能扩展消费者&#xff08;无灰度&#xff09;…

「QT」文件类 之 QDataStream 数据流类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定制…

QT<30> Qt中使鼠标变为转圈忙状态

前言&#xff1a;当我们在写软件时&#xff0c;在等待阻塞耗时操作时可以将鼠标变为忙状态&#xff0c;并在一段时间后恢复状态&#xff0c;可以用到GxtWaitCursor&#xff1a;Qt下基于RAII的鼠标等待光标类。 一、效果演示 二、详细代码 在项目中添加C文件&#xff0c;命名为…

Vue的基础使用

一、为什么要学习Vue 1.前端必备技能 2.岗位多&#xff0c;绝大互联网公司都在使用Vue 3.提高开发效率 4.高薪必备技能&#xff08;Vue2Vue3&#xff09; 二、什么是Vue 概念&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套 构建用户界面 的 渐进式 框架…

数据结构Python版

2.3.3 双链表 双链表和链表一样&#xff0c;只不过每个节点有两个链接——一个指向后一个节点&#xff0c;一个指向前一个节点。此外&#xff0c;除了第一个节点&#xff0c;双链表还需要记录最后一个节点。 每个结点为DLinkNode类对象&#xff0c;包括存储元素的列表data、…

FBX福币交易所恒指收跌1.96% 半导体股继续回调

查查配分析11月14日电 周四,港股三大指数集体下跌。截至收盘,恒生指数跌1.96%,恒生科技指数跌3.08%,恒生中国企业指数跌2.21%。大市成交额1733亿港元。 FBX福币凭借用户友好的界面和对透明度的承诺,迅速在加密货币市场中崭露头角,成为广大用户信赖的平台。 来源:Wind 盘面上,科…

【学习日记】notebook添加JAVA支持

作者是个大学生 这个专栏主要收集课时常用的软件 以及女朋友上课用的软件的教程 新开了gitcode 用于上传安装包 环境说明 windows11 java23.0.1 ijava1.1.2 Anaconda-2024.02 需提前配置好java环境 本篇仅对添加支持进行说明 ijava的GitCode链接NotebookAddsSupportForJava:no…

RabbitMQ 篇-深入了解延迟消息、MQ 可靠性(生产者可靠性、MQ 可靠性、消费者可靠性)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 RabbitMQ 的可靠性 2.0 发送者的可靠性 2.1 生产者重试机制 2.2 生产者确认机制 2.2.1 开启生产者确认机制 2.2.2 定义 ReturnCallback 机制 2.2.3 定义 ConfirmC…

【数据结构】AVL树

引言&#xff1a;在实际情况中&#xff0c;数据不仅仅要存储起来&#xff0c;还要进行对数据进行搜索&#xff0c;为了方便进行高效搜索(在此之前的数据结构的搜索基本都是暴力搜索)二叉搜索树应运而生。但是在极端情况下(我们按照有序的方式进行插入)&#xff0c;二叉搜索树就…

CSS的综合应用例子(网页制作)

这是html的一些最基本内容的代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <t…

MySQL查询某个数据库中特定表的空间占用大小

如果您也想要查询某个数据库中特定表的空间占用大小&#xff0c;包括数据和索引的大小&#xff0c;那么您可以使用以下SQL查询。这个查询将显示特定表在数据库中的数据大小、索引大小以及总大小。 SELECT table_name AS Table,ROUND(((data_length index_length) / 1024 / 10…

Towards Reasoning in Large Language Models: A Survey

文章目录 题目摘要引言什么是推理?走向大型语言模型中的推理测量大型语言模型中的推理发现与启示反思、讨论和未来方向 为什么要推理?结论题目 大型语言模型中的推理:一项调查 论文地址:https://arxiv.org/abs/2212.10403 项目地址: https://github.com/jeffhj/LM-reason…

进入未来城:第五周游戏指南

欢迎来到 Alpha 第 4 季第五周&#xff01; 走进霓虹闪烁的未来城街道&#xff0c;这是一座科技至上的赛博朋克大都市。鳞次栉比的摩天大楼熠熠生辉&#xff0c;拥挤的街道下则是阴森恐怖的地下世界。在这里&#xff0c;像激光鹰队长这样的超级战士正在巡逻&#xff0c;而 Ago…