Home Blog Page 68

Dân IT đọc sách như thế nào đây?

dân it đọc sách

Bài viết được sự cho phép của tác giả Huy Trần

Nhân dịp đầu năm, mình xin viết một bài bàn về chuyện đọc sách, dựa trên những kinh nghiệm cá nhân, dành tặng các bạn nào đã trót đặt ra mục tiêu đọc 10 hay 20 cuốn sách trong năm mới một lần nữa và chưa có cơ hội thực hiện. Và cả những bạn nào chưa có ý định đọc sách. Hy vọng các bạn sẽ hoàn thành mục tiêu mình đặt ra và đọc càng nhiều sách càng tốt trong năm nay 🙂

Mở bài

Như các bạn đã biết, ngay từ khi mới chập chững vào lớp 1 hay từ mẫu giáo cũng đã biết, đến khi lớn lên vào Đại học hay không vào đại học thì cũng biết luôn là đọc sách rất có lợi. Hằng ngày, mọi người vẫn kháo nhau rằng sách là kho tàng kiến thức của nhân loại, đọc sách là một phương pháp thể dục cho trí óc và là cách hiệu quả nhất để tiếp thu kho tàng kiến thức đó.

Và tất nhiên ai cũng muốn đọc sách để xem thử nó có nâng tầm hiểu biết của mình lên được không. Nhưng không phải ai cũng có thời gian để đọc, chắc hẳn chính bạn cũng đã từng rất quyết tâm mua về một tủ sách hoặc một vài ba cuốn sách, đặt ra mục tiêu trong vòng 3 tháng nghỉ hè sẽ lĩnh hội được hết cái đống này, rồi đúng y 3 năm sau cũng vẫn đặt mục tiêu tương tự cho những cuốn sách đó :v Vì một lý do đơn giản, thời gian đâu mà đọc.

Vậy một ngày có 24 tiếng, thời gian đi đâu hết nhỉ? Để xem nào, sáng 8h thức dậy, vội vã ăn sáng đánh răng, 9h phóng xe lên cty, đọc báo, “làm việc nước” trên skype, đến đâu tầm 12h thì rủ nhau ra ngoài ăn trưa, ăn trưa xong vào làm 1 giấc tới 1h30 hoặc có khi là đế chế, half-life, gần đây có cả dota, starcraft đến tận 2h chiều. Đến khi sếp bước xuống thì lật đật alt-tab ra ngoài bật IntelliJ hay Sublime lên ngồi gõ gõ. 6h chiều tắt máy đi về, tới nhà thì tầm 6h30, 7h đêm. Dân FA thì bật máy tính lên và chơi game hoặc lướt Kênh 14 tiếp, các bạn khá khẩm hơn tí thì dẫn gấu đi làm nghĩa vụ hằng ngày là chạy xe quanh thành phố, nộp sưu thuế cho mấy quán trà chanh, cà phê, loáng phát là đến 9, 10h đêm. Hôm nào cãi nhau với gấu thì đùng đùng bỏ về lấy điện thoại ra nhắn tin cãi nhau tiếp đến tận 1, 2h sáng, anh nào ko cãi nhau thì về mở máy ra chiến Freelancer hay 9gag đến tận rạng sáng ngày hôm sau…. Thế đấy, xong bà nó một ngày rồi. Thời gian đâu mà đọc sách nữa…

Dài dòng quá, thân bài

Thực ra đây là cái lịch trình sinh hoạt của mình đúng 1 năm về trước. Lúc đó thời gian thì tiêu biến thật là nhanh nhưng mà chung quy lại một ngày chả làm được cái gì nhiều, mặc dù lịch trình thì luôn sát nút như vậy. Và tất nhiên giấc mơ đọc sách để có ngày viết những bài review hoành tráng như các bậc vĩ nhân trên mạng, hay các bài xã hội học thâm nho như triết học đường phố luôn là cái giấc mơ xa vời, vì chưa bao giờ có cuốn sách nào được lật quá 10 trang đầu tiên…

Và đến lúc đó mình đặt ra câu hỏi, tại sao mình không làm được mỗi một việc đơn giản là đọc sách? Ngày xưa khi chưa có máy tính thì mình cũng đọc rất nhiều, và đọc ngấu nghiện, nào là Doraemon, Tepi, Subasa, Songoku, Kính vạn hoa, 1000 câu hỏi vì sao…  tất nhiên truyện tranh cũng là sách, nhưng không phải là sách đọc vào để học, thêm vào cho vui thôi, nhưng mà hồi đó có thể đọc quên ăn quên ngủ, mặc dù các phương tiện giải trí khác nó có rất nhiều như là máy chơi game, phim hoạt hình,… toàn những thứ đầy sức cám dỗ.

Yếu tố thứ 1: Tìm ra thể loại sách ưa thích

Vậy là mình nghiệm ra một điều, lý do khiến lâu giờ mình không tài nào đọc được, một phần là do thể loại sách không phù hợp. Khi bắt đầu đọc một cuốn sách và thấy nó không có vẻ gì là lôi cuốn, đủ để giữ chân bạn, bạn sẽ mất dần kiên nhẫn và không thể đọc tiếp được nữa. Rõ ràng rồi.

Vậy là bắt đầu đi tìm những loại sách phù hợp với mình. Sau một thời gian lân la trên tiki.vn và các nhà sách, tìm và đọc thử (đọc lướt) các thể loại thì mình đã nhận ra mình không hề phù hợp với các thể loại truyện ngôn tình, sách dạy làm giàu và cả sách tin học, thể thao =))))) mình không chia sẽ các thể loại sách mình thích đọc ra đây vì cái này không phải ai cũng giống nhau, đưa ra sẽ làm ảnh hưởng đến tính khách quan của bài viết =))

Sau khi đã tìm ra thể loại sách ưa thích, mình bắt đầu đọc, nhưng vẫn không thể thoát khỏi chuyện bị phân tâm xao lãng. Đến lúc này mình quyết định tìm hiểu một cách bài bản về chuyện đọc sách. Và rút ra thêm một vài kinh nghiệm khác.

Yếu tố thứ 2: Đọc nhiều sách một lần

Một trong những cản trở lớn nhất trong quá trình đọc sách của mình chính là việc quá quyết tâm hoàn thành 1 quyển sách trước khi đọc sang cuốn sách khác. Và tất nhiên chưa bao giờ mình có đủ thời gian để đọc hết 1 cuốn sách nào cả. Thế nên mãi mãi mình cứ dậm chân tại chỗ.

Sau khi tham khảo nhiều trên các diễn đàn, mình thấy các bạn ấy có chia sẽ một kinh nghiệm đó là đọc nhiều cuốn sách một lần. Nghe qua có vẻ vô lý nhưng khi thử nghiệm thì đúng là nó hiệu quả.

Sỡ dĩ nó hiệu quả là vì, khi chúng ta ép bản thân quá tập trung đọc một loại sách, chúng ta dễ cảm thấy nhàm chán với nội dung cuốn sách đó. Khi đó, nếu chuyển sang đọc một cuốn sách khác với thể loại khác hẳn cuốn sách đang đọc, đầu óc chúng ta sẽ trở nên thoải mái hơn, tiếp thu câu chuyện mới hiệu quả hơn và khi quay trở lại cuốn sách cũ thì cũng cảm thấy tràn trề năng lượng chứ không bị gò bó, mệt mỏi. Giống như là một phương pháp refresh lại não bộ vậy.

Đó là chưa kể, đọc nhiều quyển sách một lần sẽ làm cho tiến độ đọc của bạn tăng lên thấy rõ, giống như trong lập trình, sử dụng nhiều thread để xử lý nhiều việc bao giờ cũng nhanh hơn là xử lý theo lối hàng đợi :v

Xem thêm tuyển dụng UI UX Designer hấp dẫn trên TopDev

Yếu tố thứ 3: Mua càng nhiều sách càng tốt

Yếu tố thứ 3 này là để phụ hoạ cho yếu tố thứ 2, một khi đã chấp nhận việc đọc nhiều cuốn sách một lần thì tại sao lại không mua một đống sách về chất đầy giường mà đọc?

Thực ra việc mua nhiều sách về để dành sẽ tạo ra một tác động tâm lý khá lớn, đó là, đã tốn tiền mua sách về rồi thì phải ráng đọc cho hết. Tác động thứ 2 đó là luôn có động lực phải cố gắng đọc để có thể hoàn thành tủ sách của mình. Bạn không thể có động lực bước đi tiếp trên một con đường tối mù, không biết phía trước có gì. Nhưng nếu thấy phía trước là cả một con đường đầy thức ăn ngon tuyệt vời thì ai lại không muốn bước tiếp, đúng không?

Sẽ có bạn nói là mua nhiều sách quá sẽ làm nản chí không muốn đọc nữa, điều này không đúng. Nếu bạn mua những cuốn sách hợp khẩu vị, thì sẽ không có thứ gì làm bạn nản chí được cả.

Yếu tố thứ 4: Đặt ra những mục tiêu ngắn hạn cho việc đọc

Hầu hết trong chúng ta khi bắt đầu đọc một cuốn sách, đều đặt ra một mục tiêu rất to lớn như là: Hoàn thành cuốn sách này trong vòng một tuần, hoặc đọc 20 cuốn sách trong vòng 12 tháng,… trong khi các bạn chưa hề biết được khả năng của bản thân đọc được bao nhiêu. Điều này dẫn đến mục tiêu kia trở nên quá xa vời và rất dễ gây nản chí.

Đặt mục tiêu một cách khôn ngoan khi đọc sách là một điều quan trọng, vậy, mục tiêu thế nào là khôn ngoan?

Các loại sách thông thường bây giờ có độ dài tầm 250 đến 300 từ trên một trang sách. Và tốc độ đọc trung bình của chúng ta là tầm 400-500 từ một phút. Như vậy Trong vòng một phút các bạn có thể đọc gần 2 trang sách (nếu tập trung cao độ), và một trang sách nếu đang ở trong một môi trường không phù hợp, bị phân tâm.

Dựa vào tốc độ đọc này, chúng ta có thể đặt ra các mục tiêu ngắn như là: Đọc 10 trang sách một ngày (tương đương với hơn 10 phút đọc sách một ngày). Mục tiêu này rất dễ để đạt được, vì vậy dần dần việc đọc sách đối với bạn sẽ không còn là cực hình nữa. Và tại sao không bỏ ra nỗi 10 phút để đọc sách trong khi chúng ta có đến 24 giờ?

Cũng giống như khi tập thể dục. Sau khi đã hoàn thành được mục tiêu đặt ra, chúng ta phải đặt ra các mục tiêu cao hơn. Ngày đầu tiên có thể là 10 phút, nhưng hôm sau có thể dành ra 15 phút, rồi 30 phút, rồi 1 tiếng một ngày cho việc đọc sách. Bước phát triển từ từ sẽ giúp cho não quen dần với việc trích xuất thời gian làm một việc gì đó mới mẻ (đọc sách), và lịch trình làm việc của bạn cũng sẽ dần dần thích nghi với việc này để có thể đọc nhiều hơn mỗi ngày.

Yếu tố thứ 5: Dẹp ngay việc đọc tin lung tung trên internet

Thời gian chúng ta bỏ ra để đọc các tin bài một cách ngẫu nhiên (random articles) trên internet rất nhiều, nhưng thường thì các bài viết này không đem lại bất cứ lợi ích gì về mặt lâu dài cả.

Ví dụ như đọc được một bài báo về Hari Won và Tiến Đạt hay Trấn Thành sẽ chẳng hề giúp bạn thu lượm được kiến thức gì ngoại trừ chuyện đời tư của các nghệ sĩ. Hay đọc một bài viết về cơ chế hạ cánh các tên lửa đẩy của hãng SpaceX cũng là một loại kiến thức quá xa vời và không hề thực tế đối với bạn. Cãi nhau trên internet để bảo vệ quan điểm của mình về việc Trung Quốc có nên từ bỏ việc đưa đồng Nhân Dân Tệ lên thay thế đồng tiền chung của khối Châu Âu không, hay Lỗ đen vũ trụ có phải là manh mối để du hành ngược thời gian không, cũng chẳng đem lại cái gì cho bạn cả…

Nếu các bạn để ý và làm một phép thống kê đơn giản, có lẽ bạn sẽ giật mình vì lượng thời gian tiêu tốn cho việc đọc lung tung đó. Thời gian các bạn lãng phí cho việc đó nếu dành vào việc đọc sách thì sẽ tốt hơn rất nhiều. Hãy đọc để có thêm kiến thức trước khi vác bàn phím lên tham gia vào các cuộc đấu khẩu trên thế giới ảo.

Yếu tố thứ 6: Đọc trên nhiều loại thiết bị

Nhìn qua một lượt, chúng ta có rất nhiều cách để đọc sách trong thời đại này: Sách giấy, máy tính, điện thoại, tablet, máy đọc sách,…

  Các Phần Mềm Dành Cho Dân IT Mà Mọi Lập Trình Viên Nên Biết
  32 cuốn sách học lập trình bạn nhất định phải đọc

Một kinh nghiệm cho việc đọc là bạn nên thử thay đổi qua lại việc đọc cùng một quyển sách trên nhiều loại thiết bị khác nhau. Ví dụ, khi đi ra ngoài đường, đi dã ngoại, các bạn có thể đem bản sách giấy đi theo. Khi đi trên tàu điện hoặc xe bus, lấy máy đọc sách ra mà đọc, khi về đến nhà có thể đọc trên laptop hoặc trên iPad, khi… làm chuyện đại sự, có thể cầm điện thoại vào và đọc ngấu nghiến…

Hầu hết các phần mềm đọc sách như iBook hay Amazon Cloud Reader có khả năng đồng bộ tiến trình đọc của bạn qua nhiều thiết bị giúp cho việc chuyển đổi giữa các thiết bị dễ dàng hơn, bạn có thể đọc liên tục mà không gặp trở ngại gì.

alt text

Hiệu quả ở đây là gì? Việc thay đổi thiết bị khi đọc cũng giống như việc thay đổi môi trường làm việc vậy. Môi trường mới sẽ giúp cho chúng ta không cảm thấy nhàm chán và kích thích khả năng làm việc của chúng ta hiệu quả hơn. Đọc sách cũng cần thay đổi môi trường vậy.

Tuy nhiên, nếu có điều kiện mua các thiết bị máy đọc sách (giá tầm 1tr đến 3tr) thì nên chọn các loại máy chỉ có chức năng đọc sách, không có gì thêm như Kindle Basic, Paperwhite hay Kobo. Ưu điểm thứ nhất là các máy này khá nhẹ, cầm đọc thời gian dài không bị rớt vào mặt nếu lỡ buồn ngủ =)) Việc đọc trên iPad hay các máy Tablet khác đa dụng có thể bị các ứng dụng như Facebook, Twitter, Games,… làm phân tâm không tâp trung được.

Yếu tố thứ 7: Chọn cách đọc phù hợp tuỳ theo loại sách

Kinh nghiệm này đến từ bài viết Đọc sách nhanh trên Blog Khoa học máy tính. Mình vừa mới tìm ra bài viết này cách đây không lâu và chưa có cơ hội áp dụng nhiều, chỉ mới dùng kinh nghiệm này cho 2 cuốn sách gần đây nhất. Tuy vậy mình thấy cách này rất đáng để tham khảo.

Tuỳ thuộc vào mục đích của loại sách mà chúng ta đang muốn đọc, không phải bất cứ quyển sách nào cũng đáng để đọc ngấu nghiến, có những quyển sách chúng ta chỉ nên lướt qua một lần hoặc là bỏ hẳn không nên đọc nữa, hoặc những cuốn sách chúng ta ít quan tâm hơn nhưng vẫn biết nó hữu dụng. Cách tốt nhất là phân loại nó thành các thể loại sách để đọc theo 2 mức độ (trong bài gốc có 3 mức độ nhưng mức độ 3 mình không nghĩ là nó thích hợp):

  • Đọc kiểu Cưỡi ngựa xem hoa: Đọc lươn lướt để biết trên đời này có cái gì, và biết được thứ kiến thức gì nó nằm ở đâu để sau này có thể tiện tra cứu lại. Cách này có thể áp dụng khi đọc các bài viết kĩ thuật trên internet, nhất là các bài về lập trình, đảo mắt nhìn quanh một lượt, liếc qua vài dòng code rồi nếu thích thì đi vào đọc chi tiết nội dung bài.
  • Đọc kiểu Hiểu: Phải đọc và lĩnh hội được cuốn sách ở mức độ có thể tự xây dựng lại nội dung, có thể khái quát cũng được, mà không cần nhìn vào sách nữa. Cần có sự thực hành đi đôi với quá trình đọc sách (chạy thử code, thử giải các câu đố có trong sách, lý giải một hành động nào đó của nhân vật chính trong sách, hoặc thực hành theo các kinh nghiệm làm giàu nếu bạn dám …), đòi hỏi cần phải đọc nghiêm túc hơn.

Kết bài

Trên đây là kinh nghiệm đọc của mình, lần mò ra được trong một năm qua, sau khi vận dụng và thay đổi thì mình cũng đã hoàn thành được kha khá một vài cuốn sách trong vô số cuốn sách mà mình muốn đọc. Hy vọng các bạn sẽ có thêm động lực để đọc sau bài viết này. Và nếu có ai còn những kinh nghiệm quý giá nào khác, xin mời các bạn chia sẽ ở phần comment cuối bài viết 😀

Chúc các bạn một năm mới thành công rực rỡ  đọc nhiều, chơi nhiều và thành công cũng nhiều luôn 😀

Bài viết gốc được đăng tải tại snacky.blog

Có thể bạn quan tâm:

Xem thêm các việc làm ngành CNTT hấp dẫn trên TopDev

Hướng Dẫn Trả Lời Thư Mời Phỏng Vấn Với Nhà Tuyển Dụng

trả lời thư mời phỏng vấn
Hướng Dẫn Trả Lời Thư Mời Phỏng Vấn Với Nhà Tuyển Dụng

Sau khi CV của ứng viên đã hoàn thiện và được gửi đến vị trí mình ưng ý, nếu phù hợp, bạn sẽ được nhà tuyển dụng liên hệ và mời đến vòng phỏng vấn. Khi nhận được thư mời phỏng vấn của công ty, có nên trả lời thư mời phỏng vấn hay không và nên viết như thế nào để tạo ấn tượng tốt trong mắt nhà tuyển dụng? Tham khảo thêm các thông tin này với bài viết dưới đây nhé!

trả lời thư mời phỏng vấn
Cách trả lời thư mời phỏng vấn hiệu quả

1. Hiểu rõ về tầm quan trọng của việc trả lời thư mời phỏng vấn

Thực tế có không ít các ứng viên sau khi nhận được thư mời phỏng vấn từ đơn vị tuyển dụng sẽ không gửi lại phản hồi về việc có đồng ý tham gia hay không. Nhiều người cho rằng chỉ cần đến và tham gia phỏng vấn vào đúng khung giờ đã được đề cập là đủ. Tuy nhiên, việc trả lời thư mời phỏng vấn cũng chính là một yếu tố cực kỳ quan trọng giúp bạn chinh phục nhà tuyển dụng.

Không chỉ thế, việc để nhà tuyển dụng chờ phản hồi quá lâu cũng có thể trở thành một yếu tố khiến ứng viên mất điểm. Thậm chí, nếu đó là vị trí đang cần tuyển gấp thì bạn hoàn toàn có thể đã vuột mất cơ hội vào tay ứng viên khác có cách xử lý thông tin chuyên nghiệp hơn. Vậy nên đừng bao giờ xem nhẹ việc trả lời thư mời phỏng vấn cho nhà tuyển dụng.

2. Nên viết mail trả lời thư mời phỏng vấn như thế nào?

2.1. Về thời gian

Càng sớm càng tốt. Ngay khi nhận được thư mời phỏng vấn hay cuộc gọi mời phỏng vấn, ứng viên nên xác nhận lịch hẹn với nhà tuyển dụng. Trong trường hợp bạn không thể sắp xếp thời gian tham gia vào khung giờ đó thì việc này cũng giúp bạn và nhà tuyển dụng dễ dàng hơn trong việc tìm một thời điểm phỏng vấn khác thích hợp hơn.

Ngoài ra, nếu có bất cứ thắc mắc nào về thời gian, địa điểm hay nội dung làm việc, ứng viên đều có thể đề cập đến trong thư và nhà tuyển dụng sẽ hỗ trợ trả lời bạn vấn đề này một cách tốt nhất. Sự tiện lợi cho cả đôi bên sẽ giúp tiết kiệm được rất nhiều thời gian cho buổi phỏng vấn.

thời gian phản hồi

Một điểm cần lưu ý là trong thư mời phỏng vấn bạn nhận được, nếu thấy mail được đến những địa chỉ email khác trong công ty để cùng tham gia phỏng vấn, thì ứng viên khi trả lời nên chọn Trả lời tất cả – Reply All. Vì họ cũng là thành phần tham gia vào quá trình phỏng vấn bạn nên việc thông báo có thể đến phỏng vấn được hay không là rất cần thiết.

2.2. Cách đặt tiêu đề email

Đối với một email, tiêu đề là điều cực kỳ quan trọng, một bức thư không có tiêu đề sẽ tạo cảm giác khó chịu và khiến người nhận bỏ qua nó. Hoặc nếu có đặt tiêu đề cho email nhưng quá lan man, dài dòng và không thể hiện rõ được nội dung vấn đề, đây cũng là một điểm trừ cho ứng viên.

Bạn nên đặt tiêu đề email theo công thức như sau: [THƯ XÁC NHẬN THAM GIA PHỎNG VẤN – VỊ TRÍ ỨNG TUYỂN – HỌ TÊN]

Ví dụ: THƯ XÁC NHẬN THAM GIA PHỎNG VẤN – MARKETING EXECUTIVE – NGUYEN VAN A

Xem thêm Viết Đơn Xin Việc Như Thế Nào Để Gây Ấn Tượng Với Nhà Tuyển Dụng?

2.3. Viết nội dung trả lời thư mời phỏng vấn như thế nào để tạo ấn tượng?

Hãy mở đầu với phần chào hỏi. Hãy sử dụng những cách chào hỏi quen thuộc khi viết mail như “Dear HR Dept”, “Dear Ms/Mr A”, “Kính gửi phòng Nhân sự”,… Những cách chào hỏi như thế này vừa thể hiện thái độ lịch sự vừa đảm bảo không bỏ sót bất cứ cá nhân nhận thư nào.

Sau khi đã gửi lời chào, điều tiếp theo chắc chắn bạn không nên bỏ qua đó chính là gửi lời cảm ơn đến nhà tuyển dụng về thư mời phỏng vấn. Sau đó hãy đề cập đến lý do viết thư, rằng bạn đang xác nhận có thể đến và tham gia phỏng vấn hay không. Nếu không thể tham gia vào khung giờ nhà tuyển dụng đưa ra, bạn có thể trao đổi thêm về thời gian mình có thể tham gia và đề xuất với nhà tuyển dụng.

Ngoài ra, ứng viên cũng có thể đề cập đến một số vấn đề mình vẫn còn thắc mắc để được nhà tuyển dụng giải đáp và có dữ liệu tốt nhất khi tham gia phỏng vấn. Chẳng hạn như các thông tin cụ thể hơn về công việc ứng tuyển hay chi tiết công việc, hoặc người tham gia phỏng vấn, ngôn ngữ dùng trong buổi phỏng vấn,…

Thank you!

Kết thư bạn nên cảm ơn lại nhà tuyển dụng một lần nữa và thể hiện mong chờ cho một cơ hội hợp tác tốt đẹp trong tương lai.

Nếu có thể, hãy nhớ tạo một chữ ký cá nhân trong email của bạn. Chữ ký có thể không quá cần thiết nhưng nó thể hiện sự chuyên nghiệp và cẩn thận của người gửi mail. Chữ ký email nên bao gồm họ tên đầy đủ, số điện thoại, địa chỉ email và địa chỉ liên lạc cá nhân, vị trí công việc,…

3. Mẫu email trả lời thư phỏng vấn tham khảo

Mẫu 1:

Tiêu đề: THƯ XÁC NHẬN THAM GIA PHỎNG VẤN – MARKETING EXECUTIVE – NGUYEN VAN A

Dear HR Dept / Kính gửi Phòng Nhân sự Công ty…

Tôi rất vui khi nhận được thư mời phỏng vấn vị trí nhân viên Marketing tại quý công ty. Rất cảm ơn sự ghi nhận của công ty với những kinh nghiệm và kỹ năng mà tôi đã đề cập trong CV.

Tôi viết thư này để xác nhận sẽ đến và tham gia buổi phỏng vấn đúng giờ,

Rất mong buổi phỏng vấn sẽ diễn ra tốt đẹp và tôi có cơ hội hợp tác lâu dài cùng công ty,

Trân trọng,

Nguyễn Văn A

——-

Xem thêm Giới thiệu bản thân trong CV và bí quyết ghi điểm trong mắt nhà tuyển dụng

Mẫu 2:

Tiêu đề: THƯ PHẢN HỒI V/V KHÔNG THỂ THAM GIA PHỎNG VẤN – FULLSTACK DEVELOPER – TRẦN VĂN B

Dear HR Dept / Dear Ms A / Kính gửi quý Công ty H

Tôi rất vui khi biết được CV của mình đã được thông qua và được mời tham gia phỏng vấn cho vị trí Full-stack Developer tại công ty. Tuy nhiên, vì một số lý do cá nhân mà tôi không thể tham gia phỏng vấn theo thời gian mà công ty đã thông báo.

Tôi rất lấy làm tiếc vì vấn đề bất khả kháng này. Rất hy vọng có thể hợp tác với quý công ty trong thời gian gần nhất.

Trân trọng,

Trần Văn B.

——–

Trả lời thư mời phỏng vấn nên là điều cơ bản mà bạn cần thực hiện khi nhận được thư mời phỏng vấn. Không cần quá cầu kỳ hay trau chuốt, hãy cố gắng duy trì sự tích cực trong câu trả lời của mình và xác nhận đúng thông tin mà nhà tuyển dụng cần. Hy vọng series các bài viết liên quan đến việc ứng tuyển, soạn CV và trao đổi với nhà tuyển dụng của topdev.vn/blog sẽ giúp ích hơn cho bạn.

Có thể bạn quan tâm:

Xem thêm tuyển dụng các vị trí IT hấp dẫn trên TopDev

Hướng dẫn cài đặt xampp trên windows

cài đặt xampp
Hướng dẫn cài đặt xampp trên windows

Bài viết được sự cho phép của tác giả Lê Chí Dũng

Sau khi sử dụng Xampp + tài liệu cóp được tổng hợp lại Xampp như sau:

– XAMPP là phần mềm tích hợp các phần mềm dành cho Web Server có thể cài đặt được trên nhiều nền tảng khác nhau.

 X đầu tiên trong cụm từ XAMPP ám chỉ cross-platform nghĩa là XAMPP có thể chạy trên Windows,  Linux, Mac OS, Solaris.

A = Apache, XAMPP bao gồm 1 Apache HTTP Server giúp chạy các ứng dụng web.

M = MySQL, hệ quản trị cơ sở dữ liệu My SQL cũng được bao gồm trong XAMPP

P = PHP – ngôn ngữ lập trình PHP

P = Perl – tích hợp thư viện PEAR (Xem thêm thông tin tại http://pear.php.net/)

Ngoài ra XAMPP còn hỗ trợ các thành phần khác như: Webmail – FTP – SSL, OpenSSL, pdf class, mhash, IMAP C-Client….

Ta tiến hành cài đặt phần mềm XAMPP

1. Bạn tải phần mềm XAMPP bản mới nhất 2015 tại địa chỉ: https://www.apachefriends.org

2. Tiến hành chạy file cài XAMPP đã tải về ở trên

3. Bạn nhấn Next để tiếp tục quá trình cài đặt

4. Tiếp tục chọn Next. Hiện tại, trong mục Select a folder, mặc định là C:\xampp, nghĩa là bạn đang cài đặt phần mềm vào thư mục xampp của ổ đĩa C. Tuy nhiên, nếu để ở ổ C, khi bạn cài lại máy tính sẽ có khả năng bị xóa mất thư mục cài đăt đó.

Trong ví dụ này, mình sẽ cài đặt phần mềm XAMPP vào ổ đĩa D: bằng cách xóa đường dẫn “C:\xampp” và thay bằng “D:\xampp” trong phần Select a folder.

Xem thêm tuyển dụng Xamarin lương cao trên TopDev

5. Khi đó, thư mục cài đặt sẽ được chuyển sang ổ D:\xampp. Tiếp tục nhấn Next

6.Chọn Next

Bài viết gốc được đăng tải tại lcdung.top

Có thể bạn quan tâm:

Xem thêm các việc làm ngành CNTT hấp dẫn trên TopDev

Generate API contract sử dụng OpenAPI Generator Maven plugin

generate api contract
Generate API contract sử dụng OpenAPI Generator Maven plugin

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Sau khi định nghĩa API specs với RAML, chúng ta có thể sử dụng Spring MVC-RAML Maven plugin để generate API contract. Với API specs sử dụng OpenAPI Specification thì các bạn có thể sử dụng OpenAPI Generator Maven plugin để làm điều này. Cụ thể như thế nào? Chúng ta sẽ cùng nhau tìm hiểu trong bài viết này các bạn nhé!

Đầu tiên, mình sẽ tạo mới một Spring Boot project với Web dependency:

để làm ví dụ.

Kết quả:

 

Để làm ví dụ, mình sẽ sử dụng API specs được định nghĩa trong bài viết Cơ bản về định nghĩa RESTful Web Service API specs sử dụng OpenAPI Specification. Các bạn có thể lấy nội dung của API specs này ở đây, copy tất cả các tập tin, folder vào thư mục src/main/resources/api của project của chúng ta:

Bây giờ chúng ta sẽ khai báo OpenAPI Generator Maven plugin cơ bản như sau:

<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.4.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api/student.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>com.huongdanjava.openapispring.web</apiPackage>
<modelPackage>com.huongdanjava.openapispring.dto</modelPackage>
<output>.</output>
<configOptions>
<delegatePattern>true</delegatePattern>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>

Ở phần configuration, tag <inputSpec> sẽ trỏ đến location của tập tin API specs.

OpenAPI Generator Maven plugin cho phép chúng ta có thể generate API contract cho nhiều loại project với ngôn ngữ lập trình khác nhau. Các bạn cần khai báo Generator name để chỉ định loại project mà mình muốn generate. Danh sách Generator ở đây. Ở đây, mình đã khai báo để generate Spring project.

Mỗi Generator định nghĩa rất nhiều config option khác nhau, cho phép chúng ta generate project theo ý muốn của mình. Các bạn khai báo các config option này trong tag <configOptions>. Trong ví dụ trên, mình đang sử dụng spring generator và mình đang sử dụng một configOption của generator này là <delegatePattern> với giá trị true để sử dụng Delegate Design Pattern trong phần implementation cho API specs, tách biệt giữa interface class và phần implementation. Còn rất nhiều config option khác, các bạn có thể take a look thêm ở đây.

Tag <apiPackage> dùng để khai báo tên package mà các Controller class được generate sẽ sử dụng, còn <modelPackage> thì liên quan đến package mà các DTO class được generate sẽ sử dụng.

Tag <output> sẽ là folder chứa các tập tin được generate. Ở đây mình cấu hình tag này với giá trị “.” để chọn thư mục hiện hành. Các bạn có thể sử dụng giá trị “.” hoặc ${project.basedir} đều được.

Một điểm các bạn cần lưu ý là khi OpenAPI Generator Maven plugin generate Spring project cho chúng ta, nó sẽ override cả tập tin pom.xml, nên lúc này các bạn hãy backup lại nội dung của tập tin pom.xml trước. Mình sẽ nói các bạn cách cấu hình để OpenAPI Generator Maven plugin skip generate và override tập tin pom.xml sau!

Lúc này, nếu các bạn run project với “mvn clean compile”, refresh project, các bạn sẽ thấy kết quả như sau:

Như các bạn thấy, OpenAPI Generator Maven plugin đã generate cho chúng ta một Spring project.

Kiểm tra tập tin pom.xml, như mình đã nói ở trên, các bạn sẽ thấy nó override luôn cả tập tin pom.xml. Để skip generate và override tập tin pom.xml này, các bạn hãy mở tập tin .openapi-generator-ignore trong thư mục root của project, và thêm dòng “pom.xml” là được. Copy lại nội dung của tập tin pom.xml mà các bạn đã backup, những lần compile sau, tập tin pom.xml này sẽ không bị OpenAPI Generator Maven plugin override nữa!

Chạy lại “mvn clean compile”, kiểm tra console log, các bạn sẽ thấy những tập tin sau được generate:

INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/com/huongdanjava/openapispring/dto/Response.java
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/com/huongdanjava/openapispring/dto/Student.java
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/com/huongdanjava/openapispring/web/StudentsApiController.java
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/com/huongdanjava/openapispring/web/StudentsApi.java
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/com/huongdanjava/openapispring/web/StudentsApiDelegate.java
[INFO] Ignored /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/pom.xml (Ignored by rule in ignore file.)
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/README.md
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/org/openapitools/OpenAPI2SpringBoot.java
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/org/openapitools/RFC3339DateFormat.java
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/resources/application.properties
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/org/openapitools/configuration/HomeController.java
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/resources/openapi.yaml
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/src/main/java/com/huongdanjava/openapispring/web/ApiUtil.java
[INFO] Skipped /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/.openapi-generator-ignore (Skipped by supportingFiles options supplied by user.)
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/.openapi-generator/VERSION
[INFO] writing file /Users/khanh/Documents/code/huongdanjava.com/openapi-generator-spring/.openapi-generator/FILES

Package com.huongdanjava.openapispring.dto sẽ chứa các DTO là các Schema object mà chúng ta định nghĩa trong tập YAML.

Package com.huongdanjava.openapispring.web sẽ chứa các Controller class tương ứng với các request URL, được tổ chức theo Delegate Design Pattern, ngoài ra các bạn còn thấy một class ApiUtil định nghĩa các utilities method sử dụng trong các Controller class.

  Build executable jar sử dụng Maven Shade Plugin
  Chạy ứng dụng web với Liberty Maven plugin

Xem thêm các việc làm Magento hấp dẫn trên TopDev

Package org.openapitools định nghĩa một Controller khác, cho phép chúng ta có thể lấy nội dung của tập tin src/main/resources/openapi.yaml được generate bởi OpenAPI Generator Maven plugin. Nội dung của tập tin openapi.yaml này được convert từ nội dung của tập tin YAML của chúng ta, trong ví dụ này của mình là student.yaml.

Tập tin application.properties cũng bị override để thêm một số properties sau:

server.port=8081
spring.jackson.date-format=org.openapitools.RFC3339DateFormat
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false

Các bạn nên add tập tin application.properties này vào tập tin .openapi-generator-ignore “src/main/resources/application.properties” để khỏi bị override nữa!

Một số dependencies cần được thêm vào tập tin pom.xml để project chúng ta có thể compile được, như sau:

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.6</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.2</version>
</dependency>

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>

Các bạn cũng cần phải sửa code của class OpenapiGeneratorSpringApplication để ứng dụng của chúng ta scan đầy đủ các Controller như sau:

package com.huongdanjava.openapispring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
//// @formatter:off
@ComponentScan(
basePackages = {
"org.openapitools",
"com.huongdanjava.openapispring",
"org.openapitools.configuration"
}
)
// @formatter:on
public class OpenapiGeneratorSpringApplication {

public static void main(String[] args) {
SpringApplication.run(OpenapiGeneratorSpringApplication.class, args);
}

}

Bây giờ, nếu các bạn chạy ứng dụng và đi đến http://localhost:8081/, các bạn sẽ thấy kết quả như sau:
Đây chính là Swagger API documentation đó các bạn! Sử dụng nó, các bạn sẽ biết ứng dụng của chúng ta có bao nhiêu request URL được expose. Thông tin của mỗi request URL như thế nào, và chúng ta có thể sử dụng Swagger API documentation này để gọi đến các request URL thực tế luôn.

Hiện tại thì chúng ta chưa implement cho các request URL. Các bạn có thể thêm mới class implement interface StudentsApiDelegate để làm điều này. Ví dụ của mình như sau:

package com.huongdanjava.openapispring.web.impl;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import com.huongdanjava.openapispring.dto.Student;
import com.huongdanjava.openapispring.web.StudentsApiDelegate;

@Service
public class StudentApiDelegateImpl implements StudentsApiDelegate {

@Override
public ResponseEntity<Student> getStudentById(String ID) {
Student student = new Student();
student.setId(1L);
student.setCode("123");
student.setName("HDJ");

return ResponseEntity.ok(student);
}

}

Kết quả nếu chúng ta request tới request URL http://localhost:8081/api/students/1 lúc này như sau:

 

Có thể bạn quan tâm:
Xem thêm các việc làm ngành CNTT hấp dẫn trên TopDev

Những ai có thể làm Nghề Nhân sự?

nghề nhân sự
Những ai có thể làm Nghề Nhân sự?
Bài viết được sự cho phép của hrvnacademy.com
Nếu bạn đã có lựa chọn ngay từ khi tốt nghiệp THPT, và theo học đúng chuyên nghành Quản trị Nhân sự thì đây hoàn toàn là một lợi thế để bắt đầu với công việc này

Chào mừng các bạn đang đến với series các bài học trong Khóa học Nhân sự cơ bản cho người mới (newbie) hoàn toàn miễn phí. Tôi là Thành HR sẽ đồng hành cùng bạn trong khoá học này. Và chủ đề mà chúng ta cùng nhau tìm hiểu ngày hôm nay là Những ai có thể làm Nghề Nhân sự?

Trong bài học Nghề Nhân sự là gì, mình đã cùng nhau tìm hiểu và có cái nhìn tổng quan về Nghề Nhân sự rồi. Vậy để trở thành một chuyên viên Nhân sự khó hay dễ? Học nghành gì để làm được công việc này? Học trái nghành thì sao? Nếu bạn đang là người ngoại đạo và muốn tìm hiểu về nó thì đây là bài học dành cho bạn.

Theo quan sát từ thực tế của mình đến thời điểm này, thì có những lý do hoặc có thể gọi là duyên để bạn đến với công việc Nhân sự. Mình gọi là duyên vì thực sự có những bạn đã, đang làm nghề này nhưng có bằng cấp chuyên nghành khác. Mình cùng nhau bắt đầu bài học Những ai có thể làm Nghề Nhân sự để tìm hiểu xem ai có thể trở thành chuyên viên nhân sự bạn nhé!

  20 thuật ngữ chuyên sâu trong Quản lý Nhân sự nội bộ
  3 hướng dẫn để bắt đầu kinh doanh trong lĩnh vực nhân sự

Xem thêm tuyển dụng Technical Writer lương cao trên TopDev

Bạn được đào tạo bài bản đúng chuyên ngành

Nếu bạn đã có lựa chọn ngay từ khi tốt nghiệp THPT và theo học đúng chuyên nghành Quản trị Nhân sự thì đây hoàn toàn là một lợi thế để bắt đầu với công việc này. Bạn sẽ có kiến thức nền tảng tốt hơn về nghề, các kỹ năng bài bản, cũng như ít nhiều được tiếp xúc với Nghề nhân sự trong thời gian học tại trường và thời gian thực tập tại doanh nghiệp.

Vậy nên con đường trở thành chuyên viên Nhân sự của bạn sẽ dễ dàng hơn. Và mình muốn nhấn mạnh, nó là lợi thế chứ không phải là yếu tố duy nhất quyết định bạn sẽ thành công khi học đúng chuyên ngành nhé!

Tuy nhiên, thực tế thì có những bạn ra trường lại không theo đuổi nghề này. Mà thay vào đó, họ chọn làm nhân viên kinh doanh, marketing hoặc làm các công việc trái nghành khác để phát huy hết các điểm mạnh của riêng họ.

Theo cá nhân mình, tính cách bạn có một chút xíu hướng nội, tức là nhóm S trong mô hình nhận diện tích cách DISC sẽ phù hợp với nghề nhân sự này hơn. Điều này không đồng nghĩa là các nhóm tính cách khác thì không được làm nghề này bạn nhé!

Bạn là Kế toán trong một công ty nhỏ

Thường ở các công ty quy mô nhỏ, bộ phận kế toán hay kiêm luôn việc tính lương, làm bảo hiểm xã hội cho nhân viên công ty. Thậm chí cả việc tuyển dụng nhân viên khi có người nghỉ cần thay thế. Tức là công ty không có bộ phận tuyển dụng và C&B. Nên bạn có cơ hội tìm tòi và tìm hiểu Nghề Nhân sự để phục vụ công việc.

Đến một lúc nào đó, khi công ty mở rộng quy mô và thành lập thêm Phòng Nhân sự thì bạn được điều chuyển hoặc chủ động xin chuyển qua phòng ban mới. Có thể trong quá trình phụ trách các công việc liên quan, bạn lại cảm thấy thích thú hơn với nghề này và quyết định chuyển hướng. Nó tuyệt vời đó chứ!

Bạn bắt đầu từ một thực tập sinh

Rất nhiều bạn học chuyên ngành quản trị kinh doanh hoặc các nghành nghề khác, rồi quyết định chọn Nhân sự làm nghề nghiệp sẽ theo đuổi. Chúc mừng bạn đã biết mình thích gì và sẽ phải làm gì. Việc này giúp bạn biết bản thân có thể bắt đầu Nghề Nhân sự như thế nào là phù hợp

Thường các bạn sẽ bắt đầu bằng việc xin thực tập ở các vị trí của Phòng Nhân sự  từ 6 tháng đến 1 năm để có cơ hội học hỏi và theo đuổi nó. Mình đánh giá rất cao điều này, vì bạn đã có hành động đúng để thực hiện ước mơ và đam mê của mình.

Và thực tế trong quá trình làm nghề của mình, có ghi nhận một số ít bạn thực tập và học nghề tại Phòng Nhân sự khoảng 1 đến 2 tháng, nhưng sau đó thấy không hợp. Và chuyển qua làm nhân viên kinh doanh hoặc tự khởi nghiệp. Điều đó hoàn toàn bình thường và không có gì đáng tiếc bạn nhé! Quan trọng là bạn đã nhận ra đam mê của mình và không hối tiếc về sau, kiểu như cứ tự trách bản thân là biết ngày đó mình theo nghề nhân sự thì hay rồi…

Bạn bắt đầu bằng một khóa học ngắn hạn

Ra trường với tấm bằng đại học, rất nhiều bạn loay hoay mãi để tìm cho mình điểm bắt đầu. Sau đó đi học tất cả các khóa học nghiệp vụ có thể: nào là Kế toán, Nhân sự, Marketing,…Một số ít tìm được niềm đam mê và dấn thân với nghề Nhân sự.

Hành trình bắt đầu nào cũng khó khăn cả, nhưng trong các trường hợp trên mà mình đưa ra là tất cả họ đều quyết tâm thực hiện, vượt qua khó khăn thử thách để thực hiện đam mê của mình. Chứ không chỉ là ước mơ viễn vông.

Và đó là các ví dụ có thật trong thực tế làm nghề mình thấy, cá nhân mình cũng xuất phát từ một tấm bằng trái ngành. Qua đây, mình muốn truyền tải đến các bạn thông điệp là chỉ cần bạn chọn, xác định nghề Nhân sự phù hợp với bạn để theo đuổi lâu dài, thì chắc chắn sẽ có cách, nếu bạn bắt đầu vạch ra kế hoạch để thực hiện nó, thay vì chỉ ước mơ.

Chúc các bạn thành công.

Bài viết gốc được đăng tải tại hrvnacademy.com


Tuyển Dụng Nhân Tài IT Cùng TopDev
Đăng ký nhận ưu đãi & tư vấn về các giải pháp Tuyển dụng IT & Xây dựng Thương hiệu tuyển dụng ngay!
Hotline: 028.6273.3496 – Email: contact@topdev.vn
Dịch vụ: https://topdev.vn/page/products

Có thể bạn quan tâm:

OrgMode trên Vim

OrgMode trên Vim
OrgMode trên Vim

Bài viết được sự cho phép của tác giả Huy Trần

OrgMode là một chức năng rất hữu dụng trên Emacs.

Bạn có thể sử dụng OrgMode để quản lý công việc (todo list), hay làm công cụ soạn thảo nội dung có trình bày một cách hệ thống,… tùy theo nhu cầu cá nhân.

Trên Vim, chúng ta cũng có thể sử dụng OrgMode bằng cách cài plugin vim-orgmode.

  Cách mạng 0.5 của Neovim: Lua và Built-in LSP Client
  Cách mạng 0.4 của Neovim: Floating Window

Xem thêm tuyển dụng Project Coordinator trên TopDev

Cài đặt

Cài với VimPlug hoặc Vundle,… như sau:

Plug 'jceb/vim-orgmode'
Plug 'tpope/vim-speeddating'

Ngoài plugin này chúng ta có cài thêm plugin vim-speeddating để sử dụng chức năng insert ngày tháng.

Chúng ta có thể cấu hình một vài phím tắt thông dụng (sẽ giải thích bên dưới):

" OrgMode Keys
nnoremap <Leader><Tab> :OrgCheckBoxToggle<CR>
nnoremap <Leader>nc :OrgCheckBoxNewBelow<CR>
nnoremap <Leader>nd :OrgDateInsertTimestampActiveCmdLine<CR>
nnoremap <Leader>td :OrgBufferAgendaTodo<CR>
nnoremap <Leader>wa :OrgBufferAgendaWeek<CR>

Sử dụng

Sau khi cài đặt xong thì Vim sẽ tự động chuyển sang orgmode khi mở một file *.org bất kì.

Giả sử chúng ta có file ~/today.org, mở file này với vim bạn sẽ thấy kí hiệu org ở phần filetype, cho biết đây là file mở dạng orgmode, nếu không thấy thì chúng ta có thể set filetype tự động bằng lệnh:

:set ft=org

Heading

Heading là các đầu mục để bạn có thể dễ dàng phân loại, tổ chức nội dung của mình (tương tự các thẻ h1h2,… trong HTML)

Chèn heading

Để chèn một đầu mục (heading), bạn dùng các dấu sao * theo thứ tự của nó, ví dụ:

* Heading level 1
** Heading level 2
*** Heading level 3
** Another Heading level 2

Di chuyển giữa các heading

Khi đứng tại một heading, bạn có thể gõ } hoặc { để nhảy nhanh tới các heading tiếp theo/trước đó.

Nâng cấp, giáng cấp heading

Bạn cũng có thể chuyển một heading level 1 thành level 2 (giáng cấp – demote) hoặc một heading level 3 thành level 2 (nâng cấp – promote) bằng cách gõ >> hoặc <<.

Đóng/mở heading

Đứng tại một heading, bạn có thể nhấn phím TAB hoặc SHIFT-TAB để đóng (collapse) hoặc mở (expand) heading đó và tất cả các heading con bên trong nó theo thứ tự xoay vòng:

,-> FOLDED -> CHILDREN -> SUBTREE --.
  '-----------------------------------'

Task

Trong mỗi heading, bạn có thể gõ vào bất kì nội dung gì bạn muốn. Trong trường hợp sử dụng orgmode để làm todo list, thì nội dung bạn gõ nhiều nhất đó chính là các task.

Chèn task

Để chèn một task ta có thể chèn theo cú pháp sau:

- [ ] <nội dung task>

Đây là một task dạng check box để chúng ta có thể tick vào hoặc bỏ tick nó bất cứ lúc nào.

Để chèn nhanh, có thể xài phím tắt đã config ở trên kia là <Leader>nc (n-c có nghĩa là new check).

Tick và bỏ tick

Để tick vào một task, bạn điền dấu X vào phần trống giữa dấu ngoặc vuông [ ]:

- [X] <nội dung task>

Hoặc tick tự động bằng phím <Leader><Tab>.

Lặp lại thao tác này sẽ bỏ tick.

Làm việc với agenda

Agenda là một chức năng cực kì hay giúp tổng hợp nội dung trên file org hiện tại thành một bảng tóm tắt, cho chúng ta cái nhìn tổng quan về các task ở trạng thái TODO, hoặc tóm tắt theo ngày, theo tuần.

Trạng thái TODO/DONE

Bạn có thể thêm trạng thái TODO hoặc DONE vào đầu mỗi heading để dễ dàng theo dõi và để khởi tạo agenda, ví dụ:

* Today
** TODO Project A
  ...
** DONE Project B

Nội dung như trên có nghĩa là mục Project A đang ở trạng thái TODO và mục Project B ở trạng thái DONE.

Các nội dung có trạng thái DONE sẽ không được hiện ra khi xem agenda.

Chèn ngày tháng

Để chèn ngày tháng tại vị trí hiện tại của con trỏ, chúng ta dùng lệnh <Leader>nd (n-d là new date).

Dòng lệnh hiện ra cho phép bạn chọn ngày hiện tại bằng cách nhấn <Enter>, hoặc tăng giảm số ngày bằng cách gõ vào, ví dụ: +1d (tăng 1 ngày), +4w (tăng 4 tuần), -5m (giảm 5 tháng),…

Xem agenda các task TODO

Để xem danh sách các mục có trạng thái TODO, ta gõ <Leader>td (t-d tức là todo).

Khi ở màn hình danh sách To Do, bạn có thể dùng phím <Tab> để nhảy đến nội dung tương ứng trên file.

Xem agenda trong tuần

Để xem danh sách các task cần làm trong tuần, chúng ta dùng lệnh <Leader>wa (w-a là weekly agenda).


Trên đây là một vài thao tác cơ bản để bạn có thể bắt đầu sử dụng vim-orgmode một cách hiệu quả. Trong quá trình sử dụng, bạn có thể tìm hiểu thêm thông qua hướng dẫn sử dụng của vim-orgmode trên Github để làm việc hiệu quả hơn nhé.

Happy vimming ^^

Bài viết gốc được đăng tải tại snacky.blog

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực CNTT hấp dẫn trên TopDev

Cơ bản về định nghĩa RESTful Web Service API specs sử dụng OpenAPI Specification

restful web service
Cơ bản về định nghĩa RESTful Web Service API specs sử dụng OpenAPI Specification

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Mình đã hướng dẫn các bạn cách định nghĩa RESTful Web Service API specs sử dụng RAML. Có một cách khác để làm điều này là sử dụng OpenAPI Specification. Cụ thể như thế nào? Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu làm thế nào để định nghĩa RESTful Web Service API specs sử dụng OpenAPI Specification các bạn nhé!

  Giới thiệu Feign – Tạo ứng dụng Java RESTful Client không thể đơn giản hơn
  REST Web service: Tạo ứng dụng Java RESTful Client với Jersey Client 2.x

Xem thêm các việc làm REST API hấp dẫn trên TopDev

Chúng ta sẽ sử dụng tập tin YAML hoặc JSON để định nghĩa API specs với OpenAPI Specification. Dưới đây là nội dung tập tin YAML định nghĩa API specs trong bài viết Giới thiệu về RAML sử dụng OpenAPI Specification:

openapi: 3.0.3
info:
title: Student Information Management System
version: "1.0"
servers:
- url: https://localhost:8081/api
paths:
/students:
get:
operationId: getStudents
summary: Get all students
responses:
200:
description: Get all students successfully
content:
application/json:
example: [{ "ID": 1, "code": "001", "name": "Khanh" }, { "ID": 2, "code": "002", "name": "Quan" }]
/students/{ID}:
get:
operationId: getStudentById
summary: Get a student by ID
parameters:
- name: ID
in: path
description: "ID of the Student"
required: true
schema:
type: string
responses:
200:
description: Get student information successfully
content:
application/json:
example: { "ID": 1, "code": "001", "name": "Khanh" }
delete:
operationId: deleteStudentById
summary: Delete a student by ID
parameters:
- name: ID
in: path
description: "ID of the Student"
required: true
schema:
type: string
responses:
200:
description: Delete student information successfully
content:
application/json:
example: { "message": "Student deleted!"}

Như các bạn thấy, tương tự như định nghĩa API specs với RAML, ở đầu tập tin YAML này, chúng ta sẽ định nghĩa một số thông tin overview về API specs của mình. Chúng ta sử dụng field openapi để định nghĩa OpenAPI Specification version chúng ta sẽ sử dụng để định nghĩa API specs, thông tin về API specs bao gồm mục đích của nó (field info.title), version của API specs này (info.version). Base URL sẽ được định nghĩa sử dụng field servers.url.

Các request URL sẽ được định nghĩa với section paths.

Chúng ta không định nghĩa các request URL với giá trị bắt đầu giống nhau theo kiểu extends như bên RAML được. Chúng ta chỉ có thể gom các request có HTTP method khác nhau nhưng cùng một request URL. Trong ví dụ trên thì request URL “/students/{ID}” được định nghĩa với GET và DELETE method, chúng ta có thể gom 2 request URL này lại với nhau.

Chúng ta có thể sử dụng field operationId để identify cho mỗi request URL. Giá trị của field này là duy nhất trong 1 API specs cho mỗi request URL.

Tương tự như RAML, chúng ta cũng có thể định nghĩa response cho mỗi response status code, mỗi response có thể có kiểu dữ liệu khác nhau. Trong ví dụ của mình thì đó là application/json.

Để định nghĩa parameter cho các request URL, chúng ta sẽ sử dụng section parameters trong mỗi request. Các parameter có thể là path parameter, query parameter hay nằm trong header, cookie. Chúng ta sử dụng field in của mỗi parameter để khai báo điều này.

OpenAPI cũng hỗ trợ nhiều kiểu dữ liệu khác nhau như RAML. Các bạn có thể tham khảo thêm ở đây các bạn nhé!

Việc định nghĩa API specs trước khi bắt tay vào implement API sẽ giúp chúng ta thống nhất được API contract, các bên liên quan chỉ cần follow theo API contract này để sử dụng, giảm thiểu rất nhiều rủi ro có thể xảy ra. Hãy suy nghĩ tới nó trước khi bắt đầu implement các API RESTful Web Service các bạn nhé!

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

Xem thêm các việc làm ngành CNTT hấp dẫn trên TopDev

Giới thiệu Web service – SOAP, WSDL và ASP.NET Web Service cơ bản

web service
Giới thiệu Web service – SOAP, WSDL và ASP.NET Web Service cơ bản

Bài viết được sự cho phép của tác giả Lê Chí Dũng

Hôm nay ôn lại bài web service vừa học trên lớp sẳn tìm kiếm thông tin về web service và mình vừa thu thập được một số thông tin hay về web service và share cho ace xem. Trước tiên là phần cơ bản về SOAP, WSDL, ASP.NET Web Service (trích từ YinYangit.wordpress.com)

Web Service là gì?

– Dịch vụ Web (Web Service)  là một chuẩn để tích hợp các ứng dụng trên nền web (Web-based applications). Các ứng dụng có thể sử dụng các thành phần khác nhau để tạo thành một dịch vụ.Về bản chất, Web service dựa trên XML và HTTP, trong đó XML làm nhiệm vụ mã hóa và giải mã dữ liệu và dùng SOAP để truyền tải. Web Service không phụ thuộc vào platform nào, do đó bạn có thể dùng Web Service để truyền tải dữ liệu giữa các ứng dụng hay giữa các platform.

Ví dụ như máy chủ chạy một trang web thương mại điện tử kết nối với cổng thanh toán điện tử qua một API ( Application Programming Interface – tạo bởi công nghệ .NET) thì web services chính là nền máy chủ (IIS – Internet Information Services), và các thành phần thanh toán, các thành phần .NET được coi là các thành phầm bên ngoài (component). Các thành phần này được gọi bởi phương thức SOAP (Khác phương thức POST, GET) nên không bị gặp phải firewall khi truy xuất các thành phần bên ngoài máy chủ. Và toàn bộ các thành phần đó gọi là một Web Services.

  10 trang web hàng đầu để tìm hiểu WordPress
  10 điểm nhấn về phát triển web trong mà bạn cần phải biết

Xem thêm tuyển Web Designer hấp dẫn trên TopDev

Một ví dụ về Web Service sẵn có là dịch vụ được cung cấp bởi PayPal cho phép những người có tài khoản có thể thanh toán trên các trang web thương mại như AMAZONE bằng cách thông qua SOAP nó sẽ lấy ID của người dùng nhập vào lúc thanh toán, sau đó ID đó sẽ được đưa đến Web Service của PayPal xử lý, sau khi xử lý xong thì nó sẽ trả kết quả cho trang thanh toán của AMAZONE là tài khoảng đó có thanh toán được hay là không. Và tất nhiên là các trang web thương mại và PayPal phải có mối liên kết với nhau thì mới có thể sử dụng Web Service của PayPal.

Sơ đồ tương tác giữa User và Web Service:

  Todo App ASP.NET MVC x Entity Framework

SOAP – Simple Object Access Protocol

SOAP – Một tiêu chuẩn của W3C,  là giao thức sử dụng XML để định nghĩa dữ liệu dạng thuần văn bản (plain text) thông qua HTTP. SOAP là cách mà  Web Service sử dụng để truyền tải dữ liệu. Vì dựa trên XML nên SOAP là một giao thức không phụ thuộc platform cũng như bất kì ngôn ngữ lập trình nào.

Một thông điệp SOAP được chia thành hai phần là header và body. Phần header chỉ ra địa chỉ Web Service, host, Content-Type, Content-Length tương tự như một thông điệp HTTP.

Khi tạo một dự án Web Service, mặc định Web Visual Develop sẽ tạo cho bạn phương thức HelloWorld() sau:

public string HelloWorld()
{
   return "Hello World";
}

Một HTTP Request sẽ có dạng sau:

POST /MathService.asmx/HelloWorld HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: length

Đối với SOAP (v1.2)

-Request:

POST /MathService.asmx HTTP/1.1
Host: localhost
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<HelloWorld xmlns="http://tempuri.org/" />
</soap12:Body>
</soap12:Envelope>

Trong phần <soap12:Body> của đoạn SOAP request trên, thẻ <HelloWorld xmlns=”http://tempuri.org/” /> được dùng để các phần tử con tương ứng với các dữ liệu mà phương thức HelloWorld yêu cầu để làm tham số. Bởi vì phương thức HelloWorld không yêu cầu bất kì tham số nào, nên thẻ này cũng không có bất kì phần tử con nào.

-Response:

HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<HelloWorldResponse xmlns="http://tempuri.org/">
<HelloWorldResult>string</HelloWorldResult>
</HelloWorldResponse>
</soap12:Body>
</soap12:Envelope>

Phương thức HelloWorld() của WebService trả về dữ liệu có dạng string, và bạn có thể thấy rõ điều này trong thẻ <soap12:Body>.

Nếu cần tìm hiểu thêm về SOAP, bạn có thể tham khảo các bài hướng dẫn trên W3schools:

http://www.w3schools.com/soap/default.asp

WSDL – Web Services Description Language

WSDL là ngôn ngữ được sử dụng để mô tả đầy đủ về Web Service theo chuẩn XML như các phương thức, kiểu dữ liệu,… dựa trên XML schema.

Ví dụ một đoạn định nghĩa kiểu dữ liệu của WSDL cho phương thức HelloWorld() trên:

[]
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
<s:element name="HelloWorld">
<s:complexType />
</s:element>
<s:element name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
[]

Trong đó, phần định nghĩa các kiểu dữ liệu dùng cho request được đặt tên dựa theo tên phương thức “HelloWorld”:

<s:element name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>

Theo sau đó là phần định nghĩa các kiểu dữ liệu dùng để response được đặt tên “HelloWorldResponse”:

Việc đặt tên này cần thiết để sử dụng trong phần định nghĩa message phía sau. Tham khảo hướng dẫn về XML Schema và WSDL tại các link sau:

http://www.w3schools.com/wsdl/default.asp

http://www.w3schools.com/schema/

Xem ngay tin việc làm .NET tại các doanh nghiệp hàng đầu trên TopDev

Tạo một ASP.NET Web Service đơn giản

Trong .Net, bạn tạo ra một Web Service ra bằng cách tạo một subclass của lớp System.Web.Services.WebService, sau đó định nghĩa các phương thức có thể được triệu gọi từ client. Các phương thức này phải được đánh dấu với attribute [WebMethod].

Khi tạo một dự án mới, nếu bạn đặt phiên bản .Net sử dụng là 4, template Web Service sẽ không tồn tại do Microsoft nghĩ rằng template đó quá đơn giản. Vì vậy muốn tạo một project Web Service, bạn phải chuyển phiên bản .Net sang 3.5 . Ở đây tôi vẫn sử dụng phiên bản .Net 4 và tạo một project ASP.NET Empty Web Application với tên là Y2FirstWebService.

Tiếp đó bạn thêm item Web Service (phím tắt Ctrl + Shift + A để mở cửa sổ Add New Item) với tên là HelloService.asmx.

File code-behind HelloService.asmx.cs khi được tạo ra đã chứa sẵn phương thức HelloWorld().

HelloService.asmx:

<%@ WebService Language=”C#” CodeBehind=”HelloService.asmx.cs%>

HelloService.asmx.cs (v1):

Bạn có thể nhấn F5 chạy thử, trình duyệt sẽ mở ra và hiển thị đường link với nội dung HelloWorld. Đây chính là tên của phương thức của lớp HelloService trên. Nhấn vào link này, bạn được đưa đến một trang dùng để test phương thức HelloWorld.

Note: Bạn có thể xem nội dung WSDL được tạo ra để mô tả cho Web Service này bằng cách bằng cách nhấn vào link Service Description với địa chỉ có dạng (chạy trên localhost, port của bạn có thể khác): http://localhost:1107/MathService.asmx?WSDL.

Bên dưới bạn có thể thấy thông điệp SOAP request và response sẽ được sử dụng để giao tiếp giữa client với Web Service.

Nhấn nút Invoke, trình duyệt sẽ mở ra một trang mới với nội dung theo định dạng XML, kết quả thực sự trả về của phương thức là nội dung nằm trong thẻ <string>:

<?xml version=1.0″ encoding=”utf-8″ ?>
<string xmlns=”http://tempuri.org/>Hello World</string>

Bây giờ ta thêm một phương thức mới với tên Hello, đồng thời tạo thêm class Person để làm kiểu trả về cho phương thức Hello() này.

HelloService.asmx.cs (v2):

Chạy lại Web Service và kiểm tra phương thức Hello(), giao diện trang web sẽ thay đổi cho phép bạn nhập tham số vào:

Nhập tên vào textbox và nhấn Invoke, trình duyệt sẽ mở ra trang kết quả:

Như bạn thấy dữ liệu trả về của phương thức Hello là một đối tượng kiểu Person có hai property Name và Age. Đối tượng này sẽ được mã hóa thành XML thành nội dung mà bạn thấy ở trên với tên các thẻ khớp với tên class, property mà ta khai báo.

Xem ngay tin Việc làm ASP.NET tại TopDev

Phần kết

Vậy là bạn đã tạo được một Web Service, và như bạn có thể thấy, công việc thật đơn giản. Tuy nhiên bạn vẫn chưa biết cách áp dụng Web Service vào một vấn đề cụ thể, cũng như chưa biết cách sử dụng Web Service như thế nào. Nếu bạn quan tâm đến đề tài này, vui lòng đón xem các phần sau. Tôi sẽ trình bày chi tiết hơn về Web Service cũng như cách tạo các ứng dụng client để truy xuất Web Service.

Bài viết gốc được đăng tải tại lcdung.top

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

Giới thiệu NUnit Framework

nunit framework
Giới thiệu NUnit Framework

Bài viết được sự cho phép của vntesters.com

Cũng giống như trong lập trình Python, chúng ta có PyUnit để viết các đoạn mã thực thi việc kiểm thử đơn vị (unit test); trong lập trình .Net, chúng ta có NUnit và MSTest, 2 công nghệ để tự động hóa việc kiểm thử đơn vị. MSTest là một testing framework được phát triển bởi đội ngũ Visual Studio của MicroSort, và được cài đặt chung với phiên bản Ultimate của Visual Studio. Tất nhiên, phiên bản này là phiên bản thương mại, và chúng ta phải mua bản quyền để sử dụng. Ngược lại, NUnit là một testing framework mã nguồn mở được phát triển bởi một nhóm lập trình viên (Charlie Poole, Rob Prouse and Simone Busoli), tương tác trực tiếp với Visual Studio, đồng thời có thể chạy độc lập mà không phụ thuộc vào Visual Studio.

  Giới thiệu JUnit
  Kiểm thử đơn vị trong C# với Nunit và .Net Core

Xem thêm các việc làm C++ lương cao trên TopDev

Cài đặt NUnit

Download chương trình cài đặt NUnit tại đây và cài đặt NUnit theo hướng dẫn như cài một ứng dụng bình thường, các bạn có thể xem clip dưới đây.

Các từ khóa cơ bản sử dụng trong khi viết kiểm thử tự động với NUnit

Thuộc tính trong NUnit để xác định lớp kiểm thử (test class), phương thức kiểm thử (test method), điều khiện đầu vào (pre-condition) và kết thúc (post-condition).

  • TestFixture: Được sử dụng ở đầu mỗi lớp, xác định lớp đó là một lớp kiểm thử.
  • Test: Được sử dụng ở đầu mỗi phương thức bên trong một lớp TestFixture, xác định đó là một phương thức kiểm thử.
  • SetUp: Được sử dụng ở đầu một phương thức bên trong một lớp TestFixture, xác định đó là một phương thức điều kiện đầu vào cho từng phương thức Test. Có thể có hoặc không trong một lớp TestFixture, và chỉ nên được khai báo một lần duy nhất trong một lớp TestFixture.
  • Teardown: Được sử dụng ở đầu một phương thức bên trong một lớp TestFixture, xác định đó là một phương thức điều kiện kết thúc cho từng phương thức Test. Có thể có hoặc không trong một lớp TestFixture, và chỉ nên được khai báo một lần duy nhất trong một lớp TestFixture.
  • SetUpFixture: Được sử dụng ở đầu một lớp, xác định một lớp kiểm thử cơ bản. Trong lớp này sẽ có điều kiện bắt đầu và điều kiện kết thúc cho toàn bộ các lớp TestFixture trong một không gian tên (namespace). Có thể có hoặc không trong một không gian tên, nhưng chỉ được khai báo một lần duy nhất.
  • TestFixtureSetUp: Được sử dụng ở đầu một phương thức bên trong một lớp TestFixture, xác định đó là một phương thức điều kiện bắt đầu cho từng lớp TestFixture. Có thể có hoặc không trong một lớp TestFixture, và chỉ được khai báo một lần duy nhất.
  • TestFixtureTearDown: Được sử dụng ở đầu một phương thức bên trong một lớp TestFixture, xác định đó là một phương thức điều kiện kết thúc cho từng lớp TestFixture. Có thể có hoặc không trong một lớp TestFixture, và chỉ được khai báo một lần duy nhất.

Trong bài tới, mình sẽ giải thích thêm về các từ khóa khác trong NUnit.
[adToAppearHere]

Thực thi các phương thức kiểm thử

Sau khi viết các đoạn mã kiểm thử, chúng ta không thể chạy trực tiếp các đoạn mã này trên Visual Studio mà phải chuyển nó thành ngôn ngữ máy, và thực thi trên ứng dụng NUnit hoặc Visual Studio (từ VS2013 trở lên).

Các bạn có thể download mã nguồn Visual Studio của đoạn mã trên ở đây (hướng dẫn download tu Tusfile).

Bài viết gốc được đăng tải tại vntesters.com

Có thể bạn quan tâm:

Cách Tạo CV Xin Việc Trên Điện Thoại Bằng Chức Năng TopDevCV

cách tạo cv xin việc trên điện thoại
Cách Tạo CV Xin Việc Trên Điện Thoại Bằng TopDevCV

Nếu đã từng mất rất nhiều thời gian để tham khảo và tạo được một chiếc CV hoàn chỉnh trên máy tính, chắc chắn bạn sẽ cảm thấy khá bất tiện. Hiện nay, app TopDev đã hỗ trợ tính năng tạo CV ngay trên chính chiếc điện thoại của bạn. Vừa nhanh gọn vừa cập nhật đầy đủ thông tin, lại cho ra một chiếc CV hoàn chỉnh nhất.

Một trong những đặc điểm nổi bật của CV được tạo trên app TopDev là thiết kế của CV được thiết kế theo chuẩn dành cho các devs với một cấu trúc thống nhất. Các nhà tuyển dụng sẽ dễ dàng hơn trong việc thu thập thông tin bạn cung cấp trong CV và có được đánh giá phù hợp.

Tạo CV nhanh chóng trên website của TopDev

Hơn nữa, các mẫu CV của TopDev đã được sàng lọc và lựa chọn kỹ càng từ CV của các lập trình viên nổi tiếng trên khắp thế giới, đảm bảo giúp bạn cung cấp đúng những gì cần thiết và quan trọng cho vị trí công việc của mình. Với app TopDev, CV của bạn sẽ được đồng bộ với tất cả các thiết bị chỉ trong một tài khoản, nhờ thế bạn sẽ dễ dàng theo dõi hồ sơ của mình ở bất cứ đâu, bất cứ thiết bị nào.

Vậy cách tạo CV xin việc trên điện thoại bằng app TopDev được thực hiện như thế nào?

Hướng dẫn chi tiết cách tạo CV xin việc trên điện thoại bằng app TopDev:

Bước 1: Mở ứng dụng TopDev trên điện thoại > TopDevCV

Bước 2: Chọn Upload/Create new CV > Create new CV

Bước 3: Điền tên CV và chọn ngôn ngữ bạn muốn dùng trong quá trình tạo CV > chọn Start Making CV

Bước 4: Bạn nên đọc qua hướng dẫn được đính kèm trong app để biết cách tạo nội dung CV chất lượng nhất.

Người dùng cần điền các trường thông tin bắt buộc cần có trong CV gồm:

  • Profile information
  • Summary introduction
  • Working experience
  • Technical skill
  • Education

Các trường thông tin bắt buộc sẽ xuất hiện với thứ tự cố định trong CV của bạn và không thể sắp xếp lại.

Ngoài ra, còn có mục thông tin bổ sung – Additional Information, người dùng có thể lựa chọn điền các thông tin này hoặc không. Những thông tin nào bạn muốn thể hiện trong CV thì có thể điền và ngược lại. Bạn cũng có thể thay đổi thứ tự các trường thông tin này tùy theo nhu cầu của mình.

Bước 5: Sau khi hoàn thành việc điền thông tin và Save > chọn Preview để xem lại toàn bộ thông tin mình đã điền.

Bước 6: Chọn định dạng hiển thị CV mong muốn. TopDevCV hiện tại cung cấp 5 templates khác nhau để người dùng lựa chọn.

Sau khi chọn template ưng ý > chọn Chosen > Download/View PDF.

Hoàn thành.

Sau khi đã hoàn thành việc tạo CV, toàn bộ lịch sử chỉnh sửa sẽ được lưu và sử dụng cho lần sau. Nếu bạn muốn bổ sung thông tin hoặc thay đổi những thông tin khác trên CV, chỉ việc quay lại và chọn Edit để thay đổi.

Đơn giản, nhanh chóng và cung cấp đầy đủ thông tin, là những tiêu chí mà TopDevCV hướng đến. Hãy thử trải nghiệm sản phẩm mới này và có cho mình chiếc ưng ý nhất với vai trò là một lập trình viên nhé!

Triển khai bài toán duyệt cây nhị phân với Rust

rust
Implement bài toán duyệt cây nhị phân với Rust

Bài viết được sự cho phép của tác giả Huy Trần

Các bài giới thiệu về Rust thì nhiều quá rồi nhưng chưa thấy bài nào nói về việc sử dụng Rust hết, nên hôm nay mình sẽ bắt đầu viết một vài bài áp dụng Rust để implement một số thuật toán cơ bản, mở đầu sẽ là: Thuật toán duyệt cây nhị phân với Rust.

Sao? Không thích thuật toán à? Từ từ, đừng bỏ đi vội, mặc dù đề bài có vẻ khô khan nhưng qua bài viết này các bạn sẽ học được kha khá kiến thức quan trọng trong Rust:

  • Làm việc với struct
  • Sử dụng kiểu Option<>
  • Sử dụng kiểu Box<>
  • Khai báo biến trong Heap và Stack
  • Sử dụng các attribute để tùy biến Rust compiler
  • Sử dụng pattern matching
  • Làm việc với macro
  • Khai báo method dùng impl
  • Thao tác cơ bản với String

Và quan trọng nhất là cách sử dụng các thông báo lỗi của Rust compiler để tìm manh mối giải quyết vấn đề một cách hiệu quả.

  Cài đặt Rust trên Arch Linux
  Dynamic typing trong Rust với std::any::Any

Xem thêm các việc làm Ruby On Rails hấp dẫn trên TopDev

Kiến thức cơ bản

Nói sơ qua một chút kiến thức cơ bản, cây nhị phân (binary tree) là một loại cấu trúc dữ liệu dạng cây (tree), mỗi một node có từ một đến hai node con.

Các tên gọi quy ước trong một node của cây nhị phân:

  • Root: là node hiện tại đang xét.
  • Left: là node con bên trái của node đang xét.
  • Right: là node con bên phải của node đang xét.

Duyệt cây nhị phân (binary tree traversal) là một trong các thuật toán cơ bản khi làm việc với kiểu dữ liệu này. Có 2 cách để duyệt một cây nhị phân đó là duyệt sâu (depth first traversal) và duyệt rộng (breadth first traversal).

Đối với cách duyệt sâu, chúng ta có 3 phương pháp khác nhau, phân loại dựa theo thứ tự thăm (visit) các node con của cây:

  • In-order: Duyệt theo thứ tự Left -> Root -> Right. Ví dụ cây ở hình trên, thứ tự duyệt sẽ là: 2, 7, 5, 6, 11, 1, 8, 4, 9.
  • Pre-order: Duyệt theo thứ tự Root -> Left -> Right. Ví dụ ở cây trên, thứ tự duyệt là: 1, 7, 2, 6, 5, 11, 8, 9, 4.
  • Post-order: Duyệt theo thứ tự Left -> Right -> Root. Ví dụ ở cây trên, thứ tự duyệt là: 2, 5, 11, 6, 7, 4, 9, 8, 1.

Duyệt rộng thì chúng ta sẽ đi từng level của cây, và duyệt hết tất cả các node ở từng level. Ví dụ cây trên thứ tự duyệt sẽ là: 1, 7, 8, 2, 6, 9, 5, 11, 4.

Implementation

Chúng ta sẽ implement kiểu dữ liệu binary tree trong Rust, sau đó sẽ implement thuật toán duyệt cây cho kiểu dữ liệu này.

Trong quá trình implement, mình sẽ chỉ ra một số lỗi mà người mới học Rust thường gặp phải, và Rust compiler sẽ giúp chúng ta nhận ra và giải quyết các lỗi đó rất hiệu quả.

Còn bây giờ thì chúng ta bắt đầu thôi.

Khởi tạo dự án

Vì đây là một chương trình nhỏ, chúng ta không nhất thiết phải sử dụng cargo để tạo project mới, mà có thể tạo trực tiếp file *.rs và biên dịch nó bằng rustc.

Ở đây mình sẽ đặt tên source file của chúng ta là binary_tree.rs nằm trong thư mục ~/code/playground/.

$ mkdir -p ~/code/playground
$ cd ~/code/playground
$ touch binary_tree.rs

Chúng ta có thể chạy thử một chương trình nhỏ, ví dụ gõ vào file binary_tree.rs nội dung sau:

fn main() {
    println!("Hello World!");
}

Biên dịch và chạy đoạn code trên bằng lệnh:

$ rustc binary_tree.rs -o binary_tree
$ ./binary_tree

Bạn có thể viết 2 lệnh này vào một makefile và chạy bằng lệnh make. Hoặc nếu xài vim, bạn có thể sử dụng plugin vim-quickrun (do mình viết, shameless PR :v) để chạy nhanh bằng tổ hợp phím <Leader>e.

Xong rồi, giờ zô code thiệt nè.

Khai báo cấu trúc dữ liệu của một node

Thông thường khi implement kiểu tree, chúng ta sẽ bắt đầu implement từ một node của tree đó.

Theo như định nghĩa ở trên, một node mà chúng ta implement sẽ có các trường (fields) sau:

  • Value: Giá trị của node này, ở đây chúng ta dùng kiểu i32 (số nguyên)
  • Left: Reference tới node bên trái, giá trị này có thể rỗng (optional)
  • Right: Reference tới node bên phải, giá trị này cũng có thể rỗng (optional)

Vậy chúng ta sẽ khai báo một struct mới, gồm có 3 fields như trên:

struct Node {
    value: i32,
    left: Option<Node>,
    right: Option<Node>
}

Ở đây i32 là kiểu dữ liệu số nguyên, tương tự như int ở mấy ngôn ngữ khác vậy. Option tức là kiểu optional, nghĩa là nó có thể có giá trị tham chiếu tới đâu đó, hoặc có thể không có. Compile thử xem nào:

$ rustc binary_tree.rs -o binary_tree
 
error[E0072]: recursive type `Node` has infinite size
 --> walkthrough_binary_tree.rs:1:1
  |
1 | struct Node {
  | ^ recursive type has infinite size
  |
  = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Node` representable
 
error: aborting due to previous error

É, lỗi rồi. Lỗi ngay từ shot đầu tiên luôn :)) để xem lỗi gì nào.

Vấn đề recursive type trong Rust

Nội dung thông báo lỗi nó ghi là: recursive type Node has infinite size, tức là: Node là kiểu dữ liệu đệ quy (vì chúng ta tham chiếu tới Node bên trong chính nó), nên Rust không xác định được kích thước của nó — quá bự, vô hạn. Tại sao vậy? OK, dừng lại để nói về vấn đề này một chút nhé.

Cũng giống như C/C++, kích thước của một struct sẽ được xác định bằng tổng số kích thước các field bên trong nó. Lấy ví dụ đơn giản, nếu ta có một struct như sau:

struct Point {
    x: i32,
    y: u8
}

Thì kích thước của Point sẽ bằng kích thước của x (kiểu i32, có 4 bytes) cộng với kích thước của biến y (kiểu u8 có 1 byte), là 5 bytes cả thảy.

Quay trở lại với Node struct của chúng ta, kiểu i32 có 4 bytes, kiểu Option có 1 byte, kích thước của Node sẽ được tính bằng công thức:

Node=i32+2×Option+2×Node=4+2×1+2×Node=6+2×Node=6+2×(6+2×Node)=6+2×(6+2×(6+2×Node))=6+2×(6+2×(6+2×(6+2×Node)))=⋯

:))

Kết quả là tính mãi không ra nổi :)) vì cứ đệ quy mãi ở khúc lấy size của Node.

Boxed value và heap

Rồi, vậy cách giải quyết là gì nào? Nếu xem kĩ trong thông báo lỗi, bạn sẽ thấy 1 dòng:

...
  = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Node` representable
  ...

Xem nào, nó bảo nếu thêm Box hoặc Rc hoặc & các kiểu vào những chỗ tham chiếu tới Node thì sẽ giải quyết được. Vậy thử xem:

struct Node {
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>
}

Compile lại thì sẽ thấy lỗi đó đã hết, ngon lành! Có một cái warning xuất hiện, nhưng bây giờ chúng ta chưa cần nói tới nó, để nói tiếp về vụ Box cái đã.

Tại sao dùng Box<> lại giải quyết được vấn đề recursive struct? Đầu tiên cần hiểu Box<> là gì.

Trong Rust, mặc định mọi giá trị đều được khai báo trong stack [3], chúng ta sử dụng Box<T> khi cần khai báo một biến kiểu T trong heap. Một box thực chất là một smart pointer trỏ tới một giá trị đã được tạo ra trong heap.

Trong trường hợp này, chúng ta đặt Node vào trong Box<> để khai báo dạng Box<Node>, thì thực chất chúng ta đang khai báo một pointer trỏ tới một vùng nhớ kiểu Node trong heap, vậy nên bên trong Node struct của chúng ta lúc này, kích thước của left và right thực chất là kích thước của pointer Box<Node>, và pointer này có kiểu Option.

Trên StackOverflow cũng có một câu hỏi được trả lời khá kĩ về vấn đề Box trong recursive struct, các bạn có thể tham khảo tại đây.

Dead code warning khi compile

Okay, bây giờ quay về lại với cái warning mà rustc đưa ra lúc nãy nhé. Nội dung warning như sau:

warning: struct is never used: `Node`, #[warn(dead_code)] on by default
 --> walkthrough_binary_tree.rs:1:1
  |
1 | struct Node {
  | ^

rustc nói cho chúng ta biết là cái Node struct mà chúng ta tạo ra chưa bao giờ được sử dụng cả. Và thông báo này được đưa ra vì attribute #[warn(dead_code)] được bật sẵn khi biên dịch.

Điều này rất có ích để viết code đẹp, code chuẩn, có thể hạn chế được lượng mỡ thừa không cần thiết… à nhầm, code thừa.

Tuy nhiên vì chúng ta đang học, cho nên có thể tạm tắt thông báo này đi bằng cách thêm attribute #[allow(dead_code)] ở trước phần khai báo struct để báo cho compiler biết rằng dead_code là người quen biết, và việc cho anh ấy đi qua khỏi khâu kiểm duyệt là đúng quy trình, không cần phải lo gì cả:

#[allow(dead_code)]
struct Node {
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>
}

Giờ compile lại sẽ ko còn lỗi nào xảy ra nữa.

Tạo binary tree từ node

Vậy là chúng ta đã khai báo thành công thành phần cơ bản nhất của một binary tree, giờ chúng ta thử dùng kiểu Node này để tạo ra một binary tree xem sao nhé.

Lấy ví dụ với cây sau:

Chúng ta sẽ đi từng bước, tạo từng node một. Đầu tiên là root node của cây trên, khai báo một biến tree, kiểu Node, có 2 nhánh left và right đều là None.

#[allow(unused_variables)]
fn main() {
    let tree = Node {
        value: 1,
        left: None,
        right: None
    };
}

Đây là trạng thái cây của chúng ta lúc này:

Chúng ta cũng chèn thêm vào attribute #[allow(unused_variables)] để rust compiler không warning vì chúng ta chưa cần sử dụng biến tree này.

Giá trị của một biến Option và Box

Vậy là chúng ta đã tạo được một node đầu tiên của cây, có giá trị là 1 và 2 node con chưa có gì cả. Giờ mình sẽ giải thích tại sao lại xuất hiện giá trị None, và phải làm gì nếu muốn gán left hoặc right thành một node khác.

Một biến kiểu Option có thể mang giá trị Some(T) (trả về giá trị của T) hoặc mang giá trị None (không trả về gì cả).

Vậy để gán một giá trị không None vào cho một biến Option, chúng ta dùng lệnh Some(...), ví dụ:

let optional: Option<T> = Some(T);

Tiếp, đối với kiểu Box<T>, chúng ta có method Box::new(...) để khởi tạo giá trị cho nó, ví dụ:

let value: Box<i32> = Box::new(5);

Kết hợp 2 cú pháp trên lại, đối với kiểu Option<Box<Node>> của các node left và right, chúng ta sẽ có cách khai báo như sau:

...
left: Some(Box::new(Node { ... })),
...

Giờ thử chèn tiếp 2 node số 7 và 8 vào cây trên nào:

...
    let tree = Node {
        value: 1,
        left: Some(Box::new(Node {
            value: 7,
            left: None,
            right: None
        })),
        right: Some(Box::new(Node {
            value: 8,
            left: None,
            right: None
        }))
    };
    ...

Trạng thái cây lúc này sẽ như thế này:

Sử dụng macro để rút gọn cú pháp

Hẳn là bạn cũng cảm thấy khó chịu với câu lệnh dài dòng Some(Box::new(Node { ... })) cứ lặp đi lặp lại liên tục ở trong code.

Rust cho phép chúng ta tạo ra các macro để rút gọn các thao tác dài dòng, lặp đi lặp lại. Bạn có thể coi macro tương tự như #define của C/C++ nhưng lợi hại hơn rất nhiều. [4]

Thực ra ngay từ đầu chúng ta đã dùng macro rồi, đó chính là lệnh println!, lệnh này cũng là một macro, bạn có thể tham khảo source của Rust để xem nó được khai báo thế nào.

Sẽ rất dài dòng nếu nói chi tiết về macro ở đây, nên bạn có thể tham khảo thêm tài liệu của Rust nhé.

Chúng ta sẽ tạo ra một macro để rút gọn thao tác tạo optional Node ở trên:

macro_rules! node {
    ( $($props:ident : $value:expr),* ) => { 
        Some(Box::new(Node {
            $($props: $value),*
        })) 
    }
}

Từ giờ chúng ta có thể tạo ra các Node với cú pháp mới là:

...
left: node!(
        value: 5,
        left: None,
        right: None
      );
...

Gọn và rõ ràng hơn rất nhiều. Thử áp dụng macro node! vừa tạo để hoàn thành nốt cây nhị phân của chúng ta nào:

...
    let tree = Node {
        value: 1,
        left: node!(
            value: 7,
            left: node!(
                value: 2,
                left: None,
                right: None
            ),
            right: node!(
                value: 6,
                left: None,
                right: None
            )
        ),
        right: node!(
            value: 8,
            left: None,
            right: None
        )
    };
    ...

Giờ chúng ta đã có một cây nhị phân hoàn chỉnh đúng với yêu cầu ban đầu.

Duyệt cây

Giờ chúng ta sẽ implement thuật toán duyệt cây, vì bài viết cũng khá dài rồi nên mình sẽ chỉ trình bày một loại của thuật toán duyệt DFS, cụ thể là In-order.

Nhắc lại lý thuyết: chúng ta sẽ duyệt theo thứ tự Left -> Root -> Right.

Ý tưởng để implement thuật toán này là tạo ra một hàm trả về một chuỗi, chuỗi này lưu lại quá trình duyệt từ Node hiện tại đến các node con bên trong nó theo thứ tự của In-order.

Tạo methods với impl

Như vậy là chúng ta cần tạo ra một method cho kiểu dữ liệu Node, trong Rust chúng ta có thể sử dụng từ khóa impl để định nghĩa ra các method cho một kiểu. [5]

impl Node {
    fn traversal(&self) -> String {
        let mut output = String::new();
        // Implement here
        return output;
    }
}

Bằng cách sử dụng impl, chúng ta đã khai báo một hàm traversal() cho kiểu Node, hàm này không nhận tham số nào cả, đối vối tham số &self trong lệnh khai báo, chúng ta có thể xem nó như là từ khóa this trong các ngôn ngữ như JavaScript để xác định context của hàm hiện tại. Bạn có thể đọc thêm tài liệu về vấn đề này ở phần cuối.

Sử dụng matching để kiểm tra Node rỗng

Giải pháp của chúng ta khá là đơn giản, duyệt từng node và trả về giá trị của node đó nếu có, cộng dồn lại và trả về 1 string để in ra.

Vậy thì việc tiếp theo phải làm đó là kiểm tra xem các node con có tồn tại hay không. Nhớ lại ở trên có đề cập, một biến kiểu Option luôn trả về một trong 2 giá trị Some(...) hoặc None, vậy để kiểm tra ta có thể dùng match, cú pháp của match cũng na ná giống như switch ở các ngôn ngữ khác nên chắc không cần giải thích nhiều [6]:

match self.left {
    Some(ref node) => { },
    None => { }
}

Chắc không cần giải thích các bạn cũng sẽ hiểu là việc tiếp theo chúng ta có thể sử dụng được biến node ở trong scope của trường hợp Some(...) để tương tác với node hiện tại rồi đúng không nào? Còn trường hợp None, mặc dù không làm gì cả nhưng chúng ta vẫn phải handle nó, nếu không sẽ gặp lỗi khi compile như thế này:

error[E0004]: non-exhaustive patterns: `None` not covered
  --> binary_tree.rs:21:15
   |
21 |         match self.left {
   |               ^^^^^^^^^ pattern `None` not covered
 
error: aborting due to previous error

Thông báo lỗi nói rằng: Trường hợp None chưa được cover. Đây cũng là một ví dụ cho thấy sự khắt khe của Rust để đảm bảo không lọt bất kì trường hợp khả nghi nào có khả năng trở thành bug trong lúc runtime cả.

Đối với giá trị của một node, là kiểu i32, chúng ta có thể chuyển về kiểu String bằng lệnh .to_string(), và để thực hiện việc cộng chuỗi, ta cần đưa nó về kiểu &str, vậy cần thêm vào lệnh .as_str(), như vậy ta có đoạn code implement đầy đủ của hàm traversal() như sau:

impl Node {
    fn traversal(&self) -> String {
        let mut output = String::new();
 
        // Left
        match self.left {
            Some(ref node) => { 
                output += node.traversal().as_str();
            },
            None => { }
        }
 
        // Root
        output += self.value.to_string().as_str();
 
        // Right
        match self.right {
            Some(ref node) => { 
                output += node.traversal().as_str();
            },
            None => { }
        }
 
        return output;
    }
}

Cuối cùng, chúng ta có thể gọi hàm này để in ra nội dung của cây:

fn main() {
    ...
    println!("{}", tree.traversal());
}

Kết quả sẽ là:

2 7 6 1 8

Code đầy đủ của chúng ta sẽ là:

struct Node {
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>
}
 
macro_rules! node {
    ( $($props:ident : $value:expr),* ) => { 
        Some(Box::new(Node {
            $($props: $value),*
        })) 
    }
}
 
impl Node {
    fn traversal(&self) -> String {
        let mut output = String::new();
 
        match self.left {
            Some(ref node) => { 
                output += node.traversal().as_str();
            },
            None => { }
        }
 
        output += self.value.to_string().as_str();
 
        match self.right {
            Some(ref node) => { 
                output += node.traversal().as_str();
            },
            None => { }
        }
 
        return output;
    }
}
 
fn main() {
    let tree = Node {
        value: 1,
        left: node!(
            value: 7,
            left: node!(
                value: 2,
                left: None,
                right: None
            ),
            right: node!(
                value: 6,
                left: None,
                right: None
            )
        ),
        right: node!(
            value: 8,
            left: None,
            right: None
        )
    };
    println!("{}", tree.traversal());
}

Kết thúc

Vậy là chúng ta đã implement thành công một thuật toán đơn giản trong Rust. Và học được khá là nhiều thứ trong quá trình implement.

Các bạn có thể thử thay đổi chương trình trên để implement tiếp các kiểu Pre-orderPost-order hoặc suy nghĩ để implement thuật toán duyệt BFS xem nhé.

Vì bài viết có mục đích dẫn các bạn đi từng bước để ứng dụng Rust vào giải quyết các vấn đề thường gặp, nên thuật toán đưa ra trong bài không phải là giải pháp tối ưu cho bài toán duyệt cây nhị phân, ở phần tiếp theo chúng ta sẽ bàn về các kĩ thuật tối ưu thuật toán này.

Và đừng quên đọc thêm các link tham khảo mình tổng hợp lại ở bên dưới. Hy vọng qua bài viết khá dài này, các bạn cũng làm quen được các điểm đặc biệt trong Rust so với các ngôn ngữ khác, và có thể tiếp tục tìm hiểu thêm về ngôn ngữ thú vị này.

Tham khảo

Bài viết gốc được đăng tải tại snacky.blog

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

5 bước cơ bản hình thành nên một chương trình đào tạo hiệu quả trong doanh nghiệp

đào tạo trong doanh nghiệp
5 BƯỚC CƠ BẢN HÌNH THÀNH NÊN MỘT CHƯƠNG TRÌNH ĐÀO TẠO HIỆU QUẢ TRONG DOANH NGHIỆP

Bài viết được sự cho phép của tác giả Ánh Nguyệt

Chào buổi sáng thứ bẩy đẹp trời tại Thái Bình

Mình đang ngồi trong một lớp học về kỹ năng tuyển dụng cho các bạn phòng nhân sự của một nhà máy may, đứng lớp là hai tiền bối đầy mình kinh nghiệm và ngập tràn nhiệt huyết. Chắc chắn lớp học  ngày hôm nay sẽ rất vui và hiệu quả.

Đang ngồi giữa những âm thanh thảo luận nhóm sôi nổi, mình nghe 1 cuộc điện thoại từ một hậu bối hỏi về quy trình xây dựng chương trình đào tạo đội ngũ kế cận của doanh nghiệp

  Top 10 Trường đào tạo ngành Công nghệ thông tin tốt nhất
  Đào tạo (Training) - Đâu là thời điểm bạn cần trải nghiệm?

Xem thêm các việc làm DevOps hấp dẫn trên TopDev

Mình viết lại đây, chia sẻ lại cho mọi người:

Để có được một chương trình đào tạo phù hợp, dù là ở bất cứ vị trí nào, bạn cũng nên làm các bước sau:

Bước 1: Xác định tiêu chuẩn/Yêu cầu về KSAO của vị trí (Knowledge, Skills, Attitude, Others) nói nôm na là tìm thước đo

Nếu chuẩn chỉnh thì bạn lấy từ frame khung năng lực của công ty, trong khung năng lực đó sẽ có khung năng lực của từng vị trí. Đó chính là tiêu chuẩn

Còn nếu công ty chưa làm từ điển năng lực, chưa có khung năng lực thì cách làm rất đơn giản là bạn đi hỏi chính những người đã ở vị trí đó, những người là sếp/là nhân viên của vị trí đó để hỏi xem để làm được vị trí đó thì cần những KSAO cụ thể nào

Sau khi hỏi xong, bạn sẽ ra một list các danh sách KSAO của vị trí theo quan điểm và kinh nghiệm của những người liên quan, bạn chuẩn hóa lại, và có thể hỏi tham vấn/so sánh với các vị trí tương tự ở các công ty cùng ngành hoặc các vị trí cùng level trên thị trường => bạn ra được 1 bản KSAO tạm gọi là ok cho vị trí đó (có thể gửi confirm lại với những người bạn đã phỏng vấn)

Cùng đưa ra tất cả các vị trí để xác định các KSAO chung của đội ngũ quản lý, những KSAO đặc thù của từng vị trí sẽ được liệt kê riêng

Bước 2: Tìm khoảng trống của người kế cận so với  vị trí, bao gồm KSAO chung và KSAO đặc thù

Cách thức tiến hành: Phỏng vấn 360 dựa trên các KSAO – có thể dùng bảng trên google form

Sau khi có kết quả, bạn sẽ thấy các khoảng trống về KSAO của ứng viên cho vị trí so với tiêu chuẩn

Đó chính là căn cứ lên kế hoạch/chương trình đào tạo

Bước 3: Lên kế hoạch/Chương trình đào tạo

Bước này rất thú vị

Cách làm nhanh là lấy KSAO chung, tìm khoảng trống chung của các ứng viên tiềm năng và lên chương trình đào tạo chung.

Còn nếu làm HR đến level cao nhất hiện nay là quan tâm đến từng cá nhân thì mình sẽ ra được kế hoạch riêng cho từng cá nhân có sự thống nhất với cá nhân đó và sếp của cá nhân đó (hiện tại gọi là IDP – Individual Development Plan).

Trong chương trình đào tạo này cũng có nhiều thứ rất thú vị

Có những môn sẽ học chung vì theo KSAO chung, và có những môn phải học riêng theo KSAO đặc thù

Cách thức học có thể là đào tạo inhouse, bên ngoài, tự học, on-job-training …

Người dạy học cũng không kém phần quan trọng, giảng viên nội bộ, giảng viên bên ngoài, học qua các dự án cần phối hợp …

Và cần có lộ trình học tập trong thời gian nhất định

Khi bạn thảo luận và thống nhất được IDP với từng cá nhân, bạn sẽ rất rõ mục tiêu của cá nhân đó, và sẽ hiểu rõ hơn những lý do của từng cá nhân khi họ học. Với các cá nhân, họ cũng hiểu rõ hơn bản thân mình, và chủ động hơn trong quá trình học tập.

Bước 4: Triển khai đào tạo

Trong chương trình đào tạo tại doanh nghiệp, để hiệu quả tốt, theo kinh nghiệm triển khai mình thấy, nên có các bài kiểm tra, check, các kế hoạch hành động, các bá cáo review, các báo cáo kết quả … rất quan trọng. Vì thói quen học tập của người Việt là cứ có kiểm tra mới học hành nghiêm túc.

Và nên có một cuộc thi đua trong quá trình học tập, nên có những bảo vệ tốt nghiệp, nên có những khen thưởng kịp thời …

Những thứ nhỏ xinh làm cho lớp học hấp dẫn hơn thì việc để tâm vào các triển khai và hiểu sở thích cũng như khơi gợi được học viên dám thể hiện bản thân và chủ động khai thác thông tin từ giảng viên, chủ động đưa ra vấn đề của bản thân để cả lớp cùng giải đáp rất quan trọng – hãy là những người triển khai đào tạo có tâm 

Bước 5: follow up và đánh giá hiệu quả đào tạo

Có thể định kỳ 6 tháng hoặc 1 năm sau chương trình đào tạo bạn lại thực hiện khảo sát 360 để xem khoảng trống khi trước có được rút ngắn không

Bạn cũng có thể tham khảo các thành tích, so sánh chức danh, …

Các đối tượng khác cũng theo trình tự như vậy, để hiệu quả đào tạo phát huy hết tác dụng thì việc kiểm tra thường xuyên và followup rất quan trọng

Những công cụ chi tiết để thực hiện các bạn có thể tự tìm hiểu thêm, hoặc chúng ta cùng giao lưu để chia sẻ ^^

Bài viết gốc được đăng tải tại anhnguyet.wordpress.com


Tuyển Dụng Nhân Tài IT Cùng TopDev
Đăng ký nhận ưu đãi & tư vấn về các giải pháp Tuyển dụng IT & Xây dựng Thương hiệu tuyển dụng ngay!
Hotline: 028.6273.3496 – Email: contact@topdev.vn
Dịch vụ: https://topdev.vn/page/products

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

Sử dụng các câu lệnh điều khiển trên Selenium IDE

lệnh điều khiển
Sử dụng các câu lệnh điều khiển trên Selenium IDE

Bài viết được sự cho phép của vntesters.com

Như ở bài trước mình có nói, trong Selenium IDE cũng có một số các mở rộng (extensions) để hỗ trợ người dùng. Bài này, mình sẽ giới thiệu với các bạn một extensions khá mạnh mẽ trong việc điều hướng luồng thực thi của mã kiểm thử trên Selenium IDE – Selenium IDE Flow Control.

  Các kiểu “đợi chờ” trong Selenium Webdriver: Implicit wait, Explicit wait và Fluent wait
  Giới thiệu công cụ kiểm thử tự động Selenium

Xem thêm tuyển dụng Data Engineer hấp dẫn trên TopDev

Cài đặt Selenium IDE Flow Control

Để cài đặt phần extensions, chúng ta truy cập vào trang chủ của Selenium và download về trình duyện Firefox. Quá trình cài đặt này cũng giống như khi chúng ta cài đặt những add-on khác cho Firefox. Các bạn có thể xem clip dưới đây:

Các câu lệnh trong Selenium IDE Flow Control

Sau khi cài đặt Selenium IDE Flow Control, trong command dropdown list cua Selenium IDE, chúng ta sẽ có 7 câu lệnh mới:

  • label | mylabel – Tạo ra một nhãn
  • goto | mylabel – Di chuyển đến một nhãn
  • gotoLabel | mylabel – Tương tự như câu lệnh “goto”
  • gotoIf | expression – Đi đến một nhãn nếu thỏa điều kiện
  • while | expression – Lặp một đoạn mã với điều kiện
  • endWhile – Kết thúc một vòng lặp
  • push | value | arrayName – Điền một giá trị vào bên trong một mảng

Sử dụng các câu lệnh Flow Control

Mình ví dụ một kịch bản kiểm thử như sau:

  1. Truy cập trang web Google
  2. Tìm kiếm với từ khóa Selenium
  3. Nhấp vào đường dẫn đến trang chủ của Selenium
  4. Nếu tiêu đề của trình duyệt hiện tại không phải là Downloads thì nhấn nút Download Selenium
Tập tin cho kịch bản kiểm thử này, các bạn có thề download ở đây.

Bài viết gốc được đăng tải tại vntesters.com

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực công nghệ hấp dẫn trên TopDev

Kĩ thuật Memoize cải thiện performance

kỹ thuật memoize
Kĩ thuật Memoize cải thiện performance

Bài viết được sự cho phép của tác giả Huy Trần

Memoize là một kĩ thuật cache lại giá trị trả về của các hàm dựa trên tham số truyền vào nó.

Kĩ thuật này có thể áp dụng trên mọi ngôn ngữ lập trình, trong bài viết này mình chỉ lấy JavaScript ra làm ví dụ.

  AMP là gì? Vì sao AMP thật sự quan trọng với web performance?
  Công cụ performance test Jmeter

Xem thêm các việc làm Android lương cao trên TopDev

Đặt vấn đề: Bài toán tìm số Fibonacci

Đối với bài toán tìm số Fibonacci, chúng ta đều biết thuật toán đơn giản nhất để implement nó là sử dụng đệ quy:

Fn=Fn−1+Fn−2

Hay diễn giải bằng code như sau:

const fibo = (n) => {
  if (n === 0) return 0;
  if (n === 1) return 1;
  return fibo(n - 1) + fibo(n - 2);
}

Nhược điểm của giải pháp trên rất rõ ràng, đó là thời gian tính toán. Đối với mỗi lần tính toán, hàm fibo phải thực hiện tính toán lại từ đầu (từ n = 0) đến n. Trong quá trình đó một giá trị fibo(n) sẽ bị tính đi tính lại liên tục, một sự lãng phí không hề nhỏ.

Thời gian chạy của thuật toán trên cho kết quả fibo(50):

// fibo(50);
➜  time node fibo.js
 
193.97s user 
0.56s   system 
99%     cpu 
3:15.24 total

Và như ta đã nhận thấy, thì phần lớn thời gian tiêu tốn vào việc tính đi tính lại các giá trị trùng lặp, vậy để cải thiện, ta cần tìm một cách nào đó để lưu lại giá trị của từng phép tính fibo(n), và tránh việc tính lại từ đầu mỗi khi chạy. Cách này gọi là caching.

Vì đối với mỗi số n bất kì, ta luôn luôn chỉ có duy nhất một giá trị sau khi tính toán với hàm fibo(n). Vậy nên ta có thể tạo ra một mảng tên là cache để lưu lại các giá trị fibo(n) đã tính toán, ở lần chạy tiếp theo, chỉ việc lấy nó ra và sử dụng, không cần phải tính lại.

const cache = [];
 
const fibo = (n) => {
  if (n === 0) 
    cache[n] = 0;
  else if (n === 1) 
    cache[n] = 1;
  else 
    cache[n] = cache[n] || (fibo(n - 1) + fibo(n - 2));
  return cache[n];
}

Kết quả thời gian chạy fibo(50) được cải thiện đáng kể:

// fibo(50);
➜  time node fibo.js
 
0.05s user 
0.02s system 
98%   cpu 
0.072 total

Memoize

Ví dụ trên cho chúng ta thấy được tầm quan trọng của việc cache trong việc cải thiện tốc độ xử lý của một ứng dụng.

Tuy nhiên, cứ phải tạo một biến toàn cục cache một cách thủ công như vậy không phải là một giải pháp hay, chưa kể đến việc cần cache nhiều thứ trong một ứng dụng, chúng ta cần một phương pháp tổng quát hơn, và hiệu quả hơn. Đó là memoize.

Memoize khác với các kĩ thuật cache thông thường ở chỗ: Nó được dùng để cache giá trị trả về của một hàm, và cache được tạo ra dựa trên tham số của hàm đó.

Sau đây là một cách implement đơn giản cho hàm memoize:

const memoize = (func) => {
  let cache = {};
  let that = this;
  return (...args) => {
    let key = JSON.stringify(args);
    if(cache[key]) {
      return cache[key];
    }
    else {
      let val = func.apply(that, args);
      cache[key] = val;
      return val;
    }
  }
};

Hàm memozie nhận vào tham số là một hàm, tạo cache dựa trên các tham số truyền vào của hàm đó. Khi được gọi thì nó sẽ trả về giá trị được cache nếu có, nếu chưa có giá trị cache thì chạy chính hàm được truyền vào và lưu lại giá trị trả về.

Cực kì đơn giản đúng không? Giờ chúng ta thử implement lại hàm fibo ở đầu bài dùng memoize:

const memfibo = memoize((n) => {
  if (n === 0) return 0;
  if (n === 1) return 1;
  return memfibo(n - 1) + memfibo(n - 2);
});

Phần logic vẫn được giữ nguyên so với cách implement đầu tiên, nhưng vẫn sử dụng được cache, đó là ưu điểm của việc dùng memoize.

Chạy thử hàm memfibo vừa tạo với tham số n = 100 luôn coi sao:

// memfibo(100);
➜  time node fibo.js
 
0.06s user 
0.02s system 
97%   cpu 
0.077 total

Quá xịn 😀

Các implement khác của memoize

Trong thực tế, có thể bạn sẽ không đủ tự tin để sử dụng hàm memoize do chính mình implement, hoặc team/sếp không tin tưỏng bạn, ok, không sao, mặc dù bị tổn thưong nhưng không nên vì thế mà nản.

Chúng ta có thể sử dụng một vài implement khác như:

Ngoài ra, các bạn có thể tham khảo thêm bài viết How I wrote the fastest JavaScript memoization library để có cái nhìn sâu hơn về các kĩ thuật implement/tối ưu hóa cho memoize.

Bonus: Sử dụng memoize trong Angular filter

Một trưòng hợp mà bạn có thể sử dụng memoize khá là hiệu quả đó là khi implement một filter trong Angular. Ví dụ:

angular.module('app').filter('evenNumbers', function() {
  return function(items) {
    return items.reduce(function(result, item) {
      return (item % 2 === 0);
    }, []);
  };
});

Filter trên nhận vào một mảng và trả về một mảng mới, nhưng khi sử dụng filter trên bạn sẽ gặp lỗi:

Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!

Nguyên nhân của lỗi trên là vì: hàm reduce trả về một mảng mới mỗi lần bạn chạy filter evenNumbers, khiến cho Angular “hiểu lầm” kết quả trả về đã đưọc thay đổi và nó phải chạy một digest cycle mới để kiểm tra, và việc chạy này xảy ra liên tục.

Khi gặp những trưòng hợp như vậy, bản chỉ cần đặt hàm filter vào bên trong memoize để cache lại giá trị trả về là xong:

angular.module('app').filter('evenNumbers', function() {
  return memoize(function(items) {
    return items.reduce(function(result, item) {
      return (item % 2 === 0);
    }, []);
  });
});

Lời cuối: đồ xịn nhưng dùng nhiều thì hết xịn

Có một trưòng hợp mà dù có sử dụng memoize thì cũng vô dụng, đó là khi dữ liệu trả về của một hàm nó thay đổi không phụ thuộc vào tham số truyền vào của hàm đó. Ví dụ dữ liệu tracking dạng time series, hay giá chứng khoán, hay danh sách status trên tưòng nhà đứa bạn thích trên Facebook chẳn hạn.

Và thêm nữa, vì memoize cũng là caching, tức là dữ liệu tính toán xong được lưu lại và nằm đó luôn, không mất đi đâu cả, cho nên bạn phải nghĩ tới vấn đề khi app chạy trong một khoản thời gian dài, thì dung lưọng bộ nhớ cũng vì thế mà tăng lên. Cho nên cần phải giải phóng bộ nhớ cache của hàm memoize sau một khoản thời gian nhất định, nếu không sẽ dẫn tới tràn bộ nhớ hoặc memleak.

Vì sử dụng memoize tức là bạn đang đánh đổi memory để lấy tốc độ, chứ không có cái gì là free cả, nên nếu biết chừng mực và dùng đúng lúc đúng chỗ thì sẽ đem lại hiệu quả cao, còn ngược lại, lạm dụng quá mức thì hậu quả sẽ khó lường. Cho nên gì gì thì cũng phải hiểu để xài nhau cho tốt hơn, nhé 😀

Bài viết gốc được đăng tải tại snacky.blog

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

Tìm hiểu hệ thống lương 3P

lương 3p

Bài viết được sự cho phép của hrvnacademy.com

Một khái niệm nữa mà những bạn đang tìm hiểu Công việc C&B không thể bỏ qua đó là xây dựng, tính và chi trả lương theo 3P.

Tìm hiểu hệ thống lương 3P

Chào mừng các bạn đang đến với series các bài học trong Khoá học C&B cơ bản cho người mới (newbie) hoàn toàn miễn phí. Tôi là Thành HR sẽ đồng hành cùng bạn trong khoá học này. Và chủ đề mà chúng ta cùng nhau tìm hiểu ngày hôm nay là Tìm hiểu hệ thống lương 3P.

Chúng ta đã cùng nhau tìm hiểu về Cách xây dựng thang bảng lương, rồi cũng đã tìm hiểu qua hệ thống lương thưởng trong công ty quy mô nhỏ. Và có một khái niệm nữa mà những bạn đang tìm hiểu Công việc C&B không thể bỏ qua đó là xây dựng, tính và chi trả lương theo 3P.

Xem thêm tuyển dụng Designer lương cao trên TopDev

Lương 3P là gì?

Bất kỳ doanh nghiệp nào, khi phát triển đến một giai đoạn nào đó thì Ban Lãnh Đạo luôn muốn cải thiện năng suất người lao động, nâng cao hiệu quả làm việc để từ đó gia tăng lợi nhuận cho công ty. Và việc xây dựng được một phương pháp trả lương phù hợp cũng giúp góp phần vào điều này.

Trả lương theo 3P là một mô hình trả lương hiệu quả của Mercer được khá nhiều doanh nghiệp tại Việt Nam áp dụng trong thời gian qua nhờ tính ưu việt của nó. Tuy nhiên, lương 3P có khá nhiều phiên bản và cách áp dụng khác nhau để phù hợp với các mô hình đa dạng của các Doanh nghiệp nhỏ.

3P (3P Compensation) là hình thức trả lương được xây dựng để trả lương theo vị trí, năng lực và hiệu quả công việc của nhân viên nhằm tạo sự công bằng, khuyến khích và tạo động lực giúp mỗi nhân viên phát huy tối đa khả năng của mình trong công việc, đóng góp cho sự phát triển của công ty.

3P là từ viết tắt của 3 từ: Position, Person, Performance. Đây là 3 hạng mục cấu thành nên thu nhập của một Người lao động được nhận. Có thể hiểu nôm na là:

Thu nhập = P1 + P2 + P3. 

– P1 (Position) – Pay or position: Trả lương theo vị trí công việc. Doanh nghiệp trả lương hàng tháng cho chức danh đó, bất kể người đảm nhận là ai và có năng lực thế nào. Thông thường các Công ty sẽ căn cứ trên thang bảng lương để trả P1 này.

– P2 (Person) – Pay for Person: Trả lương theo năng lực. Dùng kết quả đánh giá năng lực nhân sự để định ra số tiền tương xứng với năng lực đó. Với các doanh nghiệp đang hoạt động thì phần P2 này có thể được thể hiện trong Quy chế lương thưởng.

– P3 (Performance) – Pay for Performance: Trả lương theo kết quả đạt được. Phần lương này nhân viên có thể được nhận hàng tháng hoặc hàng quý tùy chính sách công ty.

Tính lương chuẩn xác với công cụ tính lương gross to net của TopDev ngay!

Cách xây dựng Hệ thống lương 3P

Trong phần này, chúng ta sẽ cùng tìm hiểu các bước chuẩn để có thể xây dựng một hệ thống lương 3P. Còn thực tế triển khai thì một số công ty chấp nhận thay đổi một chút cho phù hợp với thực tế doanh nghiệp của mình.

Vì không phải Công ty nào cũng đủ điều kiện để thực hiện và áp dụng theo mô hình chuẩn. Chằng hạn như Xây dựng được khung năng lực chuẩn, áp dụng công nghệ để đánh giá hoàn thành công việc…Và mô hình chuẩn ấy chưa chắc đã hoàn toàn phù hợp ngay với văn hóa doanh nghiệp hiện tại.

Nên ở đây, mình sẽ cùng nhau tìm hiểu cách thực hiện cho một doanh nghiệp quy mô nhỏ. Vẫn giữ lại phần lõi, nhưng vẫn đảm bảo phát huy được các điểm mạnh của hệ thống lương 3P này:

Bước 1: Xây dựng P1 (Position) – Pay or position

Phần P1 – Trả lương theo vị trí công việc: Bạn có thể hình dung nó chính là phần lương mà bạn đã xây dựng trong thang bảng lương. Cho nên, nếu Công ty bạn đã xây dựng được thang bảng lương chuẩn thì có thể lấy để áp vào P1. Mình cùng tham khảo lại các bước chuẩn để xây dựng P1 như sau:

– Bắt đầu từ chiến lược kinh doanh của Công ty thể hiện qua Sứ mệnh / Tầm nhìn / Giá trị cốt lõi. Và dựa vào đó để xây dựng cơ cấu tổ chức trên cơ sở chuỗi giá trị của doanh nghiệp.

– Chuẩn hóa hệ thống chức danh, giá trị công việc (Job Evaluation) và mô tả công việc (Job Description) trên cơ sở phân bổ các phòng ban chức năng của doanh nghiệp. Từ đó xác định được cấp bậc lương và dải lương của các vị trí chức danh đảm bảo hài hòa với quỹ lương của Doanh nghiệp và cạnh tranh với thị trường.

Bước 2: Xây dựng P2 (Person) – Pay for Person

Để hiểu rõ phần P2 – Trả lương theo năng lực thì đầu tiên mình cùng nhau tìm hiểu khái niệm năng lực là gì? Các công cụ nào có thể đo lường và dùng để đánh giá năng lực nhân viên?

Năng lực (Competency): được hiểu là kiến thức, kỹ năng, khả năng và hành vi mà người lao động cần phải có để đáp ứng yêu cầu công việc, và là yếu tố giúp một cá nhân làm việc hiệu quả hơn so với những người khác. Tùy vào mỗi doanh nghiệp sẽ xây dựng hệ thống đánh giá năng lực với các tiêu chí phù hợp.

Xây dựng khung năng lực chuẩn: Để làm được việc này thì đầu tiên công ty cần xây dựng được từ điển năng lực, sau đó mới xây dựng khung năng lực cho các vị trí. Cần lưu ý phần năng lực mong đợi và thực tế mà các nhân viên đang có. Khung năng lực này cũng là cơ sở để tuyển dụng nhân sự mới đúng yêu cầu.

Bạn có thể tham khảo Mô hình ASK để xây dựng và đánh giá năng lực nhân viên theo chuẩn quốc tế. ASK là viết tắt của Attitude (Phẩm chất / Thái độ) – Skill (Kỹ năng) – Knowledge (Kiến thức), mỗi một nhóm là những yêu cầu mà doanh nghiệp đặt ra cho cá nhân để hoàn thành xuất sắc vị trí công việc cụ thể.

Những năng lực này được chấm theo 5 mức độ:

  • Mức độ 5 – Mức độ xuất sắc
  • Mức độ 4 – Mức độ tốt
  • Mức độ 3 – Mức độ khá
  • Mức độ 2 – Mức độ cơ bản
  • Mức độ 1 – Mức độ kém

Từ những căn cứ trên, Công ty sẽ tiến hành đánh giá năng lực cá nhân theo khung năng lực của từng vị trí, từ đó xác dựng level lương P2 này. Nếu công ty có số lượng nhân sự ít có thể thực hiện việc đánh giá bằng excel, nhưng nếu số lượng nhiều thì phải nghĩ đến việc sử dụng phần mềm.

Bước 3: Xây dựng P3 (Performance) – Pay for Performance

Về phần P3 – Trả lương theo kết quả đạt được này có thể hiểu nôm na như một phần thưởng thêm cho Người lao động khi họ hoàn thành tốt công việc, mang lại giá trị cộng thêm cho doanh nghiệp. Vậy thì rõ ràng, P3 là phần tạo động lực và thúc đẩy nhân viên nâng cao hiệu suất lao động, được đo đếm bằng con số rõ ràng.

Cần xác định rõ ràng ngay từ đầu phần lương P3 này sẽ chi trả hàng tháng hay hàng quý. Nếu trả hàng tháng thì P3 có thể nằm trong khoảng từ bao nhiêu đến bao nhiêu, thậm chí là bằng 0, cần cân nhắc khi cho P3 là một số âm như một số công ty đang áp dụng. Còn nếu trả theo quý thì cũng cần quy định rõ ràng và trả đúng thời hạn.

Để xây dựng được P3 thì phải bắt đầu từ Mục tiêu chiến lược của Công ty, rồi đến mục tiêu phòng ban, mục tiêu cá nhân. Mục tiêu cá nhân này được cụ thể hóa bằng tiêu chí KPI, đánh giá hàng tháng hay hàng quý thì tùy công ty quy định.

Lưu ý khi xây dựng KPI cho cá nhân cần đảm bảo yếu tố đơn giản dễ hiểu, từ 3 – 5 tiêu chí chứ đừng quá nhiều, có thể đo lường kết quả bằng các con số thì mới giúp tạo động lực cho nhân viên cố gắng hoành thành vượt chỉ tiêu. Nếu không nó chỉ mang tính hình thức và Người lao động xem phần lương P3 là phần không thể đạt được và không quan tâm thì mất ý nghĩa của lương 3P.

Một số công ty lấy mức P1 + P2 cũng sẽ là mức đóng các khoản Bảo hiểm bắt buộc cho người lao động (BHXH, BHYT, BHTN…). Vì P3 là phần biến thiên hàng tháng hoặc hàng quý mới được nhận nhân nên có thể không nằm trong khoản bắt buộc đóng. Một số khác thì chỉ đóng cho Người lao động trên mức P1, còn phần P2 họ sẽ chia thành các khoản phụ cấp không nằm trong danh mục đóng.

  4 mẹo deal lương như ý với vị trí Product Manager
  5 tips cải tiến chất lượng phát triển Mobile App

Một số sai lầm khi áp dụng lương 3P

Một số công ty đang hoạt động khi chuyển đổi sang trả lương theo 3P cần đảm bảo việc xếp lại level lương của nhân viên ở P1 + P2 không nên thấp hơn thu nhập họ đang nhận. Nên lưu ý phần năng lực kỳ vọng và năng lực thực tế khi đánh giá của nhân viên hiện tại. Có thể xử lý bằng cách bổ sung các khóa đào tạo để nâng cao năng lực cho phù hợp.

Nên xem phần lương P3 là phần mà nếu Người lao động đạt hoặc vượt chỉ tiêu mong đợi thì sẽ được nhận. Chứ đừng cắt xén ra từ thu nhập đang có của họ để hình thành phần P3 này, vì như vậy nó chẳng khác gì lấy mỡ nó rán nó. Họ sẽ xem là công ty đang không công bằng và không minh bạch.

Phần lương P3 cần quy định rõ ràng thời gian chi trả, một số công ty trả theo quý thường không trả đúng hạn dẫn đến sự bất mãn và mất động lực làm việc của nhân viên dẫn đến nghỉ việc hàng loạt. Nếu có suy nghĩ này thì công ty không nên áp dụng hệ thống lương 3P làm gì cả.

Và đặc biệt, để áp dụng hệ thống lương 3P chuẩn thì công ty nên đầu tư và hướng đến việc số hóa dữ liệu, áp dụng phần mềm trong việc phân phối chỉ tiêu, tổng hợp kết quả đánh giá tự động và có cảnh báo đánh giá theo thời gian thực sẽ phát huy được tối đa hiệu quả của hệ thống lương 3P này.

Trên đây mình đã cùng nhau tìm hiểu hệ thống lương 3P ở bước hiểu và nắm được phần cơ bản nhất của nó. Nếu bạn có bất kỳ thắc mắc nào vui lòng để lại comment bên dưới, mình sẽ trả lời cho các bạn trong thời gian sớm nhất. Trân trọng.

Bài viết gốc được đăng tải tại hrvnacademy.com


Tuyển Dụng Nhân Tài IT Cùng TopDev
Đăng ký nhận ưu đãi & tư vấn về các giải pháp Tuyển dụng IT & Xây dựng Thương hiệu tuyển dụng ngay!
Hotline: 028.6273.3496 – Email: contact@topdev.vn
Dịch vụ: https://topdev.vn/page/products

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

Thói quen viết code an toàn trong khi xây dựng ứng dụng PHP

viết code trên php
Thói quen viết code an toàn trong khi xây dụng ứng dụng PHP

Bài viết được sự cho phép của tác giả Lê Chí Dũng

Bất cứ khi nào bạn xây dựng một hệ thống xong xui và hệ thống đang chạy ngon lành đều bị phá hoại vào ngày đẹp trời nào đó.!? Vì lý do bla, bla,… gì đó họ sẵn sàng dùng mọi cách phá hoại dự án kỳ công của bạn chính vì thế xây dựng code an toàn từ lúc xuất xưởng là cần thiết, hãy áp dụng thói quen này để đảm bảo rằng các ứng dụng của bạn là an toàn mức cao nhất có thể nhé.

  • Kiểm tra hợp lệ đầu vào (validate ngoài form)
  • Bảo vệ post form (validate trong file xử lý)
  • Bảo vệ hệ thống file (ghi logs khi phát sinh tải file mà mình giới hạn)
  • Bảo vệ cơ sở dữ liệu (Chống lại sự tấn công SQL INJECTION)
  • Bảo vệ dữ liệu phiên làm việc (đảm bảo hệ thống luôn chạy tốt)
  • Bảo vệ chống lại các sơ hở của ứng dụng lệnh xuyên các trang (Cross-Site Scripting – XSS)
  • Bảo vệ chống lại các giả mạo yêu cầu xuyên các trang (Cross-Site Request Forgeries – CSRF)
  10 Frameworks tốt nhất hiện nay cho PHP

Kiểm tra hợp lệ đầu vào (validate ngoài form)

Đây là quy tắc cơ bản trong lập trình và nó cũng có 4 bước cơ bản, bạn làm theo thì chắc chắn sẽ K.O.

  1. Lập ra danh sách hợp lệ(while list) vs bất hợp lệ (black list)
  2. Luôn luôn kiểm tra hợp lệ trong black list.
  3. Kiểm tra kiểu dữ liệu đúng. ví dụ như là các số, chuỗi,…
  4. Trong quá trình xử lý danh sách bất hợp lệ cần dùng hàm thoát để giảm tải quá trình xử lý.

Tuy nhiên hiện nay các jquery validate ngày càng thông dụng nên bạn có thể tham khảo cách viết trong các jquery đó. Còn dùng framework JS thì nó cũng đảm bảo nhiều thứ hơn.

Bảo vệ post form (validate trong file xử lý)

– Attacker có thể dễ dàng fake 1 post form của bạn ở đâu đó rồi gửi vào file PHP xử lý của bạn vì mục đích nào đó. Do các ứng dụng web là phi trạng thái (stateless), nên không có cách nào để chắc chắn tuyệt đối là dữ liệu đã được gửi lên từ nơi mà bạn muốn nó đến từ đó. Mọi thứ từ các địa chỉ IP cho đến tên máy chủ, cuối cùng rồi đều có thể bị fake.

Ví dụ:

<html>
<head>
<title>Collecting your data</title>
</head>
<body>
<form action="http://path.example.com/processStuff.php" 
    method="post"><input type="text" name="answer"
    value="There is no way this is a valid response to a yes/no answer..." />
<input type="submit" value="Save" name="submit" />
</form>
</body>
</html>

– Tuy nhiên một kỹ thuật mà bạn có thể sử dụng là thẻ bài sử dụng một lần (single-use token), nó không làm cho việc fake post form của bạn bất khả thi nhưng nó trở thành một điều rắc rối kinh khủng cho người fake.!? Vì token thay đổi mỗi khi mẫu được đưa xuống client, attacker cần phải lấy một form đang gửi, lấy token, và đặt nó vào phiên bản post fake đó. Kỹ thuật này rất hại não vì khó có khả năng một ai đó lập ra một fake post form lâu dài để gửi các yêu cầu không mong muốn đến ứng dụng của bạn.

Ví dụ một thí dụ về một thẻ bài biểu mẫu dùng một lần.

<?php
session_start();
?>
<html>
<head>
<title>SQL Injection Test</title>
</head>
<body>
<?php

echo 'Session token=' . $_SESSION['token'];
echo '<br />';
echo 'Token from form=' . $_POST['token'];
echo '<br />';

if ($_SESSION['token'] == $_POST['token']) {
    /* cool, it's all good... create another one */

} else {
    echo '<h1>Go away!</h1>';
}
$token = md5(uniqid(rand(), true)); 
$_SESSION['token'] = $token; 
?>
<form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
    method="post">
" /> " />
</form> 
</body> 
</html>

Trên nguyên tắc là token này dc tạo ra để dc dùng 1 lần hoặc trong khoảng thời gian nhất định (5s, 5 min,…). Có nhiều cách tạo token an toàn

  5 tính năng mới trong PHP 7 cần phải biết

Bảo vệ hệ thống file (ghi logs khi phát sinh tải file mà mình giới hạn)

– Vấn đề đặt ra là có những file quan trọng mà bạn không muốn bất kỳ ai cũng có quyền lấy xuống mà giới hạn lấy xuống. Chính vì vậy tốt nhất là thiết kế ứng dụng sử dụng một cơ sở dữ liệu và các tên file được tạo ra và giấu kín. Tuy nhiên, không phải lúc nào cũng có thể làm thế.

Ví dụ về một thủ tục kiểm tra hợp lệ tên tệp tin. Nó sử dụng các biểu thức chính quy để đảm bảo rằng chỉ các ký tự hợp lệ là được sử dụng trong tên tệp tin và đặc biệt kiểm tra các ký tự chấm chấm: ...

function isValidFileName($file) {
    /* don't allow .. and allow any "word" character  / */
    return preg_match('/^(((?:.)(?!.))|w)+$/', $file);
}

Bảo vệ cơ sở dữ liệu (Chống lại sự tấn công SQL INJECTION)

– Vấn đề đặt ra là attacker có thể vượt qua rào validate trong front end hoặc fake post form thành công và truyền dc những giá trị không mong muốn vào file PHP để xử lý nếu ta không chặn dc những giá trị này thì 0.O sẽ có những câu lệnh SQL ko chính quy thâm nhập vào những câu SQL chính quy của bạn phá hỏng database của bạn tồi tệ hơn là tạo ra những người dùng siêu quyền hạn để đánh cắp thông tin, bla bla….

Ví dụ về form lỏng lẻo có thể bị attack

<html>
<head>
<title>SQL Injection Example</title>
</head>
<body>
<form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
Account <input name="account" type="text"/>
Number <input name="number" type="text"/>
Name <input name="name" type="text"/>
Address <input name="address" type="text"/>
</form> 
<?php if ($_POST['submit'] == 'Save') {
/* do the form processing */ 
$link = mysql_connect('hostname', 'user', 'password') or die ('Could not connect' . mysql_error()); 
mysql_select_db('test', $link); 
$col = $_POST['col']; 
$select = "SELECT " . $col . " FROM account_data WHERE account_number = " . $_POST['account_number'] . ";" ; 
echo '<p>' . $select . '</p>'; 
$result = mysql_query($select) or die('<p>' . mysql_error() . '</p>'); 
echo '<table>'; 
while ($row = mysql_fetch_assoc($result)) { 
echo '<tr>'; 
echo '<td>' . $row[$col] . '</td>'; 
echo '</tr>'; 
} 
echo '</table>'; 
mysql_close($link); 
} ?> 
</body> 
</html>

– Chúng ta có thể khắc phục dc việc này bằng nhiều cách đơn giản như

  • dùng hàm mysql_real_escape_string(). Hàm này làm sạch đầu vào của bạn, sao cho nó không còn bao gồm các ký tự không hợp lệ.
  • Kiểm tra xem định dạng chuỗi: email, username, password… đều có một định dạng nhất định, do đó có thể viết các biểu thức chính qui (Regular Expression) cho mỗi định dạng này.

Xem ngay tin tuyển dụng PHP lương cao trên TopDev

Bảo vệ dữ liệu phiên làm việc.

– Vấn đề đặt ra là chúng ta 1 hệ thống có nhiều server khác nhau để duy trì 1 hệ thống site vậy làm sao để đảm bảo được các session trong giao dịch dùng để xử lý, có thể hoạt động trên các server khác trong cùng hệ thống.!?

Để làm được điều này chúng ta cần xử lý sao cho tất cả các máy chủ đều sử dụng chung một hệ thống quản lý session .

Hoặc đơn giản là sử dụng chung một session storage .

Mặc định trong php sẽ sử dụng file session handle các file chứa session sẽ nằm trong /tmp của mỗi server .

Điều này có thể gợi ý cho chúng ta việc share chung 1 vùng lưu trữ (ví dụ dùng NFS để share chung /tmp giữa các máy chủ).

Tuy nhiên cách này lại ảnh hưởng đến tốc độ truy xuất (vì phải qua mạng – sau đó là đến việc máy host session sẽ quá tải diskio).

Một hướng khác là sử dụng một db mysql chung để lưu session , thực tế thì cách này tạm được .

Để làm như vậy chúng ta cần override lại các hàm xử lý session của php

session_set_save_handler("open", "close", "read", "write", "destroy", "gc");

Sử dụng hàm session_set_save_handler để trỏ các call back function cần thiết , như vậy chúng ta cần viết lại 6 hàm open, close , read ,write ,destroy , gc .

tạm thời chúng ta viết tạm các hàm như sau :

<?php
function open($save_path, $session_name)
{
echo "open";
}

function close()
{
echo "close";
}

function read($id)
{
echo "read";
}

function write($id, $sess_data)
{
echo "write";

}

function destroy($id)
{
echo "destroy";
}

function gc($maxlifetime)
{
echo "gc";
}

session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
// test thử phát
session_start();

?>

Chạy thử . kết quả : openread writeclose …

Như vậy chúng ta thấy thứ tự của một quá trình đọc session .

Việc tiếp theo là implement mấy hàm trên vào mysql , memcached , file , hay bất kỳ một storage nào có thể dùng chung được là xong .

với file (cái này chỉ là làm lại những gì php đã làm  )

<?php
$sess_save_path=".";
function open($save_path, $session_name)
{
global $sess_save_path;

$sess_save_path = $save_path;
return(true);
}

function close()
{
return(true);
}

function read($id)
{
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
return (string) @file_get_contents($sess_file);
}

function write($id, $sess_data)
{
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
if ($fp = @fopen($sess_file, "w")) {
$return = fwrite($fp, $sess_data);
fclose($fp);
return $return;
} else {
return(false);
}

}

function destroy($id)
{
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
return(@unlink($sess_file));
}

function gc($maxlifetime)
{
global $sess_save_path;

foreach (glob("$sess_save_path/sess_*") as $filename) {
if (filemtime($filename) + $maxlifetime < time()) {
@unlink($filename);
}
}
return true;
}

session_set_save_handler("open", "close", "read", "write", "destroy", "gc");

//test
session_start();

?>

Hay với mysql đại khái thế này :

function read($id)
{
$sql = "select * from `tb_session` where `session_id`=$id";
mysql_query($sql);
//....
}

function write($id, $sess_data)
{
$sql = "INSERT INTO `tb_session` (`session_id`, `updated_on`) VALUES ('{$this->session_id}', NOW())";
mysql_query($sql);

}

function destroy($id)
{
$sql = "Delete from `tb_session` where `session_id`=$id";
mysql_query($sql);
}

Hay với memcached :

function read($id)
{
return memcached::get("sessions/{$id}");
}

function write($id, $data)
{
return memcached::set("sessions/{$id}", $data, 3600);
}

function destroy($id)
{
return memcached::delete("sessions/{$id}");
}

Dùng memcache hay mysql cũng khá nhanh , mysql thì chạy bàng memory storage engine , bật query cache tướng đối lớn cho nhanh , có thể replication ra nhiều server để đẩy performed lên cao nữa nếu tải lớn .

Bảo vệ chống lại các sơ hở của ứng dụng lệnh xuyên các trang (Cross-Site Scripting – XSS)

– Vấn đề đặt ra là người dùng sẽ nhập nội dung và hệ thống của bạn sẽ hiển thị nội dung đó lên. Nếu nội dung người dùng nhập là script có chứa mã độc phát tán virus,… thì sao.!?

<html>
<head>
<title>Results demonstrating XSS</title>
</head>
<body>
<?php
echo("<p>You typed this:</p>");
echo("<p>");
echo($_POST['myText']);//Mã script độc lấy từ database sẽ hiển thị ở đây
echo("</p>");
?>
</body>
</html>

– Để tự bảo vệ bạn chống lại các tấn công XSS, hãy lọc đầu vào của bạn thông qua hàm htmlentities() bất cứ khi nào giá trị của một biến được in đến đầu ra. Hãy nhớ làm theo thói quen đầu tiên về kiểm tra hợp lệ dữ liệu đầu vào bằng các giá trị trong danh sách trắng trong ứng dụng web của bạn đối với tên, địa chỉ email, số điện thoại, và thông tin về hoá đơn thanh toán.

<html>
<head>
<title>Results demonstrating XSS</title>
</head>
<body>
<?php
echo("<p>You typed this:</p>");
echo("<p>");
echo(htmlentities($_POST['myText']));
//htmlentities(): đã chặn việc thực thi đoạn script độc mà chỉ hiển thị chúng lên như thml
echo("</p>"); ?> 
</body> 
</html>

Bảo vệ chống lại các giả mạo yêu cầu xuyên các trang (Cross-Site Request Forgeries – CSRF)

– Một vấn đề là chúng ta phải thật cẩn trọng với người dùng đã là member chính thức vì lúc đó họ đã có dc quyền hạng trong site và họ có thể upload, post,… thoải mái nếu họ lợi dụng quyền này để tấn công bằng cách upload file image nhưng đó thực sự không phải là file image thì sao.!?

Các cuộc tấn công CSRF thường được thực hiện dưới dạng các thẻ <img> vì trình duyệt gọi URL một cách không ý thức để lấy hình ảnh. Tuy nhiên, nguồn hình ảnh dễ dàng có thể chỉ là URL của một trang web trên cùng một site, thực hiện các việc xử lý nào đó dựa trên các tham số chuyển cho nó. Khi thẻ <img> này được đặt trong một cuộc tấn công XSS — cũng là các tấn công phổ biến nhất được ghi chép lại — người sử dụng dễ dàng có thể làm một việc gì đó với quyền ưu tiên được cấp mà không biết mình đã làm — như vậy, có thể giả mạo.

<img src="http://www.example.com/processSomething?id=hacking" />

– Lúc đó ngoài việc áp dụng các cách bảo vệ bên trên trước XSS thì chúng ta cần xác minh các form gửi lên chặc hợn. Ngoài ra, sử dụng biến hiển $_POST thay vì $_REQUEST và kiểm tra kỹ lưỡng các file được up lên nếu là hình ảnh ngoài kiểm tra file_upload,… thì cần kiểm tra size ảnh để chắc chắn đó là một hình ảnh thông qua hàm getimagesize() trong php.

Bài viết gốc được đăng tải tại lcdung.top

Có thể bạn quan tâm:

Xem thêm các việc làm PHP lương cao trên TopDev

Kiểm thử theo hướng Data-Driven với Selenium IDE

data driven
Kiểm thử theo hướng Data-Driven với Selenium IDE

Bài viết được sự cho phép của vntesters.com

Ở phần trước, mình đã giới thiệu với các bạn một phần mở rộng của Selenium IDE cho phép chúng ta điều hướng luồng thực thi của mã Selenium IDE – Selenium IDE Flow Control. Phần này, mình sẽ giới thiệu với các bạn một công cụ khác của Selenium IDE hỗ trợ chúng ta chạy kiểm thử theo hướng Data-Driven – SelBlocks.

  Download, Export file tự động với Selenium Webdriver
  Giới thiệu công cụ kiểm thử tự động Selenium

Xem thêm tuyển dụng C# lương cao trên TopDev

Cài đặt SelBlocks

Các bạn có thể download phần SelBlocks ở đây. Vì cách cài đặt cũng giống như Selenium IDE Flow Control hay những add-on khác của Selenium, nên mình sẽ không nói chi tiết, các bạn có thể xem lại cách cài đặt của Selenium IDE Flow Control trong clip dưới đây

SelBlocks, bên cạnh việc hỗ trợ chúng ta thực thi kiểm thử theo hướng data-driven, nó có cung cấp một số câu lệnh đặc biệt như:

  • if/elseIf/else
  • try/catch/finally/throw
  • for/foreach/while
  • continue/break
  • call/function/return
  • loadXmlVars/loadJsonVars
  • forXml/forJson
  • exitTest

Nhưng trong bài này, mình chỉ tập trung vào phần Data-Driven thôi 🙂

Sử dụng SelBlocks

Ví dụ như chúng ta có một kịch bản như sau

  1. Truy cập trang web Google
  2. Tìm kiếm với từ khóa Selenium/SeleniumHq
  3. Nhấp vào đường dẫn đến trang chủ của Selenium

Kiểm tra trang chủ của Selenium được hiển thị thành công.

SelBlocks sử dụng 1 tập tin XML để chứa các thông tin dữ liệu cho việc thực thi kiểm thử theo hướng data-driven. Vậy nên, trước hết chúng ta cần một file xml, chứa các thông tin mà chúng ta muốn sử dụng để lặp lại phần mã kiểm thử. Chúng ta sẽ có một tập tin xml cho hai trang từ khóa cần tìm: Selenium và SeleniumHq

<testdata>
    <vars searchterm=”Selenium”/>
    <vars searchterm=”SeleniumHq”/>
</testdata>

Sau đó, chúng ta tiến hành record các bước kiểm thử từ khóa đầu tiên. Và, thêm các câu lệnh dành cho việc data-driven. Các bước được mô tả ở video dưới đây.
[adToAppearHere]

Mã Selenium IDE, các bạn có thể download ở đây.

Bài viết gốc được đăng tải tại vntesters.com

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

Chuyện gõ tiếng Việt trên Linux

gõ tiếng việt trên linux
Chuyện gõ tiếng Việt trên Linux

Bài viết được sự cho phép của tác giả Huy Trần

Hẳn là các bạn xài Linux (dù là distro nào) thì cũng đều gặp phải một vấn đề giống nhau, đó là gõ tiếng Việt.

Đã từng gõ tiếng Việt trên Windows với Unikey hay Vietkey ngày xưa thì hẳn ai cũng cảm thấy khá là khó chịu với cái dấu gạch đít quái đản khi chuyển qua xài macOS hoặc Linux. Đối với các bạn chưa biết, thì dấu này gọi là preedit và là một phương pháp “chính thống” theo lời tác giả Trung Ngo (thành viên team phát triển BogoEngine) trong bài viết Ước mơ bộ gõ kiểu Unikey.

  10 điều bạn có thể làm với Linux mà bạn không thể làm với Windows
  6 câu lệnh linux hay dùng trong phân tích log

Xem thêm các việc làm Linux lương cao trên TopDev

1. Hai cách gõ tiếng Việt

Đại ý của bài viết trên thì: tựu chung, các bộ gõ tiếng Việt hiện nay có 2 cách để xử lý tiếng Việt khi gõ, đó là Backspace và Preedit.

  • Preedit: là cách gõ xuất hiện dấu gạch đít, đây thực chất là một vùng buffer lưu tạm các kí tự đang gõ, thay thế nó, ví dụ aa đưọc thay thành â, khi ngưòi dùng nhấn space để kết thúc từ đang gõ thì nó sẽ commit từ đó về cho UI của ứng dụng. Các bộ gõ tiếng Việt sử dụng kĩ thuật này thì có: ibus-unikey, bộ gõ tiếng Việt mặc định của macOS,…
  • Backspace giả: là cách gõ không xuất hiện dấu gạch đít, cơ chế hoạt động của giải pháp này là khi gõ các kí tự tiếng việt như aa, bộ gõ sẽ tự động gửi 2 dấu backspace vào ứng dụng, và gửi tiếp một kí tự â thay thế. Các bộ gõ sử dụng kĩ thuật này có ibus-bogo, fcitx-bogo, GoTiengViet trên macOS,…

2. Kĩ thuật Backspace

Lại nói về Backspace giả, trong bài viết của mình, tác giả Trung Ngo có nói:

Gần như tất cả các bộ gõ trên Windows (tôi không dám chắc nhưng không thể kiểm chứng được vì phần lớn là phần mềm nguồn đóng) giải quyết vấn đề này bằng cách sử dụng dấu backspace giả.

Mình nghĩ là đúng, ít ra là với một vài bộ gõ như GoTiengViet trên Windows (không biết phiên bản trên macOS và Linux anh Trần Kỳ Nam có cải tiến hay sử dụng kĩ thuật khác không, mình không kiểm chứng đưọc), vì từng có một thời gian lúc mình còn tham gia diễn đàn Câu lạc bộ VB, mọi ngưòi cũng thảo luận về vấn đề phát triển bộ gõ, kĩ thuật hồi đó mọi ngưòi áp dụng là:

- Hook keyboard để bắt lại các kí tự đang gõ
- Đưa kí tự vừa đưọc hook vào buffer và kiểm tra theo luật gõ (Telex, VNI,...)
- Gửi backspace để xóa các kí tự đã gõ, ví dụ `aa`
- Đưa kí tự cần thay thế, ví dụ `â`, vào clipboard và gửi phím `Shift + Insert` 
  hoặc `Ctrl + V` để "dán" kí tự này vào ứng dụng đang chạy.
- Hoặc gửi trực tiếp kí tự cần thay thế vào thông qua API của Windows

Và thêm một lý do nữa đó là lần cuối cùng mình check thì không nhớ trên Windows có API nào để hỗ trợ các kĩ thuật khác ngoài chuyện send key.

Hiện giờ nội dung các bài thảo luận đó vẫn còn, các bạn có thể tìm đọc để hiểu thêm:

3. Cấu trúc một bộ gõ thông thường trên Linux

Phải nói là trên X Window System mới đúng. Các bộ gõ khác nhau có nhiều cách xử lý khác nhau, đoạn này mình chỉ nói đến các bộ gõ dùng kiến trúc X Input Method (XIM) như ibusSCIM,…

Nếu để ý, các bạn sẽ thấy các bộ gõ như ibus-bogo hay scim-unikey thưòng hay tự gọi mình là một frontend cho một cái engine gì đó để dùng với ibus hoặc scim:

IBus frontend for the BoGo engine.

scim-unikey là một bộ xử lý nhập liệu tiếng Việt cho scim, sử dụng engine xử lý tiếng Việt của unikey

Đọc phần này sẽ giúp các bạn hiểu thêm về cái vụ “engine gì đó” đó.

Hình sau mô tả toàn cảnh việc xử lý nhập liệu trong một hệ thống X Window System:

  • Khi user nhấn một phím trên bàn phím, keycode của phím này sẽ đưọc gửi về cho X Server.
  • X Server gửi một sự kiện nhấn phím (KeyPress Event) tới cho các X client đang đăng ký chờ event (application sẽ làm nhiệm vụ báo cho client biết nó có nên đăng kí nhận event hay ko)
  • Event loop trên X client sẽ đọc KeyPress Event đó bằng một lệnh gọi tới hàm XNextEvent()
  • Thông tin của Event này sẽ được gửi sang Input Method (ibus, scim,…) và tại đây, tùy theo mỗi bộ gõ khác nhau, mà sẽ thực hiện việc kiểm tra các tổ hợp phím đưọc nhấn, rồi thay thế kí tự cho phù hợp với tập luật của bộ gõ, các nhà phát triển thưòng viết riêng phần xử lý này thành một engine để có thể sử dụng lại với nhiều loại bộ gõ khác nhau.
  • Trong quá trình xử lý (compose), Input Method cũng sẽ báo lại cho application biết để nó update phần preedit cho phù hợp.
  • Sau khi xử lý xong, Input Method sẽ gửi lại nội dung đã đưọc xử lý (một từ tiếng Việt hoàn chỉnh – composed text) về cho X client để in ra màn hình (bưóc này là bước commit). Commit xong thì application cũng thôi không in cái preedit ra nữa.

Engine xử lý việc bỏ dấu (thay thế kí tự phù hợp với tập luật của bộ gõ) có rất nhiều loại khác nhau, đây là tài liệu mô tả logic cho engine mà ibus-bogo sử dụng.

Việc trao đổi giữa X client và Input Method được thực hiện thông qua một phương thức gọi là XIM Protocol.

Và như các bạn thấy thì mô hình này phụ thuộc lớn vào preedit, chính vì vậy mới nói preedit là phưong pháp chuẩn cho việc soạn thảo với các ngôn ngữ ngoài tiếng Anh.

4. Thử nghiệm các bộ gõ trên Linux

Vì phần lớn thời gian làm việc của mình là trên terminal, nên chuyện chấp nhận thưong đau mà dùng Preedit khi gõ lệnh hay xài trong VIM thì mình chịu, không làm đưọc, nên dù không hoàn thiện, mình vẫn chấp nhận được các bộ gõ dùng phưong pháp dùng Backspace, vậy nên bài viết này mình chỉ nhắm tới việc tìm ra bộ gõ xài Backspace hoạt động hiệu quả nhất trên Linux.

4.1. xvnkb

Bộ gõ đầu tiên mình tìm hiểu thử là xvnkb, vì khá ấn tưọng với cái dòng release notes trên trang chủ:

WoW! After 6 years, we have a new official release! L0LzZzzZZzz… Some bugs with Firefox and other apps have been fixed in this release. Still having fun! Hahaha!

LOL.

Quá trình download và cài đặt diễn ra rất trơn tru, không có lỗi gì xảy ra.

Nhưng sau khi cài đặt xong và restart lại máy tính thì xvnkb làm crash luôn X server và máy tính không thể khởi động vào môi trưòng đồ họa đưọc nữa, đáng lý ra thì tới đây mình phải tìm hiểu thêm lý do crash là gì nhưng vì vừa tốn công cài đặt lại máy xong, khá là lưòi, không thấy chạy nữa thì cứ remove thẳng tay thôi :)) thành thật xin lỗi tác giả.

Thế là ứng viên đầu tiên đã bị loại.

4.2. ibus-bogo

Thật sự là sau khi đọc bài viết của tác giả Trung Ngo và xem homepage của project trên Github xong thì không muốn xài ibus-bogo tí nào, nhưng vì đã hết option rồi nên thôi cài vào xài luôn. Quá trình cài đặt thì khá là đơn giản:

$ sudo pacman -S ibus
$ yaourt -S ibus-bogo

Để ibus tự động khởi động thì chúng ta có thể thêm dòng sau vào ~/.xinitrc:

ibus-daemon -drx

Kết quả gõ thử nghiệm cho thấy ibus-bogo hoạt động rất tốt trên terminal, trên các trình duyệt như surffirefox,… không hiện thanh preedit phiền toái.

Tuy nhiên với chế độ gõ TELEX, một vài từ có các nguyên âm ươ như trước, bước, nước, ta bắt buộc phải gõ uwow hoặc uow nếu không muốn bị sót mất chữ ơ, ví dụ nưóc, trưóc. Các bộ gõ như GoTiengViet hay Unikey trên Mac và Windows không bị trường hợp này.

Một nhược điểm khác của ibus-bogo là không hỗ trợ các trình duyệt ChromiumGoogle Chrome, lý do thì tác giả Trung Ngo đã giải thích trong Issue #216 – ibus-bogo.

Lý do lớn nhất khiến nhiều người ngại dùng ibus-bogo có lẽ là vì project này đã không còn được maintain nữa. Ở thời điểm viết bài này, có 63 open issues trên trang Github của project. Thành viên của team phát triển là Trung Ngo đã ngừng maintain dự án vì bất đồng quan điểm với tác giả Ha-Duong Nguyen và fork ra một project khác tên là ibus-ringo (project này add thêm vài chức năng khác trong đó có preedit, nên mình ko xài :D)

4.3. fcitx-bogo

Một phiên bản khác cũng dùng BogoEngine đó là fcitx-bogo, xây dựng cho fcitx. Cách cài đặt thì cũng đơn giản:

$ pacman -S fcitx
$ yaourt -S fcitx-bogo

Khởi động với lệnh sau trong ~/.xinitrc:

fcitx -d

Kết quả thử nghiệm cho thấy bộ gõ này hoạt động tạm gọi là tốt trên mọi phần mềm, trong đó đặc biệt là có thể gõ đưọc tiếng Việt trên Chromium và Google Chrome luôn.

Tuy nhiên khi sử dụng trên terminal thì khá là lag, thời gian từ lúc gõ phím cho tới lúc hiện ra kí tự tiếng Việt rất chậm, không như ibus-bogo, và thêm một điểm trừ nữa là rất không ổn định, hay bung lụa kiểu:

Và khi gõ trên Firefox thì vẫn gặp hiện tượng không gõ đầy đủ dduwocj tiêngs Viêjt =.= (đó, bị như vậy đó).

5. Kết luận

Hy vọng bài viết này giúp các bạn có thêm cái nhìn sâu hơn về các loại bộ gõ tiếng Việt hiện có và những thử thách về mặt kĩ thuật mà chúng ta đang phải đối mặt để có thể phát triển đưọc một bộ gõ tiếng Việt hoàn chỉnh cho môi trưòng Linux.

Bài viết chỉ dựa trên quan điểm chủ quan của tác giả khi tìm hiểu về các bộ gõ không dùng preedit, nên còn nhiều thiếu sót và có nhiều nội dung liên quan đến phưong pháp chuẩn (preedit) và các kĩ thuật khác như surrounding text,… đã bị bỏ qua.

Hy vọng các bạn quan tâm có thể nhiệt tình góp ý, bổ sung và giúp mình hoàn thiện bài viết. Xin cảm ơn 😀

Bài viết gốc được đăng tải tại snacky.blog

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

Code ví dụ Spring Cloud Config Client

spring cloud config client
Code ví dụ Spring Cloud Config Client

Bài viết được sự cho phép của tác giả Trần Hữu Cương

Code ví dụ Spring Cloud Config Client

Trong ví dụ này chúng ta sẽ thực hiện load cấu hình để sử dụng cho project từ 1 server khác (Spring Cloud Config)

Việc load cấu hình từ 1 server khác thường áp dụng cho các project có nhiều instance trên nhiều server, mỗi lần đổi cấu hình ta chỉ cần đổi cấu hình trên server config là được chứ không cần phải sửa cho từng server một.

(Xem lại: Code ví dụ Spring Cloud Config Server)

  Xác thực trong Spring Cloud Config (Spring Cloud Config Authenticate)
  Authentication trong Spring Security

Xem thêm các việc làm Spring hấp dẫn trên TopDev

Code ví dụ Spring Cloud Config Client

Tạo project Spring Boot: File > New > Module

Code ví dụ Spring Cloud Config Client Code ví dụ Spring Cloud Config Client Code ví dụ Spring Cloud Config Client

Cấu trúc project sau khi hoàn thành:

Code ví dụ Spring Cloud Config Client

File Application: (giống hệt các project spring boot thông thường)

package stackjava.com.scconfigclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringCloudConfigClientApplication {
public static void main(String[] args) {
 SpringApplication.run(SpringCloudConfigClientApplication.class, args);
}

}

File bootstrap.yml:

spring:
 application:
 name: app
 profiles:
 active: dev
 cloud:
 config:
 uri: http://localhost:8888

Đây là một project spring cloud nên các thông tin cấu hình về server config, name ta sẽ đặt trong file bootstrap.yml

Trong ví dụ này mình sẽ load cấu hình từ server có địa chỉ http://localhost:8888, cấu hình được load sẽ có tên là app với profiles là dev. (Đây là server mình đã dựng trong bài trước)

File application.yml

server:
 port: 7777

Server sẽ chạy trên port 7777

File controller:

package stackjava.com.scconfigclient.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope
public class HomeController {
@Value("${database.host}")
private String host;
@Value("${database.database}")
private String database;
@Value("${database.username}")
private String username;
@Value("${database.password}")
private String password;

@GetMapping("/config")
public String config() {
return "host: " + host + "<br/>username: " + username + "<br/>password: " + password + "<br/>database: " + database;
}
}

Annotation @RefreshScope là 1 annotation của spring cloud. Các Bean được đánh dấu với annotation này sẽ được làm mới tại thời gian chạy (runtime). Mỗi khi gọi tới thì nó sẽ tạo 1 bean mới.

Start project:

 . ____ _ __ _ _

 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \

( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \

 \\/ ___)| |_)| | | | | || (_| | ) ) ) )

 ' |____| .__|_| |_|_| |_\__, | / / / /

 =========|_|==============|___/=/_/_/_/

 :: Spring Boot :: (v2.3.3.RELEASE)




2020-08-20 14:20:12.155 INFO 12476 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888

2020-08-20 14:20:12.984 INFO 12476 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=app, profiles=[dev], label=null, version=31254e088069d8f5c2651156237f2973613a9781, state=null

2020-08-20 14:20:12.985 INFO 12476 --- [ main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-configClient'}, BootstrapPropertySource {name='bootstrapProperties-C:/Users/stackjava/Desktop/spring-cloud-config-server-repo/app-dev.properties'}, BootstrapPropertySource {name='bootstrapProperties-C:/Users/stackjava/Desktop/spring-cloud-config-server-repo/app.properties'}]

...

2020-08-20 14:20:14.616 INFO 12476 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 7777 (http) with context path ''

2020-08-20 14:20:15.227 INFO 12476 --- [ main] s.c.s.SpringCloudConfigClientApplication : Started SpringCloudConfigClientApplication in 5.492 seconds (JVM running for 6.63)

2020-08-20 14:20:19.989 INFO 12476 --- [nio-7777-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'

2020-08-20 14:20:19.989 INFO 12476 --- [nio-7777-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'

2020-08-20 14:20:19.992 INFO 12476 --- [nio-7777-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms

Mở trình duyệt web và truy cập vào địa chỉ: http://localhost:7777/config

Code ví dụ Spring Cloud Config Client

Mặc dù các biến database.host, database.name... không có trong file cấu hình là application.yml nhưng nó vẫn có giá trị là do chúng đã được load từ server config.

Trong bài tiếp theo chúng ta sẽ tìm hiểu việc authen khi giao tiếp giữa spring cloud config server với spring cloud config client. Refresh lại cấu hình cho client khi cấu hình trên server, repository được thay đổi.

Download code ví dụ trên tại đây hoặc tại: https://github.com/stackjava/spring-cloud-config-client

Bài viết gốc được đăng tải tại stackjava.com

Có thể bạn quan tâm:

Xem thêm các việc làm lĩnh vực IT hấp dẫn trên TopDev

REST Web service: Tạo ứng dụng Java RESTful Client với Jersey Client 2.x

java restful client
REST Web service: Tạo ứng dụng Java RESTful Client với Jersey Client 2.x

Bài viết được sự cho phép của tác giả Giang Phan

Trong bài trước chúng ta đã cùng tìm hiểu cách xây dựng ứng dụng Java Restful web service với Jersey 1.x. Trong bài này, chúng ta sẽ cùng tìm hiểu cách tạo ra ứng dụng Java Restful web service với Jersey 2.x và ứng dụng Java RESTful Client sử dụng Jersey Client API để gọi tới RESTful web service.

  4 tips học Java cơ bản nhanh nhất dành cho Beginner Developer
  5 cách chia một mảng lớn thành nhiều mảng nhỏ trong Javascript

Xem thêm tuyển dụng Java hấp dẫn trên TopDev

1. Tạo Jersey project

Trong bài trước chúng ta đã tạo Restful web service sử dụng Jersey version 1.x. Trong bài này, chúng ta sẽ tạo Jersey project với version 2.x.

  • Jersey 1.x : các thư viện nằm trong package com.sun.
  • Jersey 2.x : các thư viện nằm trong package org.glassfish.

Vào Menu File -> New -> Dynamic Web Project -> Finish.

new dynamic web project

Nhấn chuột phải lên project vừa tạo -> Configure -> Convert to Maven Project:

Convert to Maven Project

Nhập thông tin Maven project như sau:

Maven project

Chúng ta có project như sau:

Mở file pom.xml và cập nhật lại như sau:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <groupId>RestfulWebServiceWithJersey2Example</groupId>     <artifactId>RestfulWebServiceWithJersey2Example</artifactId>     <version>0.0.1-SNAPSHOT</version>     <packaging>war</packaging>     <properties>         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>         <maven.compiler.source>1.8</maven.compiler.source>         <maven.compiler.target>1.8</maven.compiler.target>         <jersey.version>2.28</jersey.version>         <lombok.version>1.16.20</lombok.version>     </properties>     <dependencies>         <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-server -->         <dependency>             <groupId>org.glassfish.jersey.core</groupId>             <artifactId>jersey-server</artifactId>             <version>${jersey.version}</version>         </dependency>         <dependency>             <groupId>org.glassfish.jersey.containers</groupId>             <artifactId>jersey-container-servlet</artifactId>             <version>${jersey.version}</version>         </dependency>         <dependency>             <groupId>org.glassfish.jersey.inject</groupId>             <artifactId>jersey-hk2</artifactId>             <version>${jersey.version}</version>         </dependency>         <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client -->         <dependency>             <groupId>org.glassfish.jersey.core</groupId>             <artifactId>jersey-client</artifactId>             <version>${jersey.version}</version>         </dependency>         <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-common -->         <dependency>             <groupId>org.glassfish.jersey.core</groupId>             <artifactId>jersey-common</artifactId>             <version>${jersey.version}</version>         </dependency>         <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->         <dependency>             <groupId>org.glassfish.jersey.media</groupId>             <artifactId>jersey-media-json-jackson</artifactId>             <version>${jersey.version}</version>         </dependency>         <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->         <dependency>             <groupId>org.projectlombok</groupId>             <artifactId>lombok</artifactId>             <version>${lombok.version}</version>             <scope>provided</scope>         </dependency>     </dependencies>     <build>         <sourceDirectory>src</sourceDirectory>         <plugins>             <plugin>                 <groupId>org.apache.maven.plugins</groupId>                 <artifactId>maven-compiler-plugin</artifactId>                 <version>3.8.0</version>                 <configuration>                     <source>1.8</source>                     <target>1.8</target>                 </configuration>             </plugin>             <plugin>                 <groupId>org.apache.maven.plugins</groupId>                 <artifactId>maven-war-plugin</artifactId>                 <version>3.2.1</version>                 <configuration>                     <warSourceDirectory>WebContent</warSourceDirectory>                 </configuration>             </plugin>         </plugins>     </build> </project>

Lưu ý: đối với project chỉ bao gồm Client (không bao gồm Server), chúng ta chỉ cần 2 thư viện jersey-client và jersey-media-json-jackson.

Sau khi đã cập nhật file pom.xml, chúng ta cần update lại mavent project: Nhấn chuột phải lên project -> Maven -> Update Project… -> OK.

Tạo file jersey config: file này bao gồm config package chứa api và logging.

JerseyServletContainerConfig.java

package com.gpcoder.config;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.glassfish.jersey.logging.LoggingFeature;
//Deployment of a JAX-RS application using @ApplicationPath with Servlet 3.0
//Descriptor-less deployment
import org.glassfish.jersey.server.ResourceConfig;

public class JerseyServletContainerConfig extends ResourceConfig {
public JerseyServletContainerConfig() {
// if there are more than two packages then separate them with semicolon
packages("com.gpcoder.api");
register(new LoggingFeature(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), Level.INFO,
LoggingFeature.Verbosity.PAYLOAD_ANY, 10000));
}
}

Tạo file WebContent/WEB-INF/web.xml với nội dung như sau:

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">     <display-name>RESTful CRUD Example by gpcoder</display-name>     <servlet>         <servlet-name>jersey2-serlvet</servlet-name>         <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>         <init-param>             <param-name>javax.ws.rs.Application</param-name>             <param-value>com.gpcoder.config.JerseyServletContainerConfig</param-value>         </init-param>         <init-param>             <param-name>jersey.config.server.provider.classnames</param-name>             <param-value>org.glassfish.jersey.jackson.JacksonFeature</param-value>         </init-param>         <load-on-startup>1</load-on-startup>     </servlet>     <servlet-mapping>         <servlet-name>jersey2-serlvet</servlet-name>         <url-pattern>/rest/*</url-pattern>     </servlet-mapping> </web-app>

2. Jersey chuyển đổi XML và JSON sang Model Class như thế nào?

Theo mặc định Jersey API sử dụng JAXB là XML-Binding mặc định để chuyển đổi các đối tượng Java thành XML và ngược lại. Chúng ta cần gắn các Annotation của JAXB lên các class model để chú thích cách chuyển đổi cho JAXB. Chi tiết về JAXB Annotation và cách sử dụng các bạn xem lại bài viết: Hướng dẫn chuyển đổi Java Object sang XML và XML sang Java Object sử dụng Java JAXB.

Jersey chuyển đổi XML và JSON sang Model Class như thế nào

Jersey sử dụng MOXy là JSON-Binding mặc định để Jersey chuyển đổi các đối tượng JSON thành Java và ngược lại. Chúng ta không gần gắn các Annotation lên các class model.

3. Tạo Java REST Web service với Jersey2

Tạo data model:

User.java

package com.gpcoder.model;

import javax.xml.bind.annotation.XmlRootElement;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name = "user")
public class User {

private Integer id;
private String username;
}

Tạo Restful CRUD:

UserService.java

package com.gpcoder.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.gpcoder.model.User;

// URI:
// http(s)://:(port)/// // http://localhost:8080/RestfulWebServiceExample/rest/users
@Path("/users")
public class UserCrudService {

private static final Map<Integer, User> USERS = new HashMap<>();
static {
// Create dummy data
for (int i = 1; i <= 10; i++) { USERS.put(i, new User(i, "dummy " + i)); } } private int generateUniqueId() { return USERS.keySet().stream().max((x1, x2) -> x1 - x2).orElse(0) + 1;
}

@GET
@Path("/{id}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public User get(@PathParam("id") int id) {
return USERS.getOrDefault(id, new User());
}

@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List getAll() {
return new ArrayList<>(USERS.values());
}

@POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public int insert(User user) {
Integer id = generateUniqueId();
user.setId(id);
USERS.put(id, user);
return id;
}

@PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public boolean update(User user) {
return USERS.put(user.getId(), user) != null;
}

@DELETE
@Path("/{id}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public boolean delete(@PathParam("id") int id) {
return USERS.remove(id) != null;
}
}

4. Tạo Java REST Client với Jersey2 Client

Tạo Java REST Client với Jersey2 Client

JsonJerseyRestClientExample.java

package com.gpcoder.client;

import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.logging.LoggingFeature;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gpcoder.model.User;

public class UserCrudJerseyRestClientExample {

public static final String API_URL = "http://localhost:8080/RestfulWebServiceExample/rest/users";

public static void main(String[] args) {
System.out.println("Get User: " + getUser(1));

System.out.println("Get Users: " + getUsers());

Integer insertedId = createUser();
System.out.println("Created User: " + insertedId);

System.out.println("Updated User: " + updateUser(insertedId));
System.out.println("After Updated User: " + getUser(insertedId));

System.out.println("Updated User: " + deleteUser(insertedId));
}

private static Client createJerseyRestClient() {
ClientConfig clientConfig = new ClientConfig();

// Config logging for client side
clientConfig.register( //
new LoggingFeature( //
Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), //
Level.INFO, //
LoggingFeature.Verbosity.PAYLOAD_ANY, //
10000));

return ClientBuilder.newClient(clientConfig);
}

/**
* @GET /{id}
*
* Get one user with the given id
*/
private static User getUser(Integer id) {
Client client = createJerseyRestClient();
WebTarget target = client.target(API_URL).path("" + id);
return target.request(MediaType.APPLICATION_JSON_TYPE).get(User.class);
}

/**
* @GET
*
* Get all users
*/
private static List getUsers() {
Client client = createJerseyRestClient();
WebTarget target = client.target(API_URL);
GenericType<List> entity = new GenericType<List>() {
};
return target.request(MediaType.APPLICATION_JSON_TYPE).get(entity);
}

/**
* @POST
*
* create user
*/
private static Integer createUser() {
User user = new User();
user.setUsername("gpcoder client");
String jsonUser = convertToJson(user);

Client client = createJerseyRestClient();
WebTarget target = client.target(API_URL);
Response response = target.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(jsonUser, MediaType.APPLICATION_JSON));
return response.readEntity(Integer.class);
}

/**
* @PUT
*
* Update user
*/
private static Boolean updateUser(Integer id) {
User user = getUser(id);
user.setUsername(user.getUsername() + " edited");
String jsonUser = convertToJson(user);

Client client = createJerseyRestClient();
WebTarget target = client.target(API_URL);
Response response = target.request(MediaType.APPLICATION_JSON_TYPE)
.put(Entity.entity(jsonUser, MediaType.APPLICATION_JSON));
return response.readEntity(Boolean.class);
}

/**
* @DELETE
*
* Delete user
*/
private static Boolean deleteUser(Integer id) {
Client client = createJerseyRestClient();
WebTarget target = client.target(API_URL).path("" + id);
Response response = target.request(MediaType.APPLICATION_JSON_TYPE).delete();
return response.readEntity(Boolean.class);
}

private static String convertToJson(User user) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(user);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

Ứng dụng Client ở trên gửi và nhận dữ liệu kiểu json. Để gửi dữ liệu và nhận kiểu dữ liệu xml, các bạn đơn giản thay thế MediaType.APPLICATION_JSON sang MediaType.APPLICATION_XML.

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm: