程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

文件管理App如何实现灵活排序?策略模式揭秘

balukai 2025-07-17 17:17:11 文章精选 3 ℃

文件管理App如何实现灵活排序?策略模式揭秘


引言

在移动端开发中,文件管理、缓存、相册、文档浏览等场景,经常会遇到“需要对不同类型、不同大小的文件进行灵活排序”的需求。比如:你要实现一个本地文件管理App,既要能对小文件快速排序,也要保证大文件(如视频、数据库备份等)的性能不会拖慢整体体验。如何优雅、易扩展地实现这一需求?本篇将带你用策略模式搞定它。


一、现实需求分析

先看下实际中可能遇到的场景:

  • o 相册App,用户可以根据图片大小、拍摄时间、文件名等多维度排序。
  • o 文件管理器,既有小文件(图片/文本),也有大文件(视频/音频/压缩包等)。
  • o 云盘或网盘类App,客户端要支持不同排序算法,还要对大文件有特殊处理。

传统写法是什么?一大堆if-elseswitch分支,根据文件类型和大小判断用什么排序方式,代码可读性差、扩展性低、容易踩坑。


二、代码地狱:传统实现有多糟?

举个伪代码例子:


    
    
    
  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 开发中多用策略模式拆解可变流程,让每个新需求都变得可控、可预测。


Tags:

最近发表
标签列表