- Single Responsibility Principle (SRP) là gì và tại sao nó quan trọng?
SRP quy định mỗi class/module chỉ nên có một lý do để thay đổi, tức là chỉ chịu trách nhiệm cho một chức năng duy nhất. Ví dụ trong TypeScript: Lợi ích: code dễ test, dễ maintain. Dấu hiệu vi phạm: class có nhiều method…
- Open/Closed Principle (OCP) là gì? Cho ví dụ thực tế.
OCP quy định một module phải mở để mở rộng nhưng đóng để sửa đổi — thêm tính năng mới mà không cần chỉnh sửa code hiện có. Ví dụ TypeScript: OCP thường được triển khai qua Strategy pattern, Template Method hoặc polymorphism. Vi phạm…
- Liskov Substitution Principle (LSP) là gì? Khi nào bị vi phạm?
LSP quy định rằng các object của subclass phải có thể thay thế object của superclass mà không làm hỏng tính đúng đắn của chương trình. Ví dụ vi phạm thường gặp: class Rectangle có setWidth/setHeight, class Square extends Rectangle override cả hai để giữ…
- Interface Segregation Principle (ISP) là gì? Ví dụ trong TypeScript?
ISP quy định không nên bắt client implement những interface mà nó không sử dụng — thay vì một interface lớn, hãy tách thành nhiều interface nhỏ, chuyên biệt. Ví dụ: thay vì interface Animal { fly(): void; swim(): void; run(): void } bắt Dog…
- Dependency Inversion Principle (DIP) là gì? Liên quan gì đến Dependency Injection?
DIP có hai quy tắc: (1) module high-level không nên phụ thuộc module low-level, cả hai nên phụ thuộc abstraction; (2) abstraction không nên phụ thuộc detail, mà detail phụ thuộc abstraction. Ví dụ: UserService không nên new MySQLDatabase() trực tiếp mà nhận DatabaseInterface qua…
- DRY, KISS, YAGNI là gì? Khi nào nên áp dụng và khi nào không?
Ba nguyên tắc cơ bản của software engineering: - DRY (Don't Repeat Yourself): mỗi piece of knowledge chỉ nên được định nghĩa ở đúng một chỗ — trùng lặp logic nên được extract thành function/module tái sử dụng. - KISS (Keep It Simple, Stupid): ưu…
- Tại sao SOLID quan trọng trong phát triển phần mềm thực tế?
SOLID giúp viết code có khả năng bảo trì cao (maintainable), dễ mở rộng (extensible) và dễ test (testable) — ba yếu tố quyết định tuổi thọ của một dự án phần mềm. Khi team scale up, SOLID giúp nhiều developer làm việc song song…
- Làm thế nào để áp dụng SOLID trong dự án Next.js/React thực tế?
Trong React/Next.js, SRP áp dụng cho component (mỗi component một trách nhiệm), custom hook (tách logic ra khỏi UI), và service layer (tách API call ra file riêng). OCP áp dụng qua component composition và render props/children thay vì if/else trong component. DIP áp dụng…
- Singleton pattern là gì? Vấn đề của nó trong môi trường hiện đại?
Singleton đảm bảo một class chỉ có duy nhất một instance và cung cấp global access point đến instance đó. Triển khai TypeScript: Vấn đề: (1) khó unit test vì global state — mock singleton phức tạp; (2) trong Node.js multi-worker hoặc serverless, mỗi worker/function…
- Factory Method pattern là gì? Khác gì Simple Factory?
Factory Method định nghĩa interface để tạo object nhưng để subclass quyết định class nào sẽ được instantiate — class cha không hard-code class con cụ thể. Ví dụ TypeScript: Khác với Simple Factory (không phải GoF pattern): Simple Factory chỉ là một method/class có…
- Abstract Factory pattern là gì? Khi nào nên dùng thay Factory Method?
Abstract Factory cung cấp interface để tạo ra một family các object liên quan mà không cần chỉ rõ concrete class. WindowsUIFactory và MacUIFactory cùng implement interface này — nhờ vậy Button và Checkbox luôn thuộc cùng một theme. Khác Factory Method ở chỗ: Factory…
- Builder pattern là gì? Ưu điểm so với constructor có nhiều tham số?
Builder tách quá trình xây dựng object phức tạp thành các bước riêng biệt, cho phép tạo nhiều biểu diễn khác nhau của cùng một object. Ví dụ TypeScript với method chaining: Ưu điểm so với constructor nhiều tham số: không cần nhớ thứ tự…
- Prototype pattern là gì? Khi nào dùng trong JavaScript/TypeScript?
Prototype cho phép copy (clone) object hiện có mà không phụ thuộc vào class của nó — tạo object mới bằng cách sao chép prototype. JavaScript đặc biệt phù hợp vì vốn kế thừa theo prototype. Ví dụ: const config = { timeout: 3000, retries:…
- Object Pool pattern là gì? Ứng dụng thực tế trong Node.js/Go?
Object Pool giữ sẵn một tập object đã khởi tạo để tái sử dụng thay vì liên tục tạo rồi huỷ — đáng dùng khi object tốn kém lúc khởi tạo. Ứng dụng phổ biến nhất là database connection pool: thay vì mở connection mới…
- Dependency Injection (DI) là gì? Các loại DI và framework hỗ trợ?
DI là technique mà object nhận dependencies từ bên ngoài thay vì tự tạo — là cơ chế triển khai DIP. Ba loại DI: Framework DI: NestJS dùng IoC container với decorators @Injectable(), @Inject() — tự động resolve dependency tree. Trong Go không có framework…
- Khi nào nên dùng Creational patterns? Cách chọn pattern phù hợp?
Creational patterns giải quyết vấn đề tạo object một cách linh hoạt. Cách chọn: - Singleton: cần đúng một instance shared toàn app (logger, config) — nhưng ưu tiên DI container thay vì tự implement. - Factory Method: framework cần cho phép subclass override cách…
- Adapter pattern là gì? Ví dụ thực tế trong TypeScript?
Adapter cho phép các interface incompatible làm việc cùng nhau bằng cách bọc một object trong wrapper cung cấp interface mà client kỳ vọng. Ví dụ thực tế: tích hợp third-party payment SDK: Hai loại Adapter: Object Adapter (composition, prefer này) và Class Adapter (multiple…
- Bridge pattern là gì? Khác gì Adapter?
Bridge tách abstraction khỏi implementation để cả hai có thể thay đổi độc lập — giải quyết 'Cartesian product' explosion khi có nhiều dimension. Ví dụ: Shape (Circle, Square) × Renderer (SVGRenderer, CanvasRenderer) = 4 class nếu dùng inheritance, nhưng với Bridge chỉ cần 2+2…
- Composite pattern là gì? Khi nào dùng?
Composite cho phép compose objects thành tree structures để biểu diễn part-whole hierarchies — client xử lý individual objects và compositions đồng nhất qua cùng một interface. Ví dụ: file system: Client gọi getSize() trên cả File và Folder mà không cần biết loại. Trong…
- Decorator pattern là gì? Khác gì Inheritance? Ví dụ trong TypeScript?
Decorator đính kèm thêm behavior vào object tại runtime bằng cách bọc chúng trong decorator objects — thay thế cho inheritance khi cần linh hoạt. Ví dụ TypeScript: Khác Inheritance: Decorator thêm behavior tại runtime và có thể stack nhiều lớp; inheritance static tại compile…
- Facade pattern là gì? Ứng dụng trong frontend development?
Facade cung cấp simplified interface cho một hệ thống phức tạp, subsystem hoặc library — giảm dependency giữa client code và internals phức tạp. Ví dụ: thay vì client gọi trực tiếp 5 service (AuthService, UserService, ProfileService, CacheService, LogService), ta tạo UserFacade với method đơn…
- Proxy pattern là gì? Các loại Proxy phổ biến?
Proxy cung cấp surrogate object thay thế cho object khác — control access đến object gốc và có thể thêm logic trước/sau. Các loại phổ biến: (1) Virtual Proxy (lazy initialization): chỉ tạo object nặng khi thực sự cần — ví dụ lazy load image;…
- Flyweight pattern là gì? Khi nào nên dùng?
Flyweight giảm memory usage bằng cách chia sẻ state chung (intrinsic state) giữa nhiều objects tương tự — chỉ lưu state riêng (extrinsic state) trong object cụ thể. Ví dụ game: render 10,000 cây trong rừng — thay vì mỗi Tree object lưu texture/mesh riêng,…
- Khi nào nên dùng Structural patterns? Cách chọn đúng?
Structural patterns giải quyết cách compose class và object thành larger structures. Chọn theo use case: - Adapter: tích hợp incompatible interface (thường là third-party lib) - Bridge: thiết kế mới cần tách abstraction-implementation để scale độc lập - Composite: dữ liệu có dạng tree,…
- Observer pattern là gì? Ứng dụng trong JavaScript/React?
Observer định nghĩa one-to-many dependency: khi một object (Subject/Observable) thay đổi state, tất cả dependents (Observers) được notify tự động. Ví dụ TypeScript: Trong JavaScript: EventEmitter của Node.js, addEventListener trong DOM, RxJS Observable đều là Observer pattern. React: useEffect với dependency array là lazy Observer;…
- Strategy pattern là gì? So sánh với if/else và switch/case?
Strategy định nghĩa family of algorithms, encapsulate mỗi cái, và làm chúng interchangeable — cho phép algorithm thay đổi độc lập với client sử dụng nó. Ví dụ TypeScript: So với if/else: Strategy tuân thủ OCP — thêm algorithm mới không cần sửa Sorter; if/else…
- Command pattern là gì? Ứng dụng Undo/Redo?
Command encapsulate một request thành object, cho phép parameterize clients với different requests, queue/log requests, và support undoable operations. Cấu trúc: Ứng dụng thực tế: text editor (Ctrl+Z), transaction trong database, task queue (Bull, BullMQ), HTTP request retry. Trong Redux, mỗi dispatch(action) là Command —…
- Template Method pattern là gì? Khác Strategy ở điểm nào?
Template Method định nghĩa skeleton của algorithm trong method của superclass, để subclass override các bước cụ thể mà không thay đổi cấu trúc tổng thể. Ví dụ: Khác Strategy: Template Method dùng inheritance (lúc compile time); Strategy dùng composition (lúc runtime). Template Method khi…
- Chain of Responsibility là gì? Ứng dụng trong Express.js middleware?
Chain of Responsibility cho phép pass request qua chain of handlers — mỗi handler quyết định xử lý hoặc pass cho handler tiếp theo. Express.js middleware là ví dụ điển hình: app.use(authMiddleware, rateLimitMiddleware, validationMiddleware, routeHandler) — mỗi middleware gọi next() để pass request. Ví dụ…
- State pattern là gì? Khác FSM (Finite State Machine) ở đâu?
State pattern cho phép object đổi hành vi khi state nội tại đổi — nhìn từ ngoài như thể object đổi class. Thay vì rải if (state === 'idle') ... else if (state === 'loading') ... khắp codebase, mỗi state là một class mang hành…
- Mediator pattern là gì? Khác Observer và tại sao quan trọng trong microservices?
- Iterator pattern là gì? JavaScript Iterator Protocol hoạt động ra sao?
Iterator cung cấp cách access tuần tự các elements của collection mà không expose underlying representation. JavaScript hỗ trợ sẵn Iterator Protocol: object là iterator nếu có next() method trả về { value, done }. Ví dụ custom iterator: Generator functions (function) là sugar syntax…
- Visitor pattern là gì? Khi nào đây là lựa chọn đúng?
- MVC, MVP, MVVM là gì? Sự khác biệt và ứng dụng trong frontend hiện đại?
Ba architectural patterns tách UI logic: - MVC (Model-View-Controller): Controller xử lý input, update Model, View render Model — Controller và View biết nhau; truyền thống trong server-side (Rails, Laravel, Express). - MVP (Model-View-Presenter): Presenter xử lý logic thay Controller, View passive hơn chỉ nhận…
- Anti-patterns cần tránh trong software development là gì? Ví dụ thực tế?
- Bốn trụ cột của lập trình hướng đối tượng (OOP) là gì?
- Encapsulation (đóng gói): gói dữ liệu và hành vi thao tác trên dữ liệu vào một object, ẩn trạng thái nội bộ; bên ngoài chỉ truy cập qua interface công khai. Bảo vệ bất biến (invariant), giảm coupling. - Abstraction (trừu tượng): lộ ra…
- Memento pattern là gì? Nó hỗ trợ undo/redo như thế nào?
Ý tưởng: chụp và lưu lại trạng thái nội bộ của một object để sau này khôi phục, mà không phá vỡ tính đóng gói (không lộ chi tiết bên trong ra ngoài). Ba vai: - Originator: object có trạng thái cần lưu; tạo memento…
- Null Object pattern là gì? Nó giúp loại bỏ việc kiểm tra null ra sao?
Vấn đề: code rải rác if (obj != null) obj.doSomething() gây rối, dễ quên, và dẫn tới NullPointerException. Ý tưởng: thay vì trả null khi "không có gì", trả về một object triển khai cùng interface nhưng hành vi trung tính / không làm gì…
- Association, Aggregation và Composition khác nhau thế nào trong quan hệ đối tượng (UML)?
Ba mức quan hệ giữa object, ràng buộc vòng đời mạnh dần: - Association (kết hợp): hai object có liên hệ và dùng nhau nhưng độc lập vòng đời. Vd: Teacher và Student — biết nhau nhưng tồn tại riêng. UML: đường thẳng. - Aggregation…
- Cohesion và Coupling là gì? Vì sao nên hướng tới high cohesion và low coupling?
- Cohesion (độ gắn kết): mức độ các phần bên trong một module cùng phục vụ một mục đích rõ ràng. High cohesion = mọi hàm/thuộc tính trong module liên quan chặt tới một trách nhiệm (gần với SRP). - Coupling (độ phụ thuộc): mức…
- Hàm tính phí ship của em có chuỗi if/else theo loại nhà vận chuyển, mỗi lần thêm hãng mới lại phải sửa hàm đó. Em refactor thế nào?
Đây là code smell switch statements thường gặp: một hàm phân nhánh theo loại và phải sửa mỗi khi có loại mới. Cách xử lý là tách mỗi nhánh thành một strategy cùng interface, rồi chọn strategy qua một bảng tra. Thêm hãng mới =…
- Constructor của em có 8 tham số, nhiều cái optional, gọi ra toàn `null, null, true`. Nên xử lý sao?
Đó là telescoping constructor — người đọc không biết true ở vị trí thứ 6 nghĩa là gì, và hoán đổi hai tham số cùng kiểu thì compiler không bắt được. Hai hướng: 1. Object tham số (đủ dùng cho phần lớn trường hợp trong…
- Hệ thống cần khởi tạo storage/payment khác nhau theo config môi trường. Khi nào dùng Factory Method, khi nào Abstract Factory?
Factory Method giải quyết một loại object: bạn có một hàm/method quyết định tạo instance nào dựa trên input, phần code gọi chỉ thấy interface. Abstract Factory giải quyết một nhóm object phải khớp nhau. Ví dụ chọn nhà thanh toán: mỗi nhà có client…
- Vì sao Singleton hay bị gọi là anti-pattern, nhất là trong code có unit test?
Bản thân "một instance duy nhất" không sai — vấn đề nằm ở cách Singleton cổ điển tự quản lý instance và được gọi thẳng từ mọi nơi. Hệ quả: - Phụ thuộc ẩn. OrderService gọi Database.getInstance() bên trong, nên signature của hàm không hề…
- Khi đơn hàng chuyển sang trạng thái đã thanh toán, em phải gửi mail, trừ tồn kho, ghi log, đẩy notification. Hàm `markPaid` càng ngày càng dài. Xử lý sao?
Vấn đề là markPaid đang biết quá nhiều về hệ thống xung quanh: thêm một việc phải-làm-khi-thanh-toán là lại sửa nó. Tách theo hướng Observer / publish-subscribe: markPaid chỉ cập nhật trạng thái rồi phát ra một sự kiện, các bên quan tâm tự đăng…
- Em cần thêm cache, retry và đo thời gian cho một service đang chạy nhưng không muốn sửa class đó. Làm thế nào?
Dùng Decorator: bọc object gốc trong một object cùng interface, thêm hành vi trước/sau rồi giao phần việc chính cho object bên trong. Mỗi concern nằm ở một lớp riêng, ghép được theo thứ tự tuỳ ý và class gốc không đổi một dòng nào.…
- Team em gọi SDK của một cổng thanh toán rải rác khắp code. Sau này đổi cổng thì sửa rất nhiều chỗ. Nên bọc lại thế nào?
Định nghĩa interface theo ngôn ngữ nghiệp vụ của bạn, rồi viết một Adapter dịch từ interface đó sang SDK. Code nghiệp vụ chỉ nhìn thấy PaymentGateway. Đổi cổng = viết adapter mới, phần còn lại không đổi. Test cũng dễ vì thay adapter bằng…
- Controller đăng ký người dùng của em đang gọi 6 service theo thứ tự (tạo user, tạo profile, gửi mail, ghi audit...). Có nên gom lại không, và gom bằng gì?
Có — nhưng gom bằng Facade (một điểm vào cho một quy trình), không phải một class "Manager" chứa tất cả. Controller chỉ còn: validate request → gọi register → map response. Quy trình đăng ký nằm một chỗ, test được mà không cần dựng…
- Repository pattern giải quyết vấn đề gì, và khi nào nó là lớp thừa?
Repository đặt một lớp trung gian giữa domain và tầng lưu trữ: domain làm việc với một tập object trong bộ nhớ, còn chuyện SQL/ORM nằm sau interface. Nó giải quyết được: - Truy vấn nghiệp vụ có tên rõ ràng, dùng lại được, thay…
- Dependency Injection khác Service Locator ở đâu? Vì sao Service Locator thường bị đánh giá thấp hơn?
Cả hai đều tách việc tạo dependency ra khỏi việc dùng, nhưng khác nhau ở hướng luồng. DI — dependency được đưa vào từ ngoài: Service Locator — class tự đi lấy từ một registry: Vì sao DI được ưa hơn: - Phụ thuộc hiện…
- Ba job import file (CSV, Excel, XML) có cùng khung xử lý nhưng khác phần parse. Nên dùng Template Method hay Strategy?
Template Method khớp hơn khi khung xử lý cố định và chỉ vài bước thay đổi. Lớp cha giữ trình tự, lớp con điền phần khác nhau. Strategy khớp hơn khi phần thay đổi là một hành vi độc lập cần chọn lúc chạy hoặc…
- Tính năng editor cần undo/redo, và một số thao tác phải chạy nền qua queue. Command pattern giúp gì ở đây và cần lưu ý gì?
- Vòng đời đơn hàng (pending → paid → shipped → completed / cancelled / refunded) đang được kiểm bằng if rải rác. Em mô hình hoá bằng state machine thế nào?
- Áp dụng Dependency Inversion trong dự án thật nghĩa là gì? Chỉ cần tách interface ra là xong phải không?
- Open/Closed Principle trong thực tế áp dụng ra sao mà không rơi vào tình trạng abstraction sớm?
- Làm sao nhận ra một codebase đang lạm dụng design pattern, và nên cải thiện dần thế nào thay vì viết lại?