Skip to main content

Command Palette

Search for a command to run...

Syllabus software engineering (2)

Updated
44 min readView as Markdown

Giai đoạn 3 - Design Patterns (Ngày 127–189)

Tài liệu chính: Design Patterns (GoF). Mỗi pattern học theo nhịp 3 ngày: khái niệm & cấu trúc → lab thực hành → so sánh/tradeoff với pattern gần giống.

Nhóm 1: Giới thiệu (Ngày 127–128)

Ngày 127: Giới thiệu Design Patterns

  • Mục tiêu: Hiểu pattern là gì và cạm bẫy lớn nhất khi học pattern (áp dụng bừa).

  • Lý thuyết: 3 nhóm pattern GoF (Creational/Structural/Behavioral), pattern là "vocabulary" để giao tiếp thiết kế, không phải checklist bắt buộc dùng.

  • Thực hành: Đọc lại Plugin System đã xây ở Giai đoạn 1, xác định nó đang dùng ngầm pattern nào (gợi ý: Strategy/Factory).

Ngày 128: Singleton - Vì sao không ưu tiên

  • Mục tiêu: Hiểu vì sao Singleton phổ biến nhưng bị đánh giá là anti-pattern trong nhiều trường hợp.

  • Lý thuyết: Global state ẩn, khó test (khó mock), che giấu dependency thay vì inject rõ ràng qua constructor.

  • Thực hành: Viết 1 ví dụ Singleton, chỉ ra vì sao viết unit test cho code phụ thuộc Singleton đó khó khăn; đề xuất thay thế bằng Dependency Injection.

Nhóm 2: Creational Patterns (Ngày 129–140)

Ngày 129: Factory Method - Concept & Structure

  • Mục tiêu: Hiểu cách trì hoãn việc quyết định class cụ thể nào được tạo cho subclass.

  • Lý thuyết: Factory Method định nghĩa 1 interface tạo object, để subclass quyết định class nào sẽ được instantiate.

  • Thực hành: Vẽ sơ đồ UML cho Factory Method áp dụng vào ví dụ NotificationFactory (Email/SMS/Push).

Ngày 130: Factory Method - Lab

  • Mục tiêu: Thực hành implement đầy đủ.

  • Lý thuyết: Ôn lại cấu trúc Creator/ConcreteCreator/Product/ConcreteProduct.

  • Thực hành: Cài đặt NotificationFactory với 3 loại notification, viết client code không phụ thuộc concrete class.

Ngày 131: Factory Method - So sánh với Simple Factory & Abstract Factory

  • Mục tiêu: Phân biệt 3 khái niệm hay bị nhầm lẫn.

  • Lý thuyết: Simple Factory (không phải pattern GoF chính thức, chỉ là idiom) vs Factory Method (dùng inheritance) vs Abstract Factory (họ nhiều factory).

  • Thực hành: Lập bảng so sánh 3 cách tiếp cận, chỉ rõ khi nào dùng cái nào.

Ngày 132: Abstract Factory - Concept & Structure

  • Mục tiêu: Hiểu cách tạo ra cả 1 họ object liên quan mà không cần biết class cụ thể.

  • Lý thuyết: Abstract Factory cung cấp interface tạo ra các họ object có liên quan (family of related objects).

  • Thực hành: Vẽ sơ đồ UML cho ví dụ UIThemeFactory tạo ra Button/Checkbox/Scrollbar đồng bộ theo theme (Light/Dark).

Ngày 133: Abstract Factory - Lab

  • Mục tiêu: Thực hành implement family of objects.

  • Lý thuyết: Ôn lại quan hệ giữa Abstract Factory và Factory Method (Abstract Factory thường implement bằng nhiều Factory Method).

  • Thực hành: Cài đặt UIThemeFactory với LightThemeFactory/DarkThemeFactory, verify các component luôn đồng bộ theme.

Ngày 134: Abstract Factory - Khi nào dùng, Tradeoffs

  • Mục tiêu: Hiểu chi phí của pattern này.

  • Lý thuyết: Thêm 1 product mới vào family đòi hỏi sửa interface (vi phạm Open/Closed ở chiều này) - tradeoff cần cân nhắc.

  • Thực hành: Thử thêm 1 product mới (Tooltip) vào ví dụ Ngày 133, quan sát số chỗ phải sửa.

Ngày 135: Builder - Concept & Structure

  • Mục tiêu: Hiểu cách tách quá trình xây dựng object phức tạp khỏi representation cuối cùng.

  • Lý thuyết: Builder tách construction logic ra khỏi class, cho phép xây object từng bước với nhiều biến thể.

  • Thực hành: Vẽ sơ đồ UML cho HttpRequestBuilder (method, url, headers, body, timeout).

Ngày 136: Builder - Lab

  • Mục tiêu: Thực hành implement fluent API.

  • Lý thuyết: Method chaining, trả về this cho từng bước build.

  • Thực hành: Cài đặt HttpRequestBuilder đầy đủ với fluent API, viết client tạo request theo nhiều cấu hình khác nhau.

Ngày 137: Builder vs Telescoping Constructor

  • Mục tiêu: Hiểu vấn đề Builder giải quyết.

  • Lý thuyết: Telescoping constructor (nhiều constructor overload tăng dần tham số) trở nên khó đọc khi > 4-5 tham số.

  • Thực hành: Viết ví dụ class với telescoping constructor xấu, refactor sang Builder.

Ngày 138: Prototype - Concept & Structure

  • Mục tiêu: Hiểu cách tạo object mới bằng cách sao chép object có sẵn thay vì new từ đầu.

  • Lý thuyết: Prototype hữu ích khi việc khởi tạo object tốn kém (VD: load config nặng) - clone rẻ hơn tạo mới.

  • Thực hành: Vẽ sơ đồ UML cho DocumentPrototype (clone 1 template document).

Ngày 139: Prototype - Lab

  • Mục tiêu: Thực hành phân biệt shallow copy và deep copy.

  • Lý thuyết: Shallow copy chỉ copy reference cấp 1 - nguy hiểm nếu object chứa nested mutable object.

  • Thực hành: Cài đặt clone() cho 1 object có nested object, viết cả bản shallow lẫn deep, chứng minh bug khi dùng nhầm shallow.

Ngày 140: Review Creational Patterns

  • Mục tiêu: Tổng hợp toàn bộ nhóm Creational.

  • Lý thuyết: Lập bảng "Tình huống → Pattern phù hợp" cho cả 4 pattern (+Singleton).

  • Thực hành: Cho 5 tình huống thực tế, chọn đúng creational pattern và giải thích vì sao.

Nhóm 3: Structural Patterns (Ngày 141–158)

Ngày 141: Adapter - Concept & Structure

  • Mục tiêu: Hiểu cách làm 2 interface không tương thích hoạt động cùng nhau.

  • Lý thuyết: Adapter "bọc" 1 interface cũ/không tương thích thành interface mà client mong đợi.

  • Thực hành: Vẽ sơ đồ UML cho việc adapt 1 thư viện logging cũ (LegacyLogger) vào interface Logger mới của hệ thống.

Ngày 142: Adapter - Lab

  • Mục tiêu: Thực hành wrap 1 API/thư viện thật.

  • Lý thuyết: Ôn lại quan hệ composition trong Adapter (Object Adapter).

  • Thực hành: Cài đặt LegacyLoggerAdapter implement Logger, verify code mới không cần biết LegacyLogger tồn tại.

Ngày 143: Object Adapter vs Class Adapter

  • Mục tiêu: Hiểu 2 biến thể implement Adapter.

  • Lý thuyết: Object Adapter (composition, phổ biến ở ngôn ngữ không đa kế thừa) vs Class Adapter (multiple inheritance, khả dụng ở C++).

  • Thực hành: Viết Class Adapter bằng C++ (đa kế thừa), so sánh ưu nhược với Object Adapter đã viết ở Java/Python.

Ngày 144: Facade - Concept & Structure

  • Mục tiêu: Hiểu cách đơn giản hoá 1 subsystem phức tạp.

  • Lý thuyết: Facade cung cấp 1 interface đơn giản che giấu sự phức tạp bên trong của nhiều subsystem con.

  • Thực hành: Vẽ sơ đồ UML cho OrderFacade che giấu tương tác giữa Inventory/Payment/Shipping.

Ngày 145: Facade - Lab

  • Mục tiêu: Thực hành đơn giản hoá 1 quy trình nhiều bước.

  • Lý thuyết: Ôn lại Facade không thêm behavior mới, chỉ tổ chức lại lời gọi.

  • Thực hành: Cài đặt OrderFacade.placeOrder() gọi tuần tự 3 subsystem, verify client code chỉ cần gọi 1 method.

Ngày 146: Facade vs Adapter

  • Mục tiêu: Phân biệt 2 pattern hay bị nhầm vì cùng "bọc" cái gì đó.

  • Lý thuyết: Adapter thay đổi interface để tương thích; Facade đơn giản hoá interface phức tạp có sẵn (không nhất thiết không tương thích).

  • Thực hành: Lập bảng so sánh intent của 2 pattern qua ví dụ đã làm.

Ngày 147: Decorator - Concept & Structure

  • Mục tiêu: Hiểu cách thêm behavior cho object tại runtime mà không sửa class gốc.

  • Lý thuyết: Decorator bọc object gốc, thêm behavior trước/sau khi delegate cho object gốc - có thể xếp chồng nhiều decorator.

  • Thực hành: Vẽ sơ đồ UML kiểu java.io (InputStreamBufferedInputStreamGZIPInputStream).

Ngày 148: Decorator - Lab

  • Mục tiêu: Thực hành xếp chồng nhiều decorator.

  • Lý thuyết: Ôn lại mỗi decorator implement cùng interface với object nó bọc.

  • Thực hành: Cài đặt Coffee với các decorator MilkDecorator/SugarDecorator/WhipDecorator, verify có thể kết hợp tuỳ ý.

Ngày 149: Decorator vs Inheritance

  • Mục tiêu: Hiểu vì sao Decorator tránh được "combinatorial explosion" của inheritance.

  • Lý thuyết: Nếu dùng inheritance cho mọi tổ hợp (CoffeeWithMilkAndSugar, CoffeeWithSugarAndWhip...) số class tăng theo cấp số nhân - Decorator giải quyết bằng composition runtime.

  • Thực hành: Thử vẽ hierarchy inheritance cho ví dụ Coffee với 3 topping, đếm số class cần tạo, so với Decorator chỉ cần 3 class.

Ngày 150: Proxy - Concept & Structure

  • Mục tiêu: Hiểu 4 loại proxy phổ biến.

  • Lý thuyết: Virtual Proxy (lazy loading), Remote Proxy (RPC stub), Protection Proxy (access control), Logging/Caching Proxy.

  • Thực hành: Vẽ sơ đồ UML cho ImageProxy (Virtual Proxy) chỉ load ảnh thật khi cần hiển thị.

Ngày 151: Proxy - Lab

  • Mục tiêu: Thực hành lazy-loading proxy.

  • Lý thuyết: Ôn lại Proxy implement cùng interface với object thật, kiểm soát truy cập tới nó.

  • Thực hành: Cài đặt ImageProxy implement Image, verify object ảnh thật chỉ được tạo khi display() được gọi lần đầu.

Ngày 152: Proxy vs Decorator

  • Mục tiêu: Phân biệt 2 pattern có cấu trúc UML gần như giống hệt nhau.

  • Lý thuyết: Cấu trúc giống nhau nhưng intent khác: Decorator thêm behavior, Proxy kiểm soát truy cập (không nhất thiết thêm behavior).

  • Thực hành: Viết nhận xét so sánh dựa trên 2 ví dụ đã cài (Coffee Decorator vs Image Proxy).

Ngày 153: Composite - Concept & Structure

  • Mục tiêu: Hiểu cách xử lý object đơn lẻ và object nhóm (tree) theo cùng 1 interface.

  • Lý thuyết: Composite cho phép client đối xử với object đơn (Leaf) và object nhóm (Composite) giống hệt nhau.

  • Thực hành: Vẽ sơ đồ UML cho hệ thống file (File là Leaf, Directory là Composite).

Ngày 154: Composite - Lab

  • Mục tiêu: Thực hành cấu trúc cây đệ quy.

  • Lý thuyết: Ôn lại method getSize()/print() được gọi đệ quy tự nhiên qua cấu trúc Composite.

  • Thực hành: Cài đặt File/Directory implement chung interface FileSystemNode, tính tổng dung lượng đệ quy qua nhiều tầng thư mục.

Ngày 155: Composite + Visitor - Liên hệ trước

  • Mục tiêu: Thấy trước vì sao Composite hay đi cùng Visitor (sẽ học ở Ngày 177).

  • Lý thuyết: Khi cần thêm operation mới lên cấu trúc Composite mà không sửa từng node - đó là lúc cần Visitor.

  • Thực hành: Liệt kê 3 operation khác nhau (print, count, export JSON) bạn sẽ cần thêm vào ví dụ file system, ghi chú lại để áp dụng Visitor sau.

Ngày 156: Bridge - Concept & Structure

  • Mục tiêu: Hiểu cách tách abstraction khỏi implementation để cả 2 phát triển độc lập.

  • Lý thuyết: Bridge tách 1 class thành 2 hierarchy riêng (abstraction và implementation) nối với nhau qua composition thay vì inheritance.

  • Thực hành: Vẽ sơ đồ UML cho Shape (abstraction: Circle/Square) tách khỏi Renderer (implementation: VectorRenderer/RasterRenderer).

Ngày 157: Bridge - Lab

  • Mục tiêu: Thực hành tránh combinatorial explosion tương tự Decorator nhưng ở bối cảnh khác.

  • Lý thuyết: Ôn lại nếu không có Bridge, số class sẽ là (số shape) × (số renderer).

  • Thực hành: Cài đặt Shape/Renderer theo Bridge, verify thêm 1 shape mới hoặc 1 renderer mới không làm nổ số class.

Ngày 158: Bridge vs Adapter + Review Structural Patterns

  • Mục tiêu: Chốt lại toàn bộ nhóm Structural.

  • Lý thuyết: Bridge thiết kế trước (proactive) để tách 2 hierarchy; Adapter thiết kế sau (reactive) để ghép 2 interface có sẵn không tương thích.

  • Thực hành: Lập bảng tổng hợp cả 6 pattern Structural: intent, ví dụ, khi nào dùng.

Nhóm 4: Behavioral Patterns (Ngày 159–179)

Ngày 159: Strategy - Concept & Structure

  • Mục tiêu: Hiểu cách hoán đổi thuật toán/hành vi tại runtime.

  • Lý thuyết: Strategy đóng gói 1 họ thuật toán thành các class có thể thay thế cho nhau qua cùng 1 interface.

  • Thực hành: Vẽ sơ đồ UML cho SortStrategy (BubbleSort/QuickSort/MergeSort) chọn được tại runtime.

Ngày 160: Strategy - Lab

  • Mục tiêu: Thực hành thay if-else bằng Strategy, liên hệ lại PaymentMethod đã làm ở Giai đoạn 1.

  • Lý thuyết: Ôn lại Strategy pattern chính là hình thức hoá của Replace Conditional with Polymorphism đã học ở Giai đoạn 2.

  • Thực hành: Mở rộng PaymentMethod cũ, thêm khả năng chọn DiscountStrategy (NoDiscount/PercentageDiscount/FixedDiscount) độc lập với loại payment.

Ngày 161: Strategy vs State - Preview

  • Mục tiêu: Thấy trước điểm giống/khác giữa 2 pattern có cấu trúc UML gần giống hệt.

  • Lý thuyết: Strategy: client chủ động chọn thuật toán. State: object tự chuyển đổi hành vi dựa trên state nội tại.

  • Thực hành: Ghi chú lại câu hỏi "ai là người quyết định thay đổi" cho từng ví dụ đã gặp, dùng để phân biệt khi học State (Ngày 168).

Ngày 162: Observer - Concept & Structure

  • Mục tiêu: Hiểu cơ chế publish-subscribe cổ điển.

  • Lý thuyết: Observer định nghĩa quan hệ 1-nhiều: khi Subject đổi state, mọi Observer đăng ký đều được thông báo.

  • Thực hành: Vẽ sơ đồ UML cho StockPrice (Subject) và nhiều PriceDisplay (Observer).

Ngày 163: Observer - Lab

  • Mục tiêu: Thực hành implement event system cơ bản.

  • Lý thuyết: Ôn lại subscribe()/unsubscribe()/notify().

  • Thực hành: Cài đặt StockPrice + 2 loại Observer (bảng giá, cảnh báo), verify khi giá đổi tất cả observer được gọi.

Ngày 164: Observer - Pitfalls

  • Mục tiêu: Hiểu lỗi thực tế hay gặp khi dùng Observer trong production.

  • Lý thuyết: Memory leak do quên unsubscribe (lapsed listener problem), thứ tự notify không đảm bảo, observer ném exception làm hỏng cả chuỗi notify.

  • Thực hành: Viết ví dụ minh hoạ memory leak (observer không được GC vì subject vẫn giữ reference), đề xuất cách khắc phục (weak reference/explicit unsubscribe).

Ngày 165: Command - Concept & Structure

  • Mục tiêu: Hiểu cách đóng gói 1 yêu cầu (request) thành object.

  • Lý thuyết: Command biến 1 hành động thành object có thể lưu trữ, xếp hàng đợi, log, hoặc undo.

  • Thực hành: Vẽ sơ đồ UML cho TextEditor với các Command (InsertCommand/DeleteCommand).

Ngày 166: Command - Lab

  • Mục tiêu: Thực hành tính năng undo/redo - lý do chính người ta dùng Command.

  • Lý thuyết: Ôn lại mỗi Command lưu đủ thông tin để tự execute()undo().

  • Thực hành: Cài đặt TextEditor với command history stack, hỗ trợ undo/redo nhiều bước.

Ngày 167: Command vs Strategy

  • Mục tiêu: Phân biệt 2 pattern cùng "đóng gói hành vi thành object".

  • Lý thuyết: Strategy tập trung vào "làm cách nào" (thuật toán thay thế nhau); Command tập trung vào "làm gì, khi nào, có thể hoàn tác không".

  • Thực hành: Viết nhận xét so sánh dựa trên 2 ví dụ đã cài đặt.

Ngày 168: State - Concept & Structure

  • Mục tiêu: Hiểu cách object tự thay đổi hành vi khi state nội tại thay đổi.

  • Lý thuyết: State đóng gói mỗi trạng thái thành 1 class, object chuyển hành vi bằng cách đổi state object đang giữ.

  • Thực hành: Vẽ sơ đồ UML cho Order với các state (Pending/Paid/Shipped/Cancelled).

Ngày 169: State - Lab

  • Mục tiêu: Thực hành state machine thực tế.

  • Lý thuyết: Ôn lại mỗi state class biết những transition nào hợp lệ từ chính nó.

  • Thực hành: Cài đặt Order chuyển trạng thái qua các state class, verify transition không hợp lệ (VD: ShippedPending) bị chặn.

Ngày 170: State vs Strategy - Kết luận

  • Mục tiêu: Chốt lại phân biệt đã preview ở Ngày 161.

  • Lý thuyết: Cấu trúc UML gần như giống hệt, khác nhau ở "ai kiểm soát chuyển đổi" và "có nhận biết lẫn nhau giữa các state/strategy không".

  • Thực hành: Áp câu hỏi phân biệt vào ví dụ Order (State) và DiscountStrategy (Strategy) đã làm, viết kết luận rõ ràng.

Ngày 171: Mediator - Concept & Structure

  • Mục tiêu: Hiểu cách giảm coupling giữa nhiều object bằng cách tập trung giao tiếp qua 1 điểm.

  • Lý thuyết: Mediator đóng vai trò trung gian, các object không giao tiếp trực tiếp với nhau mà thông qua Mediator.

  • Thực hành: Vẽ sơ đồ UML cho ChatRoom (Mediator) điều phối nhiều User (Colleague).

Ngày 172: Mediator - Lab

  • Mục tiêu: Thực hành giảm coupling giữa nhiều component.

  • Lý thuyết: Ôn lại nếu không có Mediator, mỗi User phải biết về mọi User khác (N² kết nối).

  • Thực hành: Cài đặt ChatRoom điều phối tin nhắn giữa nhiều User, verify User không cần biết sự tồn tại của User khác.

Ngày 173: Mediator vs Observer

  • Mục tiêu: Phân biệt 2 pattern cùng liên quan tới giao tiếp gián tiếp.

  • Lý thuyết: Observer là quan hệ 1-nhiều 1 chiều (broadcast); Mediator điều phối giao tiếp nhiều-nhiều 2 chiều phức tạp hơn.

  • Thực hành: Viết nhận xét so sánh dựa trên ví dụ StockPrice (Observer) và ChatRoom (Mediator).

Ngày 174: Chain of Responsibility - Concept & Structure

  • Mục tiêu: Hiểu cách 1 request đi qua chuỗi handler cho tới khi được xử lý.

  • Lý thuyết: Mỗi handler quyết định xử lý request hoặc chuyển tiếp cho handler kế tiếp trong chain.

  • Thực hành: Vẽ sơ đồ UML cho chuỗi xử lý HTTP request (AuthHandlerRateLimitHandlerBusinessLogicHandler).

Ngày 175: Chain of Responsibility - Lab

  • Mục tiêu: Thực hành middleware-style pipeline - sẽ dùng trực tiếp ở project cuối giai đoạn.

  • Lý thuyết: Ôn lại mỗi handler giữ reference tới handler kế tiếp (successor).

  • Thực hành: Cài đặt chuỗi 3 handler xử lý request, verify request bị chặn ở AuthHandler không đi tiếp xuống các handler sau.

Ngày 176: Chain of Responsibility vs Decorator

  • Mục tiêu: Phân biệt 2 pattern đều tạo thành "chuỗi" các object bọc nhau.

  • Lý thuyết: Decorator luôn gọi qua object tiếp theo (tất cả đều chạy); CoR có thể dừng giữa chừng, chỉ 1 handler xử lý (hoặc không handler nào).

  • Thực hành: Viết nhận xét so sánh dựa trên CoffeeDecorator và request handler chain đã cài.

Ngày 177: Visitor - Concept & Structure

  • Mục tiêu: Hiểu cách thêm operation mới vào 1 cấu trúc object mà không sửa class của object đó.

  • Lý thuyết: Visitor dùng double dispatch - cho phép thêm operation mới (visit) mà không cần sửa hierarchy element đã tồn tại.

  • Thực hành: Vẽ sơ đồ UML áp dụng Visitor lên cấu trúc file system (Composite) đã làm ở Ngày 154, thêm operation ExportJsonVisitor.

Ngày 178: Visitor - Lab

  • Mục tiêu: Thực hành double dispatch thật sự (không chỉ if-instanceof).

  • Lý thuyết: Ôn lại mỗi Element có method accept(visitor) gọi ngược lại đúng method visit() tương ứng type của nó.

  • Thực hành: Cài đặt ExportJsonVisitorPrintVisitor cho cấu trúc File/Directory, verify thêm operation mới không cần sửa File/Directory.

Ngày 179: Review Behavioral Patterns

  • Mục tiêu: Tổng hợp toàn bộ nhóm Behavioral (7 pattern) và toàn bộ 17 pattern + Singleton đã học.

  • Lý thuyết: Lập bảng tổng hợp cuối: Category → Pattern → Intent → Ví dụ đã cài.

  • Thực hành: Tự quiz: cho 7 tình huống thực tế khác nhau (1 cho mỗi behavioral pattern), chọn đúng pattern và giải thích.

Nhóm 5: Project - Mini Web Framework (Ngày 180–189)

Ngày 180: Thiết kế kiến trúc tổng thể

  • Mục tiêu: Lên bản thiết kế trước khi code, xác định pattern nào dùng ở đâu.

  • Lý thuyết: Framework cần: Router (Composite-ish tree hoặc Chain), Middleware (Chain of Responsibility), DI Container (Factory/Builder), Plugin (đã có sẵn từ Giai đoạn 1).

  • Thực hành: Vẽ sơ đồ kiến trúc tổng thể Mini Web Framework, ghi chú pattern dự kiến áp dụng cho từng thành phần.

Ngày 181: Xây Router

  • Mục tiêu: Xây thành phần định tuyến request tới đúng handler.

  • Lý thuyết: Route matching (exact, path param), cấu trúc dữ liệu lưu route (trie hoặc list + regex).

  • Thực hành: Cài đặt Router với addRoute(method, path, handler)match(method, path) trả về handler + path params.

Ngày 182: Xây Middleware Pipeline

  • Mục tiêu: Áp dụng trực tiếp Chain of Responsibility đã học.

  • Lý thuyết: Middleware chạy trước/sau handler chính, có thể chặn request (giống AuthHandler đã làm Ngày 175).

  • Thực hành: Cài đặt MiddlewarePipeline cho phép đăng ký nhiều middleware, verify middleware có thể chặn hoặc pass tiếp request.

Ngày 183: Xây DI Container cơ bản

  • Mục tiêu: Áp dụng Factory/Builder để quản lý khởi tạo dependency.

  • Lý thuyết: DI Container đăng ký "cách tạo" 1 type, tự động resolve dependency qua constructor injection.

  • Thực hành: Cài đặt Container với register(type, factory)resolve(type), hỗ trợ resolve dependency lồng nhau.

Ngày 184: Tích hợp Plugin System

  • Mục tiêu: Tái sử dụng Plugin System đã xây ở Giai đoạn 1 vào framework mới.

  • Lý thuyết: Plugin trong framework có thể là middleware, có thể là route handler - cần thiết kế extension point rõ ràng.

  • Thực hành: Tích hợp PluginRegistry/PluginLoader cũ vào Mini Web Framework, cho phép plugin đăng ký route hoặc middleware.

Ngày 185: Request/Response & Handler Interface

  • Mục tiêu: Hoàn thiện các abstraction còn thiếu.

  • Lý thuyết: Request/Response object đóng gói thông tin HTTP, Handler interface thống nhất cho mọi route.

  • Thực hành: Cài đặt Request/Response, chuẩn hoá interface Handler.handle(req, res).

Ngày 186: Ứng dụng ví dụ - CRUD API

  • Mục tiêu: Kiểm tra framework hoạt động với use case thực tế.

  • Lý thuyết: Ôn lại toàn bộ các thành phần đã ghép: Router + Middleware + DI + Plugin.

  • Thực hành: Viết 1 API CRUD đơn giản (VD: quản lý Task) chạy trên chính Mini Web Framework vừa xây.

Ngày 187: Viết Test cho Framework

  • Mục tiêu: Đảm bảo framework đáng tin cậy.

  • Lý thuyết: Test ở tầng framework cần cover: route matching đúng, middleware chạy đúng thứ tự, DI resolve đúng dependency.

  • Thực hành: Viết test suite cho Router/MiddlewarePipeline/Container độc lập với CRUD API.

Ngày 188: Đo lường & Tối ưu

  • Mục tiêu: Đánh giá hiệu năng cơ bản của framework tự viết.

  • Lý thuyết: Overhead của route matching, middleware chain, DI resolve - cái nào tốn chi phí nhất.

  • Thực hành: Benchmark số request/giây xử lý được, xác định điểm nghẽn và thử tối ưu 1 điểm.

Ngày 189: Review & Retrospective Giai đoạn 3

  • Mục tiêu: Tổng kết toàn bộ Giai đoạn Design Patterns.

  • Lý thuyết: Nhìn lại bản đồ pattern → thành phần framework đã vẽ ở Ngày 180, đối chiếu với những gì thực sự dùng.

  • Thực hành: Viết báo cáo retrospective: pattern nào hữu ích nhất, pattern nào áp dụng gượng ép không cần thiết trong project này.


Giai đoạn 4 - Testing & Quality Engineering (Ngày 190–217)

Giai đoạn mới bổ sung. Không có mảng này thì mọi kiến trúc/pattern đã học ở trên không thể gọi là "production-ready".

Nhóm 1: Giới thiệu (Ngày 190–191)

Ngày 190: Giới thiệu Testing & Quality Engineering

  • Mục tiêu: Có bức tranh tổng thể về các tầng test trước khi đi sâu từng loại.

  • Lý thuyết: Test Pyramid (Unit → Integration → E2E), vì sao nên có nhiều unit test, ít E2E test.

  • Thực hành: Vẽ lại Test Pyramid, liệt kê ví dụ cụ thể cho từng tầng dựa trên Mini Web Framework đã xây ở Giai đoạn 3.

Ngày 191: Chi phí bug theo giai đoạn phát hiện

  • Mục tiêu: Hiểu vì sao đầu tư vào test sớm rẻ hơn nhiều so với sửa bug ở production.

  • Lý thuyết: Chi phí sửa bug tăng theo cấp số nhân qua từng giai đoạn (dev → test → staging → production).

  • Thực hành: Viết 1 ví dụ cụ thể (từ kinh nghiệm cá nhân hoặc giả định) minh hoạ chi phí sửa bug nếu phát hiện ở unit test vs phát hiện sau khi khách hàng report.

Nhóm 2: Unit Testing (Ngày 192–196)

Ngày 192: Unit Test cơ bản - AAA Pattern

  • Mục tiêu: Nắm cấu trúc chuẩn của 1 unit test tốt.

  • Lý thuyết: Arrange-Act-Assert, tiêu chí FIRST (Fast, Independent, Repeatable, Self-validating, Timely).

  • Thực hành: Viết 3 unit test theo AAA cho 1 hàm thuần (pure function) đơn giản.

Ngày 193: JUnit 5 (Java)

  • Mục tiêu: Thành thạo công cụ test chuẩn của Java.

  • Lý thuyết: @Test, @BeforeEach/@AfterEach, assertion (assertEquals, assertThrows), parameterized test.

  • Thực hành: Viết bộ test JUnit 5 đầy đủ cho class BankAccount đã xây ở Giai đoạn 1.

Ngày 194: pytest (Python)

  • Mục tiêu: Thành thạo công cụ test chuẩn của Python.

  • Lý thuyết: Fixture, parametrize, assertion tự nhiên (assert x == y), plugin ecosystem.

  • Thực hành: Viết bộ test pytest cho cùng logic BankAccount, dùng fixture để setup account mẫu.

Ngày 195: Google Test/Catch2 (C++)

  • Mục tiêu: Làm quen framework test phổ biến ở C++.

  • Lý thuyết: Test fixture (TEST_F), matcher (Google Mock preview), so sánh Google Test vs Catch2.

  • Thực hành: Viết bộ test cho vector/hashmap tự viết ở Giai đoạn 0 bằng Google Test hoặc Catch2.

Ngày 196: Lab - Coverage cho module thật

  • Mục tiêu: Áp dụng unit test vào codebase đã refactor ở Giai đoạn 2.

  • Lý thuyết: Đo code coverage bằng công cụ (JaCoCo/coverage.py/gcov).

  • Thực hành: Viết test cho module Order đã refactor ở Giai đoạn 2, đo coverage trước/sau, đặt mục tiêu coverage hợp lý (không chạy theo 100%).

Nhóm 3: Test Doubles & Mocking (Ngày 197–200)

Ngày 197: Phân loại Test Double

  • Mục tiêu: Phân biệt 5 loại test double hay bị gọi chung là "mock".

  • Lý thuyết: Dummy, Fake, Stub, Spy, Mock - khác nhau ở mục đích sử dụng (theo phân loại của Martin Fowler).

  • Thực hành: Cho 5 tình huống test khác nhau, xác định nên dùng loại double nào.

Ngày 198: Mocking Framework - Mockito (Java)

  • Mục tiêu: Thành thạo mock dependency trong Java.

  • Lý thuyết: @Mock, when().thenReturn(), verify().

  • Thực hành: Viết test cho 1 service phụ thuộc PaymentGateway (interface), mock PaymentGateway để test logic mà không gọi API thật.

Ngày 199: Mocking Framework - unittest.mock (Python)

  • Mục tiêu: Thành thạo mock dependency trong Python.

  • Lý thuyết: Mock, patch, MagicMock, khác biệt giữa mock ở class-level vs instance-level.

  • Thực hành: Viết test tương tự Ngày 198 bằng Python, dùng patch để thay thế lời gọi API thật.

Ngày 200: Lab - Mock dependency ngoài

  • Mục tiêu: Thực hành tình huống thực tế: test logic phụ thuộc external service.

  • Lý thuyết: Ôn lại nguyên tắc chỉ mock ở boundary của hệ thống (external API, database, filesystem), không mock nội bộ quá mức (over-mocking).

  • Thực hành: Viết test đầy đủ cho 1 flow gọi external API (VD: gửi email xác nhận đơn hàng), mock toàn bộ external call, verify logic nghiệp vụ đúng mà không cần network thật.

Nhóm 4: Test-Driven Development (Ngày 201–205)

Ngày 201: Giới thiệu TDD

  • Mục tiêu: Hiểu quy trình Red-Green-Refactor.

  • Lý thuyết: Viết test fail trước (Red) → viết code tối thiểu để pass (Green) → refactor code sạch hơn (Refactor) mà không phá test.

  • Thực hành: Thực hiện 1 chu kỳ Red-Green-Refactor hoàn chỉnh cho 1 hàm rất đơn giản (VD: isPrime).

Ngày 202: TDD Lab 1 - Class nhỏ từ đầu

  • Mục tiêu: Luyện phản xạ viết test trước khi viết code.

  • Lý thuyết: Ôn lại nguyên tắc chỉ viết code đủ để pass test hiện tại, không code trước cho tương lai.

  • Thực hành: Viết class Stack<T> hoàn toàn bằng TDD, mỗi tính năng đều bắt đầu bằng 1 test fail.

Ngày 203: TDD Lab 2 - Tính năng phức tạp hơn

  • Mục tiêu: Áp dụng TDD cho logic có nhiều edge case.

  • Lý thuyết: Ôn lại việc liệt kê edge case trước khi viết test đầu tiên giúp thiết kế API tốt hơn.

  • Thực hành: Viết DiscountCalculator (áp dụng nhiều rule giảm giá) hoàn toàn bằng TDD.

Ngày 204: TDD - Outside-In vs Inside-Out

  • Mục tiêu: Hiểu 2 trường phái tiếp cận TDD.

  • Lý thuyết: Outside-In (London School, bắt đầu từ test tích hợp/acceptance, mock hết dependency) vs Inside-Out (Chicago School, bắt đầu từ unit nhỏ nhất rồi ghép dần lên).

  • Thực hành: Viết lại 1 phần của DiscountCalculator (Ngày 203) theo hướng Outside-In, so sánh trải nghiệm với cách Inside-Out đã làm.

Ngày 205: TDD Retrospective

  • Mục tiêu: Đánh giá khách quan lợi ích và chi phí của TDD.

  • Lý thuyết: TDD giúp thiết kế API tốt hơn và có lưới an toàn ngay từ đầu, nhưng tốn thời gian ban đầu hơn - cần cân nhắc theo ngữ cảnh (code nghiệp vụ phức tạp nên TDD, code thử nghiệm/prototype có thể không cần).

  • Thực hành: Viết báo cáo so sánh: code viết bằng TDD (Ngày 202-203) vs code viết theo cách cũ (test sau) - về chất lượng thiết kế, thời gian, độ tự tin khi refactor.

Nhóm 5: Integration Testing (Ngày 206–209)

Ngày 206: Integration Test là gì

  • Mục tiêu: Phân biệt rõ Integration Test với Unit Test.

  • Lý thuyết: Integration test verify nhiều thành phần thật phối hợp đúng với nhau (không mock hết), thường chậm hơn và ít hơn unit test theo Test Pyramid.

  • Thực hành: Xác định trong Mini Web Framework (Giai đoạn 3), đâu là ranh giới nên test bằng unit test, đâu nên test bằng integration test.

Ngày 207: Integration Test với Database

  • Mục tiêu: Học cách test code có tương tác database mà không cần database production thật.

  • Lý thuyết: In-memory database (H2), Testcontainers (chạy database thật trong Docker container tạm cho test).

  • Thực hành: Viết integration test cho 1 repository layer, dùng Testcontainers khởi động PostgreSQL tạm thời cho test.

Ngày 208: Integration Test với HTTP API

  • Mục tiêu: Test toàn bộ 1 API endpoint từ request tới response.

  • Lý thuyết: Test client (MockMvc ở Java, TestClient ở FastAPI/Python), test cả tầng routing + business logic + serialization.

  • Thực hành: Viết integration test cho CRUD API Task đã xây ở Mini Web Framework, verify status code + response body.

Ngày 209: Lab - Integration Test cho Mini Web Framework

  • Mục tiêu: Hoàn thiện bộ test tích hợp cho project Giai đoạn 3.

  • Lý thuyết: Ôn lại kết hợp Testcontainers + test client cho 1 flow end-to-end trong phạm vi service.

  • Thực hành: Viết integration test đầy đủ cho flow tạo Task → lưu DB → trả response, chạy qua toàn bộ pipeline Router → Middleware → DI → Handler thật.

Nhóm 6: Contract Testing (Ngày 210–212)

Ngày 210: Giới thiệu Contract Testing

  • Mục tiêu: Hiểu vấn đề mà contract testing giải quyết.

  • Lý thuyết: E2E test giữa nhiều service tốn kém, chậm, dễ flaky - contract test verify "hợp đồng" giữa consumer và provider mà không cần cả hệ thống chạy cùng lúc.

  • Thực hành: Vẽ sơ đồ minh hoạ vấn đề khi 2 team (Order service, Payment service) đổi API mà không báo nhau, dẫn tới lỗi ở production.

Ngày 211: Consumer-Driven Contract - Pact

  • Mục tiêu: Hiểu mô hình contract test phổ biến nhất.

  • Lý thuyết: Consumer định nghĩa contract mong đợi, Provider verify contract đó trong CI của chính mình - không cần 2 service chạy cùng lúc.

  • Thực hành: Đọc tài liệu Pact, tóm tắt luồng hoạt động (consumer test → publish contract → provider verify).

Ngày 212: Lab - Contract Test giữa 2 service giả định

  • Mục tiêu: Thực hành contract test thật.

  • Lý thuyết: Ôn lại cấu trúc 1 pact file (request mong đợi, response mong đợi).

  • Thực hành: Viết consumer test định nghĩa contract cho API GET /order/{id}, viết provider verification test dựa trên contract đó.

Nhóm 7: Property-Based Testing (Ngày 213–214)

Ngày 213: Giới thiệu Property-Based Testing

  • Mục tiêu: Hiểu cách tiếp cận test khác với example-based testing truyền thống.

  • Lý thuyết: Thay vì viết test cho từng input cụ thể, định nghĩa "property" (tính chất luôn đúng) và để framework tự sinh hàng trăm input ngẫu nhiên để kiểm tra.

  • Thực hành: Xác định 1 property cho hàm sort() (VD: kết quả luôn có cùng độ dài với input, luôn tăng dần).

Ngày 214: Lab - Property Test

  • Mục tiêu: Thực hành viết property test thật.

  • Lý thuyết: Hypothesis (Python) hoặc jqwik (Java) - cách định nghĩa generator cho input.

  • Thực hành: Viết property test cho hàm sort() tự viết, verify với hàng trăm input ngẫu nhiên do framework sinh ra; thử tìm 1 bug mà example-based test trước đó bỏ sót.

Nhóm 8: Coverage & Mutation Testing (Ngày 215–216)

Ngày 215: Code Coverage - Giới hạn thật sự

  • Mục tiêu: Hiểu coverage là chỉ số hữu ích nhưng dễ bị lạm dụng.

  • Lý thuyết: Line coverage vs branch coverage; 100% coverage không đảm bảo không có bug (test có thể chạy qua code mà không thực sự assert đúng).

  • Thực hành: Viết 1 ví dụ test đạt 100% line coverage nhưng vẫn bỏ sót bug rõ ràng, để thấy giới hạn của coverage.

Ngày 216: Mutation Testing

  • Mục tiêu: Học cách đo chất lượng test thực sự, không chỉ đo coverage.

  • Lý thuyết: Mutation testing tự động sửa nhỏ code (mutant, VD đổi < thành <=), nếu test vẫn pass nghĩa là test đó yếu (không "giết" được mutant).

  • Thực hành: Chạy công cụ mutation testing (PIT cho Java, mutmut cho Python) trên bộ test đã viết ở Ngày 196, xem mutation score và cải thiện test yếu.

Nhóm 9: Review (Ngày 217)

Ngày 217: Review & Retrospective Giai đoạn 4

  • Mục tiêu: Chốt lại thành 1 chiến lược testing cá nhân áp dụng được cho production.

  • Lý thuyết: Tổng hợp lại Test Pyramid, khi nào dùng unit/integration/contract/property test, testing strategy nên gắn với risk của từng phần hệ thống (không phải test mọi thứ như nhau).

  • Thực hành: Viết "Testing Strategy Playbook" cá nhân (1-2 trang): cho từng loại thành phần trong hệ thống (business logic thuần, tầng gọi external API, tầng database, tầng API endpoint) nên dùng loại test nào, tỷ lệ bao nhiêu.


Giai đoạn 5 - Concurrency (Ngày 218–266)

Mảng nhiều syllabus OOP bỏ qua. Học song song 3 ngôn ngữ để thấy 3 triết lý concurrency rất khác nhau, sau đó đào sâu internals chung.

Nhóm 1: Giới thiệu (Ngày 218–219)

Ngày 218: Concurrency vs Parallelism

  • Mục tiêu: Phân biệt 2 khái niệm hay bị dùng lẫn.

  • Lý thuyết: Concurrency = xử lý nhiều việc trong cùng khoảng thời gian (có thể xen kẽ trên 1 core); Parallelism = thực sự chạy đồng thời trên nhiều core.

  • Thực hành: Vẽ sơ đồ timeline minh hoạ khác biệt giữa 1 chương trình concurrent chạy trên 1 core vs chạy parallel trên 4 core.

Ngày 219: Tổng quan các mô hình Concurrency

  • Mục tiêu: Có bức tranh tổng thể trước khi học chi tiết từng ngôn ngữ.

  • Lý thuyết: Thread-based (shared memory), Process-based (isolated memory), Event-loop/async (single-threaded non-blocking), Actor model (preview, sẽ gặp lại ở Distributed Systems).

  • Thực hành: Lập bảng: mỗi mô hình phù hợp với loại workload nào (CPU-bound vs I/O-bound).

Nhóm 2: Java Concurrency (Ngày 220–229)

Ngày 220: Thread cơ bản

  • Mục tiêu: Tạo và điều khiển thread thủ công trong Java.

  • Lý thuyết: Thread class, Runnable interface, start() vs run(), join().

  • Thực hành: Viết chương trình tạo 5 thread in số thứ tự của chúng, dùng join() để đợi tất cả hoàn thành.

Ngày 221: Synchronization - synchronized keyword

  • Mục tiêu: Hiểu cách bảo vệ vùng dữ liệu chia sẻ khỏi race condition.

  • Lý thuyết: Intrinsic lock (monitor lock), synchronized method vs synchronized block.

  • Thực hành: Viết bộ đếm Counter bị race condition khi nhiều thread tăng cùng lúc, sửa bằng synchronized.

Ngày 222: wait/notify

  • Mục tiêu: Hiểu cơ chế phối hợp thread ở tầng thấp trước khi dùng thư viện cao cấp hơn.

  • Lý thuyết: wait()/notify()/notifyAll() phải gọi trong synchronized block, spurious wakeup.

  • Thực hành: Viết bài toán Producer-Consumer đơn giản bằng wait()/notify() thủ công (chưa dùng BlockingQueue).

Ngày 223: ExecutorService cơ bản

  • Mục tiêu: Chuyển từ quản lý thread thủ công sang thread pool.

  • Lý thuyết: Vì sao tạo thread thủ công tốn kém (chi phí tạo/huỷ thread) - ExecutorService quản lý pool tái sử dụng.

  • Thực hành: Viết lại ví dụ Ngày 220 dùng ExecutorService.submit() thay vì tạo Thread thủ công.

Ngày 224: Các loại Thread Pool

  • Mục tiêu: Chọn đúng loại pool cho đúng workload.

  • Lý thuyết: FixedThreadPool, CachedThreadPool, ScheduledThreadPool - khác nhau ở chiến lược tạo/tái sử dụng thread.

  • Thực hành: Benchmark cùng 1 workload trên FixedThreadPoolCachedThreadPool, so sánh hành vi khi workload tăng đột biến.

Ngày 225: Future & Callable

  • Mục tiêu: Lấy kết quả trả về từ task chạy bất đồng bộ.

  • Lý thuyết: Callable<T> vs Runnable (có trả về giá trị), Future.get() blocking.

  • Thực hành: Submit nhiều Callable tính toán song song, thu kết quả qua Future.get().

Ngày 226: CompletableFuture cơ bản

  • Mục tiêu: Học cách xử lý bất đồng bộ hiện đại, không cần block.

  • Lý thuyết: thenApply/thenAccept/thenCompose, xây chuỗi xử lý bất đồng bộ không blocking.

  • Thực hành: Viết chuỗi xử lý 3 bước bất đồng bộ (fetch data → transform → save) bằng CompletableFuture.

Ngày 227: CompletableFuture nâng cao

  • Mục tiêu: Kết hợp nhiều future và xử lý lỗi.

  • Lý thuyết: thenCombine, allOf/anyOf, exceptionally/handle.

  • Thực hành: Viết ví dụ gọi 2 API song song rồi combine kết quả, thêm xử lý khi 1 trong 2 API lỗi.

Ngày 228: java.util.concurrent Collections

  • Mục tiêu: Dùng cấu trúc dữ liệu thread-safe có sẵn thay vì tự đồng bộ hoá.

  • Lý thuyết: ConcurrentHashMap (segment locking), BlockingQueue (interface cho Producer-Consumer), CopyOnWriteArrayList.

  • Thực hành: Viết lại Counter Ngày 221 dùng AtomicInteger, so sánh với bản synchronized.

Ngày 229: Lab - Producer-Consumer với BlockingQueue

  • Mục tiêu: Áp dụng toàn bộ kiến thức Java Concurrency vào bài toán kinh điển.

  • Lý thuyết: Ôn lại BlockingQueue tự xử lý wait/notify bên trong, an toàn hơn tự viết thủ công.

  • Thực hành: Viết Producer-Consumer hoàn chỉnh với nhiều producer/consumer dùng LinkedBlockingQueue, verify không mất/trùng dữ liệu.

Nhóm 3: Python Concurrency (Ngày 230–239)

Ngày 230: Threading cơ bản

  • Mục tiêu: Làm quen threading module.

  • Lý thuyết: Thread, Lock, cú pháp tương tự Java nhưng hành vi runtime khác biệt.

  • Thực hành: Viết lại ví dụ đếm race condition (giống Ngày 221) bằng Python threading, sửa bằng Lock.

Ngày 231: GIL - Global Interpreter Lock

  • Mục tiêu: Hiểu ràng buộc đặc thù của CPython.

  • Lý thuyết: GIL chỉ cho phép 1 thread thực thi Python bytecode tại 1 thời điểm - threading không giúp tăng tốc CPU-bound task trong CPython.

  • Thực hành: Benchmark 1 task CPU-bound (tính tổng số nguyên tố) chạy bằng nhiều thread, quan sát không có speedup (thậm chí chậm hơn single-thread).

Ngày 232: Khi nào Threading vẫn hữu ích

  • Mục tiêu: Hiểu GIL không có nghĩa là threading vô dụng.

  • Lý thuyết: GIL được nhả ra khi chờ I/O - threading vẫn hiệu quả cho I/O-bound task (đọc file, gọi network).

  • Thực hành: Benchmark 1 task I/O-bound (gọi nhiều HTTP request) bằng threading, quan sát speedup rõ rệt so với tuần tự.

Ngày 233: Multiprocessing cơ bản

  • Mục tiêu: Vượt qua giới hạn GIL cho CPU-bound task.

  • Lý thuyết: Process, Pool - mỗi process có GIL riêng, chạy thực sự song song trên nhiều core.

  • Thực hành: Viết lại benchmark Ngày 231 bằng multiprocessing.Pool, quan sát speedup thực sự.

Ngày 234: Multiprocessing - Chia sẻ dữ liệu giữa Process

  • Mục tiêu: Hiểu process không share memory như thread, cần cơ chế riêng.

  • Lý thuyết: Queue, Pipe, shared_memory/Value/Array cho dữ liệu chia sẻ có kiểm soát.

  • Thực hành: Viết Producer-Consumer giữa 2 process dùng multiprocessing.Queue.

Ngày 235: Asyncio cơ bản

  • Mục tiêu: Làm quen mô hình concurrency thứ 3: event loop.

  • Lý thuyết: Coroutine (async def), await, event loop chạy single-thread nhưng xử lý được nhiều I/O đồng thời.

  • Thực hành: Viết 1 coroutine đơn giản mô phỏng I/O bằng asyncio.sleep(), quan sát cách event loop xen kẽ nhiều coroutine.

Ngày 236: Asyncio - Task & gather

  • Mục tiêu: Chạy nhiều coroutine thực sự song song (về mặt I/O).

  • Lý thuyết: asyncio.create_task() để schedule coroutine chạy nền, asyncio.gather() để đợi nhiều task cùng lúc.

  • Thực hành: Viết 5 coroutine mô phỏng gọi API (sleep ngẫu nhiên), chạy song song bằng gather, so sánh tổng thời gian với chạy tuần tự.

Ngày 237: Asyncio - I/O thực tế

  • Mục tiêu: Áp dụng asyncio vào tình huống thật, không chỉ sleep().

  • Lý thuyết: Thư viện async thực (aiohttp), vì sao không thể trộn code blocking (requests) vào trong coroutine mà không mất lợi ích async.

  • Thực hành: Viết chương trình gọi 10 HTTP request bằng aiohttp chạy đồng thời, đo thời gian so với gọi tuần tự bằng requests.

Ngày 238: So sánh Threading vs Multiprocessing vs Asyncio

  • Mục tiêu: Có tiêu chí rõ ràng chọn đúng công cụ cho đúng bài toán.

  • Lý thuyết: Threading (I/O-bound, cần shared state), Multiprocessing (CPU-bound), Asyncio (I/O-bound, số lượng connection lớn, chi phí thread cao).

  • Thực hành: Lập bảng quyết định cho 5 tình huống thực tế (web scraping, tính toán ma trận lớn, web server xử lý nhiều connection...).

Ngày 239: Lab - Web Scraper 3 cách tiếp cận

  • Mục tiêu: Thấy khác biệt thực tế qua benchmark trực tiếp.

  • Lý thuyết: Ôn lại toàn bộ Python Concurrency.

  • Thực hành: Viết cùng 1 web scraper (tải 50 trang) bằng threading, multiprocessing, và asyncio; so sánh thời gian chạy và giải thích kết quả.

Nhóm 4: C++ Concurrency (Ngày 240–249)

Ngày 240: std::thread cơ bản

  • Mục tiêu: Tạo và điều khiển thread trong C++.

  • Lý thuyết: std::thread, join()/detach(), truyền tham số vào thread function.

  • Thực hành: Viết chương trình tạo 5 std::thread, mỗi thread in ID của chính nó, join() toàn bộ trước khi kết thúc main.

Ngày 241: std::mutex

  • Mục tiêu: Bảo vệ critical section bằng mutex thủ công.

  • Lý thuyết: std::mutex, lock()/unlock(), nguy cơ quên unlock() khi có exception.

  • Thực hành: Viết Counter bị race condition, sửa bằng mutex.lock()/unlock() thủ công, cố tình gây lỗi quên unlock để thấy hậu quả.

Ngày 242: std::lock_guard & std::unique_lock

  • Mục tiêu: Áp dụng RAII cho quản lý lock, tránh lỗi Ngày 241.

  • Lý thuyết: lock_guard (RAII đơn giản), unique_lock (linh hoạt hơn, hỗ trợ condition_variable).

  • Thực hành: Viết lại Counter Ngày 241 bằng lock_guard, verify lock luôn được giải phóng kể cả khi có exception.

Ngày 243: std::condition_variable

  • Mục tiêu: Phối hợp giữa các thread dựa trên điều kiện.

  • Lý thuyết: condition_variable.wait()/notify_one()/notify_all(), tương đương wait/notify của Java nhưng cần unique_lock đi kèm.

  • Thực hành: Viết Producer-Consumer bằng condition_variable + unique_lock.

Ngày 244: std::future & std::promise

  • Mục tiêu: Lấy kết quả từ thread bất đồng bộ.

  • Lý thuyết: promise đặt giá trị, future lấy giá trị (blocking get()), tương đương Future/CompletableFuture bên Java.

  • Thực hành: Viết ví dụ 1 thread tính toán, set kết quả vào promise, thread chính lấy qua future.get().

Ngày 245: std::async

  • Mục tiêu: Chạy task bất đồng bộ đơn giản hơn thread + promise thủ công.

  • Lý thuyết: std::async tự động trả về future, có thể chạy async hoặc deferred tuỳ launch policy.

  • Thực hành: Viết lại ví dụ Ngày 244 ngắn gọn hơn bằng std::async.

Ngày 246: std::atomic

  • Mục tiêu: Thao tác lock-free cho các kiểu dữ liệu đơn giản.

  • Lý thuyết: std::atomic<T>, compare-and-swap (CAS), khi nào atomic đủ dùng thay vì mutex.

  • Thực hành: Viết lại Counter bằng std::atomic<int>, benchmark so với bản dùng mutex.

Ngày 247: Memory Order trong C++

  • Mục tiêu: Hiểu các mức độ đảm bảo thứ tự bộ nhớ khi dùng atomic.

  • Lý thuyết: memory_order_relaxed, acquire/release, seq_cst - trade-off giữa hiệu năng và tính đúng đắn.

  • Thực hành: Đọc ví dụ code dùng memory_order_relaxed sai cách gây bug, giải thích tại sao.

Ngày 248: Thread Pool tự viết - Concept

  • Mục tiêu: Thiết kế trước khi code, chuẩn bị cho project cuối giai đoạn.

  • Lý thuyết: Thread pool cần: task queue thread-safe, N worker thread lấy task từ queue, cơ chế shutdown an toàn.

  • Thực hành: Vẽ sơ đồ kiến trúc Thread Pool sẽ viết bằng C++.

Ngày 249: Lab - Thread Pool hoàn chỉnh

  • Mục tiêu: Hoàn thiện implementation thực tế.

  • Lý thuyết: Ôn lại condition_variable + mutex + queue kết hợp cho task queue.

  • Thực hành: Cài đặt ThreadPool với submit(task) trả về future, N worker cố định, shutdown() graceful.

Nhóm 5: Concurrency Internals (Ngày 250–257)

Ngày 250: Race Condition

  • Mục tiêu: Định nghĩa chính xác và học cách phát hiện.

  • Lý thuyết: Race condition xảy ra khi kết quả phụ thuộc vào thứ tự thực thi không xác định của nhiều thread truy cập dữ liệu chia sẻ.

  • Thực hành: Chạy lại ví dụ Counter chưa đồng bộ hoá (từ Ngày 221/230/241) nhiều lần, ghi lại kết quả sai khác nhau mỗi lần chạy.

Ngày 251: Deadlock

  • Mục tiêu: Hiểu 4 điều kiện cần để deadlock xảy ra.

  • Lý thuyết: Coffman conditions (Mutual Exclusion, Hold and Wait, No Preemption, Circular Wait).

  • Thực hành: Viết cố tình 1 chương trình deadlock (2 thread, 2 lock, lock theo thứ tự ngược nhau), dùng debugger xác nhận cả 2 thread đang bị block.

Ngày 252: Chiến lược phòng tránh Deadlock

  • Mục tiêu: Có công cụ thực tế để tránh deadlock trong code thật.

  • Lý thuyết: Lock ordering (luôn lock theo 1 thứ tự cố định), lock timeout (tryLock), giảm phạm vi giữ lock.

  • Thực hành: Sửa ví dụ deadlock Ngày 251 bằng lock ordering, verify không còn deadlock.

Ngày 253: Livelock & Starvation

  • Mục tiêu: Phân biệt 2 vấn đề dễ nhầm với deadlock.

  • Lý thuyết: Livelock (thread liên tục đổi trạng thái để tránh nhau nhưng không tiến triển), Starvation (1 thread không bao giờ được cấp tài nguyên do thread khác luôn ưu tiên hơn).

  • Thực hành: Viết ví dụ minh hoạ livelock (2 thread "nhường" nhau vô hạn), giải thích khác biệt với deadlock.

Ngày 254: Memory Visibility

  • Mục tiêu: Hiểu vì sao 1 thread có thể không thấy giá trị mới nhất do thread khác ghi.

  • Lý thuyết: CPU cache per-core, compiler reordering - nếu không có synchronization, không có gì đảm bảo thread khác thấy write kịp thời.

  • Thực hành: Viết ví dụ kinh điển: 1 thread set flag true, thread khác loop chờ flag mà không dùng volatile/synchronized, minh hoạ nguy cơ vòng lặp vô hạn (compiler tối ưu đọc từ register).

Ngày 255: Happens-Before - Java Memory Model

  • Mục tiêu: Hiểu quy tắc chính thức đảm bảo memory visibility trong Java.

  • Lý thuyết: Happens-before relationship, các nguồn tạo happens-before (synchronized, volatile, Thread.start()/join()).

  • Thực hành: Sửa ví dụ Ngày 254 (phiên bản Java) bằng volatile, giải thích vì sao nó tạo happens-before.

Ngày 256: Happens-Before - Liên hệ C++ Memory Model

  • Mục tiêu: Thấy khái niệm tương đương ở C++ đã gặp Ngày 247.

  • Lý thuyết: memory_order_acquire/release chính là cách C++ biểu diễn happens-before một cách tường minh, chi tiết hơn Java.

  • Thực hành: Lập bảng so sánh cơ chế đảm bảo visibility giữa Java (volatile/synchronized) và C++ (memory_order).

Ngày 257: Lab - Phát hiện Race Condition bằng công cụ

  • Mục tiêu: Học cách phát hiện bug concurrency tự động thay vì chỉ đọc code.

  • Lý thuyết: ThreadSanitizer (C++), jcstress (Java) - công cụ chuyên phát hiện race condition mà test thông thường khó bắt được.

  • Thực hành: Chạy ThreadSanitizer hoặc jcstress trên 1 đoạn code cố tình có race condition, đọc báo cáo lỗi được sinh ra.

Nhóm 6: Project - Concurrent Task Scheduler (Ngày 258–266)

Ngày 258: Thiết kế kiến trúc Task Scheduler

  • Mục tiêu: Lên thiết kế tổng thể trước khi code.

  • Lý thuyết: Scheduler cần: task queue, worker pool, priority handling, retry, graceful shutdown.

  • Thực hành: Vẽ sơ đồ kiến trúc, chọn ngôn ngữ chính cho project (khuyến nghị Java vì có sẵn java.util.concurrent phong phú).

Ngày 259: Task Queue Thread-Safe

  • Mục tiêu: Xây thành phần lưu trữ task trung tâm.

  • Lý thuyết: Ôn lại BlockingQueue (Java) hoặc tự viết bằng mutex+condition_variable (C++).

  • Thực hành: Cài đặt TaskQueue hỗ trợ nhiều producer đẩy task, nhiều consumer lấy task an toàn.

Ngày 260: Worker Pool

  • Mục tiêu: Nhiều worker xử lý task song song từ queue.

  • Lý thuyết: Ôn lại Thread Pool đã xây Ngày 249 (nếu dùng C++) hoặc ExecutorService (nếu dùng Java).

  • Thực hành: Cài đặt N worker liên tục lấy task từ TaskQueue và thực thi, verify task được xử lý đúng, không bị lấy trùng bởi 2 worker.

Ngày 261: Task Priority

  • Mục tiêu: Hỗ trợ task quan trọng được xử lý trước.

  • Lý thuyết: PriorityBlockingQueue (Java) hoặc priority_queue + đồng bộ hoá thủ công (C++).

  • Thực hành: Thêm priority vào Task, verify worker luôn lấy task priority cao nhất trước.

Ngày 262: Task Scheduling theo thời gian

  • Mục tiêu: Hỗ trợ task chạy trễ hoặc lặp lại định kỳ.

  • Lý thuyết: Delayed task (chạy sau X giây), periodic task (chạy lặp lại mỗi X giây) - tương tự ScheduledExecutorService.

  • Thực hành: Thêm khả năng schedule task chạy sau 1 khoảng delay, và task lặp lại định kỳ.

Ngày 263: Xử lý lỗi & Retry

  • Mục tiêu: Scheduler không được "chết" vì 1 task lỗi.

  • Lý thuyết: Retry với exponential backoff, giới hạn số lần retry, dead-letter (task thất bại vĩnh viễn cần lưu lại để xem xét).

  • Thực hành: Thêm cơ chế retry cho task thất bại, verify sau N lần retry thất bại thì task được đưa vào danh sách "failed" thay vì retry vô hạn.

Ngày 264: Graceful Shutdown

  • Mục tiêu: Dừng scheduler an toàn, không làm mất dữ liệu.

  • Lý thuyết: Graceful shutdown cần: ngừng nhận task mới, đợi task đang chạy hoàn thành (hoặc timeout), không kill thread giữa chừng.

  • Thực hành: Cài đặt shutdown() cho scheduler, verify task đang chạy được hoàn thành trước khi chương trình thoát.

Ngày 265: Test cho Scheduler

  • Mục tiêu: Verify hành vi concurrency đúng, không chỉ đúng ở single-thread.

  • Lý thuyết: Ôn lại kỹ thuật test concurrency (chạy lại nhiều lần, dùng ThreadSanitizer/jcstress nếu cần, test race condition có chủ đích).

  • Thực hành: Viết test cho scheduler: nhiều producer đẩy task đồng thời, verify không mất task; test priority; test retry; test graceful shutdown.

Ngày 266: Review & Retrospective Giai đoạn 5

  • Mục tiêu: Tổng kết toàn bộ Concurrency.

  • Lý thuyết: Nhìn lại toàn bộ 3 mô hình (thread/process/async) và internals (race condition, deadlock, memory visibility) đã áp dụng vào Scheduler như thế nào.

  • Thực hành: Viết báo cáo retrospective, đánh giá những quyết định thiết kế concurrency đã đưa ra trong project, chỉ ra điểm có thể cải thiện thêm.

Knowledge

Part 1 of 50