Back to Final Review Hub 返回期末复习中心
Day 4 - Final Review Day 4 - 期末复习

📖 Software Engineering Theory 📖 软件工程理论

Master SDLC, Git, Testing, HTTP, Design Principles & JavaScript Functions 掌握 SDLC、Git、测试、HTTP、设计原则与 JavaScript 函数

🔄 Software Development Lifecycle (SDLC) 🔄 软件开发生命周期 (SDLC)

The Software Development Lifecycle (SDLC) is the structured process used to develop software. Understanding its phases is crucial for exam questions about project management and software engineering practices. 软件开发生命周期 (SDLC) 是用于开发软件的结构化流程。理解其各个阶段对于项目管理和软件工程实践的考试题目至关重要。

📋 The 5 Core Phases of SDLC 📋 SDLC 的 5 个核心阶段

  1. Requirements Analysis需求分析 - Gather and document what the software should do - 收集和记录软件应该做什么
  2. Design设计 - Plan the architecture, UI/UX, and technical approach - 规划架构、UI/UX 和技术方法
  3. Implementation/Coding实现/编码 - Write the actual code - 编写实际代码
  4. Testing测试 - Find and fix bugs, verify requirements are met - 发现并修复错误,验证是否满足需求
  5. Deployment & Maintenance部署与维护 - Release to users and provide ongoing support - 发布给用户并提供持续支持

💡 Exam Tip: What's NOT part of SDLC? 💡 考试技巧:什么不是 SDLC 的组成部分?

Risk Management is often confused as an SDLC phase, but it's actually part of Project Management, not the core SDLC itself. While important, it's not one of the 5 fundamental phases listed above. 风险管理常被误认为是 SDLC 阶段,但它实际上是项目管理的一部分,不是核心 SDLC 本身。虽然很重要,但它不是上述 5 个基本阶段之一。

🔀 Git Workflow: The Correct Order 🔀 Git 工作流:正确的顺序

Git is the version control system used in COMP1531. Understanding the correct order of Git commands is essential for both lab work and exam questions. Git 是 COMP1531 中使用的版本控制系统。理解 Git 命令的正确顺序对于实验工作和考试题目都至关重要。

// ❌ WRONG ORDER git commit → git add → git push // Error: nothing to commit // ❌ WRONG ORDER git push → git add → git commit // Error: local changes not committed // ✅ CORRECT ORDER git add → git commit → git push

📝 What Each Command Does 📝 每个命令的作用

Command命令 Purpose用途 Analogy类比
git add Stage changes for commit暂存更改以供提交 Adding items to shopping cart将商品添加到购物车
git commit Save staged changes locally在本地保存暂存的更改 Placing the order下单
git push Send commits to remote repository将提交发送到远程仓库 Shipping the order发货
💡 Remember: You must add before you can commit, and you must commit before you can push. This is the fundamental Git workflow! 💡 记住:你必须先add才能commit,必须先commit才能push。这是基本的 Git 工作流!

📦 npm: Development vs Production Dependencies 📦 npm:开发依赖与生产依赖

⚠️ Common Exam Question: What does --save-dev do? ⚠️ 常见考试题:--save-dev 是做什么的?

Correct Answer: It installs a package that is needed only for development (e.g., testing frameworks, build tools) and NOT included in production dependencies. 正确答案:它安装仅在开发时需要的包(如测试框架、构建工具),不包含在生产依赖中。

🔄 Regular vs Dev Dependencies 🔄 常规依赖 vs 开发依赖

Type类型 Command命令 Installed in Production?生产环境安装?
Regular常规 npm install express ✅ Yes✅ 是
Dev Only仅开发 npm install jest --save-dev ❌ No❌ 否

💡 Examples of Dev Dependencies 💡 开发依赖示例

  • Testing frameworks (Jest, Mocha)测试框架(Jest、Mocha)
  • Linting tools (ESLint, Prettier)代码检查工具(ESLint、Prettier)
  • Build tools (Webpack, Vite)构建工具(Webpack、Vite)
  • TypeScript compilerTypeScript 编译器

🔍 Dynamic vs Static Verification 易错点 🔍 动态 vs 静态验证 易错点

🚨 TRICKY: Don't Be Fooled by "Analysis"! 🚨 陷阱:别被"分析"迷惑了!

The keyword "executing" (执行) means DYNAMIC verification. Static analysis tools might "analyze" code, but if you're running the code with test data, it's DYNAMIC! 关键词"executing"(执行)表示动态验证。静态分析工具可能"分析"代码,但如果你运行代码并用测试数据测试,那就是动态验证!

📊 Core Difference 📊 核心区别

Aspect方面 Static Verification静态验证 Dynamic Verification动态验证
Code executed?运行代码? ❌ No❌ 否 ✅ Yes✅ 是
Method方法 Analyzes code text分析代码文本 Runs with test data用测试数据运行
Examples例子 ESLint, TypeScript compiler, Code reviewESLint、TypeScript 编译器、代码审查 Jest tests, Manual testing, DebuggingJest 测试、手动测试、调试
// Static Analysis - No execution, finds errors by reading code function add(a: number, b: number): number { return a + "hello"; // TypeScript error WITHOUT running } // Dynamic Verification - MUST run code to find issues function divide(a, b) { return a / b; } divide(10, 0); // Only discovers divide-by-zero WHEN RUNNING
💡 Memory Trick: executing = Dynamic, without running = Static. Focus on whether the code RUNS! 💡 记忆技巧: executing(执行)= 动态,without running(不运行)= 静态。关注代码是否运行!

🌐 HTTP Methods: GET vs POST 易错点 🌐 HTTP 方法:GET vs POST 易错点

🚨 TRICKY: Don't Confuse Behavior with Protocol! 🚨 陷阱:不要混淆行为与协议!

GET requests are "usually" cached and POST are "usually" not cached, but that's browser behavior, NOT the fundamental protocol difference! The real difference is where the data goes! GET 请求"通常"被缓存,POST"通常"不被缓存,但那是浏览器行为,不是协议层面的本质区别!真正的区别在于数据去哪里!

📋 The FUNDAMENTAL Difference (Exam Answer A) 📋 本质区别(考试答案 A)

Feature特性 GET POST
Data Location数据位置 URL query string ?key=valueURL 查询字符串 ?key=value Request Body请求体
Visibility可见性 Visible in URL在 URL 中可见 Not in URL不在 URL 中显示
Bookmarkable可收藏 ✅ Yes✅ 是 ❌ No❌ 否
Length Limit长度限制 ~2000 chars (URL limit)~2000 字符(URL 限制) No practical limit无实际限制
// GET - Data in URL (visible, bookmarkable) fetch('/api/users?name=John&age=25') // Full URL: https://api.com/users?name=John&age=25 // POST - Data in body (hidden, not in URL) fetch('/api/users', { method: 'POST', body: JSON.stringify({ name: 'John', age: 25 }) }) // URL: https://api.com/users (clean!)
💡 Memory Trick: GET gets data FROM the URL. POST posts data IN the body. Caching is secondary behavior! 💡 记忆口诀: GET 从 URL 获取数据。POST 把数据放入请求体。缓存是次要行为!

💾 Data Persistence: Immediate vs Batched 易错点 💾 数据持久化:立即 vs 批量 易错点

🚨 TRICKY: It's About Performance, Not Space! 🚨 陷阱:是关于性能,不是空间!

Frequent disk writes don't "double" the space - they overwrite. The real issue is I/O performance: disk operations are 100,000x slower than memory operations! 频繁的磁盘写入不会让空间"翻倍"——它们是覆盖写入的。真正的问题是I/O 性能:磁盘操作比内存操作慢 10 万倍!

⚡ Performance Comparison ⚡ 性能对比

Operation Speed (relative to CPU): CPU Calculation ████████████████████████████████ 1 ns Memory Access ████████████████████ 100 ns SSD Read/Write ████ 10,000 ns HDD Read/Write █ 10,000,000 ns Disk I/O is 100,000x SLOWER than memory!
// ❌ BAD: Write on every API call app.post('/order', (req, res) => { orders.push(newOrder); fs.writeFileSync('orders.json', JSON.stringify(orders)); // Every request! res.send('OK'); }); // Problem: Each request blocks on disk I/O // ✅ GOOD: Batch writes let pendingWrites = []; setInterval(() => { if (pendingWrites.length > 0) { fs.writeFileSync('orders.json', JSON.stringify(pendingWrites)); pendingWrites = []; } }, 5000); // Write every 5 seconds // Benefit: Multiple orders = one disk write
💡 Remember: The main downside of immediate persistence is performance degradation due to frequent I/O operations, not increased disk space usage. 💡 记住:立即持久化的主要缺点是由于频繁 I/O 操作导致的性能下降,而不是磁盘空间使用增加。

🧪 Branch Coverage: Testing ALL Paths 🧪 分支覆盖:测试所有路径

Branch coverage measures whether every possible path through your code has been tested. Unlike line coverage (which just checks if code ran), branch coverage requires testing BOTH outcomes of every decision. 分支覆盖衡量是否测试了代码中所有可能的路径。与行覆盖率(只检查代码是否运行)不同,分支覆盖需要测试每个决策的两种结果。

function grade(score) { if (score >= 85) return 'HD'; // Branch 1: true/false else if (score >= 50) return 'PS'; // Branch 2: true/false else return 'FL'; // Branch 3: (implied) } // ❌ [90] only - Tests: score >= 85 (true), score >= 50 (never reached) // Coverage: INCOMPLETE // ❌ [50, 90] - Tests: score >= 85 (true, false), but never FL path // Coverage: INCOMPLETE // ✅ [49, 50, 85] - Tests: ALL branches // 49 → score < 50 (FL) ✓ // 50 → 50 <= score < 85 (PS) ✓ // 85 → score >= 85 (HD) ✓ // Coverage: 100%
💡 Exam Tip: For 100% branch coverage, you need test values that hit each boundary and each range. Don't forget the "else" path! 💡 考试技巧:要获得 100% 分支覆盖率,你需要测试每个边界和每个范围的值。别忘了 "else" 分支!

⚠️ Error Handling: try-catch-finally Order ⚠️ 错误处理:try-catch-finally 顺序

try { riskyFunction(); // If this throws... } catch(e) { handleError(e); // ...catch runs } finally { cleanup(); // ...finally ALWAYS runs } // Execution Order (when error occurs): // 1. try → riskyFunction() throws // 2. catch → handleError(e) // 3. finally → cleanup() // Always runs!

📋 Execution Flow Summary 📋 执行流程总结

Scenario场景 Order顺序
No error无错误 tryfinally
Error thrown抛出错误 trycatchfinally
💡 Key Point: finally ALWAYS executes, regardless of whether an error occurred or not. This is why it's used for cleanup (closing files, releasing resources). 💡 关键点:finally始终会执行,无论是否发生错误。这就是为什么它用于清理操作(关闭文件、释放资源)。

🔒 Security: Never Trust the Client! 🔒 安全:永远不要信任客户端!

🚨 Security Anti-Pattern 🚨 安全反模式

If a frontend cache (localStorage) replaces server-side authorization checks, that's a critical security vulnerability. Users can edit localStorage and gain unauthorized access! 如果前端缓存(localStorage)替代了服务器端授权检查,那就是一个严重的安全漏洞。用户可以编辑 localStorage 并获得未授权访问!

// ❌ VULNERABLE: Trusting frontend cache app.get('/admin', (req, res) => { const role = localStorage.getItem('role'); // User can edit this! if (role === 'admin') return res.send('Welcome Admin'); return res.status(403).send('Forbidden'); }); // ✅ SECURE: Server-side authorization check app.get('/admin', (req, res) => { // Verify token from server-side session if (req.session && req.session.role === 'admin') { return res.send('Welcome Admin'); } return res.status(403).send('Forbidden'); });
💡 Security Principle: Never trust data from the client. Authentication and authorization MUST happen on the server. Frontend "optimizations" that bypass server checks are security holes. 💡 安全原则:永远不要信任来自客户端的数据。认证和授权必须在服务器上进行。绕过服务器检查的前端"优化"是安全漏洞。

📐 Coding Principles: KISS, DRY, YAGNI, SOLID 易错点 📐 编码原则:KISS、DRY、YAGNI、SOLID 易错点

🚨 TRICKY: Know Which Principle Matches Which Description! 🚨 陷阱:要知道哪个原则对应哪个描述!

"Only write the code you know you will need" = YAGNI (You Aren't Gonna Need It), NOT DRY! "只写你知道你会需要的代码" = YAGNI(你不会需要它),不是 DRY!

📊 The 4 Principles Quick Reference 📊 4 个原则快速参考

Acronym缩写 Full Name全称 Core Idea核心思想
YAGNI You Aren't Gonna Need It Only write code you know you need只写你确定需要的代码
DRY Don't Repeat Yourself Avoid code duplication避免代码重复
KISS Keep It Simple, Stupid Choose the simplest solution选择最简单的解决方案
SOLID 5 OOP principles Single responsibility, Open/Closed, etc.单一职责、开闭原则等
// ❌ Violating YAGNI: Writing "future-proof" code function createUser(name, email) { // "We might need multi-language support later!" const translations = loadAllLanguages(); // Not needed NOW // "We might add SMS notifications!" const smsService = initSMSService(); // Not needed NOW return { name, email }; } // ✅ Following YAGNI: Only what's needed NOW function createUser(name, email) { return { name, email }; } // ❌ Violating DRY: Code duplication function areaCircle(r) { return 3.14 * r * r; } function areaSquare(s) { return s * s; } function areaRect(w, h) { return w * h; } // ✅ Following DRY: Abstract common logic function area(shape, ...dims) { const formulas = { circle: (r) => 3.14 * r * r, square: (s) => s * s, rectangle: (w, h) => w * h }; return formulas[shape](...dims); }
💡 Memory Tricks:
YAGNI = "You" = you don't need that code yet
DRY = "Dry" = code should be dry, no watery duplication
KISS = "Kiss" = keep it simple
💡 记忆技巧:
YAGNI = "You" = 你不需要那些代码
DRY = "Dry" = 代码要干燥,不要有重复的水分
KISS = "Kiss" = 保持简单

📝 Requirements: Use Cases vs User Stories 📝 需求:用例 vs 用户故事

📊 Comparison 📊 对比

Aspect方面 User Stories用户故事 Use Cases用例
Format格式 Informal, simple非正式、简单 Formal, detailed正式、详细
Focus重点 User value, goals用户价值、目标 System flow, all scenarios系统流程、所有场景
Best for最适合 Agile, rapid iteration敏捷开发、快速迭代 Stable requirements, documentation稳定需求、文档化
// User Story format As a , I want , So that . // Example: As a student, I want to view my exam marks, So that I can track my academic progress.
💡 Exam Tip: For agile/startup contexts, choose User Stories. They emphasize user value and support iteration. Use Cases are more formal and lock in design early. 💡 考试技巧:对于敏捷/初创环境,选择用户故事。它们强调用户价值并支持迭代。用例更正式,会早期锁定设计。

📚 Documentation: Why Skip It? 📚 文档:为什么要跳过?

⚠️ Long-term Consequence of Skipping Docs ⚠️ 跳过文档的长期后果

The most significant consequence is reduced maintainability and knowledge transfer. When developers leave or new ones join, undocumented code becomes a liability. 最显著的后果是可维护性降低和知识传递困难。当开发人员离开或新人员加入时,未记录的代码会成为负担。

💡 Key Point: Skipping documentation might speed up deployment NOW, but creates technical debt that slows down FUTURE development and onboarding. 💡 关键点:跳过文档现在可能加快部署,但会产生技术债务,会减慢未来的开发和入职。

📄 Swagger/OpenAPI: What It Does 易错点 📄 Swagger/OpenAPI:它能做什么 易错点

🚨 TRICKY: Swagger Describes, It Doesn't Execute! 🚨 陷阱:Swagger 描述,它不执行!

Swagger defines API structure (endpoints, parameters, data types) but does NOT generate authentication tokens, store credentials, or encrypt messages. Those are runtime operations, not documentation! Swagger 定义 API 结构(端点、参数、数据类型),但生成认证令牌、存储凭据或加密消息。那些是运行时操作,不是文档!

✅ What Swagger DOES ✅ Swagger 能做什么

  • Define endpoints (URL paths)定义端点(URL 路径)
  • Specify request parameters指定请求参数
  • Show data types/schemas显示数据类型/模式
  • Document response formats记录响应格式

❌ What Swagger Does NOT Do ❌ Swagger 不做什么

  • Generate authentication tokens (backend does this)生成认证令牌(后端做这个)
  • Store credentials securely (that's a key vault)安全存储凭据(那是密钥保管库)
  • Encrypt HTTP messages (that's HTTPS/TLS)加密 HTTP 消息(那是 HTTPS/TLS)
# Swagger/OpenAPI specification openapi: 3.0.0 paths: /users/{id}: # [A] Defines endpoint get: parameters: # [A] Defines parameters - name: id in: path schema: type: integer # [D] Shows data type responses: 200: content: application/json: schema: type: object properties: name: type: string # [D] Shows data type
💡 Remember: Swagger is a documentation tool. It describes what an API looks like. It doesn't execute business logic or handle security operations. 💡 记住:Swagger 是一个文档工具。它描述 API 的样子。它不执行业务逻辑或处理安全操作。

🎯 JavaScript: First-Class Functions 易错点 🎯 JavaScript:一等函数 易错点

🚨 TRICKY: First-Class ≠ Hoisting! 🚨 陷阱:一等函数 ≠ 变量提升!

"Functions must be declared at the top of the file" describes Hoisting, NOT First-Class status! First-Class means functions can be treated like values. "函数必须在文件顶部声明"描述的是变量提升,不是一等函数状态!一等函数意味着函数可以像一样被对待。

🎯 What "First-Class" REALLY Means 🎯 "一等"的真正含义

In JavaScript, functions are first-class citizens, meaning they can be: 在 JavaScript 中,函数是一等公民,意味着它们可以:

  • Stored in variables存储在变量中
  • Passed as arguments to other functions作为参数传递给其他函数
  • Returned as values from functions作为值从函数返回
  • Assigned as object properties作为对象属性赋值
// First-Class Function Examples: // 1. Stored in variables const greet = function(name) { return `Hello, ${name}!`; }; // 2. Passed as arguments const names = ['Alice', 'Bob']; const greetings = names.map(greet); // Function as argument! // 3. Returned as values function createMultiplier(n) { return function(x) { // Return a function! return x * n; }; } // 4. Assigned as object properties const calculator = { add: function(a, b) { return a + b; }, // Function as property subtract: (a, b) => a - b };
💡 Don't confuse:
First-Class = functions can be used like values (stored, passed, returned)
Hoisting = function declarations can be called before they're defined
These are DIFFERENT concepts!
💡 不要混淆:
一等函数 = 函数可以像值一样使用(存储、传递、返回)
变量提升 = 函数声明可以在定义之前调用
这是不同的概念!

🔢 JavaScript: Higher-Order Functions 易错点 🔢 JavaScript:高阶函数 易错点

🚨 TRICKY: Function vs Function Call Result! 🚨 陷阱:函数 vs 函数调用结果!

If result = double(5), then result stores a NUMBER (the return value), NOT a function reference! The function was called (parentheses!), so it executed and returned a value. 如果 result = double(5),那么 result 存储的是一个数字(返回值),不是函数引用!函数被调用了(有括号!),所以它执行并返回了一个值。

function makeMultiplier(n: number) { return function (x: number): number { // Anonymous function! return x * n; }; } const double = makeMultiplier(2); // double IS a function: function(x) { return x * 2; } const result = double(5); // result = 5 * 2 = 10 (a NUMBER, not a function!) // Key distinction: const add = (a, b) => a + b; // add is a FUNCTION const sum = add(3, 4); // sum is a NUMBER (7)

📊 Higher-Order Function Definition 📊 高阶函数定义

A higher-order function is a function that does at least one of the following: 高阶函数是满足以下至少一项的函数:

  1. Takes a function as an argument (e.g., map, filter)接受函数作为参数(如 mapfilter
  2. Returns a function as a result (e.g., makeMultiplier)返回函数作为结果(如 makeMultiplier
// Higher-Order Function Examples: // Type 1: Takes a function as argument [1, 2, 3].map(x => x * 2); // map takes a function [1, 2, 3].filter(x => x > 1); // filter takes a function // Type 2: Returns a function function makeMultiplier(n) { return x => x * n; // Returns a function } // Combining both: function withLogging(fn) { return function(...args) { console.log('Calling with:', args); const result = fn(...args); console.log('Result:', result); return result; }; }
💡 Exam Tip: When you see (), a function is being CALLED and returns a VALUE. Without (), you're referring to the function itself. This is the key distinction! 💡 考试技巧:当你看到 ()时,函数正在被调用并返回一个。没有 ()时,你指的是函数本身。这是关键区别!

📋 Requirements: Functional vs Non-functional 📋 需求:功能性 vs 非功能性

📊 Quick Comparison 📊 快速对比

Type类型 Question it Answers回答的问题 Examples例子
Functional功能性 What the system DOES系统做什么 User can view marks, Admin can ban users用户可以查看成绩、管理员可以封禁用户
Non-functional非功能性 HOW the system performs系统如何执行 Speed, Security, Usability, Scalability速度、安全性、易用性、可扩展性
💡 Exam Tip: "Staff can view project marks but not exam marks" is Functional - it specifies WHAT data different users can/cannot access. 💡 考试技巧:"工作人员可以查看项目成绩但不能查看考试成绩"是功能性需求——它指定了不同用户可以/不能访问什么数据。

✅ Acceptance Criteria: Given-When-Then Format ✅ 验收标准:Given-When-Then 格式

Scenario-based acceptance criteria use the Given-When-Then format to describe testable user scenarios. 基于场景的验收标准使用 Given-When-Then 格式来描述可测试的用户场景。

GIVEN a COMP1531 staff member is logged into the system WHEN they navigate to view a student's marks THEN project marks are displayed AND exam marks are NOT displayed

📝 Format Breakdown 📝 格式分解

  • GIVEN - The initial context/precondition初始上下文/前置条件
  • WHEN - The action/user trigger操作/用户触发器
  • THEN - The expected outcome预期结果
  • AND - Additional outcomes附加结果

⚔️ Git: Resolving Merge Conflicts ⚔️ Git:解决合并冲突

Merge conflicts occur when two branches have modified the same lines in different ways, and Git cannot automatically resolve them. 合并冲突发生在两个分支以不同方式修改了相同的行,Git 无法自动解决它们。

// Conflict markers in the file: <<<<<<< HEAD Your code here ======= Their code here >>>>>>> branch-name // Resolution steps: 1. git status // See conflicted files 2. Open file, resolve conflict manually 3. Remove conflict markers 4. git add 5. git commit -m "Resolve merge conflict" 6. git push

🔢 Cyclomatic Complexity Calculation 🔢 圈复杂度计算

📐 Formula 📐 公式

Cyclomatic Complexity = Decision Points + 1 圈复杂度 = 决策点数 + 1

// Example code: if (start < 0) { // Decision point 1 return -1; } else if (start === 0) { // Decision point 2 return 0; } let i = start; while (i > 0) { // Decision point 3 if (i === 1) { // Decision point 4 console.log("last"); } i--; } // Complexity = 4 + 1 = 5

🔄 DRY Principle: Refactoring Duplicate Code 🔄 DRY 原则:重构重复代码

// ❌ Violating DRY - duplicated logic function aliceMarks() { let total = 0; for (let i = 0; i < arguments.length; i++) { total += arguments[i]; } console.log("Alice's total = " + total); } function bobMarks() { let total = 0; for (let i = 0; i < arguments.length; i++) { total += arguments[i]; } console.log("Bob's total = " + total); } // ✅ Following DRY - extracted common logic function totalMarks(name, marks) { let total = 0; for (let i = 0; i < marks.length; i++) { total += marks[i]; } console.log(`${name}'s total marks = ${total}`); } // Usage: totalMarks('Alice', [85, 90, 78]); totalMarks('Bob', [70, 88, 95]);