🎯 Mục tiêu
- Giúp tổ chức cấu trúc đối tượng và class trong hệ thống phức tạp.
- Giữ cho cấu trúc linh hoạt nhưng hiệu quả và dễ bảo trì.
- Khác với creational patterns (tạo đối tượng), structural patterns giúp kết nối và sắp xếp chúng hợp lý hơn.
🧠 Khi nào nên dùng Structural Patterns
Trường hợp | Vai trò của Structural Pattern |
---|---|
🧩 Kết hợp nhiều đối tượng thành hệ thống lớn | Composite, Facade, Bridge… |
🔁 Thêm tính năng mà không sửa đổi code cũ | Decorator, Proxy |
🔗 Kết nối các interface không tương thích | Adapter |
🔍 Giảm phụ thuộc giữa các thành phần | Tăng tính mở rộng, dễ test |
💡 Ý nghĩa trong thực tế
- Giúp chuẩn hoá cách nhóm phát triển tổ chức code (ví dụ như việc logging được làm thống nhất).
- Giảm nguy cơ code lặp, gắn chặt, và rối loạn kiến trúc khi nhiều dev cùng làm việc.
- Dễ dàng thay thế hoặc hoán đổi các thành phần mà không ảnh hưởng đến logic chính.
🔌 The Adapter Pattern – Mẫu Bộ Chuyển Đổi
🧭 Mục tiêu
- Kết nối hai interface không tương thích mà không cần thay đổi code gốc.
- Đóng vai trò như bộ chuyển đổi nguồn điện – giúp hệ thống dùng được “thiết bị” không cùng chuẩn.
📌 Khi nào nên dùng
Trường hợp | Giải thích |
---|---|
🔄 Interface mismatch | Client cần interface A, nhưng object chỉ hỗ trợ B |
💼 Tích hợp hệ thống cũ (legacy code) | Không sửa code cũ mà vẫn dùng được |
🔧 Nâng cao khả năng tương tác | Làm cho hai lớp vốn không tương thích có thể làm việc cùng nhau |
🧪 Đảm bảo type safety trong TypeScript | Đặc biệt hữu ích khi bật strict mode |
📐 UML Adapter Pattern


- Client gọi
callApiV1()
thuộc interfaceApiServiceV1
- ApiClientV2 chỉ có
callApiV2()
– không tương thích - Adapter (
ApiClientV2Adapter
): cài đặtApiServiceV1
, và chuyển tiếp gọi sangApiClientV2
🧱 Classic Implementation: Hệ đo chiều dài (feet ↔ meters)
🎯 Giao diện Metric:
interface MetricCalculator { getDistanceInMeters(): number; } class MetricSystem implements MetricCalculator { constructor(private distanceInMeters: number) {} getDistanceInMeters() { return this.distanceInMeters; } }
🧮 Lớp cần tích hợp: Imperial
class ImperialSystem { constructor(private distanceInFeet: number) {} getDistanceInFeet() { return this.distanceInFeet; } }
🔌 Adapter
class ImperialToMetricAdapter implements MetricCalculator { constructor(private imperialSystem: ImperialSystem) {} getDistanceInMeters(): number { return this.imperialSystem.getDistanceInFeet() * 0.3048; } }
👨💻 Client code
class Reporter { static reportDistance(calculator: MetricCalculator) { console.log(`The distance is ${calculator.getDistanceInMeters()} meters.`); } } const metric = new MetricSystem(5); const imperial = new ImperialSystem(10); const adapter = new ImperialToMetricAdapter(imperial); Reporter.reportDistance(metric); // 5 meters Reporter.reportDistance(adapter); // 3.048 meters
🧪 Testing Adapter
- ✅ Đảm bảo adapter trả đúng kết quả chuyển đổi
- ✅ Có thể test bằng
expect(...).toBeCloseTo(...)
để kiểm tra số thập phân - ✅ Adapter tuân thủ interface
MetricCalculator
npm run test --adaptor
⚠️ Criticism – Hạn chế
Hạn chế | Mô tả |
---|---|
🧱 Thêm nhiều lớp adapter → phức tạp | Mỗi interface không khớp là thêm một adapter |
🐞 Debug khó hơn | Phải kiểm tra qua lớp adapter để tìm bug |
🐢 Có thể giảm hiệu năng (rất nhỏ) | Nếu adapter làm nhiều việc chuyển đổi |
🤯 Adapter không mô phỏng đúng behavior gốc | Dễ dẫn đến lỗi ngầm |
🔄 Dễ bị “version mismatch” khi lớp gốc thay đổi | Adapter không cập nhật kịp sẽ bị lỗi hoặc sai logic |
💡 Gợi ý:
- Dùng Partial Type, Mapped Type trong TS để giảm boilerplate
- Giữ adapter đơn giản, rõ ràng và kiểm tra kỹ lưỡng
🌍 Real-world Examples
Hệ thống | Adapter được dùng ở đâu |
---|---|
Sequelize (ORM) | Dùng adapter để kết nối MySQL, PostgreSQL, SQLite… qua dialect |
React Native | Dùng adapter để kết nối module Android / iOS với JS API |
Filesystem adapters | Thư viện đọc file dùng adapter để hỗ trợ local, S3, FTP… |
Testing frameworks | Dùng adapter để hỗ trợ test runner khác nhau (Jest, Vitest, Mocha…) |
🎨 Decorator Pattern – Mẫu trang trí
💡 Định nghĩa
Decorator là một mẫu thiết kế thuộc nhóm cấu trúc (structural), cho phép mở rộng hành vi của một đối tượng mà không cần sửa đổi trực tiếp lớp gốc.
Nó là giải pháp thay thế linh hoạt cho kế thừa, giúp mở rộng tính năng mà vẫn giữ nguyên cấu trúc ban đầu.
🔔 Ghi chú:
TypeScript 5 hỗ trợ decorators như một phần chuẩn ECMAScript – cho phép trang trí class, phương thức, thuộc tính – mở rộng phạm vi so với OOP truyền thống.
📌 Đặc điểm chính
- 🛠 Mở rộng hành vi lúc runtime.
- 🧱 Ưu tiên composition (kết hợp) thay vì inheritance (kế thừa).
- 🔄 Có thể bọc (wrap) đệ quy nhiều lớp decorator.
- 🧩 Các decorator giữ nguyên interface của đối tượng gốc → đảm bảo tính nhất quán.
📍 Khi nào nên dùng Decorator
- 🎯 Cần gán thêm trách nhiệm mới cho đối tượng tại runtime.
- 🚫 Tránh tạo quá nhiều subclass (class explosion).
- 🧼 Mở rộng hành vi mà không sửa class gốc → tuân theo nguyên lý Open-Closed.
- 🧾 Xử lý concerns cắt ngang (cross-cutting concerns) như log, cache, transaction. *
- 🧠 Ứng xử có điều kiện (runtime behavior toggle). *
🧰 UML & Cài đặt mẫu


Cấu trúc UML gồm:
Component
: Interface cơ bảnConcreteComponent
: Cài đặt ban đầuDecorator
: Lớp trừu tượng bọc componentConcreteDecoratorA
,ConcreteDecoratorB
: Mở rộng behavior
🧪 Ví dụ: Hệ thống FileReader
interface FileReader { read(filePath: string): string } class SimpleFileReader implements FileReader { read(filePath: string): string { return `Content of ${filePath}` } } abstract class FileReaderDecorator implements FileReader { constructor(protected reader: FileReader) {} abstract read(filePath: string): string }
➕ Thêm Encryption & Compression
class EncryptionDecorator extends FileReaderDecorator { read(filePath: string): string { const content = this.reader.read(filePath) return `Encrypted(${content})` } } class CompressionDecorator extends FileReaderDecorator { read(filePath: string): string { const content = this.reader.read(filePath) return `Compressed(${content})` } }
🔄 Sử dụng
let reader: FileReader = new SimpleFileReader() reader = new CompressionDecorator(reader) reader = new EncryptionDecorator(reader) console.log(reader.read("example.txt"))
🧪 Testing
- ✅ Đảm bảo decorator không làm mất behavior gốc.
- ✅ Behavior của decorator phải chính xác.
- ✅ Thứ tự decorator quan trọng!
- ✅ Kiểm tra side-effects nếu có (console.log, mutate state…)
⚠️ Khuyết điểm
- 📉 Phụ thuộc chặt vào interface gốc.
- 😵💫 Code có thể trở nên khó đọc nếu có nhiều lớp wrapper.
- 🐢 Gây chậm nếu dùng trong hệ thống yêu cầu real-time.
- 🔀 Sai thứ tự decorator có thể gây lỗi ngầm.
🌐 Ví dụ thực tế
- Nest.js:
@Controller('dogs') export class DogsController { @Get() findAll() { return 'This action returns all dogs' } }
→ @Controller, @Get() là decorators giúp ánh xạ logic vào route.
🏢 Façade Pattern – Mẫu giao diện đơn giản hóa
💡 Định nghĩa
Façade là mẫu thiết kế giúp tạo giao diện đơn giản để ẩn đi sự phức tạp của hệ thống con. Giống như app điều khiển nhà thông minh giúp bạn bật đèn, điều hòa, mở cửa chỉ bằng 1 nút.
📌 Mục tiêu chính
- 🧘♂️ Giảm độ phức tạp khi tương tác với hệ thống.
- ✂️ Tách biệt client và hệ thống bên trong.
- 🧼 Tạo lớp trừu tượng cấp cao → tăng khả năng bảo trì.
📍 Khi nào nên dùng
- 🔄 Hệ thống backend phức tạp → cần facade để đơn giản hóa.
- 🔍 Muốn ẩn chi tiết implement hệ thống con.
- 🧱 Xây tầng (layering): Facade dùng làm entry point mỗi tầng.
- 🧬 Giảm phụ thuộc giữa client ↔ subsystem.
🧰 Cài đặt TypeScript
interface SubsystemA { operationA1(): void operationA2(): void } interface SubsystemB { operationB1(): void operationB2(): void } class Facade { constructor( private subsystemA: SubsystemA, private subsystemB: SubsystemB ) {} simplifiedOperation1(): void { this.subsystemA.operationA1() this.subsystemB.operationB1() } simplifiedOperation2(): void { this.subsystemA.operationA2() this.subsystemB.operationB2() this.subsystemA.operationA1() } }
🧪 Testing
- ✅ Facade phải gọi đúng thứ tự các subsystem.
- ✅ Không làm lộ phức tạp bên dưới cho client.
- ✅ Có thể viết test mock cho từng subsystem để test Facade độc lập.
⚠️ Phê bình
- 🐙 Dễ biến thành “God Object” nếu chứa quá nhiều logic.
- 🧨 Khó mở rộng nếu mọi thứ dồn hết vào một facade.
- ⚙️ Có thể gây mất tính linh hoạt do bị bó buộc vào giao diện đơn giản.
🌐 Ví dụ thực tế
class UserManagementFacade { private authService = new AuthService() private userProfileService = new UserProfileService() async login(username: string, password: string) { const token = await this.authService.login(username, password) const profile = await this.userProfileService.getUserProfile(username) return { token, profile } } }
🎯 Composite Pattern – Mẫu Thiết Kế Thành Phần
📌 Ý tưởng chính
- Mẫu thiết kế Composite cho phép xây dựng cây đối tượng có cùng giao diện, gồm các thành phần đơn lẻ (leaf) và thành phần phức tạp (composite).
- Các đối tượng này cùng implement một interface để có thể xử lý thống nhất.
🧠 Ví dụ thực tế:
- Hệ thống file: thư mục chứa được cả file và thư mục con. Ta có thể thực hiện thao tác như tính tổng dung lượng hay liệt kê nội dung mà không cần biết đó là file hay thư mục.
📌 Khi nào nên dùng Composite Pattern
- Khi cần biểu diễn cấu trúc phân cấp (hierarchical) của đối tượng.
- Khi muốn client không cần phân biệt giữa đối tượng đơn và tập hợp đối tượng.
- Khi cần thao tác đồng nhất bất kể mức độ phức tạp của cấu trúc.
✅ Ưu điểm:
- Tính mở rộng cao, dễ thêm/bớt thành phần.
- Thống nhất cách gọi, kể cả với cấu trúc cây sâu.
🛠 UML Class Diagram & Triển khai
Interface chung:
interface FileSystemComponent { display(): string; }
Lớp Leaf:
class File implements FileSystemComponent { constructor(private name: string) {} display(): string { return `File: ${this.name}`; } }
Lớp Composite:
class Directory implements FileSystemComponent { private components: FileSystemComponent[] = []; constructor(private name: string) {} add(component: FileSystemComponent): void { this.components.push(component); } display(): string { return `Directory: ${this.name}\n` + this.components.map(c => c.display()).join('\n'); } }
Cách dùng:
const root = new Directory("Root"); const file1 = new File("file1.txt"); const file2 = new File("file2.txt"); const subDir = new Directory("Subdirectory"); const file3 = new File("file3.txt"); subDir.add(file3); root.add(file1); root.add(file2); root.add(subDir); console.log(root.display());
🧪 Testing
- Viết test unit cho
File
vàDirectory
. - Viết integration test kiểm tra cấu trúc cây sau khi thêm nhiều node.
❌ Nhược điểm & Lưu ý
- Interface quá tổng quát → các leaf bị ép implement các method không cần thiết như
add()
. - Performance thấp nếu cấu trúc cây quá sâu.
- Khó kiểm soát loại thành phần được thêm vào.
- Vòng lặp vô hạn nếu thiết kế không đúng (gây cycle).
🌍 Use Case thực tế
- DOM Tree trong HTML.
- UI Component Tree trong các thư viện như React, Angular.
🎯 Proxy Pattern – Mẫu Thiết Kế Đại Diện
📌 Ý tưởng chính
Proxy cho phép gọi phương thức của một đối tượng thông qua một đối tượng khác (được gọi là đại diện hoặc “proxy object”).
Proxy thường dùng để kiểm soát truy cập, lazy load, caching, logging, bảo mật, v.v.
🧠 Ví dụ thực tế:
- Thư ký nhận điện thoại thay giám đốc, lọc cuộc gọi trước khi chuyển.
- Vue.js, MobX, ORM sử dụng Proxy để theo dõi thay đổi hoặc lazy loading.
📌 Khi nào nên dùng Proxy Pattern
Tình huống dùng Proxy | Mô tả |
---|---|
🐌 Lazy Initialization | Khởi tạo chậm các đối tượng nặng |
🔐 Access Control | Kiểm soát quyền truy cập |
📜 Logging & Audit | Ghi log khi gọi method |
⚡ Caching | Lưu kết quả để tránh tính toán lặp lại |
✅ Validation & Retry | Kiểm tra dữ liệu trước khi gọi method thật |
🛠 UML Class Diagram & Cấu trúc
Interface chung:
export interface Store { save(data: string): void; }
Lớp thực:
export class TextStore implements Store { save(data: string): void { console.log(`TextStore: saving "${data}"`); } }
Lớp Proxy:
export class ProxyTextStore implements Store { constructor(private textStore?: TextStore) {} save(data: string): void { console.log(`ProxyTextStore: preparing to save "${data}"`); if (!this.textStore) { console.log("ProxyTextStore: Lazy init TextStore"); this.textStore = new TextStore(); } this.textStore.save(data); } }
🚀 Cách dùng
// Direct usage const direct = new TextStore(); direct.save("Direct data"); // Proxy usage (lazy init) const proxy = new ProxyTextStore(); proxy.save("Proxy data 1"); // init here proxy.save("Proxy data 2"); // reused // Proxy with pre-initialized object const preInit = new ProxyTextStore(new TextStore()); preInit.save("Pre-initialized data");
🧪 Testing
- Kiểm tra lazy initialization có diễn ra không.
- Kiểm tra Proxy có gọi đúng phương thức của real object.
- Gợi ý: dùng mock object/log để theo dõi chuỗi gọi hàm.
Lệnh chạy test:
$ npm run test --proxy
❌ Nhược điểm & Lưu ý
Nhược điểm | Mô tả |
---|---|
🐢 Tăng độ trễ | Gọi qua thêm một lớp nên chậm hơn |
🧩 Phức tạp hoá code | Đặc biệt nếu lồng nhiều proxy |
🧼 Quản lý vòng đời khó | Proxy cần kiểm soát vòng đời của object thực |
🧪 Khó kiểm thử | Cần test cả proxy lẫn real object, khó mock |
🧱 Khó debug | Proxy chặn lỗi, gây khó trace khi có lỗi thực tế |
🌍 Use Case thực tế
Ứng dụng | Mô tả |
---|---|
Vue.js | Dùng Proxy để reactive data |
MobX | Proxy theo dõi thay đổi trạng thái |
ORM | Lazy load các bản ghi liên kết |
Image loading | Chỉ tải ảnh thật khi sắp vào viewport |
Logging wrapper | Proxy thêm log cho mỗi hành động ghi dữ liệu |
🌉 Bridge Pattern – Mẫu Thiết Kế Cầu Nối
📌 Ý tưởng chính
Bridge Pattern tách abstraction (khái niệm trừu tượng) khỏi implementation (cách hiện thực) để chúng có thể phát triển độc lập.
💡 Ví dụ thực tế:
- Remote điều khiển (abstraction) có thể điều khiển TV, Loa, Máy lạnh (implementation) — mỗi loại có cách hoạt động khác nhau nhưng chia sẻ cùng 1 giao diện điều khiển.
📌 Khi nào nên dùng Bridge Pattern
Trường hợp dùng | Mô tả |
---|---|
✂️ Tách riêng abstraction & implementation | Giúp code dễ mở rộng & bảo trì |
🧩 Khi muốn mở rộng cả hai chiều | Abstraction & Implementation đều có thể thêm mới |
🔄 Chuyển đổi implementation lúc runtime | Ví dụ: thay đổi cơ chế tưới cây tùy thời tiết |
♻️ Dùng chung logic implement | Nhiều abstraction dùng chung một implement mà không dùng kế thừa phức tạp |
🧠 UML & Cấu trúc
Interface của phần “Implementation”:
interface WateringMechanism { water(amount: number): void; checkWaterLevel(): number; refill(amount: number): void; }
Abstraction (SmartPlantCare):
abstract class SmartPlantCare { protected mechanism: WateringMechanism; protected moistureThreshold: number; constructor(mechanism: WateringMechanism, threshold: number) { this.mechanism = mechanism; this.moistureThreshold = threshold; } abstract waterPlant(currentMoisture: number): void; abstract adjustWatering(weatherForecast: string): void; }
🧪 Implement cụ thể
Cơ chế tưới – MistSprayer
class MistSprayer implements WateringMechanism { private waterReservoir: number = 500; water(amount: number): void { this.waterReservoir -= amount; console.log(`Misting ${amount}ml of water`); } checkWaterLevel(): number { return this.waterReservoir; } refill(amount: number): void { this.waterReservoir += amount; console.log(`Refilled ${amount}ml of water`); } }
Loại cây cụ thể – TropicalPlantCare
class TropicalPlantCare extends SmartPlantCare { constructor(mechanism: WateringMechanism) { super(mechanism, 60); } waterPlant(currentMoisture: number): void { if (currentMoisture < this.moistureThreshold) { this.mechanism.water(100); } else { console.log("Tropical plant doesn't need watering"); } } adjustWatering(weatherForecast: string): void { if (weatherForecast.includes("humidity")) { this.moistureThreshold += 10; console.log("Adjusted for humid weather"); } else if (weatherForecast.includes("dry")) { this.moistureThreshold -= 10; console.log("Adjusted for dry weather"); } } }
📋 Ưu điểm & Nhược điểm
✅ Ưu điểm:
- Tách rời logic & hiện thực
- Mở rộng độc lập abstraction hoặc implement
- Dễ maintain và test
- Thay đổi lúc runtime
❌ Nhược điểm:
Vấn đề | Mô tả |
---|---|
🧠 Khó hiểu hơn | Gây rối với team chưa quen với pattern |
🧱 Overkill nếu chỉ có 1 implement | Không cần cầu khi chỉ có 1 bờ |
🧩 Tăng số lớp | Abstraction + implement => phức tạp hóa codebase |
🌍 Use Case thực tế
Use Case | Mô tả |
---|---|
Logger | Tách phần log (abstraction) khỏi phần lưu trữ (console, file, cloud…) |
UI rendering | UI component dùng các render engine khác nhau: WebGL, Canvas |
Smart home | Điều khiển cây, đèn, máy lạnh, mỗi loại dùng cơ chế khác nhau nhưng cùng interface |
Ví dụ Logger:
interface Appender { append(message: string): void; } abstract class Logger { constructor(protected appender: Appender) {} abstract log(message: string): void; } class ConsoleAppender implements Appender { append(msg: string): void { console.log(`[Console] ${msg}`); } } class DebugLogger extends Logger { log(msg: string): void { this.appender.append(`DEBUG: ${msg}`); } }
🧪 Testing
- Test riêng
WateringMechanism
với các implement khác nhau. - Test logic
SmartPlantCare
với nhiều kiểu cơ chế (Mist, Drip…). - Test tính năng thay đổi hành vi theo
weatherForecast
.
Chạy test:
$ npm run test --bridge
🎯 Flyweight Pattern là gì?
Flyweight là một mẫu thiết kế thuộc nhóm Structural, dùng để tối ưu hóa bộ nhớ bằng cách chia sẻ trạng thái chung giữa các object “nặng ký” thay vì tạo mới từng cái.
Nó cực kỳ hữu ích trong các hệ thống có rất nhiều đối tượng tương tự nhau (game, editor, web hiệu năng cao…).
🧠 Ý tưởng chính
- Trạng thái nội tại (Intrinsic): là phần dữ liệu dùng chung giữa nhiều đối tượng (ví dụ: màu xe, hãng xe…).
- Trạng thái bên ngoài (Extrinsic): là phần dữ liệu duy nhất cho từng đối tượng cụ thể (ví dụ: biển số, chủ xe…).
🧥 Ví dụ hình ảnh: Giống như nhiều vũ công dùng chung một số bộ trang phục truyền thống đắt tiền. Ai diễn thì lấy mặc, ai không diễn thì không cần tạo mới đồ.
✅ Khi nào nên dùng Flyweight Pattern?
- Khi hệ thống có số lượng lớn object giống nhau và việc tạo mới mỗi cái là tốn kém tài nguyên.
- Khi muốn giảm thiểu việc tạo mới object bằng cách dùng lại từ cache.
- Khi bộ nhớ giới hạn hoặc muốn giảm áp lực lên Garbage Collector.
- Khi có thể phân tách được trạng thái chung (intrinsic) và trạng thái riêng biệt (extrinsic) của object.
🔧 UML và thành phần chính
📦 Các thành phần:
- Flyweight (interface): Định nghĩa phương thức
perform
, nhận tham số ngoại sinh. - ConcreteFlyweight: Cài đặt Flyweight, có dữ liệu nội tại (shared state).
- FlyweightFactory: Quản lý cache và tái sử dụng các flyweight.
- Client: Luôn tương tác qua factory thay vì tạo object trực tiếp.
💡 Factory kiểm tra cache → Nếu có thì tái sử dụng, nếu không thì tạo mới.
💻 Ví dụ TypeScript
export interface Flyweight { perform(customization: { id: string }): void } export class ConcreteFlyweight implements Flyweight { constructor(private sharedState: Object) {} public perform(customization: { id: string }): void { console.log( `Shared: ${JSON.stringify(this.sharedState)}, Unique: ${customization.id}` ) } }
Factory với QuickLRU:
import QuickLRU from 'quick-lru'; export class FlyweightFactory { private cache = new QuickLRU({ maxSize: 1000 }); public getFlyweight(sharedState: Object): Flyweight { const key = JSON.stringify(sharedState); if (!this.cache.has(key)) { console.log("Creating new flyweight."); this.cache.set(key, new ConcreteFlyweight(sharedState)); } else { console.log("Reusing flyweight."); } return this.cache.get(key)!; } }
Client sử dụng:
tsCopyEditconst factory = new FlyweightFactory() function addCar(plates: string, owner: string, brand: string, model: string, color: string) { const flyweight = factory.getFlyweight({ brand, model, color }); flyweight.perform({ id: `${plates}_${owner}` }); }
🧪 Testing cần kiểm tra gì?
- ✅ Flyweight được dùng lại (so sánh reference).
- ✅ Đúng hành vi cho
perform
với tham số ngoài (extrinsic). - ✅ Factory tạo đúng object khi chưa có trong cache.
$ npm run test --flyweight
⚠️ Criticisms – Hạn chế của Flyweight
🧱 Vấn đề | 📌 Ghi chú |
---|---|
Tăng độ phức tạp | Việc tách intrinsic/extrinsic đôi khi làm code khó đọc hơn |
Tối ưu sớm (premature optimization) | Đôi khi không đáng dùng nếu không thực sự cần |
Khó thay đổi shared state | Vì shared giữa nhiều object → thay đổi có thể gây bug diện rộng |
Độ linh hoạt hạn chế | Không phù hợp cho những hệ thống ít lặp lại đối tượng |
🌐 Ứng dụng thực tế
📄 Text Editor: Mỗi ký tự không cần giữ riêng thông tin về font/size/color. Thay vào đó, chia sẻ cùng một flyweight cho các ký tự có cùng định dạng.
interface CharacterFlyweight { render(position: { x: number, y: number }): void; } class CharacterFlyweightFactory { private characters = new Map<string, CharacterFlyweight>(); getCharacter(char: string, font: string, size: number, color: string): CharacterFlyweight { const key = `${char}-${font}-${size}-${color}`; if (!this.characters.has(key)) { this.characters.set(key, new CharacterFlyweightImpl(char, font, size, color)); } return this.characters.get(key)!; } }
Để lại một bình luận
Bạn phải đăng nhập để gửi bình luận.