软工个人项目WordCount(Swift5.1)

WordCount项目相关要求

Github项目地址:https://github.com/wyf973733114/WordCount

1. 题目描述

Word Count
1. 实现一个简单而完整的软件工具(源程序特征统计程序)。
2. 进行单元测试、回归测试、效能测试,在实现上述程序的过程中使用相关的工具。
3. 进行个人软件过程(PSP)的实践,逐步记录自己在每个软件工程环节花费的时间。

2. WC 项目要求

wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。

实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:

wc.exe [parameter] [file_name]

 

基本功能列表

wc.exe -c file.c     //返回文件 file.c 的字符数 -- 已实现

wc.exe -w file.c    //返回文件 file.c 的词的数目  -- 已实现

wc.exe -l file.c      //返回文件 file.c 的行数 -- 已实现

扩展功能
    -s   递归处理目录下符合条件的文件。 -- 已实现
    -a   返回更复杂的数据(代码行 / 空行 / 注释行)。-- 已实现

空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。

代码行:本行包括多于一个字符的代码。

注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:

    } //注释
在这种情况下,这一行属于注释行。

[file_name]: 文件或目录名,可以处理一般通配符。

高级功能-- 未实现

 -x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。

需求举例:
  wc.exe -s -a *.c


返回当前目录及子目录中所有*.c 文件的代码行数、空行数、注释行数。

3. 解题思路

  • 一开始想实现成终端调用的模式,所以查了一些 Swift 和 Shell 的混编资料,最后由于对 Shell 不太熟悉就换成 纯Swift 的可执行文件模式
  • 考虑 -s 和 -a、-c、-l、-w的操作对象不同——前者是文件夹,后者是文件,因此将指令分成一级指令(对文件操作)和二级指令(对文件夹操作)
  • 如果文件的存放路径里有wc.exe或者-s字样,不应该触发相应的方法。因此不能直接判断输入的字符串是否包含-s等关键字样
  • 考虑到以后的指令内容可能进行更改或者拓展,所以实现的时候不是简单地将数据写死

4. 遇到的困难及解决方法

  • 困难描述
    •  Swift的API经历过一次很大的革新,网上的部分资料并不能作为参考
  • 做过哪些尝试
    •  查找官方文档,测试,理解
  • 是否解决
    •  是
  • 有何收获
    •  所有的技术都在革新,有些方法跟不上时代更替,所以查阅资料的时候要留意它的书写日期

5.设计实现过程

  • WC+String:自定义的字符串拓展方法(正则匹配、删去空格、删去特殊字符)
  • WordCount:对单个文件进行读取,计算代码行数、空行数、注释行数、字符数和单词数 ,实现一级指令 —— -a、-c、-l、-w
  • WordCountManager:动态创建一个或多个WorCount对象(与需要打开的文件数目对应),在此基础上实现二级指令 —— -s
  • main:创建WordCountManager对象,进行二级指令操作和一级指令操作
    •  main函数代码:
      import Foundation
      
      let manager = WordCountManager()
      // 二级指令操作
      manager.operatingSecond()
      // 一级指令操作
      manager.operatingFirst()

6.关键代码or设计说明

  • WordCount —— 操作计算的实现、属性对外只可读:
    /// 文件信息读取类
    class WordCount {
        
        /// 属性对外只可读,防外部进行更改
        private(set) var message: String
        private(set) var spaceLineCount = 0 // 空行数
        private(set) var codeLineCount = 0  // 代码行数
        private(set) var noteLineCount = 0  // 注释行数
        private(set) var path: String = ""   // 路径
        
        /// 文件内容的行数 (只读)
        var lineCount: Int {
            get {
                var count = 0
                message.enumerateLines { _,_ in
                    count += 1  // 计数
                }
                return count
            }
        }
        
        /// 文件单词数
        var  wordCount: Int {
            get {
                let chararacterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters)
                // 根据 标点符号、空格、换行 进行单词计算
                let components = message.components(separatedBy: chararacterSet)
                let words = components.filter { !$0.isEmpty }   // 滤去空白的单词
                return words.count
            }
        }
        
        /// 文件字符数,包括换行符和j空格符等
        var charCount: Int {
            get {
                return message.count
            }
        }
        /// 可失败初始化器
        /// - Parameter filePath: 文件路径
        init?(_ filePath: String?) {
            var success = false
            (success,message) = WordCount.readFile(filePath)
            if success {
                path = filePath!
            }else {
                // 初始化失败
                print(message)
                return nil
            }
        }
    
        /// 类方法(读取文件),返回(Bool, String)。Bool标志文件读取是否成功,成功时String中为读取的数据,失败时String中为失败的原因,
        /// - Parameter filePath: 文件路径
        static func readFile(_ filePath: String?) -> (Bool, String) {
            // 文件管理者
            let manager = FileManager.default
            guard let data = manager.contents(atPath: filePath ?? "") else {
                return (false,"找不到文件")  // 根据传入参数找不到内容
            }
    guard let readString
    = String(data: data, encoding: String.Encoding.utf8) else { return (false,"数据解析失败") // 数据解析编码格式错误 } return (true, readString) } func dealCode() { //只执行一次 var noteFlag = false message.enumerateLines { (line, false) in self.lineType(line, noteFlag: &noteFlag) } }
    /// 传入一行字符,判断其类型(对应的属性计数将会增加) private func lineType(_ line: String, noteFlag: inout Bool) { // 正则去空格和特殊字符(预处理)、后续需求更改特殊字符的集合时可以在这里修改 let code = line.pregReplace(pattern: "[{|}| |(|)]", with: "") // 1. 先判断是不是注释 if self.isNote(code, noteFlag: &noteFlag) { return } // 2. 再判断是不是空行 (字符串已经过预处理,只含空格和特殊字符的被解释为空行) if code.count == 0 { //代码中除空格字符外,其他字符数量大于1,为代码行 spaceLineCount += 1 return } // 3. 剩下的是代码行 codeLineCount += 1 } /// 判断该行是否是注释 /// - Parameters: /// - code: 需要判断的哪一行字符串 /// - noteFlag: 判断是否属于注释块中(/* 这中间可能会换行,然后写代码,此时将其中的代码解释为注释 */) private func isNote(_ code: String, noteFlag: inout Bool) -> Bool{ var increased = false // 标志是否是注释行 if code.contains("/*") { noteFlag = true } if noteFlag || code.hasPrefix("//") { self.noteLineCount += 1 increased = true } if code.contains("*/") { noteFlag = false } return increased } }
  • WordCountManager —— 指令的拓展性:
    //
    //  WordCountManager.swift
    //  Word_Count
    //
    //  Created by 峰 on 2020/3/17.
    //  Copyright © 2020 峰. All rights reserved.
    //
    
    import Foundation
    
    class WordCountManager {
        var wordCounts: [WordCount] = []
        var path: String = ""
        // 使用数组便于指令拓展
        // 一级指令数组(对单个文件),先后次序会影响操作的优先级(左边最高)
        let parametersFirst = ["-a", "-c", "-w", "-l"]
        // 二级指令数组(对于文件夹),暂时只有一个二级指令
        let parametersSecond = ["-s"]
        
        // 要进行的一级指令操作数组
        var operationsFirst:[String] = []
        // 要进行的二级指令操作数组
        var operationsSecond:[String] = []
        
        init() {
            ///输入指令进行构造
            while wordCounts.count == 0 {
                guard let input = readLine() else {
                    //  测试环境下会触发,真实运行环境下不会触发
                    debugPrint("输入不可为空")
                    continue
                }
                // 1. 去空格形成指令
                var command = input.removeAllSapce
                
                // 2. 验证指令命令 - "wc.exe"
                guard command.verify("wc.exe") else {
                    print("指令错误")
                    continue
                }
                // 2.1. 验证二级指令
                for item in parametersSecond {
                    if command.verify(item){
                        operationsSecond.append(item)
                    }
                }
                // 2.2. 验证一级指令
                for item in parametersFirst {
                    if command.verify(item){
                        operationsFirst.append(item)
                    }
                }
                // 2.3 没有输入指令
                if operationsFirst.count + operationsSecond.count == 0 {
                    print("没有输入的指令")
                    continue
                }
                
                // 3. 存储剩下的文件路径
                path = command
                if let _ = WordCount(path){
                    // 路径有效则停止构建
                    break
                }
            }
        }
        
        /// 根据二级指令数组对文件夹进行操作(这样写是为了方便拓展)
        func operatingSecond() {
            for item in operationsSecond {
                switch item {
                case "-s":
                    //文件管理
                    let manager = FileManager.default
                    // 文件名
                    let fileName = (path as NSString).lastPathComponent
                    // 路径
                    let folder = (path as NSString).deletingLastPathComponent
                    
                    guard let subPaths = manager.subpaths(atPath: folder) else {
                        print("找不到子文件或子文件夹")
                        return
                    }
                    for subPath in subPaths {
                        if (subPath as NSString).lastPathComponent == fileName,
                            let wordCount = WordCount(folder + "/" + subPath) {
                            wordCounts.append(wordCount)
                        }
                    }
                    break
                default:
                    break
                }
            }
        }
        
        /// 根据一级指令数组对单个文件进行操作
        func operatingFirst() {
            // 若二级指令为空,传递文件路径进行读取、计算
            if operationsSecond.count == 0, let wordCount = WordCount(path){
                wordCounts.append(wordCount)
            }
            for wordCount in wordCounts {
                print(wordCount.path)
                // 如果进行了二级指令,将会对多个文件进行相同的操作
                for item in operationsFirst {
                    switch item {
                    case "-a":
                        // 进行代码模式处理
                        wordCount.dealCode()
                        print("文件空行数为\(wordCount.spaceLineCount)")
                        print("文件代码行数为\(wordCount.codeLineCount)")
                        print("文件注释行数为\(wordCount.noteLineCount)")
                        break
                    case "-c":
                        print("文件字符数为\(wordCount.charCount)")
                        break
                    case "-w":
                        print("文件单词数为\(wordCount.wordCount)")
                        break
                    case "-l":
                        print("文件行数为:\(wordCount.lineCount)")
                        break
                    default:
                        print("指令未实现")
                        break
                    }
                }
            }
        }
    }

8.测试

  • 错误处理 (1. 未输入指令wc.exe、2. 未输入具体操作、3. 输入文件不正确)
    • 测试: 
      •   
      •   
  • 基本指令单独使用(-a、-c、-l、-w、-s)
    • 测试: -a、-s
      •  被测试文件的内容(0行空行,2行代码,6行注释)  
      •  测试结果 :
    • 测试:-c、-l、-w
      •  被测试文件的内容(1行、3个词、9个字符(包括空格字符,如果不希望空格算字符也可在读取后用正则将空格删去))
      •  测试结果:
  • 基本指令的组合使用(可任意组合,例如:-s -l、-s -w...)这里只演示两种
    • (这里的指令组合暂时需要按指令优先级来 -s > -a > -c > -w > -l ,优先级高的要在前面)

附表: 

PSP

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划  15  20
· Estimate · 估计这个任务需要多少时间  15  20
Development 开发  4*24*60(4天)  3*24*60(3天)
· Analysis · 需求分析 (包括学习新技术)  2*60   6*60
· Design Spec · 生成设计文档  4*60  3*60
· Design Review · 设计复审 (和同事审核设计文档)  3*60  2*60
· Coding Standard · 代码规范 (为目前的开发制定合适的规范)  5*60  6*60
· Design · 具体设计  7*60  7*60
· Coding · 具体编码  4*60  5*60
· Code Review · 代码复审  2*60  3*60
· Test · 测试(自我测试,修改代码,提交修改)  2*60  5*60 
Reporting 报告  3*60   3*60
· Test Report · 测试报告  2*60  2*60
· Size Measurement · 计算工作量  15  30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划  30   60
  合计  5*24*60(5天)  3*24*60(3天)
 
    

学习进度条(每周追加)

第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 127+43+125+4 = 296 296 8 8 进行错误处理、方便需求拓展
           
   总结:做项目时要考虑到需求未来的拓展和用户的使用体验,PSP预计的时间有时候不太准确,写代码的时间占少部分。

WordCount项目相关要求

Github项目地址:https://github.com/wyf973733114/WordCount

1. 题目描述

Word Count
1. 实现一个简单而完整的软件工具(源程序特征统计程序)。
2. 进行单元测试、回归测试、效能测试,在实现上述程序的过程中使用相关的工具。
3. 进行个人软件过程(PSP)的实践,逐步记录自己在每个软件工程环节花费的时间。

2. WC 项目要求

wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。

实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:

wc.exe [parameter] [file_name]

 

基本功能列表

wc.exe -c file.c     //返回文件 file.c 的字符数 -- 已实现

wc.exe -w file.c    //返回文件 file.c 的词的数目  -- 已实现

wc.exe -l file.c      //返回文件 file.c 的行数 -- 已实现

扩展功能
    -s   递归处理目录下符合条件的文件。 -- 已实现
    -a   返回更复杂的数据(代码行 / 空行 / 注释行)。-- 已实现

空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。

代码行:本行包括多于一个字符的代码。

注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:

    } //注释
在这种情况下,这一行属于注释行。

[file_name]: 文件或目录名,可以处理一般通配符。

高级功能-- 未实现

 -x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。

需求举例:
  wc.exe -s -a *.c


返回当前目录及子目录中所有*.c 文件的代码行数、空行数、注释行数。

3. 解题思路

  • 一开始想实现成终端调用的模式,所以查了一些 Swift 和 Shell 的混编资料,最后由于对 Shell 不太熟悉就换成 纯Swift 的可执行文件模式
  • 考虑 -s 和 -a、-c、-l、-w的操作对象不同——前者是文件夹,后者是文件,因此将指令分成一级指令(对文件操作)和二级指令(对文件夹操作)
  • 如果文件的存放路径里有wc.exe或者-s字样,不应该触发相应的方法。因此不能直接判断输入的字符串是否包含-s等关键字样
  • 考虑到以后的指令内容可能进行更改或者拓展,所以实现的时候不是简单地将数据写死

4. 遇到的困难及解决方法

  • 困难描述
    •  Swift的API经历过一次很大的革新,网上的部分资料并不能作为参考
  • 做过哪些尝试
    •  查找官方文档,测试,理解
  • 是否解决
    •  是
  • 有何收获
    •  所有的技术都在革新,有些方法跟不上时代更替,所以查阅资料的时候要留意它的书写日期

5.设计实现过程

  • WC+String:自定义的字符串拓展方法(正则匹配、删去空格、删去特殊字符)
  • WordCount:对单个文件进行读取,计算代码行数、空行数、注释行数、字符数和单词数 ,实现一级指令 —— -a、-c、-l、-w
  • WordCountManager:动态创建一个或多个WorCount对象(与需要打开的文件数目对应),在此基础上实现二级指令 —— -s
  • main:创建WordCountManager对象,进行二级指令操作和一级指令操作
    •  main函数代码:
      import Foundation
      
      let manager = WordCountManager()
      // 二级指令操作
      manager.operatingSecond()
      // 一级指令操作
      manager.operatingFirst()

6.关键代码or设计说明

  • WordCount —— 操作计算的实现、属性对外只可读:
    /// 文件信息读取类
    class WordCount {
        
        /// 属性对外只可读,防外部进行更改
        private(set) var message: String
        private(set) var spaceLineCount = 0 // 空行数
        private(set) var codeLineCount = 0  // 代码行数
        private(set) var noteLineCount = 0  // 注释行数
        private(set) var path: String = ""   // 路径
        
        /// 文件内容的行数 (只读)
        var lineCount: Int {
            get {
                var count = 0
                message.enumerateLines { _,_ in
                    count += 1  // 计数
                }
                return count
            }
        }
        
        /// 文件单词数
        var  wordCount: Int {
            get {
                let chararacterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters)
                // 根据 标点符号、空格、换行 进行单词计算
                let components = message.components(separatedBy: chararacterSet)
                let words = components.filter { !$0.isEmpty }   // 滤去空白的单词
                return words.count
            }
        }
        
        /// 文件字符数,包括换行符和j空格符等
        var charCount: Int {
            get {
                return message.count
            }
        }
        /// 可失败初始化器
        /// - Parameter filePath: 文件路径
        init?(_ filePath: String?) {
            var success = false
            (success,message) = WordCount.readFile(filePath)
            if success {
                path = filePath!
            }else {
                // 初始化失败
                print(message)
                return nil
            }
        }
    
        /// 类方法(读取文件),返回(Bool, String)。Bool标志文件读取是否成功,成功时String中为读取的数据,失败时String中为失败的原因,
        /// - Parameter filePath: 文件路径
        static func readFile(_ filePath: String?) -> (Bool, String) {
            // 文件管理者
            let manager = FileManager.default
            guard let data = manager.contents(atPath: filePath ?? "") else {
                return (false,"找不到文件")  // 根据传入参数找不到内容
            }
    guard let readString
    = String(data: data, encoding: String.Encoding.utf8) else { return (false,"数据解析失败") // 数据解析编码格式错误 } return (true, readString) } func dealCode() { //只执行一次 var noteFlag = false message.enumerateLines { (line, false) in self.lineType(line, noteFlag: &noteFlag) } }
    /// 传入一行字符,判断其类型(对应的属性计数将会增加) private func lineType(_ line: String, noteFlag: inout Bool) { // 正则去空格和特殊字符(预处理)、后续需求更改特殊字符的集合时可以在这里修改 let code = line.pregReplace(pattern: "[{|}| |(|)]", with: "") // 1. 先判断是不是注释 if self.isNote(code, noteFlag: &noteFlag) { return } // 2. 再判断是不是空行 (字符串已经过预处理,只含空格和特殊字符的被解释为空行) if code.count == 0 { //代码中除空格字符外,其他字符数量大于1,为代码行 spaceLineCount += 1 return } // 3. 剩下的是代码行 codeLineCount += 1 } /// 判断该行是否是注释 /// - Parameters: /// - code: 需要判断的哪一行字符串 /// - noteFlag: 判断是否属于注释块中(/* 这中间可能会换行,然后写代码,此时将其中的代码解释为注释 */) private func isNote(_ code: String, noteFlag: inout Bool) -> Bool{ var increased = false // 标志是否是注释行 if code.contains("/*") { noteFlag = true } if noteFlag || code.hasPrefix("//") { self.noteLineCount += 1 increased = true } if code.contains("*/") { noteFlag = false } return increased } }
  • WordCountManager —— 指令的拓展性:
    //
    //  WordCountManager.swift
    //  Word_Count
    //
    //  Created by 峰 on 2020/3/17.
    //  Copyright © 2020 峰. All rights reserved.
    //
    
    import Foundation
    
    class WordCountManager {
        var wordCounts: [WordCount] = []
        var path: String = ""
        // 使用数组便于指令拓展
        // 一级指令数组(对单个文件),先后次序会影响操作的优先级(左边最高)
        let parametersFirst = ["-a", "-c", "-w", "-l"]
        // 二级指令数组(对于文件夹),暂时只有一个二级指令
        let parametersSecond = ["-s"]
        
        // 要进行的一级指令操作数组
        var operationsFirst:[String] = []
        // 要进行的二级指令操作数组
        var operationsSecond:[String] = []
        
        init() {
            ///输入指令进行构造
            while wordCounts.count == 0 {
                guard let input = readLine() else {
                    //  测试环境下会触发,真实运行环境下不会触发
                    debugPrint("输入不可为空")
                    continue
                }
                // 1. 去空格形成指令
                var command = input.removeAllSapce
                
                // 2. 验证指令命令 - "wc.exe"
                guard command.verify("wc.exe") else {
                    print("指令错误")
                    continue
                }
                // 2.1. 验证二级指令
                for item in parametersSecond {
                    if command.verify(item){
                        operationsSecond.append(item)
                    }
                }
                // 2.2. 验证一级指令
                for item in parametersFirst {
                    if command.verify(item){
                        operationsFirst.append(item)
                    }
                }
                // 2.3 没有输入指令
                if operationsFirst.count + operationsSecond.count == 0 {
                    print("没有输入的指令")
                    continue
                }
                
                // 3. 存储剩下的文件路径
                path = command
                if let _ = WordCount(path){
                    // 路径有效则停止构建
                    break
                }
            }
        }
        
        /// 根据二级指令数组对文件夹进行操作(这样写是为了方便拓展)
        func operatingSecond() {
            for item in operationsSecond {
                switch item {
                case "-s":
                    //文件管理
                    let manager = FileManager.default
                    // 文件名
                    let fileName = (path as NSString).lastPathComponent
                    // 路径
                    let folder = (path as NSString).deletingLastPathComponent
                    
                    guard let subPaths = manager.subpaths(atPath: folder) else {
                        print("找不到子文件或子文件夹")
                        return
                    }
                    for subPath in subPaths {
                        if (subPath as NSString).lastPathComponent == fileName,
                            let wordCount = WordCount(folder + "/" + subPath) {
                            wordCounts.append(wordCount)
                        }
                    }
                    break
                default:
                    break
                }
            }
        }
        
        /// 根据一级指令数组对单个文件进行操作
        func operatingFirst() {
            // 若二级指令为空,传递文件路径进行读取、计算
            if operationsSecond.count == 0, let wordCount = WordCount(path){
                wordCounts.append(wordCount)
            }
            for wordCount in wordCounts {
                print(wordCount.path)
                // 如果进行了二级指令,将会对多个文件进行相同的操作
                for item in operationsFirst {
                    switch item {
                    case "-a":
                        // 进行代码模式处理
                        wordCount.dealCode()
                        print("文件空行数为\(wordCount.spaceLineCount)")
                        print("文件代码行数为\(wordCount.codeLineCount)")
                        print("文件注释行数为\(wordCount.noteLineCount)")
                        break
                    case "-c":
                        print("文件字符数为\(wordCount.charCount)")
                        break
                    case "-w":
                        print("文件单词数为\(wordCount.wordCount)")
                        break
                    case "-l":
                        print("文件行数为:\(wordCount.lineCount)")
                        break
                    default:
                        print("指令未实现")
                        break
                    }
                }
            }
        }
    }

8.测试

  • 错误处理 (1. 未输入指令wc.exe、2. 未输入具体操作、3. 输入文件不正确)
    • 测试: 
      •   
      •   
  • 基本指令单独使用(-a、-c、-l、-w、-s)
    • 测试: -a、-s
      •  被测试文件的内容(0行空行,2行代码,6行注释)  
      •  测试结果 :
    • 测试:-c、-l、-w
      •  被测试文件的内容(1行、3个词、9个字符(包括空格字符,如果不希望空格算字符也可在读取后用正则将空格删去))
      •  测试结果:
  • 基本指令的组合使用(可任意组合,例如:-s -l、-s -w...)这里只演示两种
    • (这里的指令组合暂时需要按指令优先级来 -s > -a > -c > -w > -l ,优先级高的要在前面)

附表: 

PSP

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划  15  20
· Estimate · 估计这个任务需要多少时间  15  20
Development 开发  4*24*60(4天)  3*24*60(3天)
· Analysis · 需求分析 (包括学习新技术)  2*60   6*60
· Design Spec · 生成设计文档  4*60  3*60
· Design Review · 设计复审 (和同事审核设计文档)  3*60  2*60
· Coding Standard · 代码规范 (为目前的开发制定合适的规范)  5*60  6*60
· Design · 具体设计  7*60  7*60
· Coding · 具体编码  4*60  5*60
· Code Review · 代码复审  2*60  3*60
· Test · 测试(自我测试,修改代码,提交修改)  2*60  5*60 
Reporting 报告  3*60   3*60
· Test Report · 测试报告  2*60  2*60
· Size Measurement · 计算工作量  15  30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划  30   60
  合计  5*24*60(5天)  3*24*60(3天)
 

学习进度条(每周追加)

第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 127+43+125+4 = 296 296 8 8 进行错误处理、方便需求拓展
           

猜你喜欢

转载自www.cnblogs.com/wyf973733114/p/12503473.html
今日推荐