网站首页 > 文章精选 正文
文件管理App如何实现灵活排序?策略模式揭秘
引言
在移动端开发中,文件管理、缓存、相册、文档浏览等场景,经常会遇到“需要对不同类型、不同大小的文件进行灵活排序”的需求。比如:你要实现一个本地文件管理App,既要能对小文件快速排序,也要保证大文件(如视频、数据库备份等)的性能不会拖慢整体体验。如何优雅、易扩展地实现这一需求?本篇将带你用策略模式搞定它。
一、现实需求分析
先看下实际中可能遇到的场景:
- o 相册App,用户可以根据图片大小、拍摄时间、文件名等多维度排序。
- o 文件管理器,既有小文件(图片/文本),也有大文件(视频/音频/压缩包等)。
- o 云盘或网盘类App,客户端要支持不同排序算法,还要对大文件有特殊处理。
传统写法是什么?一大堆if-else或switch分支,根据文件类型和大小判断用什么排序方式,代码可读性差、扩展性低、容易踩坑。
二、代码地狱:传统实现有多糟?
举个伪代码例子:
func sortFiles(files: [File]) -> [File] {
if files.count < 1000 {
// 小文件直接快速排序
return files.sorted { $0.size < $1.size }
} else if files.count < 10000 {
// 中等规模用归并排序
return mergeSort(files)
} else {
// 超大文件用外部排序(伪代码)
return externalSort(files)
}
}
func mergeSort(_ files: [File]) -> [File] {
guard files.count > 1 else { return files }
let middle = files.count / 2
let left = mergeSort(Array(files[0..<middle]))
let right = mergeSort(Array(files[middle..<files.count]))
return merge(left, right)
}
func merge(_ left: [File], _ right: [File]) -> [File] {
var merged: [File] = []
var i = 0, j = 0
while i < left.count && j < right.count {
if left[i].size < right[j].size {
merged.append(left[i])
i += 1
} else {
merged.append(right[j])
j += 1
}
}
while i < left.count {
merged.append(left[i])
i += 1
}
while j < right.count {
merged.append(right[j])
j += 1
}
return merged
}
func externalSort(_ files: [File]) -> [File] {
// 假设 files 太大,无法全部加载进内存
let chunkSize = 1000
var sortedChunks: [[File]] = []
// 1. 分块排序并写入临时文件
for chunk in stride(from: 0, to: files.count, by: chunkSize) {
let end = min(chunk + chunkSize, files.count)
let subArray = Array(files[chunk..<end])
let sorted = subArray.sorted { $0.size < $1.size }
// 这里可以写入磁盘临时文件,简化为内存分块
sortedChunks.append(sorted)
}
// 2. 多路归并分块
return kWayMerge(sortedChunks)
}
// 多路归并,将多个已排序的 chunk 合并为一个大有序数组
func kWayMerge(_ chunks: [[File]]) -> [File] {
var result: [File] = []
var pointers = Array(repeating: 0, count: chunks.count)
while true {
var minIndex: Int?
var minFile: File?
for (i, chunk) in chunks.enumerated() {
if pointers[i] < chunk.count {
let file = chunk[pointers[i]]
if minFile == nil || file.size < minFile!.size {
minFile = file
minIndex = i
}
}
}
guard let idx = minIndex, let file = minFile else { break }
result.append(file)
pointers[idx] += 1
}
return result
}
Kotlin 类似:
fun sortFiles(files: List<File>): List<File> {
return when {
files.size < 1000 -> files.sortedBy { it.size }
files.size < 10000 -> mergeSort(files)
else -> externalSort(files)
}
}
fun mergeSort(files: List<File>): List<File> {
if (files.size <= 1) return files
val middle = files.size / 2
val left = mergeSort(files.subList(0, middle))
val right = mergeSort(files.subList(middle, files.size))
return merge(left, right)
}
fun merge(left: List<File>, right: List<File>): List<File> {
val result = mutableListOf<File>()
var i = 0
var j = 0
while (i < left.size && j < right.size) {
if (left[i].size < right[j].size) {
result.add(left[i++])
} else {
result.add(right[j++])
}
}
while (i < left.size) {
result.add(left[i++])
}
while (j < right.size) {
result.add(right[j++])
}
return result
}
fun externalSort(files: List<File>): List<File> {
val chunkSize = 1000
val sortedChunks = mutableListOf<List<File>>()
// 1. 分块排序
var i = 0
while (i < files.size) {
val end = minOf(i + chunkSize, files.size)
val subList = files.subList(i, end)
val sorted = subList.sortedBy { it.size }
// 实际可写磁盘临时文件,这里用内存列表演示
sortedChunks.add(sorted)
i += chunkSize
}
// 2. 多路归并分块
return kWayMerge(sortedChunks)
}
fun kWayMerge(chunks: List<List<File>>): List<File> {
val result = mutableListOf<File>()
val pointers = MutableList(chunks.size) { 0 }
while (true) {
var minIndex: Int? = null
var minFile: File? = null
for ((i, chunk) in chunks.withIndex()) {
if (pointers[i] < chunk.size) {
val file = chunk[pointers[i]]
if (minFile == null || file.size < minFile.size) {
minFile = file
minIndex = i
}
}
}
if (minIndex == null || minFile == null) break
result.add(minFile)
pointers[minIndex] = pointers[minIndex] + 1
}
return result
}
弊端:
- o 业务分支堆积,主流程越来越乱。
- o 扩展、测试、维护困难。新加排序算法要改主流程,容易出错。
- o 新增策略,容易遗漏 case,难以复用。
三、策略模式“拆弹”:让排序算法灵活切换
策略模式思想:把每种排序算法封装成独立“策略”,让主流程只关心“怎么切换”,而不关心“怎么实现”。新增/修改排序方式只需加新策略,无须修改主流程。
1. 定义策略接口
protocol FileSortStrategy {
func sort(files: [File]) -> [File]
}
interface FileSortStrategy {
fun sort(files: List<File>): List<File>
}
2. 实现不同策略
Swift
struct QuickSortStrategy: FileSortStrategy {
func sort(files: [File]) -> [File] {
return files.sorted { $0.size < $1.size }
}
}
struct MergeSortStrategy: FileSortStrategy {
func sort(files: [File]) -> [File] {
// 这里只是示意,可以用更高效的归并实现
return files.sorted { $0.modifiedTime < $1.modifiedTime }
}
}
struct ExternalSortStrategy: FileSortStrategy {
func sort(files: [File]) -> [File] {
// 伪代码:分片写入磁盘,归并外排
print("外部排序大文件,返回伪结果")
return files
}
}
Kotlin
class QuickSortStrategy : FileSortStrategy {
override fun sort(files: List<File>): List<File> {
return files.sortedBy { it.size }
}
}
class MergeSortStrategy : FileSortStrategy {
override fun sort(files: List<File>): List<File> {
// 实际用更高效归并实现,这里简单演示
return files.sortedBy { it.modifiedTime }
}
}
class ExternalSortStrategy : FileSortStrategy {
override fun sort(files: List<File>): List<File> {
println("外部排序大文件,返回伪结果")
return files
}
}
3. 排序上下文(Context)
class FileSorter {
private var strategy: FileSortStrategy
init(strategy: FileSortStrategy) {
self.strategy = strategy
}
func sort(files: [File]) -> [File] {
return strategy.sort(files: files)
}
func setStrategy(_ strategy: FileSortStrategy) {
self.strategy = strategy
}
}
class FileSorter(private var strategy: FileSortStrategy) {
fun sort(files: List<File>): List<File> = strategy.sort(files)
fun setStrategy(strategy: FileSortStrategy) {
this.strategy = strategy
}
}
4. 客户端选择合适策略
let files = [/* ... 一堆 File ... */]
let strategy: FileSortStrategy =
files.count < 1000 ? QuickSortStrategy() :
files.count < 10000 ? MergeSortStrategy() :
ExternalSortStrategy()
let sorter = FileSorter(strategy: strategy)
let sortedFiles = sorter.sort(files: files)
val files = listOf<File>(/* ... */)
val strategy: FileSortStrategy = when {
files.size < 1000 -> QuickSortStrategy()
files.size < 10000 -> MergeSortStrategy()
else -> ExternalSortStrategy()
}
val sorter = FileSorter(strategy)
val sortedFiles = sorter.sort(files)
四、移动端开发中的实际场景
1. 相册/视频管理App
- o 照片量小时直接排序,视频多时用归并,极大时用外部排序。
- o 不同策略独立,不影响 UI 层,易于维护和升级。
2. 聊天/IM/云盘文件浏览
- o 文件量变化大,灵活切换内存排序和磁盘排序算法。
- o 适合多端协作,策略易测试、易复用。
3. 大文件备份/同步
- o 数据量超大,策略模式可实现“边读取边排序”。
- o 新增排序算法只需扩展策略接口。
五、优缺点总结与实战建议
优点
- o 主流程极简,排序逻辑高内聚、低耦合。
- o 可任意扩展新的排序算法,支持运行时动态切换。
- o 有利于单元测试、代码复用和团队协作。
缺点
- o 策略类较多时管理稍繁琐,可结合工厂/依赖注入简化。
- o 简单场景下不建议过度使用,避免“设计模式至上”。
实战建议
- o 优先用策略模式管理“易变的算法/行为”,如排序、渲染、推送、支付等。
- o 推荐结合枚举/配置项/依赖注入,让业务更灵活可配置。
- o 写单测时直接替换策略,无需 mock 全部流程。
六、观点升华
策略模式让我们的代码更加面向未来、面向变化。对于移动端这种需求多变、业务频繁调整的环境来说,它是提升架构灵活性、降低维护成本的利器。
哪怕今天你只用了一种排序算法,明天需求变了,只要加一行策略,不必大动干戈。
推荐大家在实际 App 开发中多用策略模式拆解可变流程,让每个新需求都变得可控、可预测。
- 上一篇: 设计模式一网打尽(设计模式大全)
- 下一篇: 【多线程】Java多线程与并发编程全解析
猜你喜欢
- 2025-07-17 阿里云短视频 SDK For Android 快速接入
- 2025-07-17 谈谈 Unsafe 在 Java 中的作用(unsafe_unretained)
- 2025-07-17 Spring Native 中文文档(spring文档中文版)
- 2025-07-17 Java线程池ThreadPoolExecutor实现原理剖析
- 2025-07-17 以后我准备告别String.format()了,因为它不够香!
- 2025-07-17 你真的了解java中的泛型吗?(java的泛型及实现原理)
- 2025-07-17 框架拦截器的秘密:责任链模式全解析!
- 2025-07-17 MapStruct架构设计(mapstruct官方文档)
- 2025-07-17 迭代器模式进阶:遍历数据时,为何不能修改集合?
- 2025-07-17 工厂、建造者、装饰器、适配器:解密JDK设计模式的实战妙用
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)
- mysql数据库面试题 (57)
- fmt.println (52)