Android 常用设计模式和实例

一、什么是设计模式?

设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

好了,上面其实都是废话,摘抄的别人的。

开发好几年了,没有特别去注意过自己的项目设计要怎样怎样,其实在无形中,实现了各种各样的设计模式。在这儿做个总结。

二、设计模式的六个原则

设计模式的 六大原则 是软件设计的核心思想,它们确保代码的 高内聚、低耦合,提高代码的 可读性、扩展性和维护性

1、迪米特法则,又称最少知道原则(Demeter Principle)

2、里氏替换原则,即基类和子类之间的关系。子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

3、依赖倒转原则,即降低客户与实现模块之间的耦合。就是该用抽象类和接口的时候尽量用,不要去依赖具体的实现类。

  • 高层模块不应该依赖低层模块,而应该依赖抽象
  • 细节(具体实现)应该依赖于抽象(接口)

4、接口隔离原则,不要强迫一个类实现它用不到的接口,应拆分接口,让接口更小更具体

比如一个人有生活,工作和睡觉,每个事物都有很多方法去实现,不应该把它全放在人这个接口,而是拆分出去。避免无用方法。

5.开闭原则,对扩展开放,对修改关闭,即在不修改已有代码的情况下,允许扩展功能。

  • 避免修改已有代码,减少风险
  • 支持扩展,提高系统的灵活性

6.单一职责原则,一个类应该 只有一个引起它变化的原因,即一个类只做一件事。别一个类把所有事儿包圆了,代码臃肿且不好维护迭代。

三、开发者常用到的设计模式及案例

1.单例模式

📌 作用:确保某个类在整个应用生命周期中只有一个实例,并提供一个全局访问点。这个在开发中会经常使用到。

📌 场景

  • 全局管理对象(如 SharedPreferences、数据库 Room 实例)
  • 网络请求管理(Retrofit)
  • 应用程序级别的配置管理

示例1:retrofit(kotlin)

object RetrofitClient {private val retrofit: Retrofit by lazy {Retrofit.Builder().baseUrl("https://api.example.com/").addConverterFactory(GsonConverterFactory.create()).build()}fun getService(): ApiService = retrofit.create(ApiService::class.java)
}

优点:使用 lazy 懒加载,只有在真正需要时才会创建 Retrofit 实例,避免应用启动时的额外资源占用。

当然,我现在项目使用的是枚举enum

enum class RetrofitManager {INSTANCE;val retrofit: Retrofitinit {retrofit = getRetrofitObject()}private fun getRetrofitObject(): Retrofit {val client = OkHttpClient.Builder().connectTimeout(20, TimeUnit.SECONDS).readTimeout(20, TimeUnit.SECONDS).writeTimeout(20, TimeUnit.SECONDS).build()return Retrofit.Builder().baseUrl(AppAPI.BASE_URL).client(client).addConverterFactory(GsonConverterFactory.create()).build()}
}

在 Kotlin 中,使用 enum class 创建单例是有效的,因为:

  1. 枚举的实例是唯一的
    • INSTANCE 只会在 RetrofitManager 类加载时创建一次,符合单例模式的定义
  2. JVM 保证枚举单例的线程安全
    • enum 的实例在类加载时就初始化,JVM 确保只有一个实例,避免了反射攻击和反序列化创建新实例的问题
2.工厂模式

📌 作用:通过工厂方法创建对象,而不是直接 new,提高代码的可扩展性和维护性。
📌 场景

  • 不同类型的 ViewHolder 创建
  • 不同类型的对话框(Dialog)创建
  • 网络请求数据解析(如 Gson TypeAdapter)

例如我们在实现一个多类型的列表时,需要在 onCreateViewHolder() 里写多个 when 语句进行条件判断去inflate不同的布局。

例如:

override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): RecyclerView.ViewHolder {return if (viewType == TYPE_ITEM) {val view = LayoutInflater.from(parent.context).inflate(R.layout.item_home_more_type, parent, false)MoreVm(view, itemClickListener)} else {val view = LayoutInflater.from(parent.context).inflate(R.layout.item_footer_loading, parent, false)FooterViewHolder(view)}}

这时候,我们就可以用到工厂模式,可以提高代码复用性。

class ViewHolderFactory {companion object {fun create(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {return when (viewType) {TYPE_TEXT -> TextViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false))TYPE_IMAGE -> ImageViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false))else -> throw IllegalArgumentException("Unknown viewType: $viewType")}}}
}
3.观察者模式

作用:允许对象间定义一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。
📌 场景

  • LiveData + ViewModel(MVVM 架构)
  • EventBus / RxJava
  • 广播接收器(BroadcastReceiver)

这个在现在的开发里用的地方太多了,示例:

class MyViewModel : ViewModel() {val userData: MutableLiveData<String> = MutableLiveData()fun updateUserData(newData: String) {userData.value = newData}fun requestData(){...updateUserData(newData)...}
}// Activity 中监听数据变化
viewModel.userData.observe(this, Observer { data ->textView.text = data
})
4.策略模式

作用:定义一组算法,把它们封装起来,并使它们可以互相替换。
📌 场景

  • 不同的图片加载策略(Glide/Picasso)
  • 不同的动画策略
  • 不同的网络请求缓存策略

示例:

interface ImageLoaderStrategy {fun loadImage(context: Context, url: String, imageView: ImageView)
}class GlideImageLoader : ImageLoaderStrategy {override fun loadImage(context: Context, url: String, imageView: ImageView) {Glide.with(context).load(url).into(imageView)}
}class PicassoImageLoader : ImageLoaderStrategy {override fun loadImage(context: Context, url: String, imageView: ImageView) {Picasso.get().load(url).into(imageView)}
}// 使用
val imageLoader: ImageLoaderStrategy = GlideImageLoader()
imageLoader.loadImage(context, "https://example.com/image.jpg", imageView)

🔹 优势:让不同的策略可以在运行时切换,提升代码的灵活性。在平时的开发里,我会用于判断图片大小,是否对图片压缩,图片的缓存方式进行策略模式的设计

5. 责任链模式

 作用:将多个处理逻辑串联起来,按照顺序依次处理请求,直到满足条件的处理器处理完成。记住:责任链模式中的每个处理节点(拦截器、处理者)可以选择是否处理请求,或将其传递给下一个处理者。所谓的链式调用并不符合这个设计模式。
📌 场景

  • OkHttp 的拦截器(Interceptor)
  • Android 事件分发(onTouchEvent)
  • 权限请求链

示例:

    private fun getRetrofitObject(): Retrofit {val loggingInterceptor = HttpLoggingInterceptor { message ->LogUtils.i("RetrofitLog", "retrofitBack = $message")}.apply { level = HttpLoggingInterceptor.Level.BODY }val client = OkHttpClient.Builder().addInterceptor(loggingInterceptor).addInterceptor(HeaderInterceptor()).connectTimeout(20, TimeUnit.SECONDS).readTimeout(20, TimeUnit.SECONDS).writeTimeout(20, TimeUnit.SECONDS).build()return Retrofit.Builder().baseUrl(AppAPI.BASE_URL).client(client).addConverterFactory(GsonConverterFactory.create()).build()}
}

🔹 优势:可以动态地添加或移除责任节点,提高代码的扩展性。我上面就设置了日志拦截器和请求拦截器,

  • 灵活性:你可以自由添加或删除拦截器,改变请求和响应的处理顺序。
  • 可扩展性:每个拦截器都可以独立工作,允许按需添加处理逻辑,形成一个灵活的扩展点。
  • 解耦:每个拦截器只关注自己需要处理的任务,不必知道其他拦截器的逻辑,符合单一职责原则。
6. 适配器模式

作用:将一个接口转换成客户端期望的接口,使不兼容的类可以协同工作。
📌 场景

  • RecyclerView 适配器(Adapter)
  • 数据库适配(Room)

例如后端返回的数据更改了,变成了:

{"userId": "12345","user_name": "Lee","user_email": "lee@example.com"
}

而我之前设计的实体类是:

data class UserProfile(val id: String,val name: String,val email: String
)

这时候我只需要写一个方法:

// 适配器类,将 API 数据转换为 UserProfile 结构
class UserAdapter(private val apiData: ApiUser) {fun toUserProfile(): UserProfile {return UserProfile(id = apiData.userId,name = apiData.user_name,email = apiData.user_email)}
}

 有一说一,我这个示例,很简陋,能力有限,在需求开发中在这种场景使用过。

🔹 优势

  1. 桥接不同的数据结构,避免 API 数据格式和 UI 组件直接绑定,提升代码灵活性。
  2. 让已有代码无需修改,只需更改适配器,保持项目稳定。
  3. 提高代码复用性,适配器可以被多个 UI 组件或业务模块复用。
7. 代理模式(Proxy Pattern)

作用:为对象提供一个代理类,控制对原对象的访问。
📌 场景

  • 动态权限申请(ActivityCompat.requestPermissions)
  • Retrofit 动态代理
  • AOP(Aspect-Oriented Programming)例如retrofit的日志拦截,埋点等,AOP(面向切面编程)主要是基于代理模式实现的,但它不仅仅局限于代理模式,还可以结合字节码操作(如 ASM、Javassist)和编译器插桩(AspectJ)等方式

AOP 本质上就是通过代理模式,在不修改原代码的情况下,动态地增强功能(如日志、权限控制、监控等)。
在 AOP 的实现中,通常使用 动态代理(JDK 动态代理、CGLIB 代理) 来拦截方法调用,并在方法执行前后执行额外的逻辑。

📌 代理模式核心思路:

  1. 静态代理:预先定义代理类,手动编写代理逻辑(适用于少量接口)。
  2. 动态代理:运行时生成代理对象,动态添加方法增强(适用于大规模 AOP)。
  3. 字节码操作:直接修改 .class 字节码(如 ASM、Javassist、AspectJ)。

说来惭愧,这个设计模式我在自己的项目开发里并没有如何去设计过,不过去了解了一下,

可以使用 Kotlin 注解 + 反射 实现AOP,进行方法执行时间拦截。

示例1:

//定义一个注解
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class TimeLog//在我要使用的方法上加一个注解
class TestClass {@TimeLogfun testMethod() {Thread.sleep(500)  // 模拟耗时操作}
}
//📌 使用反射 + AOP 拦截
fun logExecutionTime(obj: Any) {val methods = obj::class.java.declaredMethodsfor (method in methods) {if (method.isAnnotationPresent(TimeLog::class.java)) {val start = System.currentTimeMillis()method.invoke(obj)  // 调用方法val end = System.currentTimeMillis()Log.d("AOP", "${method.name} 执行时间: ${end - start}ms")}}
}val test = TestClass()
logExecutionTime(test)

示例2:使用 AspectJ(编译期 AOP)

目的:无须修改任何activity代码,监听 Activity 生命周期,自动埋点

@Aspect
class LifecycleAspect {@Before("execution(* android.app.Activity.onCreate(..))")fun beforeOnCreate(joinPoint: JoinPoint) {val activity = joinPoint.target as ActivityLog.d("AOP", "Activity ${activity::class.java.simpleName} - onCreate")}
}

常用的三方库里面很多都是基于上述的设计模式去实现的,我自己一般可能就只会用到前面四个,哈哈哈

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

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

相关文章

e2studio开发RA2E1(9)----定时器GPT配置输入捕获

e2studio开发RA2E1.9--定时器GPT配置输入捕获 概述视频教学样品申请硬件准备参考程序源码下载新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user_uart_callback…

【PS 2022】Adobe Genuine Service Alert 弹出

电脑总是弹出Adobe Genuine Service Alert弹窗 1. 不关掉弹窗并打开任务管理器&#xff0c;找到Adobe Genuine Service Alert&#xff0c;并右键进入文件所在位置 2 在任务管理器中结束进程并将文件夹中的 .exe 文件都使用空文档替换掉 3. 打开PS不弹出弹窗&#xff0c;解决&a…

RoboGrasp:一种用于稳健机器人控制的通用抓取策略

25年1月来自北京大学和哈佛大学的论文“RoboGrasp: A Universal Grasping Policy for Robust Robotic Control”。 模仿学习和世界模型在推进通用机器人学习方面显示出巨大的潜力&#xff0c;而机器人抓取仍然是实现精确操控的关键挑战。现有方法通常严重依赖机械臂状态数据和…

接口测试Day12-持续集成、git简介和安装、Gitee远程仓库、jenkins集成

持续集成 概念&#xff1a; 团队成员将自己的工作成果&#xff0c;持续集成到一个公共平台的过程。成员可以每天集成一次&#xff0c;也可以一天集成多 次。 相关工具&#xff1a; 本地代码管理&#xff1a;git远程代码管理&#xff1a;gitee(国内)、github(国外)、gitlib(公司…

C语言基础11:分支结构以及if的使用

C语言基础 内容提要 分支结构 条件判断用if语句实现分支结构 分支结构 问题抛出 我们在程序设计往往会遇到如下问题&#xff0c;比如下面的函数的计算&#xff1a; y { 1 / x 当 x ≠ 0 时 10000 当 x 0 时 y \begin{cases} 1/x \quad当x\neq0时\\ \\ 10000 \quad当x0…

81页精品PPT | 华为流程与信息化实践与架构规划分享

华为流程与信息化实践与架构规划分享主要围绕华为在业务流程与信息化建设方面的经验、企业架构规划方法以及企业数字化转型路径展开。华为通过持续的业务变革和信息化建设&#xff0c;从本土企业逐步发展为国际化、全球化企业&#xff0c;其管理体系以持续创新和世界级管理体系…

【最大开支——优先队列,计算增量】

题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; using pll pair<ll, int>; #define x first #define y second const int N 1e5 10; int n, m; int k[N], b[N], cnt[N]; priority_queue<pll, vector<pll>> pq; // d…

174款复古Y2K酸性镀铬银色金属多样化锁链链条铁链几何抽象PNG免扣元素设计套装 Studio 2AM - Chains

Chains 是以链条纹理为主题的设计元素的集合。以 PNG 格式以高分辨率创建&#xff0c;但文件大小较小&#xff0c;因此不会占用硬盘空间。“Chains” 是以 PNG 格式提供的以链条为主题的设计元素的高分辨率集合。该套装包括 174 个银色、生锈和彩虹色材料的链条纹理&#xff0c…

将 AMD Zynq™ RFSoC 扩展到毫米波领域

目录 将 AMD Zynq™ RFSoC 扩展到毫米波领域Avnet XRF RFSoC 系统级模块适用于 MATLAB 的 Avnet RFSoC Explorer 工具箱5G mmWave PAAM 开发平台突破性的宽带毫米波波束成形特征&#xff1a;OTBF103 Mathworks Simulink 模型优化毫米波应用中的射频信号路径 用于宽带毫米波上/下…

IDEA中打包maven项目,提示Compilation failure

使用IDEA打包maven项目&#xff0c;报错如下&#xff1a; 解决方法&#xff1a;在pom文件中指定JDK版本即可 <properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target> </propertie…

Idea集成deepseek生成代码

今天我带大家在idea上安装CodeGpt插件&#xff0c;这个插件可以根据我们的提示词生产代码&#xff0c;我们一起试试。 1、安装插件 打开idea&#xff0c;再点击setting菜单&#xff0c;按以下步骤操作。 安装完成后&#xff0c;一定要点击第四步“ok”。再次点击菜单setting…

C++STL(六)——list模拟

目录 本次所需实现的三个类一、结点类的模拟实现构造函数 二、迭代器类的模拟实现为什么有迭代器类迭代器类的模板参数说明构造函数运算符的重载- -运算符的重载和!运算符的重载*运算符的重载->运算符的重载引入模板第二个和第三个参数 三、list的模拟实现3.1 默认成员函数构…

Ubuntu安装PgSQL17

参考官网教程&#xff0c;Ubuntu24 apt在线安装Postgres 17 1. 要手动配置 Apt 存储库 # 导入存储库签名密钥&#xff1a; sudo apt install curl ca-certificates sudo install -d /usr/share/postgresql-common/pgdg sudo curl -o /usr/share/postgresql-common/pgdg/apt…

【iOS自动化】Xcode配置WebDriverAgent

WebDriverAgent 是 iOS 端自动化测试的工具&#xff0c;这里记录下 MacOS 环境 Xcode 如何配置 WebDriverAgent。 【重要】环境准备 ‼️ 注意&#xff1a;Xcode 版本需要支持对应的 iOS 版本&#xff0c;而 Xcode 版本又依赖 MacOS 版本&#xff1b;在开始部署前&#xff0c…

Golang:精通sync/atomic 包的Atomic 操作

在本指南中&#xff0c;我们将探索sync/atomic包的细节&#xff0c;展示如何编写更安全、更高效的并发代码。无论你是经验丰富的Gopher还是刚刚起步&#xff0c;你都会发现有价值的见解来提升Go编程技能。让我们一起开启原子运算的力量吧&#xff01; 理解Go中的原子操作 在快…

Mp4视频播放机无法播放视频-批量修改视频分辨率(帧宽、帧高)

背景 家人有一台夏新多功能 视频播放器(夏新多功能 视频播放器),用来播放广场舞。下载了一些广场舞视频, 只有部分视频可以播放,其他视频均无法播放,判断应该不是帧速率和数据速率的限制, 分析可能是播放器不支持帧高度大于720的视频。由于视频文件较多,需要借助视频编…

【Python】字典

个人主页&#xff1a;GUIQU. 归属专栏&#xff1a;Python 文章目录 1. 字典概述2. 字典的创建与初始化2.1 直接使用花括号创建2.2 使用 dict() 构造函数创建2.3 字典推导式创建 3. 字典的基本操作3.1 访问字典中的值3.2 修改和添加键值对3.3 删除键值对 4. 字典的遍历4.1 遍历键…

STM32系统架构介绍

STM32系统架构 1. CM3/4系统架构2. CM3/4系统架构-----存储器组织结构2.1 寄存器地址映射&#xff08;特殊的存储器&#xff09;2.2 寄存器地址计算2.3 寄存器的封装 3. CM3/4系统架构-----时钟系统 STM32 和 ARM 以及 ARM7是什么关系? ARM 是一个做芯片标准的公司&#xff0c…

鸿蒙NEXT开发-发布三方库

开发一个三方库 如需发布一个 har 包&#xff0c;必须包含 oh-package.json5、README.md&#xff0c;CHANGELOG.md 和 LICENSE 四个文件&#xff0c;若文件缺失&#xff0c;会导致上架至中心仓失败。 HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以…

CSS 实现下拉菜单效果实例解析

1. 引言 在 Web 开发过程中&#xff0c;下拉菜单是一种常见且十分实用的交互组件。很多前端教程都提供过简单的下拉菜单示例&#xff0c;本文将以一个简洁的实例为出发点&#xff0c;从 HTML 结构、CSS 样式以及整体交互逻辑三个层面进行详细解析&#xff0c;帮助大家理解纯 C…