Chúc mừng anh ạ!! Chúc anh thật nhiều sức khỏe, thuận lợi trong công việc và luôn yêu đời. Đọc bài viết của anh giúp em có thêm động lực để phấn đấu hơn!! Cảm ơn anh đã chia sẻ♥️
Cảm ơn bạn nhé. Cảm ơn bạn đã đặt câu hỏi. Mình xin giải thích bằng 2 ví dụ khác như sau:
DIP và OCP
Cả Nguyên Lý Đảo Ngược Phụ Thuộc (DIP) và Nguyên Lý Đóng/Mở (OCP) đều đóng vai trò quan trọng trong việc tạo ra mã nguồn linh hoạt và dễ bảo trì. Tuy nhiên, mỗi nguyên lý lại có điểm nhấn riêng biệt và cách thức áp dụng khác nhau.
Nguyên Lý
Định Nghĩa
Mục Tiêu
OCP
Các module phải mở cho việc mở rộng nhưng đóng cho việc sửa đổi.
Tạo ra ứng dụng có thể dễ dàng mở rộng mà không cần sửa đổi code hiện tại.
DIP
Các module cấp cao không nên phụ thuộc vào các module cấp thấp, cả hai nên phụ thuộc vào abstraction.
Giảm sự phụ thuộc giữa các module, làm cho hệ thống dễ dàng thay đổi và bảo trì.
Để làm rõ hơn sự khác biệt giữa Nguyên Lý Đóng/Mở (OCP) và Nguyên Lý Đảo Ngược Phụ Thuộc (DIP), chúng ta cùng xem xét kỹ hơn thông qua hai ví dụ cụ thể, mỗi nguyên lý một, trong bối cảnh phát triển ứng dụng với React.
1. Nguyên Lý Đóng/Mở (OCP)
OCP nhấn mạnh việc thiết kế components và modules sao cho chúng có thể dễ dàng mở rộng mà không cần sửa đổi code hiện tại.
Ví dụ về OCP:
Xét component Button ban đầu:
constButton=({ type, onClick })=>{if(type ==="save"){return<buttonclassName="save"onClick={onClick}>Save</button>;}if(type ==="cancel"){return<buttonclassName="cancel"onClick={onClick}>Cancel</button>;}// Thêm nhiều điều kiện cho các loại button khác...};
Component này không tuân thủ OCP vì mỗi khi bạn cần một loại nút mới, bạn phải sửa đổi Button để thêm một điều kiện mới.
Để tuân thủ OCP, bạn có thể tái cấu trúc như sau:
constButton=({ className, onClick, children })=>(<buttonclassName={className}onClick={onClick}>{children}</button>);
Sau đó, sử dụng Button để tạo các nút mở rộng mà không cần sửa đổi Button gốc:
Ở đây, Button được thiết kế để dễ dàng mở rộng (thêm SaveButton, CancelButton), mà không cần sửa đổi code của chính nó.
2. Nguyên Lý Đảo Ngược Phụ Thuộc (DIP)
DIP nhấn mạnh việc thiết kế các module sao cho module cấp cao không phụ thuộc trực tiếp vào module cấp thấp, mà cả hai đều phụ thuộc vào abstractions.
Ví dụ về DIP:
Giả sử bạn có một component form đơn giản:
// Component form không tuân thủ DIPconstUserForm=({ saveUser })=>{consthandleSubmit=(event)=>{
event.preventDefault();// Xử lý và gọi saveUser};return<formonSubmit={handleSubmit}>...</form>;};
Ở đây, UserForm phụ thuộc trực tiếp vào hàm saveUser, một logic cụ thể. Điều này khiến UserForm khó tái sử dụng với các hành động khác.
Để áp dụng DIP, bạn có thể thiết kế lại như sau:
// Component form tuân thủ DIPconstUserForm=({ onSubmit })=>{return<formonSubmit={onSubmit}>...</form>;};
UserForm giờ đây chỉ phụ thuộc vào một abstraction (onSubmit), không còn phụ thuộc trực tiếp vào hành động cụ thể nào, giúp nó dễ dàng tái sử dụng với các hành động khác nhau:
Chào a, chắc a cũng thấy avt e quen quen :V. Bài này trước đó thực ra e đã đọc rồi và có thực hành theo a nên cũng hiểu kha khá (vì lúc đó e cũng hơi non nên chưa ngấm được hết kiến thức của a viết ra :V). Nhung vì thời gian dài vừa rồi ko có động đến docker nên cũng ko có nhớ được chuẩn. Đợt này e có quay lại docker và như đã nói thì e đã chuyển sang docker-desktop để cho tiện việc học và thực hành hơn. Nhưng cuối cùng sau khá nhiều lần chạy thử thì e đã nhận ra là behaviour của docker engine và docker desktop đổi với owner của file hay directory là khác nhau. Cụ thể:
Nếu như những ngày trước e có sử dụng docker engine hay vừa rồi e có switch docker context từ docker desktop về docker engine thì tất cả những điều a viết trong bài đều dúng.
Còn nếu sử dụng docker desktop (đã tích hợp sẵn docker engine của nó và các service cần thiết khác nữa ) thì rất nhiều thứ khác như là:
nếu sử dụng bind mount thì tất cả các file hoặc directory thuộc user hiện tại của host sau khi mount vào container đều có owner là root (dù cho user trong container hiện tại có là root hay www-data hay là ai đi nữa)
nếu tạo file từ bên trong container (ví dự user trong container là root) thì file được tạo nếu xem từ trong container thì của root, nhưng nếu cùng thời điểm đó xem ở ngoài host thì lại là của user hiện tại
nếu bên ngoài có 1 file thuộc root thì sau khi bind mount thì xem trong container thì owner lại chuyển thành nobody
Toàn bộ những gạch đầu dòng trên đều là do e tự thử nghiệm ko dưới 10 lần rồi tổng kết lại được vây
Qua đây thì thấy được nhược điểm của docker desktop là không những sử dụng vm chứ ko được tối ưu như docker engine thì vấn đề với owner + permission cũng là thứ cần được đẵn đo để đánh đổi lấy ưu điểm là tiện lợi, dễ thao tác.
Nhân đây cũng cảm ơn a về series docker này và vì đã trả lời nhiệt tình các thắc mắc của m.n
@maitrungduc1410 nhân tiện cho e hỏi ở docs có phần nào đề cập đến phần owner + permission của bind mount ko nhỉ a. Như là file sau khi mount vào container thì owner sẽ là của user nào ấy ạ. Hoặc nếu a có thể chia sẻ về phần này thêm thì rất tuyệt với ạ. Em cảm ơn
cảm ơn tác giả vì bài viết , ví dụ thực tế dễ hiểu . Mình có thắc mắc về ví dụ DIP và OCP , mình thấy 2 phần này ví dụ như nhau . mong bạn giải thích chi tiết hơn . Chúc bạn sức khỏe
Về bản chất await keyword không phải là một thứ cần thiết trong các ngôn ngữ lập trình, công việc chính của nó giúp tăng tính rõ ràng của code, và kotlin thì bỏ đi cái sự rõ ràng đó. Lấy ví dụ trong swift, khi gọi một method thông thường của một actor, nếu gọi bên trong actor không cần await bởi vì nó được chạy đồng bộ, còn nếu gọi bên ngoài actor thì cần await keyword để ám chỉ method này chạy ở bên trong actor, là một miền độc lập khác với nơi gọi nó. Trường hợp sử dụng một hàm async bên trong một actor, và await keyword lúc này ám chỉ hàm đó có thể được chạy bên ngoài actor, và do đó các hàm khác có thể chạy bên trong actor lúc này. Nếu trường hợp là kotlin thì là lập trình viên ngầm hiểu (gọi là lập trình viên tự đưa tay cho compiler dắt đi). Làm mọi thứ ngọn nhẹ thì tốt, nhưng gọn nhẹ tới mức không rõ ràng thì mình thì không đánh giá cao điều này ở kotlin (cũng như cách mà CancellationException phá vỡ triết lý của unchecked exception trong kotlin, và để logic hóa nó thì kotlin gọi catch Exception tổng quát là anti-pattern trong khi nhiều trường hợp (ví dụ như viết một framework) cần phải làm điều này).
Anh giải thích kỹ hơn câu nói này của anh được không ạ "Rất nhiều các ứng viên khi mình hỏi về DI đều mention đến Spring context, Spring IoC container, nào là nơi chứa các bean, rồi thì được inject này nọ, có ứng viên còn nhầm lẫn với bean scope, nói cả về việc scope singleton không cần khởi tạo nhiều lần?"
THẢO LUẬN
Chúc mừng anh ạ!! Chúc anh thật nhiều sức khỏe, thuận lợi trong công việc và luôn yêu đời. Đọc bài viết của anh giúp em có thêm động lực để phấn đấu hơn!! Cảm ơn anh đã chia sẻ♥️
Oh nice catch e ơi
Trước giờ a toàn chạy trên VPS Ubuntu nên chỉ cài docker engine, và khi deploy cũng vậy, lên k8s cũng thế
Nên behaviour nó khá là ổn định
Giờ thì a đã hiểu tại sao trên Mac (Docker desktop) của a nó cũng xêm xêm như trên ubuntu rồi
Cảm ơn bạn nhé. Cảm ơn bạn đã đặt câu hỏi. Mình xin giải thích bằng 2 ví dụ khác như sau:
DIP và OCP
Cả Nguyên Lý Đảo Ngược Phụ Thuộc (DIP) và Nguyên Lý Đóng/Mở (OCP) đều đóng vai trò quan trọng trong việc tạo ra mã nguồn linh hoạt và dễ bảo trì. Tuy nhiên, mỗi nguyên lý lại có điểm nhấn riêng biệt và cách thức áp dụng khác nhau.
Để làm rõ hơn sự khác biệt giữa Nguyên Lý Đóng/Mở (OCP) và Nguyên Lý Đảo Ngược Phụ Thuộc (DIP), chúng ta cùng xem xét kỹ hơn thông qua hai ví dụ cụ thể, mỗi nguyên lý một, trong bối cảnh phát triển ứng dụng với React.
1. Nguyên Lý Đóng/Mở (OCP)
OCP nhấn mạnh việc thiết kế components và modules sao cho chúng có thể dễ dàng mở rộng mà không cần sửa đổi code hiện tại.
Ví dụ về OCP:
Xét component
Buttonban đầu:Component này không tuân thủ OCP vì mỗi khi bạn cần một loại nút mới, bạn phải sửa đổi
Buttonđể thêm một điều kiện mới.Để tuân thủ OCP, bạn có thể tái cấu trúc như sau:
Sau đó, sử dụng
Buttonđể tạo các nút mở rộng mà không cần sửa đổiButtongốc:Ở đây,
Buttonđược thiết kế để dễ dàng mở rộng (thêmSaveButton,CancelButton), mà không cần sửa đổi code của chính nó.2. Nguyên Lý Đảo Ngược Phụ Thuộc (DIP)
DIP nhấn mạnh việc thiết kế các module sao cho module cấp cao không phụ thuộc trực tiếp vào module cấp thấp, mà cả hai đều phụ thuộc vào abstractions.
Ví dụ về DIP:
Giả sử bạn có một component form đơn giản:
Ở đây,
UserFormphụ thuộc trực tiếp vào hàmsaveUser, một logic cụ thể. Điều này khiếnUserFormkhó tái sử dụng với các hành động khác.Để áp dụng DIP, bạn có thể thiết kế lại như sau:
UserFormgiờ đây chỉ phụ thuộc vào một abstraction (onSubmit), không còn phụ thuộc trực tiếp vào hành động cụ thể nào, giúp nó dễ dàng tái sử dụng với các hành động khác nhau:Chốt lại:
Buttonđể dễ dàng tạoSaveButton,CancelButtonmà không cần sửa đổiButton.UserFormsao cho nó chỉ phụ thuộc vàoonSubmit, một abstraction, thay vì một hành động cụ thể.Hy vọng qua sự so sánh này, bạn có thể rõ ràng phân biệt được hai nguyên lý này và áp dụng chúng một cách hiệu quả trong dự án React của mình.
Chào a, chắc a cũng thấy avt e quen quen :V. Bài này trước đó thực ra e đã đọc rồi và có thực hành theo a nên cũng hiểu kha khá (vì lúc đó e cũng hơi non nên chưa ngấm được hết kiến thức của a viết ra :V). Nhung vì thời gian dài vừa rồi ko có động đến docker nên cũng ko có nhớ được chuẩn. Đợt này e có quay lại docker và như đã nói thì e đã chuyển sang docker-desktop để cho tiện việc học và thực hành hơn. Nhưng cuối cùng sau khá nhiều lần chạy thử thì e đã nhận ra là behaviour của docker engine và docker desktop đổi với owner của file hay directory là khác nhau. Cụ thể: Nếu như những ngày trước e có sử dụng docker engine hay vừa rồi e có switch docker context từ docker desktop về docker engine thì tất cả những điều a viết trong bài đều dúng. Còn nếu sử dụng docker desktop (đã tích hợp sẵn docker engine của nó và các service cần thiết khác nữa ) thì rất nhiều thứ khác như là:
Toàn bộ những gạch đầu dòng trên đều là do e tự thử nghiệm ko dưới 10 lần rồi tổng kết lại được vây Qua đây thì thấy được nhược điểm của docker desktop là không những sử dụng vm chứ ko được tối ưu như docker engine thì vấn đề với owner + permission cũng là thứ cần được đẵn đo để đánh đổi lấy ưu điểm là tiện lợi, dễ thao tác. Nhân đây cũng cảm ơn a về series docker này và vì đã trả lời nhiệt tình các thắc mắc của m.n
😜😜😜
@just-pthai-it e đọc đến bài non root của a nhé, nhưng mà cứ thong thả đi từng bài đừng nhảy cóc , dần dần e sẽ thấy
Thanks
Hay quá, tuổi trẻ tài cao, hâm mộ
@maitrungduc1410 nhân tiện cho e hỏi ở docs có phần nào đề cập đến phần owner + permission của bind mount ko nhỉ a. Như là file sau khi mount vào container thì owner sẽ là của user nào ấy ạ. Hoặc nếu a có thể chia sẻ về phần này thêm thì rất tuyệt với ạ. Em cảm ơn
ad ơi, cho mình hỏi khi mình đã build xong cụm k8s bằng RKE này, nếu mình muốn scale cụm k8s thêm node worker hay master thì làm thế nào ạ
đợt này codesandbox thay đổi tính năng hơi khác tí, em bị lỗi ở link nào mục nào để anh kiểm tra lại
cảm ơn tác giả vì bài viết , ví dụ thực tế dễ hiểu . Mình có thắc mắc về ví dụ DIP và OCP , mình thấy 2 phần này ví dụ như nhau . mong bạn giải thích chi tiết hơn . Chúc bạn sức khỏe
Chúc mừng năm mới anh ạ😀. Lâu lắm rồi mới thấy anh viết lại bài, vẫn ấn tượng a từ bài giảng Docker của anh cùng với thầy William Cường
chúc e năm mới thật nhiều thành công nhé
cố lên e ơi, rụng làm lại
hóng các bài viết tiếp theo.
Một số bạn không nắm rõ vấn đề nên trả lời những ý không liên quan ý mà em, rồi trả lời lái sang cái này cái kia.
anh ơi codesanbox bị lỗi rồi anh có cách nào để cho em xem được code không anh
Về bản chất
awaitkeyword không phải là một thứ cần thiết trong các ngôn ngữ lập trình, công việc chính của nó giúp tăng tính rõ ràng của code, vàkotlinthì bỏ đi cái sự rõ ràng đó. Lấy ví dụ trongswift, khi gọi một method thông thường của mộtactor, nếu gọi bên trongactorkhông cầnawaitbởi vì nó được chạy đồng bộ, còn nếu gọi bên ngoàiactorthì cầnawaitkeyword để ám chỉ method này chạy ở bên trongactor, là một miền độc lập khác với nơi gọi nó. Trường hợp sử dụng một hàmasyncbên trong mộtactor, vàawaitkeyword lúc này ám chỉ hàm đó có thể được chạy bên ngoàiactor, và do đó các hàm khác có thể chạy bên trongactorlúc này. Nếu trường hợp làkotlinthì là lập trình viên ngầm hiểu (gọi là lập trình viên tự đưa tay cho compiler dắt đi). Làm mọi thứ ngọn nhẹ thì tốt, nhưng gọn nhẹ tới mức không rõ ràng thì mình thì không đánh giá cao điều này ởkotlin(cũng như cách màCancellationExceptionphá vỡ triết lý củaunchecked exceptiontrongkotlin, và để logic hóa nó thìkotlingọi catchExceptiontổng quát là anti-pattern trong khi nhiều trường hợp (ví dụ như viết một framework) cần phải làm điều này).Anh giải thích kỹ hơn câu nói này của anh được không ạ "Rất nhiều các ứng viên khi mình hỏi về DI đều mention đến Spring context, Spring IoC container, nào là nơi chứa các bean, rồi thì được inject này nọ, có ứng viên còn nhầm lẫn với bean scope, nói cả về việc scope singleton không cần khởi tạo nhiều lần?"