[{"data":1,"prerenderedAt":2536},["ShallowReactive",2],{"navigation_docs":3,"-tu-hoc-oop-design-pattern":420,"-tu-hoc-oop-design-pattern-surround":2531},[4,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":26},"Phỏng vấn .NET","i-lucide-briefcase","\u002Fphong-van","1.phong-van",[10,27,66,111,128],{"title":11,"icon":12,"path":13,"stem":14,"children":15,"page":26},"Tổng quan","i-lucide-compass","\u002Fphong-van\u002Ftong-quan","1.phong-van\u002F1.tong-quan",[16,21],{"title":17,"path":18,"stem":19,"icon":20},"Thị trường HCM","\u002Fphong-van\u002Ftong-quan\u002Fthi-truong","1.phong-van\u002F1.tong-quan\u002F1.thi-truong","i-lucide-trending-up",{"title":22,"path":23,"stem":24,"icon":25},"Lộ trình 7 ngày","\u002Fphong-van\u002Ftong-quan\u002Flo-trinh","1.phong-van\u002F1.tong-quan\u002F2.lo-trinh","i-lucide-calendar-days",false,{"title":22,"icon":25,"path":28,"stem":29,"children":30,"page":26},"\u002Fphong-van\u002Flo-trinh","1.phong-van\u002F2.lo-trinh",[31,36,41,46,51,56,61],{"title":32,"path":33,"stem":34,"icon":35},"Ngày 1 — C# cơ bản","\u002Fphong-van\u002Flo-trinh\u002Fngay-1-csharp-co-ban","1.phong-van\u002F2.lo-trinh\u002F1.ngay-1-csharp-co-ban","i-lucide-1",{"title":37,"path":38,"stem":39,"icon":40},"Ngày 2 — OOP và SOLID","\u002Fphong-van\u002Flo-trinh\u002Fngay-2-oop-solid","1.phong-van\u002F2.lo-trinh\u002F2.ngay-2-oop-solid","i-lucide-2",{"title":42,"path":43,"stem":44,"icon":45},"Ngày 3 — C# nâng cao","\u002Fphong-van\u002Flo-trinh\u002Fngay-3-csharp-nang-cao","1.phong-van\u002F2.lo-trinh\u002F3.ngay-3-csharp-nang-cao","i-lucide-3",{"title":47,"path":48,"stem":49,"icon":50},"Ngày 4 — Database và SQL","\u002Fphong-van\u002Flo-trinh\u002Fngay-4-database-sql","1.phong-van\u002F2.lo-trinh\u002F4.ngay-4-database-sql","i-lucide-4",{"title":52,"path":53,"stem":54,"icon":55},"Ngày 5 — ASP.NET Core và EF Core","\u002Fphong-van\u002Flo-trinh\u002Fngay-5-aspnet-efcore","1.phong-van\u002F2.lo-trinh\u002F5.ngay-5-aspnet-efcore","i-lucide-5",{"title":57,"path":58,"stem":59,"icon":60},"Ngày 6 — DSA coding","\u002Fphong-van\u002Flo-trinh\u002Fngay-6-dsa","1.phong-van\u002F2.lo-trinh\u002F6.ngay-6-dsa","i-lucide-6",{"title":62,"path":63,"stem":64,"icon":65},"Ngày 7 — Mock và HR","\u002Fphong-van\u002Flo-trinh\u002Fngay-7-mock-interview","1.phong-van\u002F2.lo-trinh\u002F7.ngay-7-mock-interview","i-lucide-7",{"title":67,"icon":68,"path":69,"stem":70,"children":71,"page":26},"Ngân hàng câu hỏi","i-lucide-database","\u002Fphong-van\u002Fngan-hang-cau-hoi","1.phong-van\u002F3.ngan-hang-cau-hoi",[72,77,82,87,92,97,102,107],{"title":73,"path":74,"stem":75,"icon":76},"C# (60+ câu)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fcsharp","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F1.csharp","i-lucide-hash",{"title":78,"path":79,"stem":80,"icon":81},"OOP và SOLID (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Foop","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F2.oop","i-lucide-shapes",{"title":83,"path":84,"stem":85,"icon":86},"SQL (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fsql","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F3.sql","i-lucide-database-zap",{"title":88,"path":89,"stem":90,"icon":91},"ASP.NET và EF (50+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-ef","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F4.aspnet-ef","i-lucide-server",{"title":93,"path":94,"stem":95,"icon":96},"DSA và Coding (30+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdsa-coding","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F5.dsa-coding","i-lucide-binary",{"title":98,"path":99,"stem":100,"icon":101},"HR và Soft skill (25+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fhr-behavior","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F6.hr-behavior","i-lucide-users",{"title":103,"path":104,"stem":105,"icon":106},"Design Pattern (55+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdesign-pattern","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F7.design-pattern","i-lucide-puzzle",{"title":108,"path":109,"stem":110,"icon":91},"ASP.NET MVC và Web API","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-mvc-webapi","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F8.aspnet-mvc-webapi",{"title":112,"itle":113,"icon":114,"path":115,"stem":116,"children":117,"page":26},"Tai Lieu","Tài liệu và Cheat sheet","i-lucide-book-marked","\u002Fphong-van\u002Ftai-lieu","1.phong-van\u002F4.tai-lieu",[118,123],{"title":119,"path":120,"stem":121,"icon":122},"Cheat Sheet","\u002Fphong-van\u002Ftai-lieu\u002Fcheatsheet","1.phong-van\u002F4.tai-lieu\u002F1.cheatsheet","i-lucide-clipboard-list",{"title":124,"path":125,"stem":126,"icon":127},"Nguồn tham khảo","\u002Fphong-van\u002Ftai-lieu\u002Fnguon","1.phong-van\u002F4.tai-lieu\u002F2.nguon","i-lucide-link",{"title":129,"icon":130,"path":131,"stem":132,"children":133,"page":26},"Phỏng vấn English","i-lucide-globe","\u002Fphong-van\u002Fenglish-interview","1.phong-van\u002F5.english-interview",[134,138,143,148],{"title":135,"path":136,"stem":137,"icon":130},"Tổng quan English","\u002Fphong-van\u002Fenglish-interview\u002Ftong-quan","1.phong-van\u002F5.english-interview\u002F1.tong-quan",{"title":139,"path":140,"stem":141,"icon":142},"Vocabulary","\u002Fphong-van\u002Fenglish-interview\u002Fvocabulary","1.phong-van\u002F5.english-interview\u002F2.vocabulary","i-lucide-book-a",{"title":144,"path":145,"stem":146,"icon":147},"Technical Q và A (EN)","\u002Fphong-van\u002Fenglish-interview\u002Ftechnical-en","1.phong-van\u002F5.english-interview\u002F3.technical-en","i-lucide-code",{"title":149,"path":150,"stem":151,"icon":152},"Behavioral (EN)","\u002Fphong-van\u002Fenglish-interview\u002Fbehavioral-en","1.phong-van\u002F5.english-interview\u002F4.behavioral-en","i-lucide-message-square-text",{"title":154,"icon":155,"path":156,"stem":157,"children":158},"Tự học","i-lucide-graduation-cap","\u002Ftu-hoc","2.tu-hoc",[159,161,287,310,333,357,384],{"title":154,"path":156,"stem":160,"icon":155},"2.tu-hoc\u002Findex",{"title":162,"icon":68,"path":163,"stem":164,"children":165},"Database","\u002Ftu-hoc\u002Fdatabase","2.tu-hoc\u002F1.database\u002Findex",[166,167,197,222,243,248],{"title":162,"path":163,"stem":164,"icon":68},{"title":168,"icon":91,"path":169,"stem":170,"children":171,"page":26},"SQL Server","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server","2.tu-hoc\u002F1.database\u002F2.sql-server",[172,177,182,187,192],{"title":173,"path":174,"stem":175,"icon":176},"1. Giới thiệu và Cài đặt","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F1.gioi-thieu-cai-dat","i-lucide-play-circle",{"title":178,"path":179,"stem":180,"icon":181},"2. Cú pháp cơ bản","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F2.cu-phap-co-ban","i-lucide-code-2",{"title":183,"path":184,"stem":185,"icon":186},"3. Truy vấn nâng cao","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F3.truy-van-nang-cao","i-lucide-git-merge",{"title":188,"path":189,"stem":190,"icon":191},"4. Đặc thù và Performance","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F4.dac-thu-performance","i-lucide-gauge",{"title":193,"path":194,"stem":195,"icon":196},"5. Q và A phỏng vấn","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F5.cau-hoi-phong-van","i-lucide-message-circle-question",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":26},"PostgreSQL","i-simple-icons-postgresql","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql","2.tu-hoc\u002F1.database\u002F3.postgresql",[203,206,209,212,215,218],{"title":173,"path":204,"stem":205,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":207,"stem":208,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F2.cu-phap-co-ban",{"title":183,"path":210,"stem":211,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F3.truy-van-nang-cao",{"title":188,"path":213,"stem":214,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F4.dac-thu-performance",{"title":193,"path":216,"stem":217,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F5.cau-hoi-phong-van",{"title":219,"path":220,"stem":221,"icon":96},"6. Tích hợp C# và Postgres","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftich-hop-csharp","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F6.tich-hop-csharp",{"title":223,"icon":224,"path":225,"stem":226,"children":227,"page":26},"MySQL","i-simple-icons-mysql","\u002Ftu-hoc\u002Fdatabase\u002Fmysql","2.tu-hoc\u002F1.database\u002F4.mysql",[228,231,234,237,240],{"title":173,"path":229,"stem":230,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F4.mysql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":232,"stem":233,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F4.mysql\u002F2.cu-phap-co-ban",{"title":183,"path":235,"stem":236,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F4.mysql\u002F3.truy-van-nang-cao",{"title":188,"path":238,"stem":239,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F4.mysql\u002F4.dac-thu-performance",{"title":193,"path":241,"stem":242,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F4.mysql\u002F5.cau-hoi-phong-van",{"title":244,"path":245,"stem":246,"icon":247},"So sánh 3 RDBMS","\u002Ftu-hoc\u002Fdatabase\u002Fso-sanh","2.tu-hoc\u002F1.database\u002F5.so-sanh","i-lucide-columns-3",{"title":249,"path":250,"stem":251,"children":252,"icon":254},"Supabase (BaaS)","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002Findex",[253,255,259,264,269,274,278,283],{"title":249,"path":250,"stem":251,"icon":254},"i-simple-icons-supabase",{"title":256,"path":257,"stem":258,"icon":68},"1. Database","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fdatabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002F1.database",{"title":260,"path":261,"stem":262,"icon":263},"2. Authentication","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fauthentication","2.tu-hoc\u002F1.database\u002F6.supabase\u002F2.authentication","i-lucide-key",{"title":265,"path":266,"stem":267,"icon":268},"3. Storage & Realtime","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fstorage-realtime","2.tu-hoc\u002F1.database\u002F6.supabase\u002F3.storage-realtime","i-lucide-zap",{"title":270,"path":271,"stem":272,"icon":273},"4. Edge Functions & AI","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Ffunctions-ai","2.tu-hoc\u002F1.database\u002F6.supabase\u002F4.functions-ai","i-lucide-brain",{"title":275,"path":276,"stem":277,"icon":181},"5. API & SDKs","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fapi-sdk","2.tu-hoc\u002F1.database\u002F6.supabase\u002F5.api-sdk",{"title":279,"path":280,"stem":281,"icon":282},"6. Platform & Deploy","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fplatform-deploy","2.tu-hoc\u002F1.database\u002F6.supabase\u002F6.platform-deploy","i-lucide-cloud",{"title":284,"path":285,"stem":286,"icon":196},"7. Interview & Q&A","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Finterview","2.tu-hoc\u002F1.database\u002F6.supabase\u002F7.interview",{"title":288,"icon":289,"path":290,"stem":291,"children":292},"TypeScript","i-simple-icons-typescript","\u002Ftu-hoc\u002Ftypescript","2.tu-hoc\u002F2.typescript\u002Findex",[293,294,297,301,306],{"title":288,"path":290,"stem":291,"icon":289},{"title":173,"path":295,"stem":296,"icon":176},"\u002Ftu-hoc\u002Ftypescript\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F2.typescript\u002F1.gioi-thieu-cai-dat",{"title":298,"path":299,"stem":300,"icon":81},"2. Type System","\u002Ftu-hoc\u002Ftypescript\u002Ftype-system","2.tu-hoc\u002F2.typescript\u002F2.type-system",{"title":302,"path":303,"stem":304,"icon":305},"3. Advanced types","\u002Ftu-hoc\u002Ftypescript\u002Fadvanced-types","2.tu-hoc\u002F2.typescript\u002F3.advanced-types","i-lucide-braces",{"title":307,"path":308,"stem":309,"icon":196},"4. Q và A phỏng vấn","\u002Ftu-hoc\u002Ftypescript\u002Fcau-hoi-phong-van","2.tu-hoc\u002F2.typescript\u002F4.cau-hoi-phong-van",{"title":311,"icon":81,"path":312,"stem":313,"children":314},"OOP","\u002Ftu-hoc\u002Foop","2.tu-hoc\u002F3.oop\u002Findex",[315,316,321,326,330],{"title":311,"path":312,"stem":313,"icon":81},{"title":317,"path":318,"stem":319,"icon":320},"1. Bốn tính chất","\u002Ftu-hoc\u002Foop\u002F4-tinh-chat","2.tu-hoc\u002F3.oop\u002F1.4-tinh-chat","i-lucide-circle-dot",{"title":322,"path":323,"stem":324,"icon":325},"2. SOLID","\u002Ftu-hoc\u002Foop\u002Fsolid","2.tu-hoc\u002F3.oop\u002F2.solid","i-lucide-shield",{"title":327,"path":328,"stem":329,"icon":106},"3. Design Pattern","\u002Ftu-hoc\u002Foop\u002Fdesign-pattern","2.tu-hoc\u002F3.oop\u002F3.design-pattern",{"title":307,"path":331,"stem":332,"icon":196},"\u002Ftu-hoc\u002Foop\u002Fcau-hoi-phong-van","2.tu-hoc\u002F3.oop\u002F4.cau-hoi-phong-van",{"title":334,"icon":305,"path":335,"stem":336,"children":337},"Generic","\u002Ftu-hoc\u002Fgeneric","2.tu-hoc\u002F4.generic\u002Findex",[338,339,344,349,354],{"title":334,"path":335,"stem":336,"icon":305},{"title":340,"path":341,"stem":342,"icon":343},"1. Khái niệm và cú pháp","\u002Ftu-hoc\u002Fgeneric\u002Fkhai-niem","2.tu-hoc\u002F4.generic\u002F1.khai-niem","i-lucide-book",{"title":345,"path":346,"stem":347,"icon":348},"2. Constraint và Variance","\u002Ftu-hoc\u002Fgeneric\u002Fconstraint-variance","2.tu-hoc\u002F4.generic\u002F2.constraint-variance","i-lucide-filter",{"title":350,"path":351,"stem":352,"icon":353},"3. C# vs TS","\u002Ftu-hoc\u002Fgeneric\u002Fso-sanh","2.tu-hoc\u002F4.generic\u002F3.so-sanh","i-lucide-git-compare",{"title":307,"path":355,"stem":356,"icon":196},"\u002Ftu-hoc\u002Fgeneric\u002Fcau-hoi-phong-van","2.tu-hoc\u002F4.generic\u002F4.cau-hoi-phong-van",{"title":358,"icon":359,"path":360,"stem":361,"children":362},"Nuxt 4","i-simple-icons-nuxt","\u002Ftu-hoc\u002Fnuxt-4","2.tu-hoc\u002F5.nuxt-4\u002Findex",[363,364,367,372,376,379],{"title":358,"path":360,"stem":361,"icon":359},{"title":173,"path":365,"stem":366,"icon":176},"\u002Ftu-hoc\u002Fnuxt-4\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F5.nuxt-4\u002F1.gioi-thieu-cai-dat",{"title":368,"path":369,"stem":370,"icon":371},"2. Routing và Rendering","\u002Ftu-hoc\u002Fnuxt-4\u002Frouting-rendering","2.tu-hoc\u002F5.nuxt-4\u002F2.routing-rendering","i-lucide-route",{"title":373,"path":374,"stem":375,"icon":91},"3. Data và Server","\u002Ftu-hoc\u002Fnuxt-4\u002Fdata-server","2.tu-hoc\u002F5.nuxt-4\u002F3.data-server",{"title":307,"path":377,"stem":378,"icon":196},"\u002Ftu-hoc\u002Fnuxt-4\u002Fcau-hoi-phong-van","2.tu-hoc\u002F5.nuxt-4\u002F4.cau-hoi-phong-van",{"title":380,"path":381,"stem":382,"icon":383},"5. Practical Todo App","\u002Ftu-hoc\u002Fnuxt-4\u002Fpractical-todo-app","2.tu-hoc\u002F5.nuxt-4\u002F5.practical-todo-app","i-lucide-check-square",{"title":385,"path":386,"stem":387,"children":388,"icon":390},"Tailwind CSS","\u002Ftu-hoc\u002Ftailwindcss","2.tu-hoc\u002F6.tailwindcss\u002Findex",[389,391,396,401,406,411,416],{"title":385,"path":386,"stem":387,"icon":390},"i-simple-icons-tailwindcss",{"title":392,"path":393,"stem":394,"icon":395},"1. Layout & Spacing","\u002Ftu-hoc\u002Ftailwindcss\u002Flayout-spacing","2.tu-hoc\u002F6.tailwindcss\u002F1.layout-spacing","i-lucide-layout",{"title":397,"path":398,"stem":399,"icon":400},"2. Typography & Colors","\u002Ftu-hoc\u002Ftailwindcss\u002Ftypography-colors","2.tu-hoc\u002F6.tailwindcss\u002F2.typography-colors","i-lucide-type",{"title":402,"path":403,"stem":404,"icon":405},"3. Interactive & Effects","\u002Ftu-hoc\u002Ftailwindcss\u002Finteractive-effects","2.tu-hoc\u002F6.tailwindcss\u002F3.interactive-effects","i-lucide-sparkles",{"title":407,"path":408,"stem":409,"icon":410},"4. Responsive & Dark Mode","\u002Ftu-hoc\u002Ftailwindcss\u002Fresponsive-darkmode","2.tu-hoc\u002F6.tailwindcss\u002F4.responsive-darkmode","i-lucide-monitor-smartphone",{"title":412,"path":413,"stem":414,"icon":415},"5. Customization & Theme","\u002Ftu-hoc\u002Ftailwindcss\u002Fcustomization","2.tu-hoc\u002F6.tailwindcss\u002F5.customization","i-lucide-settings-2",{"title":417,"path":418,"stem":419,"icon":181},"6. Practical Projects","\u002Ftu-hoc\u002Ftailwindcss\u002Fpractical-projects","2.tu-hoc\u002F6.tailwindcss\u002F6.practical-projects",{"id":421,"title":422,"body":423,"description":2524,"extension":2525,"links":2526,"meta":2527,"navigation":2528,"path":328,"seo":2529,"stem":329,"__hash__":2530},"docs\u002F2.tu-hoc\u002F3.oop\u002F3.design-pattern.md","Design Pattern — 12 pattern phổ biến nhất",{"type":424,"value":425,"toc":2502},"minimark",[426,431,448,451,513,519,529,533,538,548,856,882,888,892,897,975,982,986,991,1108,1130,1134,1138,1143,1241,1245,1254,1425,1445,1449,1454,1559,1572,1576,1580,1589,1704,1711,1715,1723,1826,1829,1833,1838,1967,1970,1974,1983,2132,2136,2141,2269,2287,2291,2300,2437,2446,2450,2490,2498],[427,428,430],"h2",{"id":429},"design-pattern-là-gì","Design Pattern là gì?",[432,433,434,435,439,440,443,444,447],"p",{},"Pattern = ",[436,437,438],"strong",{},"giải pháp lặp lại"," cho ",[436,441,442],{},"vấn đề lặp lại"," trong design. Không phải code copy-paste — là ",[436,445,446],{},"template để áp dụng",".",[432,449,450],{},"Theo \"Gang of Four\" (Gamma, Helm, Johnson, Vlissides) — 23 pattern chia 3 nhóm:",[452,453,454,470],"table",{},[455,456,457],"thead",{},[458,459,460,464,467],"tr",{},[461,462,463],"th",{},"Nhóm",[461,465,466],{},"Tập trung vào",[461,468,469],{},"Ví dụ",[471,472,473,487,500],"tbody",{},[458,474,475,481,484],{},[476,477,478],"td",{},[436,479,480],{},"Creational",[476,482,483],{},"Tạo object",[476,485,486],{},"Singleton, Factory, Builder, Prototype, Abstract Factory",[458,488,489,494,497],{},[476,490,491],{},[436,492,493],{},"Structural",[476,495,496],{},"Lắp ráp class\u002Fobject",[476,498,499],{},"Adapter, Decorator, Facade, Proxy, Composite, Bridge, Flyweight",[458,501,502,507,510],{},[476,503,504],{},[436,505,506],{},"Behavioral",[476,508,509],{},"Tương tác + thuật toán",[476,511,512],{},"Strategy, Observer, Command, Iterator, State, Template Method, Chain of Responsibility, Mediator, Visitor, Memento, Interpreter",[432,514,515,516,447],{},"Section này tập trung ",[436,517,518],{},"12 pattern hay được hỏi PV nhất",[520,521,524,525,528],"callout",{"color":522,"icon":523},"warning","i-lucide-star","⭐ Pattern hay được hỏi PV junior+: ",[436,526,527],{},"Singleton, Factory, Builder, Adapter, Decorator, Facade, Strategy, Observer, Repository, Unit of Work",". Hiểu các pattern này = đủ qua PV mid.",[427,530,532],{"id":531},"a-creational-patterns","A — Creational Patterns",[534,535,537],"h3",{"id":536},"_1-singleton","1. Singleton",[539,540,541],"blockquote",{},[432,542,543,544,547],{},"Đảm bảo class chỉ có ",[436,545,546],{},"1 instance"," suốt app lifetime.",[549,550,551,634],"tabs",{},[552,553,556],"div",{"icon":554,"label":555},"i-simple-icons-csharp","C# (thread-safe, lazy)",[557,558,563],"pre",{"className":559,"code":560,"language":561,"meta":562,"style":562},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","public sealed class Logger {\n    private static readonly Lazy\u003CLogger> _instance = new(() => new Logger());\n    public static Logger Instance => _instance.Value;\n    \n    private Logger() {}\n    \n    public void Log(string msg) => Console.WriteLine($\"[LOG] {msg}\");\n}\n\n\u002F\u002F Dùng\nLogger.Instance.Log(\"Hello\");\n","csharp","",[564,565,566,574,580,586,592,598,603,609,615,622,628],"code",{"__ignoreMap":562},[567,568,571],"span",{"class":569,"line":570},"line",1,[567,572,573],{},"public sealed class Logger {\n",[567,575,577],{"class":569,"line":576},2,[567,578,579],{},"    private static readonly Lazy\u003CLogger> _instance = new(() => new Logger());\n",[567,581,583],{"class":569,"line":582},3,[567,584,585],{},"    public static Logger Instance => _instance.Value;\n",[567,587,589],{"class":569,"line":588},4,[567,590,591],{},"    \n",[567,593,595],{"class":569,"line":594},5,[567,596,597],{},"    private Logger() {}\n",[567,599,601],{"class":569,"line":600},6,[567,602,591],{},[567,604,606],{"class":569,"line":605},7,[567,607,608],{},"    public void Log(string msg) => Console.WriteLine($\"[LOG] {msg}\");\n",[567,610,612],{"class":569,"line":611},8,[567,613,614],{},"}\n",[567,616,618],{"class":569,"line":617},9,[567,619,621],{"emptyLinePlaceholder":620},true,"\n",[567,623,625],{"class":569,"line":624},10,[567,626,627],{},"\u002F\u002F Dùng\n",[567,629,631],{"class":569,"line":630},11,[567,632,633],{},"Logger.Instance.Log(\"Hello\");\n",[552,635,636],{"icon":289,"label":288},[557,637,641],{"className":638,"code":639,"language":640,"meta":562,"style":562},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","class Logger {\n    private static _instance: Logger | null = null;\n    \n    private constructor() {}\n    \n    static get instance(): Logger {\n        return Logger._instance ??= new Logger();\n    }\n    \n    log(msg: string) { console.log(`[LOG] ${msg}`); }\n}\n\nLogger.instance.log(\"Hello\");\n","typescript",[564,642,643,657,686,691,704,708,726,753,758,762,818,822,827],{"__ignoreMap":562},[567,644,645,649,653],{"class":569,"line":570},[567,646,648],{"class":647},"spNyl","class",[567,650,652],{"class":651},"sBMFI"," Logger",[567,654,656],{"class":655},"sMK4o"," {\n",[567,658,659,662,665,669,672,674,677,680,683],{"class":569,"line":576},[567,660,661],{"class":647},"    private",[567,663,664],{"class":647}," static",[567,666,668],{"class":667},"swJcz"," _instance",[567,670,671],{"class":655},":",[567,673,652],{"class":651},[567,675,676],{"class":655}," |",[567,678,679],{"class":651}," null",[567,681,682],{"class":655}," =",[567,684,685],{"class":655}," null;\n",[567,687,688],{"class":569,"line":582},[567,689,591],{"class":690},"sTEyZ",[567,692,693,695,698,701],{"class":569,"line":588},[567,694,661],{"class":647},[567,696,697],{"class":647}," constructor",[567,699,700],{"class":655},"()",[567,702,703],{"class":655}," {}\n",[567,705,706],{"class":569,"line":594},[567,707,591],{"class":690},[567,709,710,713,716,719,722,724],{"class":569,"line":600},[567,711,712],{"class":647},"    static",[567,714,715],{"class":647}," get",[567,717,718],{"class":667}," instance",[567,720,721],{"class":655},"():",[567,723,652],{"class":651},[567,725,656],{"class":655},[567,727,728,732,734,736,739,742,745,748,750],{"class":569,"line":605},[567,729,731],{"class":730},"s7zQu","        return",[567,733,652],{"class":690},[567,735,447],{"class":655},[567,737,738],{"class":690},"_instance",[567,740,741],{"class":655}," ??=",[567,743,744],{"class":655}," new",[567,746,652],{"class":747},"s2Zo4",[567,749,700],{"class":667},[567,751,752],{"class":655},";\n",[567,754,755],{"class":569,"line":611},[567,756,757],{"class":655},"    }\n",[567,759,760],{"class":569,"line":617},[567,761,591],{"class":690},[567,763,764,767,770,774,776,779,782,785,788,790,793,795,798,802,805,807,810,812,815],{"class":569,"line":624},[567,765,766],{"class":667},"    log",[567,768,769],{"class":655},"(",[567,771,773],{"class":772},"sHdIc","msg",[567,775,671],{"class":655},[567,777,778],{"class":651}," string",[567,780,781],{"class":655},")",[567,783,784],{"class":655}," {",[567,786,787],{"class":690}," console",[567,789,447],{"class":655},[567,791,792],{"class":747},"log",[567,794,769],{"class":667},[567,796,797],{"class":655},"`",[567,799,801],{"class":800},"sfazB","[LOG] ",[567,803,804],{"class":655},"${",[567,806,773],{"class":690},[567,808,809],{"class":655},"}`",[567,811,781],{"class":667},[567,813,814],{"class":655},";",[567,816,817],{"class":655}," }\n",[567,819,820],{"class":569,"line":630},[567,821,614],{"class":655},[567,823,825],{"class":569,"line":824},12,[567,826,621],{"emptyLinePlaceholder":620},[567,828,830,833,835,838,840,842,844,847,850,852,854],{"class":569,"line":829},13,[567,831,832],{"class":690},"Logger",[567,834,447],{"class":655},[567,836,837],{"class":690},"instance",[567,839,447],{"class":655},[567,841,792],{"class":747},[567,843,769],{"class":690},[567,845,846],{"class":655},"\"",[567,848,849],{"class":800},"Hello",[567,851,846],{"class":655},[567,853,781],{"class":690},[567,855,752],{"class":655},[520,857,860,865],{"color":858,"icon":859},"primary","i-lucide-info",[432,861,862],{},[436,863,864],{},"Khi nào dùng Singleton?",[866,867,868,872],"ul",{},[869,870,871],"li",{},"Logger, config, in-memory cache.",[869,873,874,875,878,879,447],{},"Thực tế: trong app dùng ",[436,876,877],{},"DI container"," thay vì viết Singleton thủ công — ",[564,880,881],{},"services.AddSingleton\u003CILogger, Logger>()",[432,883,884,887],{},[436,885,886],{},"Phê phán Singleton:"," global state → khó test, dễ hidden coupling. Dùng DI container scoped lifetime đúng hơn.",[534,889,891],{"id":890},"_2-factory-method","2. Factory Method",[539,893,894],{},[432,895,896],{},"Định nghĩa interface tạo object, cho subclass quyết concrete class.",[557,898,900],{"className":559,"code":899,"language":561,"meta":562,"style":562},"public interface IShape { void Draw(); }\npublic class Circle : IShape { public void Draw() {} }\npublic class Square : IShape { public void Draw() {} }\n\npublic class ShapeFactory {\n    public IShape Create(string type) => type switch {\n        \"circle\" => new Circle(),\n        \"square\" => new Square(),\n        _ => throw new ArgumentException($\"Unknown: {type}\")\n    };\n}\n\n\u002F\u002F Dùng\nvar shape = factory.Create(\"circle\");\nshape.Draw();\n",[564,901,902,907,912,917,921,926,931,936,941,946,951,955,959,963,969],{"__ignoreMap":562},[567,903,904],{"class":569,"line":570},[567,905,906],{},"public interface IShape { void Draw(); }\n",[567,908,909],{"class":569,"line":576},[567,910,911],{},"public class Circle : IShape { public void Draw() {} }\n",[567,913,914],{"class":569,"line":582},[567,915,916],{},"public class Square : IShape { public void Draw() {} }\n",[567,918,919],{"class":569,"line":588},[567,920,621],{"emptyLinePlaceholder":620},[567,922,923],{"class":569,"line":594},[567,924,925],{},"public class ShapeFactory {\n",[567,927,928],{"class":569,"line":600},[567,929,930],{},"    public IShape Create(string type) => type switch {\n",[567,932,933],{"class":569,"line":605},[567,934,935],{},"        \"circle\" => new Circle(),\n",[567,937,938],{"class":569,"line":611},[567,939,940],{},"        \"square\" => new Square(),\n",[567,942,943],{"class":569,"line":617},[567,944,945],{},"        _ => throw new ArgumentException($\"Unknown: {type}\")\n",[567,947,948],{"class":569,"line":624},[567,949,950],{},"    };\n",[567,952,953],{"class":569,"line":630},[567,954,614],{},[567,956,957],{"class":569,"line":824},[567,958,621],{"emptyLinePlaceholder":620},[567,960,961],{"class":569,"line":829},[567,962,627],{},[567,964,966],{"class":569,"line":965},14,[567,967,968],{},"var shape = factory.Create(\"circle\");\n",[567,970,972],{"class":569,"line":971},15,[567,973,974],{},"shape.Draw();\n",[432,976,977,978,981],{},"Caller không cần ",[564,979,980],{},"new Circle()"," — biết type là đủ.",[534,983,985],{"id":984},"_3-builder","3. Builder",[539,987,988],{},[432,989,990],{},"Tách quá trình build phức tạp khỏi representation. Fluent API.",[557,992,994],{"className":559,"code":993,"language":561,"meta":562,"style":562},"public class Pizza {\n    public string Base { get; set; }\n    public List\u003Cstring> Toppings { get; set; } = new();\n    public string Sauce { get; set; }\n}\n\npublic class PizzaBuilder {\n    private readonly Pizza _pizza = new();\n    \n    public PizzaBuilder WithBase(string b) { _pizza.Base = b; return this; }\n    public PizzaBuilder AddTopping(string t) { _pizza.Toppings.Add(t); return this; }\n    public PizzaBuilder WithSauce(string s) { _pizza.Sauce = s; return this; }\n    public Pizza Build() => _pizza;\n}\n\n\u002F\u002F Dùng — fluent chain\nvar p = new PizzaBuilder()\n    .WithBase(\"Thin crust\")\n    .AddTopping(\"Cheese\")\n    .AddTopping(\"Mushroom\")\n    .WithSauce(\"Tomato\")\n    .Build();\n",[564,995,996,1001,1006,1011,1016,1020,1024,1029,1034,1038,1043,1048,1053,1058,1062,1066,1072,1078,1084,1090,1096,1102],{"__ignoreMap":562},[567,997,998],{"class":569,"line":570},[567,999,1000],{},"public class Pizza {\n",[567,1002,1003],{"class":569,"line":576},[567,1004,1005],{},"    public string Base { get; set; }\n",[567,1007,1008],{"class":569,"line":582},[567,1009,1010],{},"    public List\u003Cstring> Toppings { get; set; } = new();\n",[567,1012,1013],{"class":569,"line":588},[567,1014,1015],{},"    public string Sauce { get; set; }\n",[567,1017,1018],{"class":569,"line":594},[567,1019,614],{},[567,1021,1022],{"class":569,"line":600},[567,1023,621],{"emptyLinePlaceholder":620},[567,1025,1026],{"class":569,"line":605},[567,1027,1028],{},"public class PizzaBuilder {\n",[567,1030,1031],{"class":569,"line":611},[567,1032,1033],{},"    private readonly Pizza _pizza = new();\n",[567,1035,1036],{"class":569,"line":617},[567,1037,591],{},[567,1039,1040],{"class":569,"line":624},[567,1041,1042],{},"    public PizzaBuilder WithBase(string b) { _pizza.Base = b; return this; }\n",[567,1044,1045],{"class":569,"line":630},[567,1046,1047],{},"    public PizzaBuilder AddTopping(string t) { _pizza.Toppings.Add(t); return this; }\n",[567,1049,1050],{"class":569,"line":824},[567,1051,1052],{},"    public PizzaBuilder WithSauce(string s) { _pizza.Sauce = s; return this; }\n",[567,1054,1055],{"class":569,"line":829},[567,1056,1057],{},"    public Pizza Build() => _pizza;\n",[567,1059,1060],{"class":569,"line":965},[567,1061,614],{},[567,1063,1064],{"class":569,"line":971},[567,1065,621],{"emptyLinePlaceholder":620},[567,1067,1069],{"class":569,"line":1068},16,[567,1070,1071],{},"\u002F\u002F Dùng — fluent chain\n",[567,1073,1075],{"class":569,"line":1074},17,[567,1076,1077],{},"var p = new PizzaBuilder()\n",[567,1079,1081],{"class":569,"line":1080},18,[567,1082,1083],{},"    .WithBase(\"Thin crust\")\n",[567,1085,1087],{"class":569,"line":1086},19,[567,1088,1089],{},"    .AddTopping(\"Cheese\")\n",[567,1091,1093],{"class":569,"line":1092},20,[567,1094,1095],{},"    .AddTopping(\"Mushroom\")\n",[567,1097,1099],{"class":569,"line":1098},21,[567,1100,1101],{},"    .WithSauce(\"Tomato\")\n",[567,1103,1105],{"class":569,"line":1104},22,[567,1106,1107],{},"    .Build();\n",[520,1109,1110,1121],{"color":858,"icon":523},[432,1111,1112,1113,1116,1117],{},"⭐ ",[436,1114,1115],{},"Câu PV:"," ",[1118,1119,1120],"em",{},"\"Builder khác Constructor với nhiều parameter chỗ nào?\"",[539,1122,1123],{},[432,1124,1125,1126,1129],{},"\"Constructor 10 parameter (",[564,1127,1128],{},"new Pizza(crust, topping1, topping2, sauce, ...)",") khó đọc, dễ nhầm order. Builder cho chain method với tên rõ nghĩa, optional dễ skip, partial state OK lúc build.\"",[427,1131,1133],{"id":1132},"b-structural-patterns","B — Structural Patterns",[534,1135,1137],{"id":1136},"_4-adapter","4. Adapter",[539,1139,1140],{},[432,1141,1142],{},"Adapter làm \"phiên dịch\" giữa 2 interface không tương thích.",[557,1144,1146],{"className":559,"code":1145,"language":561,"meta":562,"style":562},"\u002F\u002F Legacy class — không thể sửa\npublic class OldPaymentGateway {\n    public void DoPayment(string cardNo, double amt) { \u002F* ... *\u002F }\n}\n\n\u002F\u002F Interface mới ứng dụng dùng\npublic interface IPaymentService {\n    Task ChargeAsync(string cardNo, decimal amount);\n}\n\n\u002F\u002F Adapter\npublic class OldGatewayAdapter : IPaymentService {\n    private readonly OldPaymentGateway _old;\n    public OldGatewayAdapter(OldPaymentGateway old) => _old = old;\n    \n    public Task ChargeAsync(string cardNo, decimal amount) {\n        _old.DoPayment(cardNo, (double)amount);\n        return Task.CompletedTask;\n    }\n}\n",[564,1147,1148,1153,1158,1163,1167,1171,1176,1181,1186,1190,1194,1199,1204,1209,1214,1218,1223,1228,1233,1237],{"__ignoreMap":562},[567,1149,1150],{"class":569,"line":570},[567,1151,1152],{},"\u002F\u002F Legacy class — không thể sửa\n",[567,1154,1155],{"class":569,"line":576},[567,1156,1157],{},"public class OldPaymentGateway {\n",[567,1159,1160],{"class":569,"line":582},[567,1161,1162],{},"    public void DoPayment(string cardNo, double amt) { \u002F* ... *\u002F }\n",[567,1164,1165],{"class":569,"line":588},[567,1166,614],{},[567,1168,1169],{"class":569,"line":594},[567,1170,621],{"emptyLinePlaceholder":620},[567,1172,1173],{"class":569,"line":600},[567,1174,1175],{},"\u002F\u002F Interface mới ứng dụng dùng\n",[567,1177,1178],{"class":569,"line":605},[567,1179,1180],{},"public interface IPaymentService {\n",[567,1182,1183],{"class":569,"line":611},[567,1184,1185],{},"    Task ChargeAsync(string cardNo, decimal amount);\n",[567,1187,1188],{"class":569,"line":617},[567,1189,614],{},[567,1191,1192],{"class":569,"line":624},[567,1193,621],{"emptyLinePlaceholder":620},[567,1195,1196],{"class":569,"line":630},[567,1197,1198],{},"\u002F\u002F Adapter\n",[567,1200,1201],{"class":569,"line":824},[567,1202,1203],{},"public class OldGatewayAdapter : IPaymentService {\n",[567,1205,1206],{"class":569,"line":829},[567,1207,1208],{},"    private readonly OldPaymentGateway _old;\n",[567,1210,1211],{"class":569,"line":965},[567,1212,1213],{},"    public OldGatewayAdapter(OldPaymentGateway old) => _old = old;\n",[567,1215,1216],{"class":569,"line":971},[567,1217,591],{},[567,1219,1220],{"class":569,"line":1068},[567,1221,1222],{},"    public Task ChargeAsync(string cardNo, decimal amount) {\n",[567,1224,1225],{"class":569,"line":1074},[567,1226,1227],{},"        _old.DoPayment(cardNo, (double)amount);\n",[567,1229,1230],{"class":569,"line":1080},[567,1231,1232],{},"        return Task.CompletedTask;\n",[567,1234,1235],{"class":569,"line":1086},[567,1236,757],{},[567,1238,1239],{"class":569,"line":1092},[567,1240,614],{},[534,1242,1244],{"id":1243},"_5-decorator","5. Decorator",[539,1246,1247],{},[432,1248,1249,1250,1253],{},"Thêm chức năng cho object ",[436,1251,1252],{},"không sửa class gốc",", bằng cách wrap.",[557,1255,1257],{"className":559,"code":1256,"language":561,"meta":562,"style":562},"public interface ICoffee {\n    string Description();\n    decimal Cost();\n}\n\npublic class SimpleCoffee : ICoffee {\n    public string Description() => \"Coffee\";\n    public decimal Cost() => 30000;\n}\n\npublic abstract class CoffeeDecorator : ICoffee {\n    protected ICoffee _coffee;\n    protected CoffeeDecorator(ICoffee coffee) => _coffee = coffee;\n    public virtual string Description() => _coffee.Description();\n    public virtual decimal Cost() => _coffee.Cost();\n}\n\npublic class MilkDecorator : CoffeeDecorator {\n    public MilkDecorator(ICoffee c) : base(c) {}\n    public override string Description() => $\"{_coffee.Description()}, Milk\";\n    public override decimal Cost() => _coffee.Cost() + 8000;\n}\n\npublic class CaramelDecorator : CoffeeDecorator {\n    public CaramelDecorator(ICoffee c) : base(c) {}\n    public override string Description() => $\"{_coffee.Description()}, Caramel\";\n    public override decimal Cost() => _coffee.Cost() + 10000;\n}\n\n\u002F\u002F Wrap chain\nICoffee coffee = new CaramelDecorator(new MilkDecorator(new SimpleCoffee()));\nConsole.WriteLine($\"{coffee.Description()} = {coffee.Cost()} VND\");\n\u002F\u002F Coffee, Milk, Caramel = 48000 VND\n",[564,1258,1259,1264,1269,1274,1278,1282,1287,1292,1297,1301,1305,1310,1315,1320,1325,1330,1334,1338,1343,1348,1353,1358,1362,1367,1373,1379,1385,1391,1396,1401,1407,1413,1419],{"__ignoreMap":562},[567,1260,1261],{"class":569,"line":570},[567,1262,1263],{},"public interface ICoffee {\n",[567,1265,1266],{"class":569,"line":576},[567,1267,1268],{},"    string Description();\n",[567,1270,1271],{"class":569,"line":582},[567,1272,1273],{},"    decimal Cost();\n",[567,1275,1276],{"class":569,"line":588},[567,1277,614],{},[567,1279,1280],{"class":569,"line":594},[567,1281,621],{"emptyLinePlaceholder":620},[567,1283,1284],{"class":569,"line":600},[567,1285,1286],{},"public class SimpleCoffee : ICoffee {\n",[567,1288,1289],{"class":569,"line":605},[567,1290,1291],{},"    public string Description() => \"Coffee\";\n",[567,1293,1294],{"class":569,"line":611},[567,1295,1296],{},"    public decimal Cost() => 30000;\n",[567,1298,1299],{"class":569,"line":617},[567,1300,614],{},[567,1302,1303],{"class":569,"line":624},[567,1304,621],{"emptyLinePlaceholder":620},[567,1306,1307],{"class":569,"line":630},[567,1308,1309],{},"public abstract class CoffeeDecorator : ICoffee {\n",[567,1311,1312],{"class":569,"line":824},[567,1313,1314],{},"    protected ICoffee _coffee;\n",[567,1316,1317],{"class":569,"line":829},[567,1318,1319],{},"    protected CoffeeDecorator(ICoffee coffee) => _coffee = coffee;\n",[567,1321,1322],{"class":569,"line":965},[567,1323,1324],{},"    public virtual string Description() => _coffee.Description();\n",[567,1326,1327],{"class":569,"line":971},[567,1328,1329],{},"    public virtual decimal Cost() => _coffee.Cost();\n",[567,1331,1332],{"class":569,"line":1068},[567,1333,614],{},[567,1335,1336],{"class":569,"line":1074},[567,1337,621],{"emptyLinePlaceholder":620},[567,1339,1340],{"class":569,"line":1080},[567,1341,1342],{},"public class MilkDecorator : CoffeeDecorator {\n",[567,1344,1345],{"class":569,"line":1086},[567,1346,1347],{},"    public MilkDecorator(ICoffee c) : base(c) {}\n",[567,1349,1350],{"class":569,"line":1092},[567,1351,1352],{},"    public override string Description() => $\"{_coffee.Description()}, Milk\";\n",[567,1354,1355],{"class":569,"line":1098},[567,1356,1357],{},"    public override decimal Cost() => _coffee.Cost() + 8000;\n",[567,1359,1360],{"class":569,"line":1104},[567,1361,614],{},[567,1363,1365],{"class":569,"line":1364},23,[567,1366,621],{"emptyLinePlaceholder":620},[567,1368,1370],{"class":569,"line":1369},24,[567,1371,1372],{},"public class CaramelDecorator : CoffeeDecorator {\n",[567,1374,1376],{"class":569,"line":1375},25,[567,1377,1378],{},"    public CaramelDecorator(ICoffee c) : base(c) {}\n",[567,1380,1382],{"class":569,"line":1381},26,[567,1383,1384],{},"    public override string Description() => $\"{_coffee.Description()}, Caramel\";\n",[567,1386,1388],{"class":569,"line":1387},27,[567,1389,1390],{},"    public override decimal Cost() => _coffee.Cost() + 10000;\n",[567,1392,1394],{"class":569,"line":1393},28,[567,1395,614],{},[567,1397,1399],{"class":569,"line":1398},29,[567,1400,621],{"emptyLinePlaceholder":620},[567,1402,1404],{"class":569,"line":1403},30,[567,1405,1406],{},"\u002F\u002F Wrap chain\n",[567,1408,1410],{"class":569,"line":1409},31,[567,1411,1412],{},"ICoffee coffee = new CaramelDecorator(new MilkDecorator(new SimpleCoffee()));\n",[567,1414,1416],{"class":569,"line":1415},32,[567,1417,1418],{},"Console.WriteLine($\"{coffee.Description()} = {coffee.Cost()} VND\");\n",[567,1420,1422],{"class":569,"line":1421},33,[567,1423,1424],{},"\u002F\u002F Coffee, Milk, Caramel = 48000 VND\n",[520,1426,1427,1430,1431,1434,1435,1434,1438,1434,1441,1444],{"color":858,"icon":859},[436,1428,1429],{},"Ví dụ kinh điển trong .NET:"," Stream chain — ",[564,1432,1433],{},"FileStream"," → ",[564,1436,1437],{},"BufferedStream",[564,1439,1440],{},"CryptoStream",[564,1442,1443],{},"GZipStream",". Mỗi cấp add chức năng mà không sửa.",[534,1446,1448],{"id":1447},"_6-facade","6. Facade",[539,1450,1451],{},[432,1452,1453],{},"Cung cấp interface đơn giản cho subsystem phức tạp.",[557,1455,1457],{"className":559,"code":1456,"language":561,"meta":562,"style":562},"\u002F\u002F Subsystem phức tạp\npublic class CPU { public void Start() {} public void Execute() {} }\npublic class Memory { public void Load(int addr) {} }\npublic class HardDrive { public byte[] Read(int sector, int size) => new byte[0]; }\n\n\u002F\u002F Facade\npublic class Computer {\n    private readonly CPU _cpu = new();\n    private readonly Memory _mem = new();\n    private readonly HardDrive _hd = new();\n    \n    public void Start() {\n        _cpu.Start();\n        _mem.Load(0);\n        _hd.Read(0, 512);\n        _cpu.Execute();\n    }\n}\n\n\u002F\u002F Client chỉ cần\nnew Computer().Start();\n",[564,1458,1459,1464,1469,1474,1479,1483,1488,1493,1498,1503,1508,1512,1517,1522,1527,1532,1537,1541,1545,1549,1554],{"__ignoreMap":562},[567,1460,1461],{"class":569,"line":570},[567,1462,1463],{},"\u002F\u002F Subsystem phức tạp\n",[567,1465,1466],{"class":569,"line":576},[567,1467,1468],{},"public class CPU { public void Start() {} public void Execute() {} }\n",[567,1470,1471],{"class":569,"line":582},[567,1472,1473],{},"public class Memory { public void Load(int addr) {} }\n",[567,1475,1476],{"class":569,"line":588},[567,1477,1478],{},"public class HardDrive { public byte[] Read(int sector, int size) => new byte[0]; }\n",[567,1480,1481],{"class":569,"line":594},[567,1482,621],{"emptyLinePlaceholder":620},[567,1484,1485],{"class":569,"line":600},[567,1486,1487],{},"\u002F\u002F Facade\n",[567,1489,1490],{"class":569,"line":605},[567,1491,1492],{},"public class Computer {\n",[567,1494,1495],{"class":569,"line":611},[567,1496,1497],{},"    private readonly CPU _cpu = new();\n",[567,1499,1500],{"class":569,"line":617},[567,1501,1502],{},"    private readonly Memory _mem = new();\n",[567,1504,1505],{"class":569,"line":624},[567,1506,1507],{},"    private readonly HardDrive _hd = new();\n",[567,1509,1510],{"class":569,"line":630},[567,1511,591],{},[567,1513,1514],{"class":569,"line":824},[567,1515,1516],{},"    public void Start() {\n",[567,1518,1519],{"class":569,"line":829},[567,1520,1521],{},"        _cpu.Start();\n",[567,1523,1524],{"class":569,"line":965},[567,1525,1526],{},"        _mem.Load(0);\n",[567,1528,1529],{"class":569,"line":971},[567,1530,1531],{},"        _hd.Read(0, 512);\n",[567,1533,1534],{"class":569,"line":1068},[567,1535,1536],{},"        _cpu.Execute();\n",[567,1538,1539],{"class":569,"line":1074},[567,1540,757],{},[567,1542,1543],{"class":569,"line":1080},[567,1544,614],{},[567,1546,1547],{"class":569,"line":1086},[567,1548,621],{"emptyLinePlaceholder":620},[567,1550,1551],{"class":569,"line":1092},[567,1552,1553],{},"\u002F\u002F Client chỉ cần\n",[567,1555,1556],{"class":569,"line":1098},[567,1557,1558],{},"new Computer().Start();\n",[520,1560,1112,1561,1571],{"color":522,"icon":523},[436,1562,1563,1564,1567,1568],{},"ASP.NET ",[564,1565,1566],{},"MapControllers()",", ",[564,1569,1570],{},"services.AddDbContext()"," đều là Facade — wrap setup phức tạp thành 1 dòng.",[427,1573,1575],{"id":1574},"c-behavioral-patterns","C — Behavioral Patterns",[534,1577,1579],{"id":1578},"_7-strategy","7. Strategy",[539,1581,1582],{},[432,1583,1584,1585,1588],{},"Định nghĩa ",[436,1586,1587],{},"họ thuật toán",", mỗi cái 1 class, swap được lúc runtime.",[557,1590,1592],{"className":559,"code":1591,"language":561,"meta":562,"style":562},"public interface IDiscountStrategy {\n    decimal Apply(decimal total);\n}\n\npublic class NoDiscount : IDiscountStrategy {\n    public decimal Apply(decimal total) => total;\n}\n\npublic class TenPercent : IDiscountStrategy {\n    public decimal Apply(decimal total) => total * 0.9m;\n}\n\npublic class StudentDiscount : IDiscountStrategy {\n    public decimal Apply(decimal total) => total > 100000 ? total * 0.7m : total * 0.85m;\n}\n\npublic class Cart {\n    private readonly IDiscountStrategy _strategy;\n    public Cart(IDiscountStrategy strategy) => _strategy = strategy;\n    public decimal Checkout(decimal total) => _strategy.Apply(total);\n}\n\n\u002F\u002F Đổi strategy lúc runtime\nvar cart = new Cart(new StudentDiscount());\n",[564,1593,1594,1599,1604,1608,1612,1617,1622,1626,1630,1635,1640,1644,1648,1653,1658,1662,1666,1671,1676,1681,1686,1690,1694,1699],{"__ignoreMap":562},[567,1595,1596],{"class":569,"line":570},[567,1597,1598],{},"public interface IDiscountStrategy {\n",[567,1600,1601],{"class":569,"line":576},[567,1602,1603],{},"    decimal Apply(decimal total);\n",[567,1605,1606],{"class":569,"line":582},[567,1607,614],{},[567,1609,1610],{"class":569,"line":588},[567,1611,621],{"emptyLinePlaceholder":620},[567,1613,1614],{"class":569,"line":594},[567,1615,1616],{},"public class NoDiscount : IDiscountStrategy {\n",[567,1618,1619],{"class":569,"line":600},[567,1620,1621],{},"    public decimal Apply(decimal total) => total;\n",[567,1623,1624],{"class":569,"line":605},[567,1625,614],{},[567,1627,1628],{"class":569,"line":611},[567,1629,621],{"emptyLinePlaceholder":620},[567,1631,1632],{"class":569,"line":617},[567,1633,1634],{},"public class TenPercent : IDiscountStrategy {\n",[567,1636,1637],{"class":569,"line":624},[567,1638,1639],{},"    public decimal Apply(decimal total) => total * 0.9m;\n",[567,1641,1642],{"class":569,"line":630},[567,1643,614],{},[567,1645,1646],{"class":569,"line":824},[567,1647,621],{"emptyLinePlaceholder":620},[567,1649,1650],{"class":569,"line":829},[567,1651,1652],{},"public class StudentDiscount : IDiscountStrategy {\n",[567,1654,1655],{"class":569,"line":965},[567,1656,1657],{},"    public decimal Apply(decimal total) => total > 100000 ? total * 0.7m : total * 0.85m;\n",[567,1659,1660],{"class":569,"line":971},[567,1661,614],{},[567,1663,1664],{"class":569,"line":1068},[567,1665,621],{"emptyLinePlaceholder":620},[567,1667,1668],{"class":569,"line":1074},[567,1669,1670],{},"public class Cart {\n",[567,1672,1673],{"class":569,"line":1080},[567,1674,1675],{},"    private readonly IDiscountStrategy _strategy;\n",[567,1677,1678],{"class":569,"line":1086},[567,1679,1680],{},"    public Cart(IDiscountStrategy strategy) => _strategy = strategy;\n",[567,1682,1683],{"class":569,"line":1092},[567,1684,1685],{},"    public decimal Checkout(decimal total) => _strategy.Apply(total);\n",[567,1687,1688],{"class":569,"line":1098},[567,1689,614],{},[567,1691,1692],{"class":569,"line":1104},[567,1693,621],{"emptyLinePlaceholder":620},[567,1695,1696],{"class":569,"line":1364},[567,1697,1698],{},"\u002F\u002F Đổi strategy lúc runtime\n",[567,1700,1701],{"class":569,"line":1369},[567,1702,1703],{},"var cart = new Cart(new StudentDiscount());\n",[520,1705,1706,1707,1710],{"color":858,"icon":268},"Strategy + DI = pattern rất phổ biến. Inject ",[564,1708,1709],{},"IDiscountStrategy"," qua constructor, container quyết concrete.",[534,1712,1714],{"id":1713},"_8-observer","8. Observer",[539,1716,1717],{},[432,1718,1584,1719,1722],{},[436,1720,1721],{},"1-n"," dependency — khi 1 object đổi state, các observer được thông báo.",[557,1724,1726],{"className":559,"code":1725,"language":561,"meta":562,"style":562},"\u002F\u002F .NET có built-in event system — chính là Observer\npublic class StockMarket {\n    public event EventHandler\u003CStockPriceChangedEvent>? PriceChanged;\n    \n    public void UpdatePrice(string symbol, decimal newPrice) {\n        PriceChanged?.Invoke(this, new StockPriceChangedEvent(symbol, newPrice));\n    }\n}\n\npublic class StockPriceChangedEvent : EventArgs {\n    public string Symbol { get; }\n    public decimal NewPrice { get; }\n    public StockPriceChangedEvent(string s, decimal p) { Symbol = s; NewPrice = p; }\n}\n\n\u002F\u002F Subscribe\nvar market = new StockMarket();\nmarket.PriceChanged += (sender, e) => Console.WriteLine($\"{e.Symbol}: {e.NewPrice}\");\nmarket.PriceChanged += (sender, e) => SaveToDatabase(e);\n\nmarket.UpdatePrice(\"AAPL\", 150.5m);   \u002F\u002F cả 2 observer chạy\n",[564,1727,1728,1733,1738,1743,1747,1752,1757,1761,1765,1769,1774,1779,1784,1789,1793,1797,1802,1807,1812,1817,1821],{"__ignoreMap":562},[567,1729,1730],{"class":569,"line":570},[567,1731,1732],{},"\u002F\u002F .NET có built-in event system — chính là Observer\n",[567,1734,1735],{"class":569,"line":576},[567,1736,1737],{},"public class StockMarket {\n",[567,1739,1740],{"class":569,"line":582},[567,1741,1742],{},"    public event EventHandler\u003CStockPriceChangedEvent>? PriceChanged;\n",[567,1744,1745],{"class":569,"line":588},[567,1746,591],{},[567,1748,1749],{"class":569,"line":594},[567,1750,1751],{},"    public void UpdatePrice(string symbol, decimal newPrice) {\n",[567,1753,1754],{"class":569,"line":600},[567,1755,1756],{},"        PriceChanged?.Invoke(this, new StockPriceChangedEvent(symbol, newPrice));\n",[567,1758,1759],{"class":569,"line":605},[567,1760,757],{},[567,1762,1763],{"class":569,"line":611},[567,1764,614],{},[567,1766,1767],{"class":569,"line":617},[567,1768,621],{"emptyLinePlaceholder":620},[567,1770,1771],{"class":569,"line":624},[567,1772,1773],{},"public class StockPriceChangedEvent : EventArgs {\n",[567,1775,1776],{"class":569,"line":630},[567,1777,1778],{},"    public string Symbol { get; }\n",[567,1780,1781],{"class":569,"line":824},[567,1782,1783],{},"    public decimal NewPrice { get; }\n",[567,1785,1786],{"class":569,"line":829},[567,1787,1788],{},"    public StockPriceChangedEvent(string s, decimal p) { Symbol = s; NewPrice = p; }\n",[567,1790,1791],{"class":569,"line":965},[567,1792,614],{},[567,1794,1795],{"class":569,"line":971},[567,1796,621],{"emptyLinePlaceholder":620},[567,1798,1799],{"class":569,"line":1068},[567,1800,1801],{},"\u002F\u002F Subscribe\n",[567,1803,1804],{"class":569,"line":1074},[567,1805,1806],{},"var market = new StockMarket();\n",[567,1808,1809],{"class":569,"line":1080},[567,1810,1811],{},"market.PriceChanged += (sender, e) => Console.WriteLine($\"{e.Symbol}: {e.NewPrice}\");\n",[567,1813,1814],{"class":569,"line":1086},[567,1815,1816],{},"market.PriceChanged += (sender, e) => SaveToDatabase(e);\n",[567,1818,1819],{"class":569,"line":1092},[567,1820,621],{"emptyLinePlaceholder":620},[567,1822,1823],{"class":569,"line":1098},[567,1824,1825],{},"market.UpdatePrice(\"AAPL\", 150.5m);   \u002F\u002F cả 2 observer chạy\n",[520,1827,1828],{"color":858,"icon":859},"Trong RxJS, Vue reactive, Angular signals — Observer là core. Khi học framework hiện đại, hiểu Observer là điểm cộng lớn.",[534,1830,1832],{"id":1831},"_9-command","9. Command",[539,1834,1835],{},[432,1836,1837],{},"Đóng gói request thành object → queue, log, undo được.",[557,1839,1841],{"className":559,"code":1840,"language":561,"meta":562,"style":562},"public interface ICommand {\n    void Execute();\n    void Undo();\n}\n\npublic class AddTextCommand : ICommand {\n    private readonly Document _doc;\n    private readonly string _text;\n    \n    public AddTextCommand(Document d, string text) { _doc = d; _text = text; }\n    \n    public void Execute() => _doc.Add(_text);\n    public void Undo() => _doc.RemoveLast(_text.Length);\n}\n\npublic class CommandInvoker {\n    private readonly Stack\u003CICommand> _history = new();\n    \n    public void Run(ICommand cmd) {\n        cmd.Execute();\n        _history.Push(cmd);\n    }\n    \n    public void Undo() {\n        if (_history.Count > 0) _history.Pop().Undo();\n    }\n}\n",[564,1842,1843,1848,1853,1858,1862,1866,1871,1876,1881,1885,1890,1894,1899,1904,1908,1912,1917,1922,1926,1931,1936,1941,1945,1949,1954,1959,1963],{"__ignoreMap":562},[567,1844,1845],{"class":569,"line":570},[567,1846,1847],{},"public interface ICommand {\n",[567,1849,1850],{"class":569,"line":576},[567,1851,1852],{},"    void Execute();\n",[567,1854,1855],{"class":569,"line":582},[567,1856,1857],{},"    void Undo();\n",[567,1859,1860],{"class":569,"line":588},[567,1861,614],{},[567,1863,1864],{"class":569,"line":594},[567,1865,621],{"emptyLinePlaceholder":620},[567,1867,1868],{"class":569,"line":600},[567,1869,1870],{},"public class AddTextCommand : ICommand {\n",[567,1872,1873],{"class":569,"line":605},[567,1874,1875],{},"    private readonly Document _doc;\n",[567,1877,1878],{"class":569,"line":611},[567,1879,1880],{},"    private readonly string _text;\n",[567,1882,1883],{"class":569,"line":617},[567,1884,591],{},[567,1886,1887],{"class":569,"line":624},[567,1888,1889],{},"    public AddTextCommand(Document d, string text) { _doc = d; _text = text; }\n",[567,1891,1892],{"class":569,"line":630},[567,1893,591],{},[567,1895,1896],{"class":569,"line":824},[567,1897,1898],{},"    public void Execute() => _doc.Add(_text);\n",[567,1900,1901],{"class":569,"line":829},[567,1902,1903],{},"    public void Undo() => _doc.RemoveLast(_text.Length);\n",[567,1905,1906],{"class":569,"line":965},[567,1907,614],{},[567,1909,1910],{"class":569,"line":971},[567,1911,621],{"emptyLinePlaceholder":620},[567,1913,1914],{"class":569,"line":1068},[567,1915,1916],{},"public class CommandInvoker {\n",[567,1918,1919],{"class":569,"line":1074},[567,1920,1921],{},"    private readonly Stack\u003CICommand> _history = new();\n",[567,1923,1924],{"class":569,"line":1080},[567,1925,591],{},[567,1927,1928],{"class":569,"line":1086},[567,1929,1930],{},"    public void Run(ICommand cmd) {\n",[567,1932,1933],{"class":569,"line":1092},[567,1934,1935],{},"        cmd.Execute();\n",[567,1937,1938],{"class":569,"line":1098},[567,1939,1940],{},"        _history.Push(cmd);\n",[567,1942,1943],{"class":569,"line":1104},[567,1944,757],{},[567,1946,1947],{"class":569,"line":1364},[567,1948,591],{},[567,1950,1951],{"class":569,"line":1369},[567,1952,1953],{},"    public void Undo() {\n",[567,1955,1956],{"class":569,"line":1375},[567,1957,1958],{},"        if (_history.Count > 0) _history.Pop().Undo();\n",[567,1960,1961],{"class":569,"line":1381},[567,1962,757],{},[567,1964,1965],{"class":569,"line":1387},[567,1966,614],{},[432,1968,1969],{},"Ứng dụng: editor undo\u002Fredo, transaction, job queue (MediatR).",[534,1971,1973],{"id":1972},"_10-template-method","10. Template Method",[539,1975,1976],{},[432,1977,1978,1979,1982],{},"Cha định nghĩa ",[436,1980,1981],{},"skeleton"," thuật toán, con override các bước cụ thể.",[557,1984,1986],{"className":559,"code":1985,"language":561,"meta":562,"style":562},"public abstract class DataExporter {\n    \u002F\u002F Template method — không override\n    public void Export(string file) {\n        var data = LoadData();\n        var transformed = Transform(data);\n        var formatted = Format(transformed);\n        WriteFile(file, formatted);\n    }\n    \n    protected abstract List\u003CRecord> LoadData();\n    protected abstract List\u003CRecord> Transform(List\u003CRecord> data);\n    protected abstract string Format(List\u003CRecord> data);\n    \n    \u002F\u002F Hook chung\n    private void WriteFile(string file, string content) {\n        File.WriteAllText(file, content);\n    }\n}\n\npublic class CsvExporter : DataExporter {\n    protected override List\u003CRecord> LoadData() { \u002F* ... *\u002F return new(); }\n    protected override List\u003CRecord> Transform(List\u003CRecord> d) => d;\n    protected override string Format(List\u003CRecord> d) => \n        string.Join(\"\\n\", d.Select(r => $\"{r.Id},{r.Name}\"));\n}\n\npublic class JsonExporter : DataExporter {\n    protected override List\u003CRecord> LoadData() { \u002F* ... *\u002F return new(); }\n    protected override List\u003CRecord> Transform(List\u003CRecord> d) => d;\n    protected override string Format(List\u003CRecord> d) => JsonSerializer.Serialize(d);\n}\n",[564,1987,1988,1993,1998,2003,2008,2013,2018,2023,2027,2031,2036,2041,2046,2050,2055,2060,2065,2069,2073,2077,2082,2087,2092,2097,2102,2106,2110,2115,2119,2123,2128],{"__ignoreMap":562},[567,1989,1990],{"class":569,"line":570},[567,1991,1992],{},"public abstract class DataExporter {\n",[567,1994,1995],{"class":569,"line":576},[567,1996,1997],{},"    \u002F\u002F Template method — không override\n",[567,1999,2000],{"class":569,"line":582},[567,2001,2002],{},"    public void Export(string file) {\n",[567,2004,2005],{"class":569,"line":588},[567,2006,2007],{},"        var data = LoadData();\n",[567,2009,2010],{"class":569,"line":594},[567,2011,2012],{},"        var transformed = Transform(data);\n",[567,2014,2015],{"class":569,"line":600},[567,2016,2017],{},"        var formatted = Format(transformed);\n",[567,2019,2020],{"class":569,"line":605},[567,2021,2022],{},"        WriteFile(file, formatted);\n",[567,2024,2025],{"class":569,"line":611},[567,2026,757],{},[567,2028,2029],{"class":569,"line":617},[567,2030,591],{},[567,2032,2033],{"class":569,"line":624},[567,2034,2035],{},"    protected abstract List\u003CRecord> LoadData();\n",[567,2037,2038],{"class":569,"line":630},[567,2039,2040],{},"    protected abstract List\u003CRecord> Transform(List\u003CRecord> data);\n",[567,2042,2043],{"class":569,"line":824},[567,2044,2045],{},"    protected abstract string Format(List\u003CRecord> data);\n",[567,2047,2048],{"class":569,"line":829},[567,2049,591],{},[567,2051,2052],{"class":569,"line":965},[567,2053,2054],{},"    \u002F\u002F Hook chung\n",[567,2056,2057],{"class":569,"line":971},[567,2058,2059],{},"    private void WriteFile(string file, string content) {\n",[567,2061,2062],{"class":569,"line":1068},[567,2063,2064],{},"        File.WriteAllText(file, content);\n",[567,2066,2067],{"class":569,"line":1074},[567,2068,757],{},[567,2070,2071],{"class":569,"line":1080},[567,2072,614],{},[567,2074,2075],{"class":569,"line":1086},[567,2076,621],{"emptyLinePlaceholder":620},[567,2078,2079],{"class":569,"line":1092},[567,2080,2081],{},"public class CsvExporter : DataExporter {\n",[567,2083,2084],{"class":569,"line":1098},[567,2085,2086],{},"    protected override List\u003CRecord> LoadData() { \u002F* ... *\u002F return new(); }\n",[567,2088,2089],{"class":569,"line":1104},[567,2090,2091],{},"    protected override List\u003CRecord> Transform(List\u003CRecord> d) => d;\n",[567,2093,2094],{"class":569,"line":1364},[567,2095,2096],{},"    protected override string Format(List\u003CRecord> d) => \n",[567,2098,2099],{"class":569,"line":1369},[567,2100,2101],{},"        string.Join(\"\\n\", d.Select(r => $\"{r.Id},{r.Name}\"));\n",[567,2103,2104],{"class":569,"line":1375},[567,2105,614],{},[567,2107,2108],{"class":569,"line":1381},[567,2109,621],{"emptyLinePlaceholder":620},[567,2111,2112],{"class":569,"line":1387},[567,2113,2114],{},"public class JsonExporter : DataExporter {\n",[567,2116,2117],{"class":569,"line":1393},[567,2118,2086],{},[567,2120,2121],{"class":569,"line":1398},[567,2122,2091],{},[567,2124,2125],{"class":569,"line":1403},[567,2126,2127],{},"    protected override string Format(List\u003CRecord> d) => JsonSerializer.Serialize(d);\n",[567,2129,2130],{"class":569,"line":1409},[567,2131,614],{},[534,2133,2135],{"id":2134},"_11-repository-ddd-flavoured","11. Repository (DDD-flavoured)",[539,2137,2138],{},[432,2139,2140],{},"Tách logic data access khỏi business logic.",[557,2142,2144],{"className":559,"code":2143,"language":561,"meta":562,"style":562},"public interface IUserRepository {\n    Task\u003CUser?> GetByIdAsync(int id);\n    Task\u003CList\u003CUser>> FindByCityAsync(string city);\n    Task AddAsync(User user);\n    Task UpdateAsync(User user);\n    Task DeleteAsync(int id);\n}\n\npublic class EfUserRepository : IUserRepository {\n    private readonly AppDbContext _ctx;\n    public EfUserRepository(AppDbContext ctx) => _ctx = ctx;\n    \n    public Task\u003CUser?> GetByIdAsync(int id) => _ctx.Users.FindAsync(id).AsTask();\n    public Task\u003CList\u003CUser>> FindByCityAsync(string city) =>\n        _ctx.Users.Where(u => u.City == city).ToListAsync();\n    public async Task AddAsync(User u) {\n        _ctx.Users.Add(u); await _ctx.SaveChangesAsync();\n    }\n    \u002F\u002F ...\n}\n\n\u002F\u002F Service không biết DB — chỉ thấy IUserRepository\npublic class UserService {\n    private readonly IUserRepository _repo;\n    public UserService(IUserRepository repo) => _repo = repo;\n}\n",[564,2145,2146,2151,2156,2161,2166,2171,2176,2180,2184,2189,2194,2199,2203,2208,2213,2218,2223,2228,2232,2237,2241,2245,2250,2255,2260,2265],{"__ignoreMap":562},[567,2147,2148],{"class":569,"line":570},[567,2149,2150],{},"public interface IUserRepository {\n",[567,2152,2153],{"class":569,"line":576},[567,2154,2155],{},"    Task\u003CUser?> GetByIdAsync(int id);\n",[567,2157,2158],{"class":569,"line":582},[567,2159,2160],{},"    Task\u003CList\u003CUser>> FindByCityAsync(string city);\n",[567,2162,2163],{"class":569,"line":588},[567,2164,2165],{},"    Task AddAsync(User user);\n",[567,2167,2168],{"class":569,"line":594},[567,2169,2170],{},"    Task UpdateAsync(User user);\n",[567,2172,2173],{"class":569,"line":600},[567,2174,2175],{},"    Task DeleteAsync(int id);\n",[567,2177,2178],{"class":569,"line":605},[567,2179,614],{},[567,2181,2182],{"class":569,"line":611},[567,2183,621],{"emptyLinePlaceholder":620},[567,2185,2186],{"class":569,"line":617},[567,2187,2188],{},"public class EfUserRepository : IUserRepository {\n",[567,2190,2191],{"class":569,"line":624},[567,2192,2193],{},"    private readonly AppDbContext _ctx;\n",[567,2195,2196],{"class":569,"line":630},[567,2197,2198],{},"    public EfUserRepository(AppDbContext ctx) => _ctx = ctx;\n",[567,2200,2201],{"class":569,"line":824},[567,2202,591],{},[567,2204,2205],{"class":569,"line":829},[567,2206,2207],{},"    public Task\u003CUser?> GetByIdAsync(int id) => _ctx.Users.FindAsync(id).AsTask();\n",[567,2209,2210],{"class":569,"line":965},[567,2211,2212],{},"    public Task\u003CList\u003CUser>> FindByCityAsync(string city) =>\n",[567,2214,2215],{"class":569,"line":971},[567,2216,2217],{},"        _ctx.Users.Where(u => u.City == city).ToListAsync();\n",[567,2219,2220],{"class":569,"line":1068},[567,2221,2222],{},"    public async Task AddAsync(User u) {\n",[567,2224,2225],{"class":569,"line":1074},[567,2226,2227],{},"        _ctx.Users.Add(u); await _ctx.SaveChangesAsync();\n",[567,2229,2230],{"class":569,"line":1080},[567,2231,757],{},[567,2233,2234],{"class":569,"line":1086},[567,2235,2236],{},"    \u002F\u002F ...\n",[567,2238,2239],{"class":569,"line":1092},[567,2240,614],{},[567,2242,2243],{"class":569,"line":1098},[567,2244,621],{"emptyLinePlaceholder":620},[567,2246,2247],{"class":569,"line":1104},[567,2248,2249],{},"\u002F\u002F Service không biết DB — chỉ thấy IUserRepository\n",[567,2251,2252],{"class":569,"line":1364},[567,2253,2254],{},"public class UserService {\n",[567,2256,2257],{"class":569,"line":1369},[567,2258,2259],{},"    private readonly IUserRepository _repo;\n",[567,2261,2262],{"class":569,"line":1375},[567,2263,2264],{},"    public UserService(IUserRepository repo) => _repo = repo;\n",[567,2266,2267],{"class":569,"line":1381},[567,2268,614],{},[520,2270,1112,2271,2274,2275,2278,2279,2282,2283,2286],{"color":522,"icon":523},[436,2272,2273],{},"Tranh cãi Repository với ORM hiện đại:"," EF Core, Prisma, Drizzle đã là Repository + Unit of Work sẵn → đôi khi wrap thêm Repository là ",[436,2276,2277],{},"lớp thừa",". Quy tắc thực dụng: nếu có ",[436,2280,2281],{},"logic query đặc thù tái sử dụng"," → tạo Repository; nếu chỉ wrap ",[564,2284,2285],{},"_ctx.Users.Where(...)"," → bỏ.",[534,2288,2290],{"id":2289},"_12-unit-of-work","12. Unit of Work",[539,2292,2293],{},[432,2294,2295,2296,2299],{},"Nhóm nhiều thao tác DB thành ",[436,2297,2298],{},"1 transaction",", track changes, commit atomic.",[557,2301,2303],{"className":559,"code":2302,"language":561,"meta":562,"style":562},"public interface IUnitOfWork : IDisposable {\n    IUserRepository Users { get; }\n    IOrderRepository Orders { get; }\n    Task\u003Cint> SaveChangesAsync();\n}\n\npublic class EfUnitOfWork : IUnitOfWork {\n    private readonly AppDbContext _ctx;\n    public EfUnitOfWork(AppDbContext ctx) {\n        _ctx = ctx;\n        Users = new EfUserRepository(ctx);\n        Orders = new EfOrderRepository(ctx);\n    }\n    \n    public IUserRepository Users { get; }\n    public IOrderRepository Orders { get; }\n    public Task\u003Cint> SaveChangesAsync() => _ctx.SaveChangesAsync();\n    public void Dispose() => _ctx.Dispose();\n}\n\n\u002F\u002F Service dùng — 2 thao tác, 1 transaction\npublic async Task TransferAsync(int fromId, int toId, decimal amount) {\n    var from = await _uow.Users.GetByIdAsync(fromId);\n    var to = await _uow.Users.GetByIdAsync(toId);\n    from.Balance -= amount;\n    to.Balance += amount;\n    await _uow.SaveChangesAsync();   \u002F\u002F 1 transaction\n}\n",[564,2304,2305,2310,2315,2320,2325,2329,2333,2338,2342,2347,2352,2357,2362,2366,2370,2375,2380,2385,2390,2394,2398,2403,2408,2413,2418,2423,2428,2433],{"__ignoreMap":562},[567,2306,2307],{"class":569,"line":570},[567,2308,2309],{},"public interface IUnitOfWork : IDisposable {\n",[567,2311,2312],{"class":569,"line":576},[567,2313,2314],{},"    IUserRepository Users { get; }\n",[567,2316,2317],{"class":569,"line":582},[567,2318,2319],{},"    IOrderRepository Orders { get; }\n",[567,2321,2322],{"class":569,"line":588},[567,2323,2324],{},"    Task\u003Cint> SaveChangesAsync();\n",[567,2326,2327],{"class":569,"line":594},[567,2328,614],{},[567,2330,2331],{"class":569,"line":600},[567,2332,621],{"emptyLinePlaceholder":620},[567,2334,2335],{"class":569,"line":605},[567,2336,2337],{},"public class EfUnitOfWork : IUnitOfWork {\n",[567,2339,2340],{"class":569,"line":611},[567,2341,2193],{},[567,2343,2344],{"class":569,"line":617},[567,2345,2346],{},"    public EfUnitOfWork(AppDbContext ctx) {\n",[567,2348,2349],{"class":569,"line":624},[567,2350,2351],{},"        _ctx = ctx;\n",[567,2353,2354],{"class":569,"line":630},[567,2355,2356],{},"        Users = new EfUserRepository(ctx);\n",[567,2358,2359],{"class":569,"line":824},[567,2360,2361],{},"        Orders = new EfOrderRepository(ctx);\n",[567,2363,2364],{"class":569,"line":829},[567,2365,757],{},[567,2367,2368],{"class":569,"line":965},[567,2369,591],{},[567,2371,2372],{"class":569,"line":971},[567,2373,2374],{},"    public IUserRepository Users { get; }\n",[567,2376,2377],{"class":569,"line":1068},[567,2378,2379],{},"    public IOrderRepository Orders { get; }\n",[567,2381,2382],{"class":569,"line":1074},[567,2383,2384],{},"    public Task\u003Cint> SaveChangesAsync() => _ctx.SaveChangesAsync();\n",[567,2386,2387],{"class":569,"line":1080},[567,2388,2389],{},"    public void Dispose() => _ctx.Dispose();\n",[567,2391,2392],{"class":569,"line":1086},[567,2393,614],{},[567,2395,2396],{"class":569,"line":1092},[567,2397,621],{"emptyLinePlaceholder":620},[567,2399,2400],{"class":569,"line":1098},[567,2401,2402],{},"\u002F\u002F Service dùng — 2 thao tác, 1 transaction\n",[567,2404,2405],{"class":569,"line":1104},[567,2406,2407],{},"public async Task TransferAsync(int fromId, int toId, decimal amount) {\n",[567,2409,2410],{"class":569,"line":1364},[567,2411,2412],{},"    var from = await _uow.Users.GetByIdAsync(fromId);\n",[567,2414,2415],{"class":569,"line":1369},[567,2416,2417],{},"    var to = await _uow.Users.GetByIdAsync(toId);\n",[567,2419,2420],{"class":569,"line":1375},[567,2421,2422],{},"    from.Balance -= amount;\n",[567,2424,2425],{"class":569,"line":1381},[567,2426,2427],{},"    to.Balance += amount;\n",[567,2429,2430],{"class":569,"line":1387},[567,2431,2432],{},"    await _uow.SaveChangesAsync();   \u002F\u002F 1 transaction\n",[567,2434,2435],{"class":569,"line":1393},[567,2436,614],{},[432,2438,2439,2442,2443,447],{},[564,2440,2441],{},"DbContext"," của EF Core ",[436,2444,2445],{},"chính là Unit of Work + Repository",[427,2447,2449],{"id":2448},"anti-pattern-tránh","Anti-pattern — tránh",[520,2451,2454],{"color":2452,"icon":2453},"error","i-lucide-x-circle",[866,2455,2456,2462,2468,2474,2480],{},[869,2457,2458,2461],{},[436,2459,2460],{},"God Class \u002F God Object",": class 2000 dòng làm mọi thứ. Vi phạm SRP.",[869,2463,2464,2467],{},[436,2465,2466],{},"Anemic Domain Model",": entity chỉ có get\u002Fset, mọi logic ở service. OK với DTO\u002FAPI, không tốt cho domain phức tạp.",[869,2469,2470,2473],{},[436,2471,2472],{},"Singleton lạm dụng",": dùng cho mọi service → global state, khó test.",[869,2475,2476,2479],{},[436,2477,2478],{},"Spaghetti inheritance",": kế thừa 5+ cấp, fragile base class.",[869,2481,2482,2485,2486,2489],{},[436,2483,2484],{},"Service locator",": dùng ",[564,2487,2488],{},"Locator.Get\u003CIFoo>()"," thay vì inject — hide dependency, khó test.",[520,2491,2493,2494],{"color":858,"icon":2492},"i-lucide-arrow-right","Tiếp theo: ",[2495,2496,2497],"a",{"href":331},"Câu hỏi phỏng vấn OOP",[2499,2500,2501],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":562,"searchDepth":576,"depth":576,"links":2503},[2504,2505,2510,2515,2523],{"id":429,"depth":576,"text":430},{"id":531,"depth":576,"text":532,"children":2506},[2507,2508,2509],{"id":536,"depth":582,"text":537},{"id":890,"depth":582,"text":891},{"id":984,"depth":582,"text":985},{"id":1132,"depth":576,"text":1133,"children":2511},[2512,2513,2514],{"id":1136,"depth":582,"text":1137},{"id":1243,"depth":582,"text":1244},{"id":1447,"depth":582,"text":1448},{"id":1574,"depth":576,"text":1575,"children":2516},[2517,2518,2519,2520,2521,2522],{"id":1578,"depth":582,"text":1579},{"id":1713,"depth":582,"text":1714},{"id":1831,"depth":582,"text":1832},{"id":1972,"depth":582,"text":1973},{"id":2134,"depth":582,"text":2135},{"id":2289,"depth":582,"text":2290},{"id":2448,"depth":576,"text":2449},"Creational (Singleton, Factory, Builder), Structural (Adapter, Decorator, Facade), Behavioral (Strategy, Observer, Command...). Ví dụ C# + TS.","md",null,{},{"title":327,"icon":106},{"title":422,"description":2524},"Y-0rIyLKir-HjSZAmqkdcws9PLMCNQdg07exqSVCk2Y",[2532,2534],{"title":322,"path":323,"stem":324,"description":2533,"icon":325,"children":-1},"SRP, OCP, LSP, ISP, DIP — case study refactor mỗi nguyên lý. Ví dụ C# + TypeScript.",{"title":307,"path":331,"stem":332,"description":2535,"icon":196,"children":-1},"50+ câu hỏi OOP + SOLID + Design Pattern cho fresher\u002Fjunior — đáp án ngắn, ví dụ.",1783490358701]