Chuyện gì xảy ra nếu chúng ta dùng var() với một biến undefined
Trước hết phải nói CSS và HTML là ngôn ngữ không quá strictly, một vài lỗi nhỏ không làm làm chết nguyên trang, nếu dùng var() với một biến không tồn tại, không gây ra ảnh hưởng parsing.
Sẽ có nhiều lý do cho việc xuất biến undefined trong var, có thể là
Gõ nhầm tên biến
Tưởng là có nhưng chưa define biến này
Biến đó ko tồn tại trên trang đang xem
ol li{--foo:red;}ul li{color:var(--foo);}
Bởi, lời khuyên là luôn đặt biết ở node trên cùng như :root hay html, như vậy biến có thể truy xuất ở bất cứ đâu trong DOM.
Mình mới bước chân vào con đường lập trình này chưa được bao lâu, nhưng chỉ xét riêng khía cạnh phát triển web, trong vòng 5 năm (2015 – 2020) qua đã có nhiều sự thay đổi đáng kể:
Javascript được sử dụng nhiều hơn: do sự nâng cấp của các trình duyệt, cũng như sự ra đời của NodeJS, khiến cho javascript có thể được sử dụng cả ở backend lẫn frontend.
Các trang web chú trọng nhiều cho giao diện mobile: Bây giờ làm gì còn ai không sử dụng smartphone cơ chứ, nên một trang web cũng cần phải được tối ưu cả cho mobile nữa.
Frontend phát triển mạnh: Sự ra đời của hàng loạt các frontend framework như vue, angular, reactjs đã khiến cho các frontend developer thời nay không chỉ là “biết cắt giao diện từ photoshop” nữa, họ cũng phải quan tâm tới giải thuật, tới cấu trúc dữ liệu không kém cạnh gì các backend developer.
Và còn nhiều sự thay đổi khác nữa…
Chứng kiến sự thay đổi trên, mới thấy rằng, quả đúng là “không có gì là mãi mãi”, mọi thứ xung quanh ta luôn không ngừng thay đổi. Và nếu không bắt kịp với sự thay đổi đó, chúng ta sẽ sớm thành những “người tối cổ hiện đại”.
“Không có gì là mãi mãi” cũng nhắc nhở mình phải luôn biết cách làm mới kiến thức, làm mới bản thân, đừng ngủ quên khi thấy bản thân đã làm tốt ở thời điểm hiện tại. Bởi bạn làm tốt hôm nay, đâu có nghĩa là bạn sẽ làm tốt ngày mai.
II. Sẵn sàng thay đổi để phù hợp
Cũng vì “không có gì là mãi mãi”, nên việc thay đổi để phù hợp là điều “tất lẽ dĩ ngẫu” nếu bạn không muốn bị bỏ lại phía sau.
Lúc mới học lập trình, thật sự mình rất ngại học cái mới, dù hiểu rằng cái mình biết đã cũ và không còn được trọng dụng ở giai đoạn hiện tại, nhưng vì một lý do nào đó mà mình vẫn không chịu học. Có thể là do mình lười, hoặc công nghệ mình biết mới chỉ đang ngấp ngoải mà chưa thật sự chết, làm mình không có động lực để học cái mới hơn.
Một câu chuyện nhỏ, xàm xí nhưng có thật với mình.
Ngày trước, codeigniter là một php framework được nhiều người sử dụng bởi nó đáp ứng được hầu hết các tác vụ web lúc bấy giờ. Đương nhiên, mình không thể bỏ qua một hot trend công nghệ như thế được, mình đã tìm hiểu về codeigniter và thích nó ngay lập tức. Nhưng chỉ sau 2 năm, đúng vào giai đoạn các website có sự phát triển vượt bậc, codeigniter có vẻ không còn phù hợp với các dự án web phức tạp, và một framework khác nổi lên có tên là Laravel – được mọi người ca ngợi là “ngon, đẹp, mạnh mẽ”, nhưng mình lại chẳng thèm quan tâm.
Việc mình không để ý đến Laravel, không phải vì codeigniter vẫn đủ mạnh mẽ. Thực chất, mình đã nhận ra một số điểm hạn chế của nó, nhưng vì ngại học cái mới nên mình vẫn tiếp tục sử dụng trong một thời gian dài (chừng 1 năm tiếp theo).
Rồi một ngày cuối tuần, vì muốn giết thời gian nên mình thử tìm hiểu về Laravel xem sao. Thì ôi, nó quá ngon, nó giải quyết về các vấn đề mà mình đang gặp ở thằng codeigniter. Lúc đó mới thấy bản thân sao ngu quá, sao không tìm hiểu sớm hơn thì đã tiết kiệm bao nhiêu thời gian.
Và đó là bài học đầu tiên cho mình thấy cái giá của việc ngại thay đổi.
Câu chuyện trên vốn không có gì to tát, chỉ là việc học một framework mới, thế nhưng khi nghĩ rộng ra, mới thấy rằng việc ngại thay đổi để trở nên tích cực hơn sẽ chỉ ngăn cản việc bạn phát triển mà thôi.
Cái này thì mình học được qua việc fix bug (sửa lỗi phần mềm).
Để có thể fix được bug, trước tiên mình cần phải tìm được nó, hiểu nó và tái hiện được nó. Việc tìm bug có thể khó dễ tùy trường hợp, nhưng đặc điểm chung của công việc này là “dò từng bước một”, dò đến bao giờ tìm thấy nơi phát sinh bug thì thôi.
Và sau nhiều lần tìm bug, nhất là các trường hợp bug liên hoàn bug, từ bug A dẫn tới bug B, từ bug B dẫn tới bug C,… Khiến mình nhận ra rằng thường có một bug quan trọng gây ra các bug còn lại, mà khi fix được bug đó, các bug khác cũng tự động biến mất. Các bug như vậy được mình coi là “điểm mấu chốt” của vấn đề.
Thực tế, “điểm mấu chốt” không chỉ xuất hiện dưới dạng bug của phần mềm, mà “điểm mấu chốt” tồn tại trong tất cả các sự vật, sự việc, sự kiện trong cuộc sống.
Hôm nay tôi đi làm muộn.
– Vậy tại sao tôi lại đi muộn? Vì tắc đường.
– Vậy tại sao lại tắc đường? Là vì tôi đi làm vào giờ cao điểm?
– Tại sao tôi lại đi làm vào giờ cao điểm, sao không đi sớm hơn? Vì tối qua tôi ngủ muộn, nên dậy muộn.
– Tại sao tôi tối qua tôi ngủ muộn? Vì tối qua tôi cố xem nốt bộ phim yêu thích.
Vậy điểm mấu chốt của việc đi làm muộn là do tối qua cố xem nốt bộ phim. Vì nếu không cố xem nốt phim, thì sẽ không xảy ra các sự kiện sau đó, dẫn tới việc bị đi làm muộn.
Có một phương pháp tên là 5 why (5 câu hỏi tại sao) để giúp bạn tìm ra điểm mấu chốt dễ dàng hơn.
Nếu bạn không vừa lòng với bất kỳ điều gì trong cuộc sống, thì hãy thử suy nghĩ và tìm ra điểm mấu chốt. Sau đó giải quyết điểm mấu chốt đó, rồi bạn sẽ thấy cuộc sống dễ dàng hơn nhiều.
IV. Sống có mục tiêu và kế hoạch
Làm bất kỳ công việc gì cũng cần có kế hoạch, làm phần mềm cũng vậy. Trước khi bắt đầu triển khai một tính năng mới, thường có kế hoạch và mục tiêu rất rõ ràng:
Mục đích của tính năng sắp tới là gì?
Để hoàn thiện tính năng này, cần trải qua các bước nào?
Thời gian để hoàn thiện là bao lâu?
Sẽ có bao nhiêu người tham gia?
Khi nào thì bắt đầu, khi nào thì kết thúc?
…
Việc phải lập kế hoạch trước khi làm việc cũng đã tạo cho mình thói quen lập kế hoạch cho cuộc sống:
5 năm nữa mình sẽ là ai?
Để đạt được mục tiêu 5 năm, thì mỗi năm, mỗi ngày mình phải làm gì?
Có bao nhiêu khó khăn, bao nhiêu điểm mấu chốt cần phải giải quyết để đạt được mục tiêu.
Làm sao để đo lường là mình vẫn đang thực hiện đúng?
…
Sống có mục tiêu và kế hoạch giúp mỗi ngày mình biết phải làm gì, biết việc gì nên làm, việc gì không nên làm, biết được bản thân mình đang nằm ở đâu, và mình phải cố gắng như thế nào vào ngày mai. Sau cùng, sống có mục tiêu và kế hoạch khiến mình cảm thấy cuộc đời này có ý nghĩa, bớt vô vị và tẻ nhạt.
V. Lời kết
Còn rất nhiều thứ khác nữa mà mình học được từ code, nhưng tạm thời kết thúc ở đây vì bài viết cũng hơn một nghìn từ rồi. Hẹn gặp lại các bạn ở phần tiếp theo. Mà nếu bạn cũng có những bài học hay rút ra được từ việc học lập trình, thì đừng ngại chia sẻ ở phần bình luận nhé.
Phỏng vấn được xem là “vòng loại trừ” nhằm đánh giá các ứng viên. Tất nhiên, nhà tuyển dụng sẽ luôn đặt ra những “cái bẫy” trong cuộc phỏng vấn. Ngoài hình thức phỏng vấn tình huống (Case Interview) thì phỏng vấn hành vi (Behavioral- Based Interview) được các nhà tuyển dụng cực kỳ quan tâm. Vậy đâu là những điều đáng lưu ý xoay quanh loại hình phỏng vấn này? Qua bài viết sau, TopDev sẽ giải đáp những thắc mắc đó.
Phỏng vấn hành vi là gì?
Phỏng vấn hành vi(Behavioral-Based Interview), hay còn được gọi là Phỏng vấn năng lực (Competency-Based Interview); là một phương pháp phỏng vấn dựa trên kỹ thuật đặt câu hỏi STAR (Situation, Task, Action, and Result).
Lấy cơ sở các khía cạnh phân tích về tình huống có thể xảy ra, nhà tuyển dụng khai thác và đánh giá ứng viên dựa trên quá trình tự tri nhận về các trải nghiệm cá nhân. Các phản ứng, hành vi, cảm xúc đều sẽ được ứng viên thể hiện một cách rõ nhất.
Một số mẫu câu hỏi phỏng vấn hành vi quen thuộc từ nhà tuyển dụng
– Bạn đã từng nói về các vi phạm của bản thân với cấp trên hay tổ chức của mình hay chưa? Bạn thực hiện điều đó như thế nào? Phản ứng của người trong cuộc ra sao?
– của bạn khá hoàn hảo, hãy nói về những điều bạn chưa trình bày trong CV.
– Bài học nào đối với bạn là có ý nghĩa nhất thông qua các trải nghiệm/
– Hãy mô tả lại quá trình bạn làm việc với một đồng nghiệp/khách hàng khó tính. Bạn đã có cách thức tổ chức thế nào để đảm bảo hiệu quả công việc?
– Những vai trò nào bạn đã từng đảm nhận.
– Bạn nghĩ khả năng teamwork và tư duy độc lập có thật sự cần thiết không? Hãy chia sẻ về nó
Phỏng vấn hành vi – Đặt câu hỏi thế nào là hiệu quả?
Tính hiệu quả được thể hiện qua việc khai thác toàn diện năng lực của một ứng viên. Nhà tuyển dụng cần đặt ra các câu hỏi nhằm tạo cơ hội cho ứng viên bộc lộ hết khả năng tương tác của mình.
Hãy đảm bảo rằng các ứng viên có đủ thời gian để suy nghĩ. Bởi lẽ, các các câu hỏi phỏng vấn hành vi là thách thức không hề dễ dàng. Đừng hối thúc ứng viên! Điều đó có thể tạo ra tác dụng ngược và khiến cho sự đánh giá bị chênh lệch.
3 nguyên tắc cần lưu ý
Một số ứng viên có thể còn bỡ ngỡ với việc phỏng vấn hành vi. Vì vậy, điều nhà tuyển dụng cần quan tâm là linh động đặt ra các cơ hội để ứng viên thỏa sức mô tả về bản thân mình. Đây làm một cách gián tiếp nhằm trao cơ hội đồng thời mong muốn ứng viên có một sự chuẩn bị tốt nhất.
Đối với ứng viên là sinh viên mới ra trường hoặc có ít kinh nghiệm, nhà tuyển dụng nên đề cao sự khuyến khích. Hãy cho họ cơ hội trình bày các trải nghiệm cá nhân dù nó là một môi trường chưa chuyên nghiệp.
Chú ý đến các ví dụ mà ứng viên lựa chọn. Cách họ khái quát, dẫn dắt sẽ làm bộc lộ khả năng nhận định, phân tích vấn đề một cách rõ ràng nhất.
Đánh giá câu trả lời của ứng viên – Bài toán của sự chủ động
Trả lời “đúng tiêu chuẩn”
Nếu ứng viên có cách trả lời quá đúng theo khuôn mẫu mà nhà tuyển dụng định hình trước, đó cho thấy ứng viên đang cố gắng làm hài lòng nhà tuyển dụng. Đủ nhưng chưa hấp dẫn? Dấu ấn cá nhân chưa nổi bật. Và thật đáng tiếc, nhà tuyển dụng không tìm kiếm sự nhàm chán. Tuy nhiên, một số trường hợp, các bạn vẫn có thể có được những vé vớt nếu thể hiện tốt hơn ở những khía cạnh khác.
Nhà tuyển dụng muốn thấy cái riêng của bạn.Đừng để các thông tin quá đại trà về bí kíp phỏng vấn kiểm soát sự thể hiện của bạn.
Trả lời quá chung chung
Thay vì đi khai thác các góc nhìn từ trải nghiệm đã có sẵn, ứng viên lại giả định các trải nghiệm. Đây là cách thức phản hồi phỏng vấn cho thấy ứng viên đang thiếu tự tin.
Điểm khác biệt lớn giữa câu hỏi phỏng vấn hành vi và câu hỏi tình huống là để xem cách thức ứng viên thực sự đã làm. Chứ không chỉ đơn thuần dừng lại ở việc tạo lập ra các trải nghiệm giả định quá lý tưởng.
Vì thế, dù trải nghiệm của bạn chưa chuyên nghiệp, bạn cũng cần phải tường thuật một cách chân thật. Nhà tuyển dụng luôn đánh giá đa chiều, chứ không chỉ ở một khía cạnh.
Không hề có câu trả lời
Đây là trường hợp mà nhà tuyển dụng dễ dàng đánh giá nhất. Ứng viên nếu không trả lời được bất cứ câu hỏi hành vi nào chứng tỏ ứng viên chưa đủ năng lực để đảm nhân công việc.
Trong trường hợp này, chúng ta sẽ vẫn xem xét khi ứng viên là người có thái độ tốt. Nhưng xét về năng lực, họ còn thiếu sót về mặt nền tảng. Nhà tuyển dụng cần tập trung vào kết quả. Và vì thế, sự linh động trong các quyết định là yếu tố cần thiết. Khó có thể chắc chắn được một ứng viên quá giỏi về năng lực sẽ làm việc tốt và ngược lại. Nhà tuyển dụng hãy dựa vào kết quả để thiết lập chỉ tiêu lựa chọn ứng viên phù hợp nhất.
Phỏng vấn hành vi có ý nghĩa rất quan trọng. Và bạn hoàn toàn có thể vận dung kỹ năng STAR để chinh phục cuộc phỏng vấn với nhà tuyển dụng. Lưu ý rằng, dù cho thách thức phỏng vấn có là gì đi chăng nữa, bạn vẫn phải cố gắng trả lời. Hãy bình tĩnh và sáng suốt trong từng lời chia sẻ, nhận định về các trải nghiệm quá khứ. TopDev hy vọng đã có thể cung cấp cho bạn nhiều thông tin thú vị xoay quanh phỏng vấn hành vi (Behavioral-Based Interview)
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
Socket là phương tiện hiệu quả để xây dựng các ứng dụng theo kiến trúc Client-Server. Trong bài viết này, tôi sẽ hướng dẫn các bạn các bước cơ bản trong việc xây dựng các ứng dụng Client-Server sử dụng Socket làm phương tiện giao tiếp theo cả hai chế độ: có nối kết (TCP – Transmission Control Protocol) và không nối kết ( UDP – User Datagram Protocol).
Các bạn nên xem bài viết giới thiệu Lập trình mạng trong Java trước khi tiếp tục xem nội dung tiếp theo của bài viết.
Mô hình Client-Server sử dụng Socket ở chế độ có nối kết (TCP)
Có thể phân thành 4 giai đoạn như sau:
Giai đoạn 1: Server tạo Socket, gán số hiệu cổng và lắng nghe yêu cầu nối kết. Server sẵn sàng phục vụ Client.socket(): Server yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển.
bind(): Server yêu cầu gán số hiệu cổng (port) cho socket.
listen(): Server lắng nghe các yêu cầu nối kết từ các client trên cổng đã được gán.
Giai đoạn 2: Client tạo Socket, yêu cầu thiết lập một nối kết với Server.
socket(): Client yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển, thông thường hệ thống tự động gán một số hiệu cổng còn rảnh cho socket của Client.
connect(): Client gởi yêu cầu nối kết đến server có địa chỉ IP và Port xác định.
accept(): Server chấp nhận nối kết của client, khi đó một kênh giao tiếp ảo được hình thành, Client và server có thể trao đổi thông tin với nhau thông qua kênh ảo này.
Giai đoạn 3: Trao đổi thông tin giữa Client và Server.
Sau khi chấp nhận yêu cầu nối kết, thông thường server thực hiện lệnh read() và nghẽn cho đến khi có thông điệp yêu cầu (Request Message) từ client gởi đến.
Server phân tích và thực thi yêu cầu. Kết quả sẽ được gởi về client bằng lệnh write().
Sau khi gởi yêu cầu bằng lệnh write(), client chờ nhận thông điệp kết quả (ReplyMessage) từ server bằng lệnh read().
Giai đoạn 4: Kết thúc phiên làm việc.
Các câu lệnh read(), write() có thể được thưc hiện nhiều lần (ký hiệu bằng hình ellipse).
Kênh ảo sẽ bị xóa khi Server hoặc Client đóng socket bằng lệnh close().
Mô hình Client-Server sử dụng Socket ở chế độ không nối kết (UDP)
Có thể phân thành 3 giai đoạn như sau:
Giai đoạn 1: Server tạo Socket – gán số hiệu cổng.
socket(): Server yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển.
bind(): Server yêu cầu gán số hiệu cổng cho socket.
Giai đoạn 2: Client tạo Socket.
socket(): Client yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển, thông thường hệ thống tự động gán một số hiệu cổng còn rảnh cho socket của Client.
Giai đoạn 3: Trao đổi thông tin giữa Client và Server.
Sau khi tạo Socket xong, Client và Server có thể trao đổi thông tin qua lại với nhau thông qua hai hàm send() và receive().
Đơn vị dữ liệu trao đổi giữa Client và Server là các Datagram Package (Gói tin thư tín).
Protocol của ứng dụng phải định nghĩa khuôn dạng và ý nghĩa của các Datagram Package. Mỗi Datagram Package có chứa thông tin về địa chỉ người gởi và người nhận (IP, Port).
Xây dựng ứng dụng Client-Server với Socket trong Java
Thông qua các lớp trong gói java.net, các chương trình Java có thể sử dụng TCP hoặc UDP để giao tiếp qua Internet.
Lớp IntetAddress: Lớp này quản lý địa chỉ Internet bao gồm địa chỉ IP và tên máy tính.
Lớp Socket: Hỗ trợ các phương thức liên quan đến Socket cho chương trình Client ở chế độ có nối kết.
Lớp ServerSocket: Hỗ trợ các phương thức liên quan đến Socket cho chương trình Server ở chế độ có nối kết.
Lớp DatagramSocket: Hỗ trợ các phương thức liên quan đến Socket ở chế độ không nối kết cho cả Client và Server.
Lớp DatagramPacket: Lớp cài đặt gói tin dạng thư tín người dùng (Datagram Packet) trong giao tiếp giữa Client và Server ở chế độ không nối kết.
Xây dựng chương trình Client – Server ở chế độ có nối kết (TCP)
Trong phần tiếp theo tôi sẽ hướng dẫn các bạn xây dựng chương trình Client – Server đơn giản. Client sẽ gởi lần lượt các số từ 0 đến 9 tới Server. Server lần lượt sẽ gởi các số nhận được về Client.
Các bước tổng quát xây dựng một chương trình Client – Server ở chế độ có nối kết như sau:
Mở một socket nối kết đến server đã biết địa chỉ IP (hay tên miền) và số hiệu cổng.
Lấy InputStream và OutputStream gán với Socket.
Tham khảo Protocol của dịch vụ để định dạng đúng dữ liệu trao đổi với Server.
Trao đổi dữ liệu với Server nhờ vào các InputStream và OutputStream.
Đóng Socket trước khi kết thúc chương trình.
Xây dựng chương trình Client ở chế độ có nối kết
Một số phương thức cần thiết để xây dựng các chương trình client sử dụng socket ở chế độ có nối kết:
public Socket(String HostName, int PortNumber) : Phương thức này dùng để nối kết đến một server có tên là HostName, cổng là PortNumber. Nếu nối kết thành công, một kênh ảo sẽ được hình thành giữa Client và Server.
HostName: Địa chỉ IP hoặc tên logic theo dạng tên miền.
PortNumber: có giả trị từ 0 ..65535
public InputStream getInputStream() : Phương thức này trả về InputStream nối với Socket. Chương trình Client dùng InputStream này để nhận dữ liệu từ Server gởi về.
public OutputStream getOutputStream() : Phương thức này trả về OutputStream nối với Socket. Chương trình Client dùng OutputStream này để gởi dữ liệu cho Server.
public close() : Phương thức này sẽ đóng Socket lại, giải phóng kênh ảo, xóa nối kết giữa Client và
Server.
Code của chương trình Client như sau:
package com.gpcoder.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class EchoChatClient {
public final static String SERVER_IP = "127.0.0.1";
public final static int SERVER_PORT = 7;
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket = null;
try {
socket = new Socket(SERVER_IP, SERVER_PORT); // Connect to server
System.out.println("Connected: " + socket);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
for (int i = '0'; i <= '9'; i++) {
os.write(i); // Send each number to the server
int ch = is.read(); // Waiting for results from server
System.out.print((char) ch + " "); // Display the results received from the server
Thread.sleep(200);
}
} catch (IOException ie) {
System.out.println("Can't connect to server");
} finally {
if (socket != null) {
socket.close();
}
}
}
}
Xây dựng chương trình Server ở chế độ có nối kết
Một số phương thức cần thiết để xây dụng các chương trình Server sử dụng socket ở chế độ có nối kết.:
public ServerSocket(int portNumber): phương thức này tạo một Socket với số hiệu cổng là portNumber mà sau đó Server sẽ lắng nghe trên cổng này.
public Socket accept(): Phương thức này lắng nghe yêu cầu nối kết của các Client. Đây là một phương thức hoạt động ở chế độ nghẽn. Nó sẽ bị nghẽn cho đến khi có một yêu cầu nối kết của client gởi đến. Khi có yêu cầu nối kết của Client gởi đến, nó sẽ chấp nhận yêu cầu nối kết, trả về
một Socket là một đầu của kênh giao tiếp ảo giữa Server và Client yêu cầu nối kết.
public InputStream getInputStream() : Phương thức này trả về InputStream nối với Socket. Chương trình Server dùng InputStream này để nhận dữ liệu từ Client gởi đến.
public OutputStream getOutputStream() : Phương thức này trả về OutputStream nối với Socket. Chương trình Server dùng OutputStream này để trả dữ liệu cho Client.
public close() : Phương thức này sẽ đóng Socket lại, giải phóng kênh ảo, xóa nối kết giữa Client và
Server.
Một Server có thể được cài đặt để phục vụ các Client theo hai cách: phục vụ tuần tự hoặc phục vụ song song.
Trong chế độ phục vụ tuần tự, tại một thời điểm Server chỉ chấp nhận một yêu cầu nối kết. Các yêu cầu nối kết của các Client khác đều không được đáp ứng (đưa vào hàng đợi).
Ngược lại trong chế độ phục vụ song song, tại một thời điểm Server chấp nhận nhiều yêu cầu nối kết và phục vụ nhiều Client cùng lúc.
Xây dựng chương trình Server phục vụ tuần tự
Các bước thực hiện như sau:
Tạo socket và gán số hiệu cổng cho server.
Lắng nghe yêu cầu nối kết.
Với một yêu cầu nối kết được chấp nhận thực hiện các bước sau:
Lấy InputStream và OutputStream gắn với Socket của kênh ảo vừa được hình thành.
Lặp lại công việc sau:
Chờ nhận các yêu cầu (công việc).
Phân tích và thực hiện yêu cầu.
Tạo thông điệp trả lời.
Gởi thông điệp trả lời về Client.
Nếu không còn yêu cầu hoặc Client kết thúc, đóng Socket và quay lại bước 2.
Code của chương trình Server như sau:
package com.gpcoder.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoChatSingleServer {
public final static int SERVER_PORT = 7;
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
try {
System.out.println("Binding to port " + SERVER_PORT + ", please wait ...");
serverSocket = new ServerSocket(SERVER_PORT);
System.out.println("Server started: " + serverSocket);
System.out.println("Waiting for a client ...");
while (true) {
try {
Socket socket = serverSocket.accept();
System.out.println("Client accepted: " + socket);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
int ch = 0;
while (true) {
ch = is.read(); // Receive data from client
if (ch == -1) {
break;
}
os.write(ch); // Send the results to client
}
socket.close();
} catch (IOException e) {
System.err.println(" Connection Error: " + e);
}
}
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if (serverSocket != null) {
serverSocket.close();
}
}
}
}
Chạy chương trình Server, ta có kết quả sau:
Tiếp tục chạy chương trình Client, ta có kết quả sau:
Xem lại cửa sổ console của Server, ta thấy kết quả như sau:
Xây dựng chương trình Server phục vụ song song
Server phục vụ song song có thể chia thành 2 phần thực hiện song song nhau:
Phần 1 (Dispatcher Thread) : Xử lý các yêu cầu nối kết. Lặp lại các công việc sau:
Lắng nghe yêu cầu nối kết của Client.
Chấp nhận một yêu cầu nối kết: Tạo kênh giao tiếp ảo mới với Client, tạo Phần 2 để xử lý các thông điệp yêu cầu của Client.
Phần 2 (Worker Thread) : Xử lý các thông điệp yêu cầu từ khách hàng. Lặp lại các công việc sau:
Chờ nhận thông điệp yêu cầu của khách hàng.
Phân tích và xử lý yêu cầu.
Gởi thông điệp trả lời cho khách hàng.
Phần 2 sẽ kết thúc khi kênh ảo bị xóa đi.
Với mỗi Client, trên Server sẽ có một Phần 2 để xử lý yêu cầu của khách hàng. Như vậy tại một thời điểm bất kỳ luôn tồn tại 1 Phần 1 và 0 hoặc nhiều Phần 2.
Code của chương trình Server như sau:
EchoChatMultiServer.java
package com.gpcoder.tcp;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class EchoChatMultiServer {
public static final int NUM_OF_THREAD = 4;
public final static int SERVER_PORT = 7;
public static void main(String[] args) throws IOException {
ExecutorService executor = Executors.newFixedThreadPool(NUM_OF_THREAD);
ServerSocket serverSocket = null;
try {
System.out.println("Binding to port " + SERVER_PORT + ", please wait ...");
serverSocket = new ServerSocket(SERVER_PORT);
System.out.println("Server started: " + serverSocket);
System.out.println("Waiting for a client ...");
while (true) {
try {
Socket socket = serverSocket.accept();
System.out.println("Client accepted: " + socket);
WorkerThread handler = new WorkerThread(socket);
executor.execute(handler);
} catch (IOException e) {
System.err.println(" Connection Error: " + e);
}
}
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if (serverSocket != null) {
serverSocket.close();
}
}
}
}
WorkerThread.java
package com.gpcoder.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class WorkerThread extends Thread {
private Socket socket;
public WorkerThread(Socket socket) {
this.socket = socket;
}
public void run() {
System.out.println("Processing: " + socket);
try {
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
while (true) {
int ch = is.read(); // Receive data from client
if (ch == -1) {
break;
}
os.write(ch); // Send the results to client
}
} catch (IOException e) {
System.err.println("Request Processing Error: " + e);
}
System.out.println("Complete processing: " + socket);
}
}
Chạy chương trình Server, ta có kết quả sau:
Chạy liên tục 3 chương trình Client, ta có kết quả như sau:
Như bạn thấy, cả 3 chương trình Client đều được Server xử lý cùng lúc.
Xây dựng chương trình Client – Server ở chế độ không nối kết (UDP)
UDP – User Datagram Protocol : cung cấp cơ chế vận chuyển không bảo đảm và không nối kết trên mạng IP, ngược với giao thức vận chuyển tin cậy, có nối kết TCP.
Cả giao thức TCP và UDP đều phân dữ liệu ra thành các gói tin. Tuy nhiên TCP có thêm vào những tiêu đề (Header) vào trong gói tin để cho phép truyền lại những gói tin thất lạc và tập hợp các gói tin lại theo thứ tự đúng đắn. UDP không cung cấp tính năngnày, nếu một gói tin bị thất lạc hoặc bị lỗi, nó sẽ không được truyền lại, và thứ tự đến đích của các gói tin cũng không giống như thứ tự lúc nó được gởi đi.
Tuy nhiên, về tốc độ, UDP sẽ truyền nhanh gấp 3 lần TCP. Cho nên chúng thường được dùng trong các ứng dụng đòi hỏi thời gian truyền tải ngắn và không cần tính chính xác cao, ví dụ truyền âm thanh, hình ảnh …
Mô hình client – server sử dụng lớp ServerSocket và Socket ở trên sử dụng giao thức TCP. Nếu muốn sử dụng mô hình client – server với giao thức UDP, ta sử dụng hai lớp java.net.DatagramSocket và java.net.DatagramPacket.
DatagramSocket được sử dụng để truyền và nhận các DatagramPacket. Dữ liệu được truyền đi là một mảng những byte, chúng được gói vào trong lớp DatagramPacket. Chiều dài của dữ liệu tối đa có thể đưa vào DatagramPacket là khoảng 60.000 byte (phụ thuộc vào dạng đường truyền). Ngoài ra DatagramPacket còn chứa địa chỉ IP và cổng của quá trình gởi và nhận dữ liệu.
Cổng trong giao thức TCP và UDP có thể trùng nhau. Trên cùng một máy tính, bạn có thể gán cổng 20 cho socket dùng giao thức TCP và cổng 20 cho socket sử dụng giao thức UDP.
Một số phương thức cần thiết để xây dựng các chương trình Client-Server sử dụng socket ở chế độ không nối kết:
public DatagramPacket(byte[] b, int n, InternetAddress ia, int port) : Phương thức này cho phép tạo một DatagramPacket chứa dữ liệu và cả địa chỉ của máy nhận dữ liệu. Phương thức trả về một đối tượng thuộc lớp DatagramePacket.
public DatagramSocket( int port) : Tạo Socket kiểu không nối kết cho Client với số hiệu cổng được xác định trong tham số (port). Nếu không xác định port, hệ thống tự động gán số hiệu cổng chưa sử dụng cho socket.
public void send(DatagramPacket dp) : Dùng để gởi một DatagramPacket đi.
public synchronized void receive(Datagrampacket dp) : Chờ nhận một DatagramPacket. Quá trình sẽ bị nghẽn cho đến khi có dữ liệu đến.
Các phương thức lấy thông tin trên một DatagramPacket nhận được:
Khi nhận được một DatagramPacket từ một quá trình khác gởi đến, ta có thể lấy thông tin trên DatagramPacket này bằng các phương thức sau:
public synchronized() InternetAddress getAddress() : Địa chỉ máy gởi.
public synchronized() int getPort() : Cổng của quá trình gởi.
public synchronized() byte[] getData() : Dữ liệu từ gói tin.
public synchronized() int getLength() : Chiều dài của dữ liệu trong gói tin.
Các phương thức đặt thông tin cho gói tin gởi:
Trước khi gởi một DatagramPacket đi, ta có thể đặt thông tin trên DatagramPacket này bằng các phương thức sau:
public synchronized() void setAddress(IntermetAddress address) : Đặt địa chỉ máy nhận.
public synchronized() void setPort(int port) : Đặt cổng quá trình nhận.
public synchronized() void setData(byte buffer[]) : Đặt dữ liệu gởi.
public synchronized() void setLength(int len) : Đặt chiều dài dữ liệu gởi.
Xây dựng chương trình Server ở chế độ không nối kết
Chương trình EchoServer cài đặt Echo Server ở chế độ không nối kết, cổng mặc định là 7. Chương trình chờ nhận từng gói tin, lấy dữ liệu ra khỏi gói tin nhận được và gởi ngược dữ liệu đó về Client.
package com.gpcoder.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class EchoServer {
public final static int SERVER_PORT = 7; // Cổng mặc định của Echo Server
public final static byte[] BUFFER = new byte[4096]; // Vùng đệm chứa dữ liệu cho gói tin nhận
public static void main(String[] args) {
DatagramSocket ds = null;
try {
System.out.println("Binding to port " + SERVER_PORT + ", please wait ...");
ds = new DatagramSocket(SERVER_PORT); // Tạo Socket với cổng là 7
System.out.println("Server started ");
System.out.println("Waiting for messages from Client ... ");
while (true) { // Tạo gói tin nhận
DatagramPacket incoming = new DatagramPacket(BUFFER, BUFFER.length);
ds.receive(incoming); // Chờ nhận gói tin gởi đến
// Lấy dữ liệu khỏi gói tin nhận
String message = new String(incoming.getData(), 0, incoming.getLength());
System.out.println("Received: " + message);
// Tạo gói tin gởi chứa dữ liệu vừa nhận được
DatagramPacket outsending = new DatagramPacket(message.getBytes(), incoming.getLength(),
incoming.getAddress(), incoming.getPort());
ds.send(outsending);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ds != null) {
ds.close();
}
}
}
}
Xây dựng chương trình Client ở chế độ không nối kết
Chương trình này cho phép người sử dụng nhận các chuỗi từ bàn phím, gởi chuỗi sang EchoServer ở chế độ không nối kết ở cổng số 7, chờ nhận và in dữ liệu từ Server gởi về ra màn hình.
package com.gpcoder.udp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class EchoClient {
public final static String SERVER_IP = "127.0.0.1";
public final static int SERVER_PORT = 7; // Cổng mặc định của Echo Server
public final static byte[] BUFFER = new byte[4096]; // Vùng đệm chứa dữ liệu cho gói tin nhận
public static void main(String[] args) {
DatagramSocket ds = null;
try {
ds = new DatagramSocket(); // Tạo DatagramSocket
System.out.println("Client started ");
InetAddress server = InetAddress.getByName(SERVER_IP);
while (true) {
System.out.println("Enter your message: ");
InputStreamReader isr = new InputStreamReader(System.in); // Nhập
BufferedReader br = new BufferedReader(isr); // một chuỗi
String theString = br.readLine(); // từ bàn phím
byte[] data = theString.getBytes(); // Đổi chuỗi ra mảng bytes
// Tạo gói tin gởi
DatagramPacket dp = new DatagramPacket(data, data.length, server, SERVER_PORT);
ds.send(dp); // Send gói tin sang Echo Server
// Gói tin nhận
DatagramPacket incoming = new DatagramPacket(BUFFER, BUFFER.length);
ds.receive(incoming); // Chờ nhận dữ liệu từ EchoServer gởi về
// Đổi dữ liệu nhận được dạng mảng bytes ra chuỗi và in ra màn hình
System.out.println("Received: " + new String(incoming.getData(), 0, incoming.getLength()));
}
} catch (IOException e) {
System.err.println(e);
} finally {
if (ds != null) {
ds.close();
}
}
}
}
Chạy chương trình Server, ta có kết quả như sau:
Chạy chương trình Client, ta có kết quả như sau:
Tại Client, nhập nội dung message là Hello. Ta có kết quả như sau:
Tại Cient, tiếp tục nhập nội dung là How are you. Ta có kết quả như sau:
Xây dựng chương trình Client-Server nối kết theo dạng multicast (truyền theo nhóm)
Các cơ chế Socket TCP và UDP đã giới thiệu ở trên đề gọi là unicast, nghĩa là giao tiếp chỉ diễn ra giữa một máy tính gửi và một máy tính nhận.
Multicast là việc gửi quảng bá (broadcast) nhưng đến một nhóm máy tính ở cùng một địa chỉ cho trước. Địa chỉ multicast là địa chỉ lớp D được xác định trong khoảng 244.0.0.0 đến 239.255.255.255. Địa chỉ 244.0.0.0 là địa chỉ riêng nên không sử dụng được.
Multicast được sử dụng trong game nhiều người chơi, trong những ứng dụng mà đối tượng là nhiều thiết bị hay nhiều máy tính cùng nhận một loại thông tin. Multicast cũng được sử dụng trong giải thuật vạch đường (Routing Protocol), khi các router muốn cập nhật thông tin với nhau.
Java hỗ trợ Multicast thông qua lớp java.net.MulticastSocket. Một Multicast Socket là 1 DatagramSocket (UDP) có khả năng gia nhập (joining) vào một nhóm các máy tính multicast trên mạng. Khi một máy tính nào gửi thông điệp đến nhóm thì tất cả các máy tính trong đó đều nhận được.
Một số phương thức cần thiết để xây dựng các chương trình Multicast:
public MulticastSocket(int port): Tạo Socket kiểu Multicast với số hiệu cổng được xác định trong tham số (port).
public void joinGroup(InetAddress group) : Tham gia nhóm Multicast tại địa chỉ xác định.
public void leaveGroup(InetAddress group) : Rời khỏi nhóm Multicast tại địa chỉ xác định.
public void send(DatagramPacket dp) : Dùng để gởi một DatagramPacket đi.
public synchronized void receive(Datagrampacket dp) : Chờ nhận một DatagramPacket.
Ví dụ:
Cài đặt một dịch vụ gởi tin nhắn trên cổng 8888, tại địa chỉ 224.0.0.1. Khi Client muốn nhận tin nhắn thì Join Group tại địa chỉ và cổng trên.
Lớp Sender
package com.gpcoder.multicast;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class MulticastSender {
public static final String GROUP_ADDRESS = "224.0.0.1";
public static final int PORT = 8888;
public static void main(String[] args) throws InterruptedException {
DatagramSocket socket = null;
try {
// Get the address that we are going to connect to.
InetAddress address = InetAddress.getByName(GROUP_ADDRESS);
// Create a new Multicast socket
socket = new DatagramSocket();
DatagramPacket outPacket = null;
long counter = 0;
while (true) {
String msg = "Sent message No. " + counter;
counter++;
outPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length, address, PORT);
socket.send(outPacket);
System.out.println("Server sent packet with msg: " + msg);
Thread.sleep(1000); // Sleep 1 second before sending the next message
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
}
Lớp Receiver
package com.gpcoder.multicast;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulticastReceiver {
public static final byte[] BUFFER = new byte[4096];
public static void main(String[] args) {
MulticastSocket socket = null;
DatagramPacket inPacket = null;
try {
// Get the address that we are going to connect to.
InetAddress address = InetAddress.getByName(MulticastSender.GROUP_ADDRESS);
// Create a new Multicast socket
socket = new MulticastSocket(MulticastSender.PORT);
// Joint the Multicast group
socket.joinGroup(address);
while (true) {
// Receive the information and print it.
inPacket = new DatagramPacket(BUFFER, BUFFER.length);
socket.receive(inPacket);
String msg = new String(BUFFER, 0, inPacket.getLength());
System.out.println("From " + inPacket.getAddress() + " Msg : " + msg);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Chạy chương trình MulticastSender, sau đó start tiếp một vài chương trình MulticastReceiver. Chúng ta nhận được kết quả như sau:
Như bạn thấy, bất kỳ client nào join group đều nhận được nội dung message giống nhau, và nhận cùng thời điểm. Chương trình Multicast Server vẫn phục vụ theo dạng UDP, chỉ khác là sẽ gửi gói tin Datagram đến địa chỉ IP thuộc lớp D.
Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng
Tôi là một người may mắn, và tôi không phải nói câu này. Nhưng giờ đã 12 năm sau cái ngày đó (2008), nhìn lại, thì đại học mang lại cho tôi những gì? Và nếu bạn trượt đại học, thì giờ làm gì đây?
Trượt đại học thì sao?
Thì buồn.
Đấy là điều đầu tiên có thể cảm nhận được. Dù chưa trải qua, chỉ cần nhìn những đứa bạn kém may mắn hơn chúng nó buồn, thậm chí còn xa lánh mình, sẽ hiểu trượt đại học thì buồn.
Thế tại sao lại buồn?
Xã hội sinh ra một định kiến: học hết cấp 3 thì PHẢI thi đại học, và đỗ đại học là giỏi, là vinh quang, còn trượt thì là … không giỏi. Bạn sẽ bị coi như 1 kẻ kém cỏi, hàng xóm, người quen, bạn bố mẹ sẽ hỏi thăm. Và bởi vì nó đã là một định kiến xã hội, nên 1 2 con người thay đổi cũng gặp rất nhiều khó khăn. Cái sức ép xã hội khiến bạn phải buồn, khiến bạn nghĩ rằng mình kém cỏi, vậy nên bạn buồn. Hay đơn giản buồn vì bố mẹ/người thân bạn buồn. Ngoài ra, phải rời xa bạn bè vì chúng nó đều đi lên thành phố học hết, cũng buồn.
Nhớ rằng “học tài, thi phận”, bởi nếu như bạn được 25 điểm mà cái trường bạn chọn năm nay nó tăng nhanh như chỉ số PM 2.5 ở thủ đô, lấy hản 27 thay vì 23 như năm trước, thì đâu phải do bạn kém? Hay thằng hàng xóm được 15 điểm, đăng ký vào trường năm nay lấy 15, thì nó lại đỗ. Hay con ông kia ôn trúng tủ, còn bạn thì lệch, nó đỗ là may hơn, chứ đâu phải giỏi hơn.
Năm 18 tuổi, tôi thi đại học vì 1 lý do đơn giản: vì đấy là điều ai cũng làm sau khi học xong lớp 12. Trong cái đầu của 1 thằng học sinh miền núi đi thi được cộng thêm 1.5 điểm, không hề từng nghĩ đến câu “tại sao lại phải học đại học”, hay “học đại học để làm gì?”. Chuyện chọn trường cũng đơn giản chỉ là “thích nghịch máy tính thì vào bách khoa” chứ chả hiểu bách khoa dạy cái gì để làm với máy tính.
Thế nên bạn có thể buồn, nhưng nhớ nghĩ kỹ hơn chút, là tại sao mình phải buồn.
Ngày ấy, hiểu đơn giản là cứ đi đại học thôi, đi đại học là thành công, đi đại học để học đại học. Học để ra trường, kiếm được 1 tấm bằng đại học, rồi cầm về để mang đi xin việc, làm ở cơ quan nào đó.
Giờ ngẫm lại, xã hội muốn mình đi học đại học mục đích ra là để kiếm cho mình được 1 công việc, nuôi bản thân, đóng góp cho xã hội.
Cái bằng là thứ công cụ để xin việc của thời ngày xưa, là tấm vé giải độc đắc để kiếm được 1 chỗ ngon trong cơ quan toàn là cán bộ.
Còn giờ là năm thứ 20 của thế kỷ 21, tấm bằng trở nên bớt quan trọng hơn nhiều, kể cả cầm 1 tấm bằng, đi xin việc thì vẫn phải THI (hay gọi nhẹ nhàng hơn là “phỏng vấn”). Ngoài ra, rất nhiều nơi / ngành nghề, không hề quan tâm đến bằng cấp, đặc biệt là ngành công nghệ thông tin.
Nếu anh giỏi, anh làm được, thì mang cái sản phẩm của mình ra mà khoe, còn anh chả có gì khoe, thì đành thôi: mang khoe cái bằng.
Xin chào, đây là website tôi làm ra, giải quyết vấn đề này, sử dụng công nghệ kia … app di động 1 triệu lượt tải trên app store …
Xin chào, em không biết code web/app nhưng em có bằng khá Bách Khoa, em chưa làm ra sản phẩm nào ngoài đoạn code tính tổng từ 1 đến 1000.
Cái bằng, không phải là mục đích của việc học đại học. Học đại học là để có được “kỹ năng”/”trình độ”, cái bằng chỉ là “bằng chứng” do trường đại học đó đánh giá, theo tiêu chuẩn của trường đó. Vậy nên nếu không học đại học, ta sẽ không có “bằng chứng” do trường đại học đó cung cấp, nhưng không hề liên quan đến chuyện “kỹ năng”/”trình độ”, thứ mà hoàn toàn có thể học ở chỗ khác.
Học đại học được gì
Nếu coi trường học như một cỗ máy, thì đầu vào là các sinh viên khá “thông minh” do điểm thi đầu vào trên mức N, còn đầu ra sẽ các kỹ sữ biết làm việc mà họ được đào tạo. Lý thuyết là vậy, còn thực tế thì khác hoàn toàn lý thuyết.
Khuôn mặt một sinh viên mùa ôn thi – Photo by Ray Hennessy on Unsplash
Nhìn lại, 5 năm đại học tôi có được:
Được lên thủ đô sống, làm quen với “văn minh xã hội”. Lần đầu xuống nhập học được học ngay bài học ăn phở không hỏi giá, bị chém đẹp 50k (thời 2008).
Những người bạn. Vui vẻ cùng nhau đến lớp ngủ, cùng nhau vượt qua các kỳ thi lại, cùng nhau uống rượu, cùng nhau đi chơi… Thứ tình bạn trong sáng (như hồi cấp 3), những người anh em chia ngọt sẻ bùi, điều mà sau này đi làm rồi, hay có gia đình riêng rồi, sẽ rất khó mà kiếm được.
Quãng thời gian sinh viên, học hành thì ít mà chơi thì nhiều, những va vấp nhẹ nhàng với xã hội khi lần đầu rời xa gia định đến 1 nơi xa lạ.
Một ngôi trường có cháo lòng, cháo sườn, bánh mì, rượu ốc ngon.
Những hoạt động thanh niên cấp tiến như sinh viên tình nguyện, tiếp sức mùa thi, chiếc khăn gió ấm, mùa đông không lạnh…
Khả năng tự học: cái này là kỹ năng tự luyện được, lôi sách lôi vở ra mà đọc mà luyện đề thi còn qua môn, chứ làm gì có ai dạy cho.
Còn kiến thức? không nhiều lắm. Những kiến thức ngày nay đi làm, đều do tôi tự mày mò, nhờ sự ham mê máy tính từ ngày còn nhỏ. Blog FAMILUG viết từ năm thứ 2 đại học là bằng chứng ấy. Tôi code Python khi ở Việt Nam chẳng mấy ai biết tên, tôi dùng Ubuntu khi mọi người còn không biết nó tồn tại.
Nói vậy không có nghĩa là 5 năm trường không dạy gì. Trường có dạy, vấn đề là bạn có học hay không? và học có giỏi hay không? Một phần nhỏ sinh viên thuộc nhóm có học, và học giỏi, ra sẽ học lên cao, xin học bổng nước ngoài, được giữ làm giảng viên, hay xin được công việc phù hợp. Nhưng nhớ là số này rất ít, một khóa học 100 sinh viên thì có 1, 2, 3, 4 thằng, chứ không phải là 20 thằng. Với nhóm này, học đại học còn có thêm cái lợi: quan hệ tốt với thầy cô, nhiều cơ hội xin học bổng du học lên cao, làm đề tài nghiên cứu khoa học cùng thầy cô … Đây là cái đầu ra mong ước, chuẩn của các trường. Số còn lại, chiếm tới hơn 90%, mới là phần đáng nói của câu chuyện.
Bằng giỏi Bách Khoa thời ấy, hiếm như khẩu trang đầu mùa COVID-19, sinh viên ra trường đúng hạn thì ít, bằng khá đã là học hành tử tế lắm rồi. Bằng khá Bách Khoa nó không nhiều nhan nhản như bằng giỏi bên kinh tế nha. Số này cỡ 2 chục người trong 1 khóa 100 người, cũng bằng số sinh viên ra trường trước hạn, bỏ học/đuổi học/quên đăng ký môn học… rồi bị đuổi.
Cái nhóm 60 sinh viên bằng “trung bình”, ra trường không đúng hạn ấy, học được gì ở trường? Năm 2008-2013+, khoa/viện toán ứng dụng và tin học Bách Khoa Hà Nội dạy:
2 năm học đại cương: toán, lý, hóa, nhiệt, điện, cơ học kỹ thuật, vẽ kỹ thuật, điện tử, triết học Mac Lê Nin, tư tưởng Hồ Chí Minh, … các môn học được coi là nền tảng phải có của mọi kỹ sư. Và để thêm chắc, các môn toán lý hóa có tới 5 7 môn khác nhau, lý 1 2 3 nữa cho nó chắc. Đây là thời kỳ học chán nản nhất cho các sinh viên, trượt/thi lại triền miên, bởi đây toàn những môn lý thuyết khó nhai. Có những môn học lại 10 kỳ, cho đến khi qua được thì cũng là lúc ra trường. Gọi là học, chứ thi xong là coi như trả hết chữ thầy, kiến thức cũng chẳng dùng đến bao giờ, như cách tra biểu đồ nhiệt chẳng hạn. Ở đâu đó, ai đó làm một nghề nào đó, có thể đang dùng, thậm chí là quan trọng. Sinh viên ra trường đi làm có người dùng tới có người không, nên nhớ đây là chương trình để đào tạo hàng loạt người, nên đứng dưới góc độ nhà trường, bạn thà dạy thừa còn hơn thiếu.
3 năm học chuyên ngành: Toán-Tin. Hầu hết sinh viên khóa này học toán-tin đơn giản là vì trượt nguyện vọng 1, rơi tự do xuống mà vào toán tin. Bách Khoa thời ấy, học 1 năm đại cương, lấy điểm 2 kỳ đầu để xét phân khoa. Đây cũng là thời mà hận những thằng giỏi học đại cương, chúng nó nộp vào CNTT ầm ầm, chỉ đơn giản là vì ngành hot, ra dễ kiếm việc lương cao – chứ thậm chí chưa chạm tay vào máy tính 1 lần. Khoa nào nhiều người vào thì hot, điểm cao, khoa nào không ai đăng ký… thì rơi tự do. Không nhiều sinh viên vào Toán-Tin để học toán, chắc cũng là một phần lý do mà kết quả học tập cũng không mấy đẹp đẽ gì. Hơn nửa chương trình chuyên ngành 3 năm này là toán, một chút ít “tin” cho có gọi là. Sinh viên muốn làm được đồ án tin để ra trường, phải tự kiếm chỗ mà học lập trình cho tử tế (tự học mà giỏi được thì tốt, ai cũng làm được thì người ta không phải mở trường, dựng trung tâm). Đừng hy vọng sẽ được học lập trình, ra thành 1 lập trình viên chuyên nghiệp từ đây. Với những sinh viên muốn theo ngành “tin học”, thì phần toán kia rất khó nuốt, khô khan, chẳng có chỗ dùng, không có hứng học, thi khó qua, điểm sẽ thấp. Chương trình học lạc hậu, đặc biệt với các môn tin, khi mà công nghệ thay đổi hàng năm còn toán thì trăm năm mới đổi. Đây là tên vài môn toán kinh (khủng) điển: giải tích hàm, phương trình đạo hàm riêng, giải tích số, giải tích phức, toán kinh tế, các phương pháp tối ưu, hệ mờ ?!? Chất lượng giảng dạy thì rất phụ thuộc vào giảng viên, một số môn có thầy cô dạy hay, phong cách hay, lôi cuốn sinh viên học dù môn học không có nhiều tính ứng dụng thấy ngay, có môn thầy đến lớp hì hục chép đầy 6 cái bảng 9 lần rồi về, sinh viên ra vào thậm chí không biết…
Đấy là thời đó, cũng gần 10 năm rồi, thời ấy học mạng neuron và hệ mờ trên giấy, chỉ biết lấy công thức ra tính, chả ai biết để làm gì, chưa ai nghe tới Machine Learning hay Deep Learning. Còn giờ thì Toán Tin đầu vào hẳn 27.56, xin được mừng cho viện nhà.
Xem chương trình đào tạo mới tại đây thấy đã bớt được nhiều môn học nền tảng, quan trọng như hóa hay nhiệt đi rồi, mừng cho các tài năng đất nước.
Nói vậy, không nghĩa là không nên học đại học. Đỗ rồi thì đi học thôi, nhưng nên thử để biết mình thích gì/hợp làm gì, mà tìm học thêm, chứ đừng thụ động ngồi chờ trường dạy cho.
Trượt đại học thì làm gì
Trượt đại học thì đi học cái khác, hoặc đi làm. Học đại học là con đường có vẻ như “dễ”/”sáng” nhất để dẫn đến “thành công”/”thành tài”, nhưng không phải con đường duy nhất. Cũng chẳng có gì đảm bảo, hàng năm sinh viên đại học, thậm chí cao học, ra trường vẫn thất nghiệp ầm ầm
Nếu muốn, nghỉ ngơi 1 thời gian rồi 3 tháng sau bắt đầu ôn thi để sang năm… chơi lại trò chơi may rủi.
Học đại học thời nay là một khoản đầu tư không nhỏ, học phí các trường công giờ cũng cao chót vót, mỗi kỳ học đóng học phí bằng 1/2 -> 1 con iPhone mới nhất vừa ra. Học 10 kỳ là 10 con (~tối thiểu 60 triệu cho 5 năm học), chưa kể thi lại, chưa tính chi phí ăn ở 4-5 năm. Nên nếu chỗ nào học phí 50 triệu, trong vòng 2 năm, hay 5 triệu trong vòng 2 tháng mà cho bạn cơ hội kiếm việc làm lương 8-10 triệu/tháng thì đừng xem là đắt, đi làm vài tháng là hồi vốn. Ngoài ra, nếu tôi học nửa năm rồi đi làm, sau 5 năm tôi có 4.5 năm kinh nghiệm, lương thừa X3 mấy cậu sinh viên mới rón rén ra trường.
Điều quan trọng là tìm ra thứ khiến mình thích thú, đam mê. Nếu có rồi, cứ thế mà học theo, nếu chưa biết, đỗ đại học, đi học 5 năm chăm chỉ ra rồi nhận ra mình không thích mới đổi, lúc ấy mới là muộn, đó mới là thất bại đáng lo.
Những thứ quan trọng cần đầu tư:
Ngoại ngữ: phổ biến, dễ học nhất là tiếng Anh. Dù học đại học hay không, đây vẫn là thứ quan trọng nhất. Nó mở ra cả 1 chân trời cơ hội, hàng triệu tài liệu hướng dẫn miễn phí bằng tiếng Anh trên internet, dù học bất cứ thứ gì, điều kiện làm các công việc lương cao, công ty nước ngoài.
Rèn luyện sức khỏe: tập gì cũng được, có sức khỏe, có cơ thể đẹp là một lợi thế cả đời, cực lớn, thậm chí giúp kiếm được nhiều công việc lương cực cao, ngàn đô 1 ngày!
Nếu muốn học ngành Công Nghệ Thông Tin, học xong đi kiếm việc làm lương khởi điểm 8-10 triệu, ghé ngay PyMi.vn để đăng ký học lập trình Python tại Hà Nội – Sài Gòn (TP HCM).
Học có 1.5 tháng mà đi làm được?
Tất nhiên là phải học, phải đầu tư thời gian, chăm chỉ luyện bài, thì học ra mới dễ xin được việc, mỗi tuần dành ~ 20 tiếng để cày cuốc bài tập. Chứ đừng nghĩ tới lớp xem giảng bài, về nhà bận đi chơi, rồi ra thì có người mời đi làm, sorry babe, ở đây không bán rượu mơ.
PyMi không chỉ dạy Python, mà sử dụng các công cụ / practice của lập trình viên trong suốt quá trình học (vd: Git/GitLab/CI/Code Review).
Lương chỉ có 8-10 triệu?
Đây là mức lương khởi điểm cho vị trí lập trình viên Python (thường là lập trình web-backend). Mức lương này ngang ngửa với sinh viên đại học mới ra trường. Còn muốn học 2 tháng ra làm lương ngàn đô, có lẽ qua bên các kênh đầu tư tài chính thì nhiều cơ hội hơn.
Sau khi đi làm 1 2 năm có thể tăng lên 15 triệu, hay $1000, và cứ thế tăng tiếp… PS: muốn > $1000, phải có ngoại ngữ tốt.
Tài liệu trên mạng đầy, lên youtube xem rồi tự học
Tự học là một kỹ năng không phải ai cũng có. Kỹ năng tự học phải luyện nhiều, mới có. Cũng chẳng phải đến đại học thì mới biết tự học. Cứ tự học nhiều (như 5 năm ở Bách Khoa) thì khác dần biết cách thôi. Nếu tự học mà hiệu quả được, đó là điều rất đáng mừng, bạn cũng không cần phải đi đại học làm gì cho mất thời gian.
Lên youtube xem/đọc tài liệu không hiểu thì lấy ai để hỏi?
Tài liệu chất lượng kém, đặc biệt các tài liệu tiếng Việt dùng Google Translate rồi paste vào. Tài liệu viết bởi những người không có trình độ, liệu bạn có phân biệt được?
Lên các diễn đàn/FaceBook group để hỏi rồi ngồi chờ hy vọng có ai đó tốt giúp trả lời, mà trả lời đúng hay sai cũng không chắc nữa.
Nếu lý do ngồi chờ cộng đồng trả lời sau vài ngày rồi mới học tiếp là để “tiết kiệm tiền”, thì chỉ chứng minh 1 điều là thời gian của bạn quá rẻ.
Tự học ngoài ra còn gặp một số vẫn đề như: “đâm đầu vào tường” nhiều, do không ai chỉ cho đâu là đúng sai, phải tự tìm ra, vậy nên mất thời gian hơn. Đi học, không hỏi thầy thì còn hỏi bạn, nhanh hơn rất nhiều. Ngoài ra bạn còn không biết mình không biết những gì.
Một khi đã tự học rồi, thì lo tập trung vào việc học, làm bài tập, tránh mất thời gian ít bổ ích trên các group online.
Các khóa học tiếng Anh trên mạng rẻ bèo, như Udemy
Nếu xem được mà hiểu thì trình độ tiếng Anh của bạn cũng thuộc top trên của người Việt Nam đó, chúc mừng nha. Như đã viết ở trên, nếu đã có tiếng Anh, thì toàn bộ phần còn lại chỉ là game dễ.
Các khóa học cũng có loại this, loại that. Học code mà chỉ xem video, không thò tay vào code, không có bài tập, không có sửa bài, thì chỉ là xem video.
Code là kỹ năng, muốn giỏi phải luyện tập nhiều. Làm ra phải được chấm (review), để biết là tốt hay xấu, đúng hay sai.
Học đại học 5 năm ra còn chẳng ăn ai, đòi học vài tháng
Bạn đã thấy có thằng học như điên 3 năm, học đủ cả sinh sử địa, giáo dục công dân nữa để “rèn luyện tư duy” mà trượt đại học khối A, còn có thằng đi ôn lò vài tháng, trúng tủ đỗ luôn chưa?
5 năm đại học không phải là học cả 5 năm, thời gian chơi có khi hết 4 năm rưỡi. 10 thằng học giỏi có đến 90 thằng loại bình thường.
Xem lại chương trình đại học Bách Khoa ở trên để thấy nếu muốn làm lập trình viên (nghề ghi đầu tiên trong bảng quảng cáo học Toán Tin), thì chỉ có vài môn học cuối khóa có chút liên quan, mà muốn ra đi làm được vẫn phải học thêm rất nhiều.
Học lâu mà sai không có ý nghĩa gì so với học đúng mà nhanh.
Kiến thức ở các trường đại học Việt Nam đều rất chậm thay đổi, trong khi công nghệ thay đổi hàng năm. 5 năm học, dùng C/Pascal vài ba project nhỏ ở trường sẽ khống kiếm cho bạn 1 công việc dev Python.
Giờ không biết đã trường nào dạy dùng git chưa, hay vẫn Turbo C màn hình xanh menu đỏ rồi tự hào công nghệ?
Học vậy không có “kiến thức cơ bản”/”kiến thức nền tảng”
“Kiến thức cơ bản” cụ thể là cái gì? hiểu cách các vi mạch điện hoạt động rồi chạy code như thế nào? (học đại học ở Việt Nam cũng không giúp bạn hiểu cái này), hay hiểu cách đổi 8 bit thành 1 byte?
Hay kiến thức cơ bản là môn “cấu trúc dữ liệu giải thuật/toán rời rạc” học trên giấy trong 1 kỳ mà cứ ngỡ như không đi đại học thì không được học/ không học được? Có bao nhiêu kiến thức cơ bản ấy ở lại trong sinh viên sau 5 năm ra trường, học môn ấy từ năm thứ 2.
Có 2 cách tiếp cần vấn đề, từ dưới lên, hoặc từ trên xuống. Các trường đại học dạy theo kiểu từ dưới lên, đến lúc lên đến trên thì sinh viên đã quên hết cái bên dưới.
Các trung tâm thì sẽ dạy từ trên xuống, học cái làm được luôn, có gì không hiểu thì đào sâu, tìm hiểu thêm.
Đừng nghĩ biết code mấy cái thuật toán sắp xếp học ở trường mà hơn người ta gõ list.sort(), cái bạn học ở trường, lúc đi ra làm, đâu có dùng? Bạn có biết list.sort() của Python dùng thuật toán gì không?
Nếu bạn thuộc “on the top của hành tinh này”, thiết kế thuật toán sắp xếp phù hợp với bài toán cụ thể, rõ ràng bạn không phải đối tượng của bài viết này.
Sao bài này toàn ví dụ viện Toán Tin – Bách Khoa Hà Nội
Vì tác giả đã học ở đó. Bạn học khoa khác, cậu chuyện có thể khác.
Học xong có làm được cái này, cái kia không?
Vui lòng đọc kỹ trang chủ Pymi và CV mẫu của học viên (cũng có link trên trang chủ).
Nói tóm lại, người muốn, sẽ tìm cách, còn không muốn, sẽ tìm lý do.
Hành động của chúng ta (Action Item)
Tự hỏi xem học đại học để làm gì?
Ngừng huyễn hoặc bản về sự cao siêu của đại học, hay “chỉ học đại học mới được học”.
Hồi tưởng xem học đại học đã học được gì có ích? và viết 1 bài.
Tìm cái bằng đại học xem nó ở góc nào.
Kết luận
"Don't let schooling interfere with your education." - Mark Twain
Trượt đại học nó không khủng khiếp như trượt vỏ chuối. Có đau vì sức ép xã hội, chứ không lo đập đầu xuống đất mà mất trí.
Đứng dậy, cầm laptop lên và đi học Python tại PyMi.vn đi nhé.
Mình là người rất yêu nghề lập trình, mình yêu lắm luôn ý, thế nhưng vẫn có lúc cũng cảm thấy ngán ngẩm mấy dòng code xanh đỏ trên cái màn hình xám xịt. Lý do ngán ngẩm không phải là mình chán nó, càng không phải mình ghét nó, mà là nó tẻ nhạt. Nhìn vào sự thật, những dòng code thật sự tẻ nhạt, nó không phát ra âm thanh hay, và cũng không ai khen một màn hình đầy code là đẹp cả.
Thế nhưng, code là một công việc quan trọng của lập trình viên, hơn nữa chúng ta đều là những người yêu nghề, bỏ code một ngày thì được, chứ bỏ code một tuần là nhớ ngay, nhưng code liên tục thì lại chán. Vậy có cách nào để việc code bớt nhàm chán hơn không, hay làm thế nào để code lâu chán hơn, thì trong bài viết này mình sẽ chỉ cho bạn vài mẹo nha.
1. VỪA CODE VỪA … NGHE NHẠC
Cách này thì chắc ai cũng biết rồi. Nếu như xem code là công việc nhàm chán, thì nghe nhạc lại rất thú vị, và chúng bù trừ cho nhau, khiến việc code trở nên thú vị hơn, nhất là nghe mấy bài EDM hay vinahouse thì cứ gọi là vừa code vừa quẩy banh nóc nhà…
Thế nhưng có vài lưu ý khi vừa code vừa nghe nhạc thế này nha:
Nếu ở chỗ đông người, hãy sắm cho mình một chiếc tai nghe thay vì bật loa ngoài sẽ gây ảnh hưởng tới mọi người. Nên chọn loại tai nghe có kết nối bluetooth để đỡ dây dợ vướng víu, hoặc sắm luôn em AirPort cho sang chảnh. Mình đang sở hữu một em Sony XB-400 nghe rất ổn.
Không nên nghe nhạc có nhiều lời, vì khả năng bạn sẽ nhẩm theo lời bài hát, cũng không nên nghe nhạc buồn (nhạc vàng, nhạc thất tình) kẻo bị phản tác dụng.
Không nên nghe nhạc quá lớn (kể cả đeo tai nghe), kẻo người khác gọi bạn sẽ không nghe thấy.
Không nên nghe nhạc trong những giai đoạn mà team cần trao đổi nhiều, vì bạn sẽ bị mất thông tin, hoặc người khác sẽ cho rằng bạn đang trong chế độ “không muốn bị làm phiền”.
2. ĐẶT RA CÁC THỬ THÁCH KHI CODE
Bạn có bao giờ đặt ra các thử thách trong khi code chưa, ví dụ như:
Cố gắng code một lần chạy đúng ngay.
Mỗi ngày học một tổ hợp phím mới để hạn chế sử dụng chuột.
Đứng code.
Code chạy sai hít đất 3 cái (chắc sớm thành gymer mất).
Trên là mấy gợi ý, bạn thử xem nha, cũng thú vị lắm đấy.
3. ĐỔI…
Đổi một vài thứ cũng là cách hiệu quả để việc code trở nên thú vị hơn:
3.1 Đổi theme IDE/Code editor
Đây có lẽ là cách nhanh gọn mà lại đem đến hiệu quả cao. Code editor/IDE là cái thứ mình nhìn vào suốt ngày, sẽ không phí thời gian nếu bạn sưu tầm cho mình vài bộ theme yêu thích để đổi qua đổi lại cho bớt nhàm chán.
Chắc mình không cần phải hướng dẫn bạn cách tìm theme đâu nhỉ, cứ lên google mà search thôi, đầy ra. Nếu muốn “hardcore” hơn, hãy thử code trên VIM.
3.2 Đổi hệ điều hành
Nếu đổi IDE/Code editor vẫn chưa đủ, thì hãy đổi hệ điều hành xem sao. Có 3 hệ điều hành phổ biến là Windows, Mac, Linux (Ubuntu), đổi qua đổi lại chắc chắn đem lại nhiều cảm giác thú vị hơn khi code.
3.3 Đổi công nghệ
Code đi code lại một stack công nghệ cũng chán, bạn thử đổi qua các công nghệ khác xem, như đổi ngôn ngữ lập trình khác, đổi vai trò code khác (backend, frontend), đổi database khác, đổi framework khác,… vừa đỡ nhàm chán, lại vừa học được thêm kiến thức mới.
3.4 Đổi góc làm việc
Sau cùng, đổi hết các thứ rồi mà bạn vẫn chán thì thử đổi luôn cả góc làm việc xem sao. Trước ngồi code thì quay mặt vào tường, giờ ngồi code quay mặt ra cửa sổ, mua một chậu sen đá, hay mấy cuốn sách mà bạn thích để trên bàn làm việc,… Nói tóm lại là làm bất cứ thứ gì để cái góc làm việc của bạn nó khác những ngày trước.
3.5 Đổi nghề
Đùa đấy… đừng đổi.
6. CODE TRÊN BÀN PHÍM CƠ
Bàn phím cơ đem lại trải nghiệm gõ sướng hơn hẳn bàn phím thông thường. Bàn phím cơ có nhiều loại, có loại thì tập trung vào việc giảm tiếng ồn khi gõ, nhưng cũng có loại lại tập trung vào việc “tạo thêm tiếng ồn” (tiếng tích tích) khi gõ, nói chung là tùy sở thích mỗi người. Nhưng chúng có đặc điểm chung cảm giác gõ nảy, êm, và rất bền, phù hợp với các anh em có thói quen code cục súc, nhất là những pha fix bug và chốt hạ bằng phím enter sẽ đã hơn.
Trong lúc viết bài này, mình cũng đang có ý định sắm một em bàn phím cơ để đổi trải nghiệm viết code, tiện chia sẻ với các bạn một số kinh nghiệm luôn nhé:
Bàn phím cơ có nhiều layout phím khác nhau, giá tiền cũng phụ thuộc nhiều vào layout phím, khi mua nhớ nhìn kỹ, đừng thấy giá rẻ mà lao vào, kẻo mua về code hỏng tay đó.
Nếu có điều kiện, nên chọn bàn phím không dây hơn là có dây.
Bàn phím cơ không dây không có giá dưới 1tr, bàn phím cơ có dây không có giá dưới 500k (giá theo năm 2020).
Đừng ham rẻ, nếu mua hãy chuẩn bị ngân sách khoảng 1tr500.
Không phải bàn phím cơ nào cũng có đèn led.
Một hãng phím cơ giá hợp lý mà chất lượng lại tốt được rất nhiều người lựa chọn là Keychron.
7. LỜI KẾT
Tạo cảm giác code thú vị hơn sẽ trực tiếp tăng hiệu quả công việc của bạn, vậy thì ngại gì mà không áp dụng thử mấy mẹo trên nhỉ. Biết đâu hiệu quả cao, lại được tăng lương thì sao.
Tạm kết ở đây, chào tạm biệt các bạn, mình đi hít đất đây, vì hôm nay code nhiều bug quá.
Trong lúc lầm Progressive Web App (PWA), chúng ta cần nhớ những điều sau
Không quan trọng user đang dùng trình duyệt gì
Hỗ trợ tốt trên nhiều loại thiết bị desktop, mobile, tablet bằng responsive
Sử dụng service worker để có thể làm việc offline hoặc khi mạng chậm
Có khả năng cài đặt, sử dụng web app manifest và sự kiện beforeinstallprompt để báo user biết có thể install
Ứng dụng sẽ thực hành là trang dự báo thời tiết
Chúng ta sẽ tìm hiểu các vấn đề Làm sao để tạo và thêm file web app manifest Cung cấp một trải nghiệm cơ bản khi không có mạng Làm sao để làm cho app có thể install Bạn cần gì để follow tuts này
Phiên bản mới nhất của Chrome
Kiến thức căn bản HTML/CSS/Javascript và Chrome DevTools
Đăng ký Dark Sky API để có 1 API key, dữ liệu thời tiết sẽ do API này cung cấp
Sao khi đã có 1 API key, chúng ta có thể kiểm tra xem nó có hoạt động hay không bằng cách mở URL này trên trình duyệt
Trước tiên chúng ta đánh giá sơ bộ trang hiện tại bằng công cụ Lighthouse có sẵn của Chrome. Trên DevTool sẽ có một tab là Audit ở cuối. Nó sẽ cho kết quả đánh giá performance, accessibility, PWA và một số thứ linh tinh khác nữa
Để chạy đánh giá 1. Mở DevTool lên, chọn vào Audits tab 2. Dùng tất cả các thiết đặt mặc định nếu ko có ý kiến gì khác 3. Click Run audit, đợi chút nó sẽ cho kết quả
Chúng ta tập trung fix mấy cái bị báo đỏ
Thêm web app manifest
Nó là một file JSON cho phép chúng ta config một số thứ khi xuất hiện trên thiết bị của user
Mở cửa sổ mới hay không display
Trang mặc định sẽ mở (nếu bạn không muốn là trang index) start_url
Icon sẽ hiển thị icons
Tên short_name
Splash screen name, icons, colors
Mở ngang hay dọc orientation
Tạo một file public/manifest.json rồi copy nội dung sau vào đó
Nếu cửa số Audit PWA có thông báo “Does not set an address-bar theme color”, chọn màu thanh address cho phù hợp với màu thương hiệu
<meta name="theme-color"content="#2F3BA2"/>
Offline mức căn bản
Bạn có thể tắt mạng, sau đó mở trang google drive, bạn sẽ thấy vẫn xem được danh sách file, folder trong drive, tất nhiên không mở xem chỉnh sửa mấy file này được, nhưng vẫn đảm bảo có những trải nghiệm “không quá tệ”
Service worker chỉ chạy trên trang web truy cập qua giao thức https
Những tính năng mà service worker cung cấp, câng nhắc nó như một dạng “nice-to-have”, đừng quá đặt nặng và xem service worker như đấng cứu thế, và cũng đừng chém xuyên lục địa về những tính năng siêu việt của service worker.
Service worker có scope (phạm vi hoạt động) theo vị trí file, nghĩa là nếu nhét nó vào file rồi đưa file này trong thư mục con scripts nó sẽ không can thiệp được gì những đoạn script nằm ngoài thư mục gốc, nếu đặt ở thư mục gốc, toàn bộ thư mục con sẽ kế thừa được service worker đăng ký ở thư mục gốc
Cache cái gì? Chúng ta có 1 file là public/offline.html, nếu không có mạng, chúng ta sẽ hiển thị file này, cache file này xuống trình duyệt là hợp lý.
// thêm xử lý sự kiện fetchif(evt.request.mode!=='navigate'){return;}
evt.respondWith(fetch(evt.request).catch(()=>{return caches.open(CACHE_NAME).then((cache)=>{return cache.match('offline.html');});}));
Nó giống như khái niệm middleware, incepter của axios vậy thôi.
Đưa đoạn fetch vào bên trong evt.respondWith() sẽ chặn trình duyệt tự ý đi mà không nói với ai, chúng ta sẽ dùng người trung gian để đi nói chuyện với thế giới. Nếu không sử dụng evt.respondWith() thì cache trước đó sẽ chẳng có tác dụng vì hết.
Bên dưới của tab Application, sẽ cho biết những gì đã được cache lại bên dưới trình duyệt
Để giả lập tình trạng mất mạng, quay lại Service workers, click chọn vào checkbox Offline. F5 nếu thấy hình em gấu này là coi như thành công.
Trên cửa sổ Service Workers có một số option khác ngoài Offline
Update on reload luôn luôn load lại service worker khi reload
Bypass for network bỏ qua service worker, chạy như không có
Trên điện thoại có thể dùng chế độ máy bay để tắt mạng và xem trang web nó sẽ ra hình thù thế nào.
Service worker life cycle
install
Sự kiện này sẽ bắn ra khi worker chạy, và chỉ được tạo ra một lần cho mỗi service worker
Thông thường nó được sử dụng để cache mọi thứ cho app
activate
Sau khi install, mỗi lần nó khởi động nó sẽ bắn ra sự kiện activate, tức là bạn truy cập lại trang web đã vào trước đó. Được dùng để xóa cache trước đó
fetch
Sự kiện này cho phép chúng ta can thiệp vào mọi network request, trước khi ra khỏi nhà, phải trình báo ở đây
Ở trên chúng ta chỉ hiển một trang HTML, thông báo với user là mày đang offline, giờ chúng ta sẽ cho nó thấy cái như là có mạng luôn, giống như khi bạn vào Google Drive lúc ko có mạng đó.
Quay lại với ví dụ, app chúng ta sử dụng object caches bên trong window, tuy nhiên không phải browser nào cũng hỗ trợ window.caches, chúng ta sẽ update hàm getForecastFromCache để rơi vào tình huống đó, vẫn ko ảnh hưởng gì tới app
// kiểm tra trước khi thực hiện những việc tiếp theoif(!('caches'inwindow)){returnnull;}const url =`${window.location.origin}/forecast/${coords}`;return caches.match(url).then((response)=>{if(response){return response.json();}returnnull;}).catch((err)=>{console.error('Error getting data from cache', err);returnnull;});
App sẽ gọi lấy dữ liệu từ 1 trong 2 hàm, thông qua cache hoặc fetch từ API. Với dữ liệu trả về, chúng ta sẽ xử lý để render lại giao diện nếu cần thiết
Vì danh sách mấy file này chúng ta thêm bằng tay, nếu sao này chúng ta thay đổi danh sách file này, phải sửa tay tên CACHE_NAME, ví dụ như từ
constCACHE_NAME='static-cache-v2';// đổi khi thay đổi danh sách file muốn cacheconstCACHE_NAME='static-cache-v3';
Nếu chỉ sửa mỗi cái tên file cũng phải đổi lại tên cache thì quá tốn công, chưa kể phía user phải down lại toàn bộ resource. Workbox do google phát triển sẽ giải quyết dùm chúng ta vấn đề này, trong quá trình build, chỉ những file nào đã thay đổi mới cần update phía người dùng
Để đảm bảo trong quá trình activate, nó ko vô tình xóa dữ liệu của chúng ta, bên trong hàm bắt sự kiện activate
public/service-worker.js
if(key !==CACHE_NAME&& key !==DATA_CACHE_NAME){
Trong sự kiện fetch của service worker, chúng ta cũng thực hiện cập nhập, chúng ta luôn ưu tiên lấy data từ API trước, chỉ khi ko thể gọi được API, chúng ta mới lôi thằng cache ra xài
if(evt.request.url.includes('/forecast/')){console.log('[Service Worker] Fetch (data)', evt.request.url);
evt.respondWith(
caches.open(DATA_CACHE_NAME).then((cache)=>{returnfetch(evt.request).then((response)=>{// nếu lấy được data từ API, xài luôn và lưu xuống cache một bảnif(response.status===200){
cache.put(evt.request.url, response.clone());}return response;}).catch((err)=>{// lỗi network thì đưa về cachereturn cache.match(evt.request);});}));return;}
evt.respondWith(
caches.open(CACHE_NAME).then((cache)=>{return cache.match(evt.request).then((response)=>{return response ||fetch(evt.request);});}));
Chúng ta bỏ phần điều kiện evt.request.mode !== 'navigate' vì không chỉ muốn lấy cache cho network request, chúng ta muốn cache mọi thứ từ HTML, CSS, script, image,…
Giờ gần như chúng ta đã đưa hết những gì cần thiết xuống cache, kiểm tra xem kết quả trên DevTool đã cache thành công chưa nhé
Thêm phần cài đặt
App thì phải cài đặt được chứ, chúng ta sẽ thêm một nút cho phép user “Cài đặt” vào màn hình điện thoại luôn
User nào thông minh như mình thì sẽ biết cách vào menu của Chrome (hoặc Safari) rồi chọn Add to Homescreen, tuy nhiên đâu ai thông minh như mình đâu!
deferredInstallPrompt.userChoice.then((choice)=>{if(choice.outcome==='accepted'){console.log('User accepted the A2HS prompt', choice);}else{console.log('User dismissed the A2HS prompt', choice);}
deferredInstallPrompt =null;});
Khi thêm vào màn hình chính thành công, hay gọi hoa mỹ là Cài đặt thành công, trình duyệt bắn tiếp một sự kiện khác, chúng ta có thể túm lấy sự kiện này và thông báo gì đó nếu thích
Bài viết được sự cho phép của tác giả Trần Anh Tuấn
Khi học HTML, CSS chắc các bạn sẽ gặp nhiều vấn đề, đang muốn thử một tính năng rất gì và này nọ cơ mà đăng lên nhóm hỏi mọi người thì không biết làm sao ngoài cách phải upload code lên một đống nhìn rối não, người ta vào đọc chẳng muốn thèm giúp chút nào luôn.
Ngồi nghiên cứu tìm tòi thì thấy có các trang cho phép code online và chạy trên đó luôn, hỗ trợ đầy đủ các ngôn ngữ về Frontend như HTML CSS SASS LESS JavaScript, thư viện các kiểu với giao diện dễ nhìn, dễ sử dụng và tiện lợi nữa. Và nổi bật một trong số các trang code online đó chính là Codepen.
# Codepen là gì?
CodePen là một nền tảng phát triển code online. Nó cho phép bạn viết code trong trình duyệt và xem kết quả online. Một trình soạn thảo code online hữu ích tập trung chủ yếu vào các ngôn ngữ về phía Frontend như HTML, CSS, JavaScript và các thư viện cũng như các Preprocessing như Sass, Less, TypeScript để hỗ trợ cho các ngôn ngữ đó.
# Hướng dẫn tạo tài khoản miễn phí
Để tạo tài khoản, các bạn truy cập vào trang codepen.io rồi sau đó các bạn nhấn vào nút Sign Up như hình
Sau đó các bạn có thể đăng ký tài khoản với các trang mạng xã hội như Twitter, Facebook hay là Github hoặc là đăng ký với Email cũng được.
Việc đăng ký rất là đơn giản, nếu các bạn đăng ký với các trang mạng xã hội thì cấp quyền là xong, rồi tự động đăng nhập luôn sau đó. Còn nếu các bạn đăng ký với Email thì các bạn sẽ điền các thông tin như là Name, Email, Username và Password vào rồi sau đó nhấn Submit là được.
Sau đó nó sẽ hiện ra trang thông tin cho phép các bạn nhập địa chỉ ,giới thiệu và ảnh avatar. Các bạn có thể cập nhật hoặc không cũng không sao rồi sau đó nhấn Save and Submit để hoàn thành.
Để tạo một Pen mới các bạn nhấn vào chữ Pen như hình
lúc này ta sẽ được một giao diện mới như hình gồm các tab chính là HTML, CSS và JS. Nút Save để lưu, nút Setting để thiết lập các cài đặt, nút Change View để đổi cấu trúc giao diện code, bên trái là icon bút chì để sửa tiêu đề của Pen.
Ở Codepen khi các bạn code HTML thì chỉ đơn giản code những thứ trong thẻ body là được không nhất thiết phải lôi cả cấu trúc HTML khi code dưới máy vào như thẻ html, head, body, script… Muốn chèn script vào hay fontawesomebootstrap thì các bạn nhấn vào nút Setting, sau đó gõ thư viện bạn muốn vào khung tìm kiếm rồi chọn sau đó nhấn Save & Close là được.
Các bạn muốn code SCSS, LESS trong CSS hay PUG trong HTML hoặc TypeScript trong JS thì các bạn cũng vào Setting rồi chọn mục tương ứng như HTML, CSS và JS lần lượt là
Ngoài ra còn có tab Behavior ở đây cho phép các bạn thiết lập sử dụng Tabs hay là Spaces tuỳ theo cách code của mỗi người. Và còn có thêm 2 lựa chọn khác là Autosave nghĩa là tự động lưu khi các bạn đang code và Auto-Updating Preview nghĩa là khi các bạn code tới đâu UI nó sẽ cập nhật tới đó ví dụ code thẻ p {color: red;} thì lập tức UI có thẻ p sẽ đổi sang đỏ ngay lập tức.
Ngoài ra còn có các thiết lập khác như là chỉnh kiểu chữ khi code, các theme hiển thị cho đẹp, dark hay là light theme, kích thước chữ, emmet khi gõ nhấn tab sẽ tự động sinh ra code… Các bạn có thể thiết lập bằng cách nhấn vào Avatar sau đó chọn mục Setting sẽ ra trang như hình dưới đây. Các bạn có thể tuỳ chỉnh tuỳ thích hoặc để thiết lập mặc định cũng được nhé.
Sau khi các bạn code xong một Pen và lưu lại thì để xem danh sách các Pens mà các bạn đã làm thì các bạn vào mục Dashboard nhé. Nó sẽ hiển thị ra danh sách cho các bạn như dưới đây
Ngoài ra sau khi các bạn code xong, các bạn muốn chia sẻ cho người khác xem thì các bạn copy đường dẫn phía trên thanh url có dạng codepen/username/sdaffjiuewrkjlz rồi đem đi chia sẻ là xong. Dưới đây là một ví dụ về Codepen mà mình code để các bạn tham khảo nha.
# Kết luận
Như vậy là mình đã nói sơ qua về Codepen cho các bạn cũng như hướng dẫn cho các bạn cách đăng ký tài khoản với Codepen, giới thiệu sơ về khả năng mạnh mẽ của Codepen là gì, cách tạo một Pen đơn giản cũng như là các thiết lập của nó. Hi vọng với những kiến thức cơ bản này sẽ giúp ích cho các bạn trong định hướng trở thành 1 Frontend-Developer thực thụ nhé.
Một chiến lược về tuyển dụng được hình thành trên nhiều khía cạnh. Một trong số đó chính là nội dung gắn với truyền thông (Recruitment Marketing).Nhưng làm thế nào để những nội dung trở nên độc đáo? Đâu là những nguyên tắc cần phải nắm bắt khi sáng tạo nội dung nhằm thu hút ứng viên? Cùng TopDev theo dõi bài viết sau để bỏ túi những ý tưởng sáng tạo nội dung hiệu quả nhất!
3 Nguyên tắc cần lưu tâm khi sáng tạo nội dung
Đây cũng được xem là 3 nguyên tắc cốt lõi để sáng tạo nội dung tuyển dụng.
Hiểu rõ về đối tượng
Bạn cần biết rõ về ứng viên trước khi lập kế hoạch xây dựng nội dung. Để nội dung đạt hiệu quả hoàn hảo, cách duy nhất là xác định trước chân dung điển hình của các ứng viên tiềm năng.
Tập trung vào ứng viên
Hãy đi tìm lời giải cho câu hỏi: “Nội dung bạn thực hiện sẽ mang lại những gì?”. Và đừng quên thử đặt mình vào vị trí các ứng viên. Hiểu được những thách thức về mặt sáng tạo giúp bạn biết đâu là nội dung mà các hình mẫu ứng viên lý tưởng sẽ bị thu hút.
Trung thực và chân thành
Đừng chỉ cố truyền tải thông điệp nội dung thông qua các logo. Thay vào đó, những hình chân thật của nhân viên và môi trường làm việc có thể tạo ra sức hút nhiều hơn. Vì đơn giản, mọi đặc trưng về văn hóa doanh nghiệp sẽ được thể hiện rất rõ.
4 ý tưởng nội dung Recruitment Marketing sáng tạo nhất
Tạo ra nội dụng sáng tạo là điều cần thực hiện. Dưới đây là 4 ý tưởng được nhiều doanh nghiệp lựa chọn cho chiến lược sáng tạo nội dung của tổ chức của mình.
Idea No.1 – Infographic
“Only text” mang lại những hiệu quả cơ bản trong việc truyền tải nội dung. Tuy nhiên, dù cho content có hay thì để đảm bảo được một sức hút tương đối thì nó còn hạn chế.
Bạn vẫn có thể phát triển cách tiếp cận ứng viên “thuần nội dung”. Nhưng nếu được, đừng ngại đầu tư đôi chút cho chính doanh nghiệp của mình. Hãy chú trọng nhiều hơn đến tính trực quan – Infographic.
“Chăm” cho nội dung tuyển dụng dưới một bản trình bày đầy sinh động là cách thức tạo ấn tượng sâu đậm nhất đối với các ứng viên. Với chỉ vài biểu tượng thú vị hơn, nội dung tuyển dụng của công ty bạn sẽ “ăn đứt” dạng checklist truyền thống đấy!
Idea No.2 – Video Recruitment
Video được xem là một hình thức hiệu quả trong việc tiếp cận ứng viên. Thương hiệu nhà tuyển dụng có tăng được độ nhận diện hay không, một phần lớn cũng nhờ sức lan tỏa từ các thông điệp về nội dung mà các video mang lại.
Thông qua hình thức thiết lập video, nhà tuyển dụng có thể cung cấp các thông tin tuyển dụng đến ứng viên đang tìm việc. Tuy nhiên, hãy thật sự khéo léo và đảm bảo tính cân bằng thông tin. Nội dung cần được xây dựng một cách dễ hiểu để ứng viên không cảm thấy bị “ngán”.
Nhiều hình thức video đa dạng mà các tổ chức có thể thực hiện: Update video tuyển dụng lên website, để chúng dưới dạng podcast, dẫn link; hoặc liên kết trực tiếp với Youtube của công ty, các Social Media Channel.
Idea No.3 – Slideshare
Nhiều người nghĩ rằng Slideshare là một hình thức chưa chuyên dụng và lỗi thời. Họ không đánh giá cao Slideshare. Và chỉ xem nó như một hình thức truyền tải nội dung thiếu chuyên nghiệp. Song, thực tế cho thấy, Slideshare vẫn là một trong những lựa chọn tốt nhất của các doanh nghiệp. Nó hữu ích và mang lại nhiều giá trị tuyệt vời hơn bạn nghĩ.
Slideshare nhấn mạnh sự truyền tải trực quan nhằm tạo ra sự tương tác đa chiều. Đồng thời, ứng viên hoàn toàn có thể chỉa sẻ thông tin về tuyển dụng cho nhau.
Hãy thiết lập một slideshare, cho nó những điểm nhấn khi đặt nó ở vị trí trung tâm trang truyển dụng hoặc những nơi tiềm năng nhất.
Idea No.4 – Career Blog
Career Blog thật sự là một ý tưởng tuyệt vời cho việc truyền tải các nội dung về tuyển dụng (Recruitment Marketing).
Blog nghề nghiệp được xem như một khía cạnh khác đầy tính thú vị hơn. Trên blog, doanh nghiệp có thể phát triển nội dung theo định hướng (Content Direction). Từ đó thiết lập các chủ đề (Topic) khác nhau. Đó có thể là:
Hướng nghiệp, đào tạo (Career guidance – Training)
Kiến thức chuyên môn các lĩnh vực (Specialist Knowlegde)
Phỏng vấn (Interview)
Các kỹ năng cần thiết trên hành trình tìm việc của ứng viên (Skills of candidate journey)
Mỗi Career blog được đều phải dựa trên nguyên tắc nền tảng là mối quan tâm của chính độc giả (tức ứng viên). Doanh nghiệp hoàn toàn có thể thu hút được nguồn ứng viên tiềm năng khi có sự chăm chút kỹ lưỡng cho Career blog của mình.
Lời kết
Hãy đảm bảo rằng doanh nghiệp của bạn hiểu rõ nguyên tắc cốt lõi của Recruitment Marketing. Việc nắm bắt sâu sắc có ý nghĩa quan trọng trong việc tạo ra các nội dung mang đậm dấu ấn của tổ chức. TopDev hy vọng bài viết sẽ cho người đọc những thông tin bổ ích về các idea sáng tạo nội dung. Hãy linh hoạt và vận dụng chúng vào qua trình tạo ra sự thu hút đối với ứng viên. Đồng thời gia tăng sức cạnh tranh về khía cạnh tuyển dụng nhân sự trên thị trường lớn.
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
Bài viết được sự cho phép của tác giả Phạm Công Sơn
Cho đến nay tôi đã làm Coder cũng phải được 12, 13 năm. Bắt đầu đi code từ năm 2007 (Trong năm này tôi đã cưới vợ và sinh ngay một đứa con trai). Tuổi đời coder cũng tạm gọi là dài. Năm nay cũng 38 tuổi rồi, nếu so sánh với một cầu thủ bóng đá chuyên nghiệp thì có lẽ nghỉ hưu cũng đã từ lâu. Vậy mà mình vẫn còn miên man code và code, bug và bug.
Skill chủ yếu vẫn là C#, Jquery, Asp.Net, Sql Server v.v… Và sau nhiều năm coder như vậy tôi có rất nhiều kinh nghiệm. So với những ngày đầu code một cách vô ý thức, bạ đâu code đó thì giờ đây cách code cũng đã thay đổi rất nhiều và với mỗi yêu cầu tôi thường phân tích kỹ, code sử dụng design pattern và code trừu tượng trước khi làm cụ thể một trường hợp nào đó.
Người ta thì sự nghiệp đầy đủ rồi mới lập gia đình. Còn tôi thì hơi ngược, năm 2007 là tôi chính thức bắt đầu đi làm sau khi cưới vợ. Lương tại thời điểm đó là 1 củ rưỡi. Và vào làm vẫn với tâm thế vừa làm vừa học hỏi. Tâm lý chung của các bạn mới ra trường thôi.
Hồi đó chủ yếu có mấy xu hướng, mấy đứa lập trình chỉ có hỏi nhau mày làm php, dotNet hay Java. Tôi từ FPT APTECH ra, cũng có học Java nhưng sau đó theo hẳn dotNet làm winform hoặc web asp.net. Vào công ty đập ngay dự án về winform, thấy cũng may mắn vì được học cái gì và làm luôn cái ấy. Trưởng dự án giao cho làm mấy Form danh mục như quản lý danh mục sản phẩm, loại sản phẩm. Cảm nhận đầu tiên là nó chả khác gì lúc đi học cả. Cũng tạo Form, kéo thả các input, button, viết sự kiện, truy xuất DataBase và lưu trữ. Và mấy Form sau thì copy nội dung của Form trước. Điều khác biệt mà lúc đó thấy “hay dữ” đó là có cái tool gen được ra các Entity có sẵn mấy hàm Insert, Update, Delete, còn sau đó trên Form có thêm xử lý gì thì tự viết hàm truy xuất. Thế thôi nhưng cũng thấy hay vãi đ* ra rồi. Và nghĩ đúng là đi làm nó khác.
Sau khi dự án làm bằng Winform kết thúc, tôi chuyển sang làm một dự án về WebForm. Cái này mình cũng được học và làm Project rồi nên cũng rất tự tin. Tuy vậy lúc mới bắt tay vào dự án cũng làm mấy danh mục trước tiên. Điều khác so với dự án trước là ông Leader ông ý có làm sẵn một Form danh mục rồi. Và ông ý bảo anh em nào được giao làm Form nào thì vào copy ra mà làm các form tương tự. Thế đấy, và hàng loạt các Form được sao chép nhau, có Form nào logic nó khác hoặc cần chỉnh thì viết thêm. Vậy mà cũng qua được mấy cái dự án.
Sau đó tôi chuyển tới một số công ty khác và đa số các công ty đều có cách làm việc như vậy. Nếu như các Module, Form nào na ná nhau. Mọi người đều copy lại và chỉnh sửa sao cho phù hợp. Không chỉ những Form quản lý mà còn nhiều các Form khác ví dụ như báo cáo. Code của dự án nào cũng khá là nặng. Và có một nhược điểm là nếu Form nào mà có lỗi thì hầu như phải chỉnh sửa lại ở tất cả các Form. Và cần thêm tính năng nào thì cũng phải mở từng đấy Form ra mà thêm.
Chính vì vậy đa số nhiều lập trình viên sau một thời gian đi làm cũng thường kêu là không có gì mới mẻ, làm đi làm lại một việc. Công việc hầu như không có sáng tạo. Đấy là cái tôi thấy, hầu như code không có thiết kế, không sử dụng design pattern và không tận dụng được tính chất OOP để kế thừa, đóng gói. Các hàm, biến static được sử dụng tràn lan vô tội vạ, code thì copy và không được viết thành thư viện.
Hãy thiết kế code, sử dụng design pattern, code trừu tượng trước khi đi vào cụ thể.
Quay trở lại các dự án thường chúng ta hay phải làm các danh mục trong đó có các tính năng như tìm kiếm, thêm, sửa, xóa, xuất excel. Form nào các bạn cũng phải làm một lưới hiển thị, làm sự kiện thêm mới, sửa, validate dữ liệu khi lưu, sự kiện xóa. Form nào cũng từng đấy bước không thể khác được.
Tôi ví dụ như làm một Form quản lý danh mục tin. Đây chỉ là ví dụ mô phỏng thôi nhé. Ta có thể có đoạn code như sau
public class ManageCategory
{
public void ShowFormEdit(Category category)
{
// Hiển thị form cập nhật hoặc thêm mới
}
public void Save(Category category)
{
// Validate trước khi lưu
// Thực hiện lưu
}
public void Delete(Category category)
{
// Validate xóa
// Thực hiện xóa
}
// Lấy dữ liệu hiển thị lên lưới
public string Keyword { set; get; }
public List<Category> GetData(int pageIndex, int pageSize, string fieldOrder, string dir)
{
// ....
}
public void ExportExcel(List<Category> categories)
{
// Điền vào excel cho client tải xuống
}
}
Rồi một Form quản lý loại sản phẩm
public class ManageProductType
{
public void ShowFormEdit(ProductType productType)
{
// Hiển thị form cập nhật hoặc thêm mới
}
public void Save(ProductType productType)
{
// Validate trước khi lưu
// Thực hiện lưu
}
public void Delete(ProductType productType)
{
// Validate xóa
// Thực hiện xóa
}
// Lấy dữ liệu hiển thị lên lưới
public string Keyword { set; get; }
public List<ProductType> GetData(int pageIndex, int pageSize, string fieldOrder, string dir)
{
// ....
}
public void ExportExcel(List<ProductType> productTypes)
{
// Điền vào excel cho client tải xuống
}
}
Như các bạn thấy thì Form nào chúng ta cũng phải làm từng đấy bước. Mà đấy là code server, chưa kể code javascript cũng vậy. Các Form mà cứ code đi code lại như vậy hoài rất mất công và sinh ra chán. Lúc gặp lỗi hoặc thêm chức năng thì Form nào cũng phải vọc vào mà sửa.
Vậy bây giờ chúng ta hãy nghĩ khác đi. Trừu tượng nó thành như này
public abstract class Manage<T>
{
public void ShowFormEdit(T t)
{
// Hiển thị form cập nhật hoặc thêm mới
}
public void Save(T t)
{
// Validate trước khi lưu
BeforeSave();
// Thực hiện lưu
}
protected virtual void BeforeSave(T t)
{
}
public void Delete(T t)
{
// Validate xóa
// Thực hiện xóa
}
// Lấy dữ liệu hiển thị lên lưới
public vitural List<T> GetData(int pageIndex, int pageSize, string fieldOrder, string dir)
{
// ....
}
public void ExportExcel(List<T> list)
{
// Điền vào excel cho client tải xuống
}
}
Và nếu là quản lý chuyên mục tin sẽ là như này
public class ManageCategory : Manage<Category>
{
public string Keyword { set; get; }
}
Nếu là quản lý loại sản phẩm
public class ManageProductType : Manage<ProductType>
{
public string Keyword { set; get; }
protected override void BeforeSave(ProductType productType)
{
// Mở rộng validate thêm
}
}
Chúng ta sẽ không phải code một lần mà lại phải code thêm một lần nữa. Bug ít đi, thêm tính năng cũng sẽ nhanh hơn. Để phát triển, tại lớp Generic các bạn có thể đặt sự kiện, phương thức virtual, abstract để các lớp cụ thể override lại. Ví dụ như ManageProductTypeoverride lại BeforeSave để viết thêm validate
Tuy vậy để code được trừu tượng các bạn sẽ phải có nhiều kỹ năng và các kiến thức cơ bản là cực kỳ cần thiết. Và với tùy theo các Framework các bạn đang theo các bạn có thể viết các mô hinh cho phù hợp. Trên đây chỉ là code ví dụ mà thôi.
Ngoài ra không chỉ làm những Form quản lý danh mục mà chúng ta có thể mở rộng ra nhiều bài toán khác nhau. Với tôi sau nhiều năm kinh nghiệm, tôi đã làm nhiều yêu cầu với nhiều mô hình khác nhau. Tuy nhiên cũng có một vài lần làm trừu tượng với những yêu cầu lớn. Điển hình như tôi tham gia dự án Staxi (Một sản phẩm đặt xe tương tự như Grab, Uber), mô hình trừu tượng một khối kết nối. Từ đó kế thừa tạo ra các khối như tổng đài, server, App Client, mỗi mỗi khối có nhiệm vụ nhận, và truyền tín hiệu. Rồi dự án phân tích dữ liệu từ hộp đen. Cũng trừ tượng mô hình phân tích để kế thừa cho phân tích dữ liệu cho Taxi, Bus và Xe vận tải. Và rất nhiều dự án khác. Tôi sẽ chia sẻ trong những lần tới.
Hiện với cách code này tôi đang áp dụng cho dự án của tôi, chắc chắn sẽ publish dự án trong một ngày sớm nhất vì tôi còn đang hoàn thiện.
Tôi cũng show cho anh em xem một Form tôi đã thực hiện trong dự án: Quản lý tỷ giá ngoại tệ. Form này khá đơn giản
Đây là định nghĩa Entity, có Attribute validate trên mỗi thuộc tính. DataSource để lấy dữ liệu hiển thị trên lưới
Nội dung Html định nghĩa lưới hiển thị dữ liệu. Nút xóa gọi hàm Delete, có confirm khi xóa. Click vào tỷ giá gọi Hàm Edit hiển thị Form cập nhật
Toàn bộ nội dung của Form kế thừa tới ManageModule trong đó đã có toàn bộ các chức năng
Nội dung của Form cập nhật gồm các Input cần thiết
Nội dung file js định nghĩa đã bao gồm toàn bộ thao tác trên lưới.
Trên đây là toàn bộ code cho một Form quản lý. Còn đây là kết quả
Đây là lưới hiển thị dữ liệu.Trên lưới có chức năng tìm kiếm, sắp xếp, xóa, sửa, thêm mới, cấu hình cột, xuất excel
Click vào thêm mới hoặc một dòng sẽ hiển thị Form cập nhật. Trên Form có sẵn các nút lưu
Form có sẵn chức năng giới thiệu tính năng
Có thể cấu hình cột được hiển thị theo ý muốn người dùng
Bài viết được sự cho phép của BBT Tạp chí Lập trình
Trong bài viết này tôi sẽ chỉ cho bạn cách thiết lập Repository design pattern trong Laravel từ đầu. Tôi sẽ sử dụng phiên bản Laravel 5.8.3, nhưng phiên bản Laravel cũng không thực sự quá quan trọng. Trước khi chúng tôi bắt đầu code, có một vài điều bạn cần biết về repository design pattern.
Repository Pattern là lớp trung gian giữa tầng Data Access và Business Logic. Repository design pattern cho phép bạn sử dụng các đối tượng mà không cần phải biết các đối tượng này được duy trì như thế nào. Về cơ bản nó là một sự trừu tượng hóa của lớp dữ liệu.
Điều này có nghĩa là Business Logic của bạn không cần phải biết cách lấy lại dữ liệu hay nguồn dữ liệu là gì. Business Logic dựa trên repository để lấy dữ liệu chính xác.
Một quan niệm sai lầm mà tôi thấy rất nhiều là các repository đang được thực hiện theo cách tạo hay cập nhật các bản ghi. Đây không phải là những gì mà repository nên làm. Các kho lưu trữ không nên tạo hoặc cập nhật dữ liệu, nhưng chỉ nên được sử dụng để truy xuất dữ liệu.
Bởi vì tôi hướng dẫn các bạn làm từ đâu, đầu tiên ta phải tạo một project laravel trước.
php artisan config:clear
Đối với phần hướng dẫn này, tôi sẽ tạo ra một blog nhỏ. Bây giờ chúng tôi đã tạo một project, chúng tôi cần tạo Controller và Model cho blog.
php artisan config:clear
Cái này sẽ tạo BlogController trong tệp app/Http/Controllers
php artisan config:clear
Ghi chú: Lựa chọn -m sẽ tạo ra một Data Migration (chuyển đổi dữ liệu). File này có thể được tìm thấy trong database/migrations
Điều này sẽ tạo Model cho Blog của bạn và lưu trữ nó trong thư mục App / Models. Đây chỉ là một trong những cách để lưu trữ các Model của bạn, và là phương pháp mà tôi thích.
Bây giờ chúng ta đã có Controller và Model, đã đến lúc xem tệp chuyển đổi mà chúng ta đã tạo. Bây giờ blog cần một tiêu đề, nội dung và trường user_id; bên cạnh các trường timestamp (dấu thời gian) mà là mặc định của Laravel.
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateBlogsTable extends Migration
{
public function up()
{
Schema::create('blogs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->text('content');
$table->integer('user_id');
$table->timestamps();
$table->foreign('user_id')
->references('id')
->on('users');
});
}
public function down()
{
Schema::dropIfExists('blogs');
}
}
Ghi chú: Nếu bạn đang dùng phiên bản cũ hơn Laravel 5.8, bạn nên thay thế dòng
php artisan config:clear
Thiết lập kho database:
Tôi dùng MySQL cho ví dụ này. Bước đầu tiên là tạo một cơ sở dữ liệu mới.
mysql -u root -p
create database laravel_repository;
Điều này sẽ tạo ra một database gọi là laravel_repository. Tiếp theo chúng ta phải thêm thông tin cơ sở dữ liệu vào tệp .env
Điều này sẽ tạo ra một database gọi là laravel_repository. Tiếp theo chúng ta phải thêm thông tin cơ sở dữ liệu vào tệp .env
Sau khi bạn đã thay đổi tệp .env, chúng tôi phải xóa bộ đệm cấu hình:
php artisan config:clear
Chạy chuyển đổi dữ liệu
Sau khi ta đã set-up xong phần database, ta có thể bắt đầu chạy phần chuyển đổi dữ liệu
php artisan config:clear
Điều này sẽ tạo blog với các trường tiêu đề, nội dung và user_id mà chúng tôi đã khai báo trong chuyển đổi dữ liệu.
Thực hiện repository design pattern
Với những gì ta đã làm, bây giờ chúng ta có thể bắt đầu thực hiện repository design pattern. Chúng tôi sẽ bắt đầu bằng cách tạo thư mục Repositories trong thư mục App. Tiếp theo chúng ta sẽ tạo thư mục Interfaces. Thư mục này sẽ được đặt trong thư mục Repositories mà chúng ta vừa tạo.
Trong thư mục Interfaces, chúng ta tạo lớp BlogReositoryInterface với hai phương thức:
Phương thức all sẽ trả về tất cả các blog
Phương thức getByUser sẽ trả về tất cả các blog mà được tạo bởi một user cụ thể.
<?php
namespace App\Repositories\Interfaces;
use App\User;
interface BlogRepositoryInterface
{
public function all();
public function getByUser(User $user);
}
Lớp cuối cùng mà chúng ta sẽ tạo là lớp BlogRepository, lớp này sẽ triển khai lớp BlogRepositoryInterface. Chúng tôi sẽ thực hiện việc nay đơn giản nhất có thể.
<?php
namespace App\Repositories;
use App\Models\Blog;
use App\User;
use App\Repositories\Interfaces\BlogRepositoryInterface;
class BlogRepository implements BlogRepositoryInterface
{
public function all()
{
return Blog::all();
}
public function getByUser(User $user)
{
return Blog::where('user_id'. $user->id)->get();
}
}
Thư mục Repositories của bạn nên trông như thế này:
Bây giờ bạn đã thành công tạo repository! Giờ ta sẽ bắt đầu sử dụng nó.
Sử dụng Repository
Để bắt đầu sử dụng BlogRepository, chúng ta nên đưa nó vào BlogController. Vì repository sẽ được chèn, nên sẽ dễ dàng trao đổi nó với một thực thi khác. Controller sẽ trông như sau:
<?php
namespace App\Http\Controllers;
use App\Repositories\Interfaces\BlogRepositoryInterface;
use App\User;
class BlogController extends Controller
{
private $blogRepository;
public function __construct(BlogRepositoryInterface $blogRepository)
{
$this->blogRepository = $blogRepository;
}
public function index()
{
$blogs = $this->blogRepository->all();
return view('blog')->withBlogs($blogs);
}
public function detail($id)
{
$user = User::find($id);
$blogs = $this->blogRepository->getByUser($user);
return view('blog')->withBlogs($blogs);
}
}
Như bạn có thể thấy mã trong controller ngắn và do đó có thể đọc được. Bạn không cần mười dòng mã để có được bộ dữ liệu bạn muốn, tất cả có thể được thực hiện trong một dòng mã nhờ vào repository. Điều này cũng rất tốt cho kiểm thử đơn vị, vì các phương thức của repository có thể dễ dàng kiểm tra qua.
Repository design pattern cũng giúp dễ dàng thay đổi giữa các nguồn dữ liệu. Trong ví dụ này, chúng tôi đang sử dụng cơ sở dữ liệu để truy xuất blog của mình. Chúng ta dựa vào Eloquent để làm điều đó cho. Nhưng giả sử ta tìm thấy một blog API tuyệt vời ở đâu đó trên mạngvà chúng tôi muốn sử dụng API này. Tất cả những gì chúng ta phải làm là viết lại BlogRepository để sử dụng API đó thay vì Eloquent.
RepositoryServiceProvider
Thay vì chèn BlogRepository vào trong BlogController, ta có thể chèn BlogRepositoryInterface và sau đó để Service Container quyết định repository nào sẽ được sử dụng. Điều này có thể được thực hiện trong phương thức boot của AppServiceProvider, nhưng tôi thích tạo 1 provider mới cho việc này để giữ mọi thứ clean.
php artisan config:clear
Lý do ta tạo ra một provider mới cho việc này là vì mọi thứ trở nên thực sự lộn xộn khi dự án của bạn bắt đầu phát triển. Hãy tưởng tượng một dự án với hơn 10 model và mỗi model đều có repository riêng. AppServiceProvider của bạn sẽ trở nên không thể đọc được.
RepositoryServiceProvider của bạn sẽ như sau:
<?php
namespace App\Providers;
use App\Repositories\BlogRepository;
use App\Repositories\Interfaces\BlogRepositoryInterface;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(
BlogRepositoryInterface::class,
BlogRepository::class
);
}
}
Hãy nhớ rằng việc hoán đổi BlogRepository với một repository khác dễ như thế nào.
Đừng quên thêm RepositoryServiceProvider vào danh sách các provider trong tệp config/app.php. Sau đó, chúng ta phải xóa bộ đệm cấu hình một lần nữa.
php artisan config:clear
Và bạn đã thực hiện thành công repository design pattern. Cũng không khó quá đúng không?
Toán học cũng chưa thật sự liên quan nhiều đến frontend, tuy nhiên toán học là nơi đã phát sinh ra khái niệm này
Ví dụ có 2 hàm, một hàm là y = 2 * x, hàm thứ 2 là y = x + 10.
Composition 2 hàm này lại, kết quả của thằng này là input của thằng kia, chúng ta sẽ có hàm mới y = (2 * x) + 10. Đó là tất cả khái niệm cần nắm
Function composition
Trong ngữ cảnh của functional programming, cũng không khác toán học, chỉ là được diễn tả bằng code
let date =getDate();let text =formatDate(date);let label =createLabel(text);showLabel(label);
Code này có vài đoạn na ná nhau, chúng ta có nhận lấy một input, convert nó sang một loại khác, rồi lại lấy kết quả đó, convert tiếp.
Làm sao để lượt bỏ hết sự na ná đó?
let steps =[
getDate,
formatDate,
createLabel,
showLabel
]
Một “vài người” cho rằng code vậy sạch hơn. Viết một helper function để tin gọn hơn nữa
functionrunSteps(steps){let result;for(let i =0; i < steps.length; i++){let step = steps[i];// làm tiếp các bước được móc nối vào
result =step(result);}return result;}
Nhờ sự trợ giúp của hàm runSteps chúng ta có thể viết lại
Nếu tổng số code bước phải làm là cố định, chúng ta muốn chạy y chang như vậy trên nhiều chỗ khác nhau, chúng ta tiếp tục đưa nó vào một function
functionshowDateLabel(){runSteps([
getDate,
formatDate,
createLabel,
showLabel
]);}// Giờ gọi ở bất kỳ đâushowDateLabel();showDateLabel();
Các bạn lập trình lại tiến một bước xa hơn, sao không rút gọn code hơn nữa bằng một hàm gọi là pipe
let showDateLabel =pipe(
getDate,
formatDate,
createLabel,
showLabel
);// Giờ gọi ở bất kỳ đâushowDateLabel();showDateLabel();
Chúng ta dấu diếm phần implement của thể đó như thế này
functionpipe(...steps){// chạy hết tất cả các function cho tuireturnfunctionrunSteps(){let result;for(let i =0; i < steps.length; i++){let step = steps[i];
result =step(result);}return result;}}
Như vậy chúng ta đã đi rất xa, rất rất xa. Từ điểm xuất phát phải gọi lần lượt các hàm một cách thủ công, chúng ta chỉ định các bước cần chạy theo thứ tự một cách sạch sẽ hơn
// code cũlet date =getDate();let text =formatDate(date);let label =createLabel(text);showLabel(label);// code mớilet showDateLabel =pipe(
getDate,
formatDate,
createLabel,
showLabel
);showDateLabel();
Nếu có thắc mắc trong đầu: ủa vậy để mần chi? Phức tạp rườm rà vãi cả ra! Hãy cân nhắc xem giữa hay cách viết trên, cách nào bạn đọc dễ hơn?
Khi hiểu được pipe và function composition bạn sẽ thấy mọi thứ gọn gàng rành mạch thật tuyệt vời, nhưng không có nghĩa là không có nhược điểm, outsource cho pipe, chúng ta không còn thấy được rõ ràng dữ liệu đã đi ra-đi vào như thế nào.
Component Composition
Một ngữ cảnh khác chúng ta cũng thấy sự xuất hiện của “composition” là lập trình UI hướng declarative. React component là một ví dụ.
Đấy cũng gọi là composition vì chúng ta đứa những component vào trong những component khác, rồi nhận được kết quả là một tổng thể chứa tất cả component
Một dạng biến thể của composition trong component là slot (làm Vue bạn sẽ biết khái niệm này)
functionHomePage(){return(<Layoutsidebar={<HomeSidebar/>}content={<HomeContent/>}>
)
}
function AboutPage() {return(<Layoutsidebar={<AboutSidebar/>}content={<AboutContent/>}>
)
}
React sẽ không đặt hẳn một khái niệm riêng cho slot vì bạn có thể làm điều đó thông qua prop
Composition vs inheritance
Người đời thường đem composition để đối chiếu với inheritance, kế thừa gặp nhiều trong class và object hơn, composition gặp nhiều trong function
Một cách cụ thể, nếu viết code theo kiểu class, bạn sẽ có xu hướng dùng lại các behavior từ một class khác bằng cách extend nó (kế thừa). Tuy nhiên, làm vậy cũng có hạn chế là rất khó tùy chỉnh các behavior sau này. Ví dụ như tình huống muốn extend không chỉ một mà nhiều class
Đôi khi, miệng đời cũng đồn đại rằng việc dùng class khiến “bạn bị khóa cứng” trong thiết kế ban đầu vì việc thay đổi kiến trúc của các class thì rất chi là tốn công. Với việc dùng composition, thay vì extend, bạn dữ nguyên hiện trạng của một instance, sử dụng trực tiếp từ instance này và cũng có thể làm gì đó kết hợp với nhiều thứ khác, có nhiều đất diễn hơn.
Nói chung, ngành phần mềm đã bỏ việc model các UI component như một dạng kế thừa nhiều tầng nhiều cấp.
Không có nói inheritance lúc nào cũng “tệ”, nó chỉ không đủ “bén như dao lam”, sử dụng cần phải tiết chế, việc kế thừa đa cấp ở một độ sâu nhất định, đòi hỏi bạn đủ kiên nhẫn để giải quyết các vấn đề của nó.
If you write your code in a style that composes functions in some way before calling them, and there are other humans on your team, make sure that you’re getting concrete benefits from this approach. It is not “cleaner” or “better”, and there is a price to pay for “beautiful” but indirect code.
Tạm kết: Nếu bạn làm việc trong team, hãy đảm bảo mọi người nhất trí với nhau lợi ích mà nó mang lại từ cách làm này. Nó không liên quan gì tới việc “cleaner-better”, nó luôn có cái giá phải trả cho “beautiful” nhưng mà code không trực quan.
Product Owner là người lên kế hoạch cho mọi thứ liên quan đến sản phẩm bao gồm cả việc User Research, làm việc với UX/UI Designer, lên kế hoạch cho việc Release và Timeline cho sản phẩm.
Product Owner hay được gọi là PO là một một thành viên trong Scrum Team, họ là người chịu trách nhiệm cho sản phẩm, là người đưa ra quyết định chính thức và cuối cùng cho việc chọn lựa tính năng của sản phẩm, giải quyết những vấn đề xoay quanh User, họ được xem như là “chủ sở hữu” sản phẩm nên có quyền hạn đưa ra quyết định thứ tự công việc, độ ưu tiên…Nói chung họ là người quyền lực nhất trong Scrum Team.
Product Owner là người lên kế hoạch cho mọi thứ liên quan đến sản phẩm bao gồm cả việc User Research, làm việc với UX/UI Designer, lên kế hoạch cho việc Release và Timeline cho sản phẩm.
Tuy nhiên Product Owner không có quyền hạn để yêu cầu team Developer phải làm như thế nào để hoàn thành một Sprint mà họ chỉ có thể giao phó, đưa ra yêu cầu mà họ muốn team Developer hoàn thành cho Scrum Master để Scrum Master truyền đạt lại cho team Developer.
product owner là gì
Vai trò và trách nhiệm của Product Owner
Vai trò
Vai trò của Product Owner khá quan trọng trong quy trình Scrum, họ là người đại diện cho khách hàng để làm việc với team Developer. Nhiệm vụ chính của họ là quản lý và giải quyết các vấn đề tồn đọng liên quan đến sản phẩm, là người chịu trách nhiệm cho tất cả các kế hoạch của sản phẩm.
Product Owner là người duy nhất có quyền hạn thay đổi thứ tự trong trong các Backlog. Khi có vấn đề phát sinh xoay quanh sản phẩm, các Developer có quyền đặt ra câu hỏi và Product Owner phải là người giải đáp thắc mắc cho team Developer để team Developer có thể hiểu rõ hơn về tính năng mà Product Owner mong muốn ở một sản phẩm, từ đó cho ra đời những sản phẩm làm Product Owner hài lòng.
Sự hài lòng của Product Owner đôi khi cũng sẽ là sự hài lòng của khách hàng, một Product Owner giỏi sẽ là người có thể thấu hiểu được hết nhu cầu của khách hàng đối với sản phẩm và đáp ứng được tất cả yêu cầu mà khách hàng đưa ra.
product owner là gì
Trách nhiệm
Quản lý backlog
Có trách nhiệm đưa ra phương án giải quyết vấn đề và các giải pháp đó phải thật sự hữu ích.
Product Owner là người có thẩm quyền trực tiếp can thiệp và theo dõi xuyên suốt quá trình hoạt động tạo ra và nâng cấp sản phẩm thông qua việc tìm hiểu, phân tích dữ liệu, nhận feedback từ khách hàng. Sau đó các Product Owner cũng sẽ là người cho ra phương án giải quyết và quyết định cuối cùng mà họ cho là tối ưu nhất.
Thực hiện quy trình User Research, từ việc phỏng vấn đến việc lập bảng câu hỏi cho khách hàng để hiểu rõ hơn về các vấn đề xoay quanh sản phẩm.
Kết hợp với team UX/UI Designer để có thể sáng tạo hóa sản phẩm, đem lại sự mới lạ thu hút đối với User.
Để giúp team Developer có thể hiểu rõ yêu cầu của người Product Owner, thì các Product Owner giỏi sẽ có thêm bước làm SRC để team Developer hiểu và làm theo đúng yêu cầu của Product Owner, tránh trường hợp “lạc đề”.
Đưa ra các đánh giá về Backlog đã xong và sắp xếp chúng theo thứ tự ưu tiên cho các Backlog chưa được hoàn thành.
Lên kế hoạch dài hạn và ngắn hạn (timeline, thời gian Release…)cho các chiến dịch nhỏ để từng bước phát triển sản phẩm.
Không như team Developer và Scrum Master, đối với các sản phẩm đã hoàn thành và được Release, thì nhiệm vụ của team Developer và Scrum Master đã hết nhưng với Product Owner thì vẫn chưa xong.
Các Product Owner phải thường xuyên theo dõi, phân tích tình hình hoạt động của sản phẩm, sự hài lòng của khách hàng… Trong trường hợp sản phẩm gặp vấn đề hoặc khách hàng muốn tối ưu các tính năng hơn nữa thì Product Owner sẽ là người tiếp nhận ý kiến nhanh chóng nhất.
Phân tích tầm nhìn cho sản phẩm
Việc chia sẻ và truyền tải định hướng tầm nhìn của sản phẩm ra sao, như thế nào một phần là trách nhiệm của Product Owner. Họ có nghĩa vụ phải lan tỏa thông tin về tính năng sản phẩm đến cho nội bộ doanh nghiệp, User và khách hàng.
Hiểu rõ sức ảnh hưởng, đối tượng các sản phẩm sẽ ảnh hưởng đến để đưa ra cái nhìn tổng quát và có đánh giá khách quan nhất từ đó có thể cho ra các phương án giải quyết hiệu quả để cải thiện và phát triển sản phẩm một cách tối ưu hơn nữa.
Làm việc, trao đổi với Scrum Master
Làm việc trực tiếp và có sức ảnh hưởng, quyền quyết định trong team Scrum Master, làm việc sâu sát với Scrum Master.
Tham gia vào các sự kiện trong team Scrum, là người động viên, thúc đẩy, đẩy nhanh tiến trình làm việc của team Scrum.
Là người thu nhận thông tin yêu cầu của khách hàng, doanh nghiệp và truyền tải những yêu cầu đó đến Scrum Master để cùng team Developer hoàn thành sản phẩm.
product owner là gì
Yếu tố để trở thành một Product Owner giỏi
Trước đây, vị trí Product Owner thường được đảm nhiệm bởi những người có kinh nghiệm làm việc phong phú và nhiều năm trong nghề nhưng trong môi trường làm việc hiện đại ngày nay thì ngày có nhiều bạn trẻ có hoài bão, ước mơ “dấn thân” vào con đường trở thành một Product Owner chuyên nghiệp.
Để có thể trở thành một Product Owner chuyên nghiệp và giỏi trong lĩnh vực của mình thì ngoài việc cố gắng bồi dưỡng kiến thức cho bản thân thì các bạn trẻ cũng nên hiểu rõ điểm mạnh và điểm yếu của mình từ đó khắc phục. Dưới đây là những yếu tố có ở một Product Owner giỏi mà bạn cần phải biết.
Hiểu biết về sản phẩm, thị trường mình đang phụ trách.
Có sự hiểu biết kỹ lưỡng về sản phẩm mà bản thân đang phụ trách là yếu tố đầu tiên và cũng quan trọng nhất mà một Product Owner bắt buộc phải có. Điều này giúp cho công việc của Product Owner trở nên hiệu quả hơn rất nhiều vì khi hiểu sản phẩm thì định hướng sản phẩm và kế hoạch sẽ chính xác hơn, đáp ứng được nhu cầu của thị trường hơn.
Trong thực tế, để có thể hoàn thiện được sản phẩm, có rất nhiều công đoạn cần phải đi qua và chưa chắc các sản phẩm sau khi hoàn thành sẽ được khách hàng đồng ý và được thị trường đón nhận. Do đó, một Product Owner có sự hiểu biết về sản phẩm lẫn thị trường sẽ có thể giảm thiểu rủi ro khi sản phẩm được Release.
product owner là gì
Dành nhiều thời gian cho công việc
Bất kì ngành nghề nào nếu muốn đạt được kết quả tốt cũng đều cần dành nhiều thời gian và tâm tư vào đó. Nghề Product Owner cũng vậy, vì là người phải chịu trách nhiệm cho toàn bộ quy trình phát triển sản phẩm, chính vì thế người làm Product Owner cần tập trung và dành thật nhiều thời gian cho công việc để có thể theo dõi tiến độ làm việc, các vấn đề phát sinh, cách giải quyết, kết quả đón nhận của User đối với sản phẩm…
Kỹ năng giao tiếp
Đây là kỹ năng phổ biến cần có trong các ngành nghề hiện nay, đối với Product Owner cũng vậy. Đối tượng làm việc của Product Owner khá rộng, từ khách hàng, doanh nghiệp đến các Product Owner khác và cuối cùng là team Scrum…họ phải giao tiếp khá nhiều bên liên quan để có thể truyền đạt được ý tưởng, mong muốn của mình.
Chính vì thế, để có thể hoàn thành tốt công việc của mình, thuyết phục được những khách hàng khó tính và được các bộ phận khác support hết mình, thì kỹ năng giao tiếp và thuyết phục người nghe cũng rất quan trọng.
Quyết đoán
Là người gần như có quyền quyết định mọi chuyện trong team Scrum, một Product Owner cần có sự quyết đoán, dứt khoát và kiên định trong những cuộc họp Daily Sprint. Trong mỗi cuộc họp, chắc hẳn sẽ có rất nhiều luồng ý kiến trái chiều và nhiều ý tưởng được đề ra và người đưa ra quyết định cuối cùng không ai khác là Product Owner. Sự quyết đoán chắc chắn sẽ là yếu tố quyết định để giúp các Product Owner có thể đưa ra quyết định một cách nhanh chóng.
Một Product Owner giỏi là một Product Owner biết lắng nghe ý kiến từ mọi người, đặt mình ở vai trò User cùng với kinh nghiệm làm việc, sự quyết đoán sẽ có thể đưa ra quyết định phù hợp nhất cho sản phẩm.
Sự khác biệt giữa Product Owner và Product Manager là gì?
Product Owner chính là người phụ trách giải quyết những vấn đề của sản phẩm xảy ra khi User khi sử dụng sản phẩm. Trong trường hợp kết quả không được khả quan thì Product Owner sẽ tiến hành quá trình cải tiến, nâng cấp sản phẩm để giúp sản phẩm đạt được KPI kinh doanh đã được đề ra từ trước của công ty.
Đôi khi các Product Owner được xem là Product Manager của các sản phẩm nhỏ nằm trong các sản phẩm lớn.
Product Manager có chức vụ cao lớn và rộng hơn, họ là người giải quyết những vấn đề mang tính khái quát, tổng quan, liên quan đến các chiến lược lâu dài của sản phẩm như: làm product roadmap, tầm nhìn, định vị sản phẩm trong thị trường,.v.v…
Emmet là một trong những tính năng built-in ưa thích của mình với text editor VS Code, và nó cũng là extension có sẵn trên các nền tảng code editor khác. Với Emmet, bạn có thể đẩy nhanh tốc độ làm việc HTML & CSS, thậm chí còn được xem là cheat-code.
Với Emmet thì việc tạo một HTML boilerplate diễn ra trong nháy mắt. Với HTML file, chỉ cần type ! bạn sẽ thấy Emmet hiển thị các gợi ý, sau đó chỉ việc Enter là đã có sẵn trang HTML blank cơ bản. Đây mình mới chỉ đang demo vài khả năng của VS Code và Emmet nên nếu chưa hiểu thì không sao, cứ đọc tiếp nhé.
Ở đây mình muốn để blockquote nó xuất hiện ngang hàng – same level với paragraph. Vì thế nên mới cần climb up, nếu không thì blockquote sẽ bị nhét vào trong paragraph.
Nhóm
Nếu cấu trúc phức tạp thì bạn có thể nhóm thẻ – group tag thay vì dùng climb up. Ví dụ này mình tạo header và footer (không climb) sử dụng ngoặc đơn ().
Có lúc bạn muốn kẹp thẻ tag vào những đoạn code có sẵn, Emmet có thể dễ dàng làm việc này. Đầu tiên, highlight đoạn code bạn cần add tag, và mở command pallet (F1) lên, search Emmet: Wrap with Abbreviation. Sau đó sẽ có dialog box để gõ element vào.
test —> <div>test</div>
Ngoài cách này thì cũng có thể dùng cú pháp Emmet tiêu chuẩn trong dialog này, wrap đoạn text với span.wrapper. Chức năng này không được gán vào phím tắt. Nên nếu bạn dùng tính năng này thường xuyên thì nên add shortcut cho nó.
Lorem Ipsum
“Lorem Ipsum” là đoạn text fake rất phổ biến được dùng để hiển thị random các giá trị dữ liệu trên trang. Chỉ cần gõ và lorem bấm Enter. Emmet sẽ tạo ra 30 từ ngẫu nhiên có thể dùng để dàn nội dung trong project.
Bạn cũng có thể tự cho số lượng chữ theo nhu cầu.
lorem10 sẽ cho bạn 10 từ random.
Gộp chung lại
Thử gộp chung những điều ở trên lại: ul.my-list>lorem10.item-$*5
Kết quả
<ul class="my-list"><li class="item-1">Lorem ipsum dolor sit amet.</li><li class="item-2">Numquam repudiandae fuga porro consequatur?</li><li class="item-3">Culpa, est. Tenetur, deleniti nihil?</li><li class="item-4">Numquam architecto corrupti quam repudiandae.</li></ul>
Dùng Emmet trong CSS
Với CSS, thì Emmet có từng cái viết tắt cho từng property, mình sẽ không liệt kê chúng ra hết nhưng sẽ đưa ra những trường hợp được dùng nhiều nhất. Bạn có thể xem full list tại đây.
Với Emmet bạn có thể tạo nên cấu trúc HTML phức tạp với chỉ một dòng, với CSS cũng làm được những điều tương tự. Tóm lại, Emmet khá đỉnh, dùng Emmet có thể giúp nâng cao năng suất và tốc độ viết HTML và CSS.
Được thành lập vào tháng 7 năm 2018, Công ty Cổ phần VinID ra đời với sứ mệnh liên kết toàn bộ hệ sinh thái VinGroup, trở thành “trợ thủ đắc lực” cho tất cả mọi người, mọi gia đình và mọi hộ kinh doanh. VinID luôn nỗ lực từng ngày để mang đến những trải nghiệm tốt nhất và nhiều lợi ích nhất cho người dùng, hướng đến mục tiêu “Vì một cuộc sống tốt đẹp hơn cho người Việt”.
VinID – Siêu ứng dụng gắn kết hệ thống sinh thái VinGroup
Công ty cổ phần VinID là công ty con trực thuộc VinGroup, tập đoàn đa ngành với vị thế dẫn đầu trong nền kinh tế cả nước.
Khởi nguồn từ các chương trình chăm sóc khách hàng từ Vingroup, VinID được biết đến là “chiếc thẻ quyền năng” giúp tối ưu quyền lợi cho khách hàng thân thiết của tập đoàn với vai trò là sản phẩm tích lũy điểm sau mỗi lần mua sắm tại các công ty thành viên của Vingroup như Vinmart, Vinmart+, Vinpearl,…. Đặc biệt, khách hàng sau khi tích lũy có thể sử dụng điểm đó để thanh toán cho các dịch vụ hoặc những lần mua sắm tiếp theo.
Ngoài chiếc thẻ cứng, khách hàng còn có thể sử dụng ứng dụng di động VinID, giải pháp hiệu quả cho người dùng trong trường hợp “để quên” hoặc không mang theo thẻ, thay thế hoàn toàn và tích hợp tất cả thao tác, hướng chủ thẻ đến việc mua sắm không tiền mặt đồng thời đem đến nhiều ưu đãi hấp dẫn.
VinID Mobile App – “trợ lý đắc lực” cho chủ thẻ VinID
Với sự phát triển của thời đại 4.0, VinID trở thành hệ sinh thái số được xây dựng với nền tảng công nghệ ưu việt kết hợp năng lực phân tích dữ liệu và thấu thiếu hành vi người dùng, hướng đến mục tiêu trở thành một trợ lý thông minh không thể thiếu với người Việt.
Đến nay, VinID đã triển khai nhiều mô hình và chức năng mới để bứt phá trở thành “siêu ứng dụng” giúp gắn kết mạng lưới toàn bộ hệ sinh thái của tập đoàn Vingroup.
Tích hợp nhiều tính năng riêng biệt, siêu ứng dụng VinID cho phép người dùng có thể:
Tích điểm, tiêu điểm trong hệ sinh thái Vingroup;
Mua hàng tươi sống thông qua tính năng Đi chợ online;
Ăn uống, mua sắm, nhận hàng tận nhà và tích điểm 7%;
Hưởng ưu đãi giảm giá mỗi ngày từ các thương hiệu lớn với VinID Voucher hay mua vé các sự kiện thể thao, giải trí nhanh chóng với VinID Ticket.
Sử dụng tính năng ví điện tử liên kết hệ thống 36 ngân hàng toàn quốc giúp người dùng dễ dàng mua sắm, thanh toán nhanh chóng, an toàn và tiện lợi.
Cùng với xu hướng phát triển mới, ví điện tử trên ứng dụng VinID ngày càng chiếm ưu thế hơn với những tiện ích đa dạng, phong phú, mang đến trải nghiệm tốt hơn cho người dùng.
VinID tìm kiếm nhân tài – Cơ hội đến với lập trình viên tài năng
Để các tiện ích trên siêu ứng dụng VinID được phát triển vượt trội và liên kết hệ thống sinh thái VinID một cách chặt chẽ, VinID liên tục chiêu mộ nhân tài công nghệ cùng góp sức.
Check ngay 2 “mảnh ghép” mà VinID đang tìm kiếm:
Product Owner không ngại khó với khả năng phân tích, tổng hợp vấn đề và đi đến những giải pháp tối ưu.
VinID tự hào là nơi quy tụ anh tài, với các chuyên gia tại Việt Nam và từ quốc tế, đóng vai trò là mắt xích quan trọng đối với sự phát triển của công ty. Chính vì vậy, mỗi thành viên tại VinID đều được tạo điều kiện tối đa cho sự phát triển sự nghiệp, mang đến cơ hội thể hiện niềm đam mê và sức sáng tạo trong công việc.
Khám phá những đãi ngộ hấp dẫn tại VinID:
Mức lương cạnh tranh, xứng đáng với năng lực và công sức đóng góp;
Lương tháng 13 + thưởng thành tích, thưởng hiệu suất hậu hĩnh;
Được hưởng trọn ưu đãi khi sử dụng dịch vụ trong hệ sinh thái Vingroup: VinID, Vinmec, Vinpearl, Golf, Safari, Vinmart, Vinschool…
Được tham gia các khóa đào tạo chuyên sâu, nâng cao tay nghề, với cơ hội thăng tiến không giới hạn.
Đồng thời trải nghiệm những điều thú vị đến từ tập đoàn hùng mạnh:
Được trực tiếp tham gia xây dựng siêu ứng dụng đẳng cấp quốc tế – VinID phục vụ cho mọi nhu cầu trong cuộc sống của người Việt;
Hòa mình vào môi trường làm việc tự do cho bạn thoải mái được là chính mình, tha hồ “vùng vẫy” với đam mê;
Làm việc trong không gian hiện đại cùng nhiều hoạt động, câu lạc bộ (bóng đá, chạy bộ,…), khu vực gym,… nâng cao sức khỏe.
Cơ hội mở ra trước mắt – Bạn còn chờ gì không mau chóng nhấn nút apply?
Xem ngay những tin đăng tuyển dụng IT mới nhất trên TopDev
Vai trò chính của giám đốc công nghệ là nhằm đảm bảo chiến lược phù hợp với mục tiêu chung của tổ chức/doanh nghiệp. Nhưng không có nghĩa, CTO sẽ chịu trách nhiệm giám sát bộ phận CNTT. Thay vào đó, điều họ thực hiện là pha trộn các nền tảng kiến thức về công nghệ của cá nhân và kiến thức thông tin được cập nhật liên tục để cung cấp cho doanh nghiệp những giải pháp về chiến lược tốt nhất có thể. Có thể nói, CTO có một vai trò quan trọng cho sự gắn kết của công ty. Cùng TopDev phân tích sâu hơn tầm quan trọng của CTO qua bài viết sau đây.
CTO – Chief Technology Office là gì?
CTO – Giám đốc công nghệ (hay Giám đốc kỹ thuật) – là người giữ vị trí quản lý cấp cao trong một doanh nghiệp. Đồng thời, họ chuyên phụ trách các vấn đề về công nghệ và kỹ thuật; điều hành hoạt động nghiên cứu và phát triển (R&D). Thông qua việc giám sát chặt chẽ các nhu cầu ngắn và dài hạn, CTO sẽ đưa ra quyết định nhằm giúp tổ chức đạt được mục tiêu. Thường thì CTO sẽ làm việc trực tiếp với giám đốc điều hành (CEO) của công ty.
Nhân tố quan trọng thể hiện tiềm năng của sự gắn kết
Sự gắn kết của CTO được thể hiện khi chính họ theo dõi đội ngũ kỹ thuật. Họ cũng là người cung cấp các hướng dẫn cần thiết cho nhân viên. Đồng thời là người hỗ trợ việc đảm bảo sự tối ưu hóa về mặt công nghệ.
Không nhất thiết mọi công ty đều cần có CTO. Nhưng nếu bạn đang điều hành một công ty khởi nghiệp với tiềm năng phát triển lớn thì CTO thật sự quan trọng. CTO đích thị sẽ là một “trung tâm dữ liệu chính về công nghệ” đấy.
Vai trò của CTO được thay đổi tương ứng với khả năng phát triển của công nghệ. Tuy vậy, một điều khó thay đổi là CTO luôn tồn tại một tiềm năng về sự gắn kết mạnh mẽ. Dù tập trung rõ ràng vào công nghệ thông tin nhưng vẫn đảm bảo được sự phù hợp; khả năng hoạt động của tổ chức và không quá bị ám ảnh bởi “cuộc đua về công nghệ”. Dù tập trung vào việc xây dựng chiến lược – một khía cạnh nhỏ nhưng vẫn bám chặt vào bối cảnh phát triển chung của doanh nghiệp, tức bức tranh tổng thể lớn hơn.
CTO – “Người anh” dẫn dắt về tầm nhìn và phương hướng hoạt động
Điểm khác biệt đồng thời cũng là vai trò quan trọng của CTO là việc tập trung vào tương lai. Tức là những gì cần thực hiện và thực hiện như thế nào.
CTO có tầm quản lý các giải pháp công nghệ hiện tại của công ty. Họ cần nhận thức được các công nghệ mới và những bài toán nhiều khía cạnh về: nguồn lực, ngân sách,… để áp dụng các giải pháp mới nhằm giải quyết mọi vấn đề. Ngoài những trách nhiệm này, các CTO cần phải có tầm nhìn xavề định hướng của công ty. Họ đưa ra các phán đoán, đánh giá về các mục tiêu tổng thể. Từ đó, họ biết cách công nghệ sẽ được áp dụng như thế nào khi tiến hành các quyết định quan trọng.
Ví dụ, công việc hằng ngày của “người anh” này là tham gia vào quá trình thảo luận. Đó là cách đưa ra các quyết định thật sự quan trọng. Mục đích chính là làm rõ các xu hướng công nghệ; tính khả thi nếu tiến hành áp dụng chúng. Ngoài ra, CTO còn phải dẫn dắt về các tầm nhìn; phương hướng hoạt động của những bộ phận khác nhau trong doanh nghiệp.
CTO – “Đại diện về công nghệ hướng ngoại”
Không thể đánh đồng CTO là phải code siêu giỏi. Bạn cần hiểu rằng đã là một CTO thì họ đều đã lăn xả ở nhiều vị trí. Và dĩ nhiên, khả năng code của họ không thể đùa được. Thật quá ngớ ngẩn để so sánh năng lực code của một CTO. Vì thực tế cho thấy rằng những điều CTO cần tập trung nhiều hơn thế.
Dường như việc code giỏi hay không không còn quá quan trọng với CTO. Họ sẽ là đại diện về công nghệ hướng ngoại. Tức là trở thành bộ mặt công nghệ cho chính công ty của mình. Điều này đồng nghĩa CTO sẽ người là tham dự các hội nghị có liên quan. Tại sao lại như vậy? Đơn giản là vì họ không chỉ nắm bắt được các tin tức công nghệ quan trọng, mà còn đại diện cho các sáng kiến công nghệ của công ty.
Chính việc tham dự tại các hội nghị, hội thảo chuyên môn, trò chuyện ngoại giao có ý nghĩa rất lớn trong việc mở rộng mạng lưới mối quan hệ. Điều này thúc đẩy sự hợp tác về các mục tiêu, dự án và mở ra nhiều cơ hội, lợi thế lớn trong tương lai. Tất nhiên, sự hợp tác nào cũng dựa trên nguyên tắc cộng tác và bình đẳng về lợi ích.
Lời kết
CTO có một vai trò điều hành quan trọng đối với việc tập trung và phát triển các mục tiêu công nghệ dài hạn. Mỗi CTO của mỗi quy mô tổ chức cần nắm bắt đúng tình hình. Và hơn hết là tầm nhìn chiến lược. Điều này sẽ thúc đẩy sự phát triển công ty diễn ra một cách tốt nhất. TopDev hi vọng bài viết có mang lại nhiều giá trị cho bạn đọc về vị trí CTO – Chief Technology Officer.
Bài viết được sự cho phép của tác giả Trần Hữu Cương
Khi học lập trình, hẳn nhiều lúc bạn nhầm lẫn giữa ngôn ngữ lập trình C với C++. Nhiều lúc người ta viết gộp lại thành C/C++. Điều này sẽ khiến bạn hiểu nhầm rằng 2 ngôn ngữ này là một. Tuy nhiên thực tế không phải thế, bạn cần phân biệt rõ hai ngôn ngữ này bởi có những dự án phần mềm chỉ viết bằng ngôn ngữ C.
C và C++ là gì?
C và C++ là hai ngôn ngữ lập trình khác nhau.
C là ngôn ngữ lập trình hướng cấu trúc được ra đời trước C++.
C++ là ngôn ngữ lập trình thừa kế, mở rộng từ C. Do đó tất cả những gì đúng với C thì cũng đều đúng với C++. Đây cũng chính là lý do vì sau người ta hay gộp chung lại thành C/C++
Chuyện là trong lúc rảnh việc ở công ty, anh em mình thường có văn hóa nghiên cứu công nghệ mới. Trong một lần nghiên cứu về NodeJS, vừa làm xong chương trình “Hello world” thì một người nói vui rằng “thế là biết NodeJS rồi đó, giờ có thể tự tin ghi vào CV là có kinh nghiệm làm việc với NodeJS rồi“. Cả team phá lên cười, vì ai cũng hiểu rằng chương trình “Hello world” là bài học vỡ lòng của developer chứ ai lại coi nó là kinh nghiệm làm việc.
Ủa vậy dư lào mới được coi là có kinh nghiệm làm việc, 6 tháng hay 1 năm, hay phải nhiều hơn? Đây chính là câu hỏi nảy ra trong đầu mình ngay sau khi cười sảng khoái, thế nhưng cũng quên bẫng đi cho tới ngày hôm nay.
I. KINH NGHIỆM LÀM VIỆC CÓ ĐƯỢC TÍNH BẰNG SỐ NĂM?
Số năm làm việc có lẽ là thước đo kinh nghiệm được sử dụng nhiều nhất. Bạn đồng ý với mình điều này chứ? Bởi chúng ta vẫn gặp các tin tuyển dụng dạng như “Tuyển dev trên 1 năm kinh nghiệm”, hay như trong CV chúng ta (cả mình) cũng thường viết rằng có X năm kinh nghiệm làm việc với công nghệ ABC nào đó.
Thế nhưng việc “cân đo” kinh nghiệm bằng số năm liệu có chính xác? Theo mình thì không, nó chỉ là cách nhanh nhất để mô tả kinh nghiệm của bạn tới đối phương mà thôi, chứ nó không thể hiện rõ kinh nghiệm của bạn.
Ví dụ anh A nói rằng có 2 năm kinh nghiệm code PHP, nhưng trong suốt 2 năm anh A chỉ quanh quẩn xây dựng mấy cái web demo, chỉ cần chạy được, chẳng bao giờ phải quan tâm tới việc tối ưu query database, tối ưu thời gian phản hồi, và lượng kiến thức anh sử dụng để code PHP trong 2 năm đó hoàn toàn có thể học được trong vòng 2 tháng. Còn anh B (anh Bình đó :D) mới chỉ có 1 năm kinh nghiệm code PHP, nhưng anh thường xuyên phải giải quyết bài toán “hóc búa”, deadline gấp, đòi hỏi tốc độ phản hồi nhanh nên anh học được thêm rất nhiều “thủ thuật” mới phục vụ cho việc code cũng như cách làm việc sao cho hiệu quả.
Vậy rõ ràng nếu lấy số năm làm thước đo kinh nghiệm thì anh A (2 năm KN) ăn đứt anh B (1 năm KN). Nhưng so sánh dưới góc độ lượng kinh nghiệm thực tế tích lũy được thì anh B lại ăn đứt anh A.
Trường hợp giống anh A như ví dụ trên mình gặp khá nhiều. Nhiều nhất là ở các bạn sinh viên mới ra trường. Bởi trong trường ĐH, các bạn ý được học lập trình từ năm 1, năm 2, vậy nghiễm nhiên sau khi ra trường là có 2 – 5 năm code. Có bạn còn tự tin nhận mình là senior developer, deal lương $1000 nhưng khi phỏng vấn thì trượt ngay ở mấy câu hỏi dành cho junior developer.
Không được tính bằng năm, vậy nên được tính bằng gì? Thật ra thì mình cũng không rõ là có thước đo “offical” nào cho kinh nghiệm hay không, nhưng mình biết một khái niệm là THANG CẤP ĐỘ TƯ DUY BLOOM.
Thang cấp độ tư duy Bloom là khái niệm được nhắc nhiều trong giáo dục, được sử dụng để xây dựng các mục tiêu giáo dục, hệ thống hóa các câu hỏi, các bài kiểm tra, đánh giá quá trình học tập.
Tuy là thang đo được áp dụng trong giao dục, nhưng khi áp sang để làm thước đo kinh nghiệm làm việc thì mình cũng thấy cũng hợp lý. Bởi mỗi lần rút kinh nghiệm cũng giống như bạn vừa học xong một bài học vậy.
Thang cấp độ tư duy Bloom được chia làm 6 cấp độ (từ dễ tới khó).
Cấp độ – Tên
Miêu tả
1 – Nhớ
Có thể nhắc lại các thông tin đã được tiếp nhận trước đó
2 – Hiểu
Nắm được ý nghĩa của thông tin, thể hiện qua khả năng diễn giải, suy diễn, liên hệ, khái quát
3 – Vận dụng
Áp dụng thông tin đã biết vào một tình huống, điều kiện mới
4 – Phân tích
Chia thông tin thành những phần nhỏ và chỉ ra mối liên hệ của chúng tới tổng thể
5 – Đánh giá
Đưa ra nhận định, phán quyết của bản thân đối với thông tin dựa trên các chuẩn mực, tiêu chí
6 – Sáng tạo
Xác lập thông tin, sự vật mới trên cơ sở những thông tin, sự vật đã có
6 Cấp độ trên được thể hiện từ dễ tới khó, tức là người ta có thể dễ dàng đạt được cấp 1, nhưng để đạt được tới cấp 6 thì phải trải qua một chặng đường dài.
Một ví dụ thể hiện kinh nghiệm làm việc với PHP qua 6 cấp độ của thang đo Bloom:
Cấp độ – Tên
Ví dụ
1 – Nhớ
Nhớ được toàn bộ cú pháp của PHP, các khái niệm trong PHP.
2 – Hiểu
Hiểu được cách sử dụng PHP, như khi nào thì lấy dữ liệu bằng $_GET, khi nào thì lấy dữ liệu bằng $_POST, phân biệt được $_SESSION với $_COOKIE.
3 – Vận dụng
Vận dụng PHP để xây dựng một website
4 – Phân tích
Phân tích ưu nhược điểm của PHP.
5 – Đánh giá
Đánh giá PHP phù hợp để giải quyết các bài toán như thế nào.
6 – Sáng tạo
Sáng tạo ra công nghệ mới giúp khắc phục nhược điểm của PHP.
Quay trở lại trường hợp code chương trình “Hello World” trên NodeJS mà mình nhắc tới ở đầu bài viết, thì mình mới chỉ đạt tới cấp độ “Nhớ” – cấp độ thấp nhất trong 6 cấp, cũng chẳng có gì to tát lắm và đấy cũng là lý do khiến cả team cười.
III. LỜI KẾT
Bài viết này mình muốn nhắn nhủ rằng “Kinh nghiệm thì không nên tính bằng số năm”, cũng như chia sẻ về 6 cấp độ tư duy Bloom để chúng ta cùng tự đánh giá lại bản thân, xem kinh nghiệm làm việc của mình thật sự đang ở đâu. Nếu kinh nghiệm ở khía cạnh nào cũng đạt mức 5 mức 6 mà công ty trả lương bèo quá thì có thể đề xuất tăng lương. Hay trước giờ luôn ảo tưởng sức mạnh tưởng mình là best nhưng thực ra không phải vậy thì nên khiêm tốn và chịu khó học hỏi thêm.
Bài viết được viết dựa trên kinh nghiệm và quan điểm cá nhân, sẵn sàng nhận mọi gạch đá.
Function get_groups(name) nhận vào 1 string, tìm kết quả ở 1 nguồn dữ liệu, và trả về kết quả cần thiết. Đây là một phiên bản ai cũng viết được:
defget_groups(name):all_groups=get_data_from_source()groups=[]ifname:forgroupinall_groups():ifname.lower()ingroup['name'].lower():groups.append({group['name']:group['id']})else:# if user provide empty string, return list customers as defaultforgroupinall_groups():if'customer'ingroup['name'].lower():groups.append(group['name'])iflen(groups)>0:returngroupselse:return"Group {} not found in groups".format(name)
Đoạn code nhìn qua tốt, chuẩn pep8, pythonic (for group in all_group() chứ không dùng range), tên đặt cũng dễ hiểu. Nhưng hãy xem thật kỹ code, nghĩ xem có gì không tốt trước khi đọc tiếp.
… … …
Function này trông có vẻ ổn, cho đến khi ta gọi function này và xử lý kết quả của nó:
nó có thể trả về 1 string, khi không tìm thấy
nó có thể trả về list của các dictionary, nếu người dùng đưa vào name khác rỗng
nó có thể trả về list của các string, nếu người dùng đưa vào string rỗng
Ai cũng có thể viết function, nhưng function trả về 3 kiểu dữ liệu khác nhau như vậy thì chỉ làm cho người dùng nó thêm khổ. Người gọi function này sẽ phải xử lý cả 3 trường hợp, hãy thử xem function sau là function DUY NHẤT gọi get_groups:
Trước tiên, code này cũng phải rẽ 3 nhánh để xử lý 3 loại đầu ra khác nhau của get_groups. Người dùng function này không có cách nào khác ngoài đọc code để hiểu function sẽ làm gì.
Lập trình viên sẽ viết code rất khổ, nếu có những function như get_groups() có trên hệ thống. Giải pháp? Viết lại function này đảm bảo một tiêu chuẩn:
Mỗi function chỉ trả về 1 kiểu dữ liệu.
Nếu get_groups trả về [] hoặc 1 list chứa danh sách các tên của group, code gọi nó chỉ còn lại :
defhandle_group_search(name):result=get_groups(name)ifnotresult:print('Not found name {} in groups'.format(name))else:forninresult:print(n.lower())
Một lý do khác mà get_groups là một function rất tệ, nó dùng kiểu dict để nhóm dữ liệu (group:id) rồi nhét vào list. Khi dữ liệu lưu vào dict mà không để tìm kiếm hay không có key cố định, nó chỉ là một nỗi đau. Để lọc ra name của groups, code sử dụng đã phải viết:
groups=[list(d.items())[0][0]fordinresult]
code trên biến dict 1 phần tử thành 1 list và truy cập lấy tên của key:
Bạn biết đấy mình đang tìm hiểu về big data trong thời gian này. Tuy cũng chưa được nhiều, nhưng bằng bấy nhiêu đã khiến mình nhận ra rằng trước giờ mình đã hiểu sai về big data. Nguyên nhân chủ yếu khiến mình nhìn nhận sai là do mình đang tìm hiểu nó dưới cái nhìn của một software engineer thay vì là một data engineer.
Trên blog phambinh.net, các bạn đọc giả chủ yếu cũng là software engineer hoặc có định hướng trở thành software engineer, để tránh các bạn bị hiểu sai về big data như mình, nên mình viết bài viết này để chia sẻ với các bạn những lầm tưởng của mình về big data trước kia.
1. COI DATABASE NẶNG LÀ BIG DATA
Mình thường chỉ làm việc với những database (mysql) có table tầm 1tr records đổ lại. Tổng dung lượng database cũng chỉ xấp xỉ 1GB. Đương nhiên đây đều là các database trên môi trường dev, còn database trên môi trường production thì nặng hơn, có điều mình … lại chưa được làm việc với database của production bao giờ.
Vì vậy mà khi tiếp tục với những database nặng cỡ tầm 50GB – nó thật sự khiến mình cảm thấy bối rối vì chưa làm việc với những database nặng như vậy bao giờ. Mình nghĩ với lượng dữ liệu thế này chắc không thể áp dụng những cách truyền thống được để giải quyết được, mà cần phải áp dụng các công nghệ của big data vào để xử lý.
Chú thích thêm: Trước đó mình chưa từng tìm hiểu thế nào là big data mà chỉ tự nghĩ rằng big là lớn, data là dữ liệu, vậy big data là dữ liệu lớn, mà cỡ 50GB thì là là lớn rồi. Cho nên auto hiểu luôn database 50GB là big data.
Cho tới khi mình tìm hiểu được big data là gì, thì mình mới thay đổi quan điểm này. Nhắc lại chút thì big data sẽ phải đáp ứng được 3V là: Volume – Variety – Velocity (Dung lượng – Độ đa dạng – Tốc độ). Trong khi đó 50GB vẫn có thể lưu ngon trên một máy tính – chưa đáp ứng được tiêu chí về Volume, database thì thường là có cấu trúc hoặc bán cấu trúc – chưa đáp ứng được tiêu chí về Variety.
Vì vậy bạn nào mà có quan điểm này giống mình thì thay đổi đi nha, 50GB vẫn còn nhỏ lắm, chỉ cần biết tối ưu chút thì với cách xử lý truyền thống vẫn xử lý phà phà.
2. COI BIG DATA LÀ SKILL CỦA SOFTWARE ENGINEER
Bạn có nhìn thấy big data ở đâu không? Nó nằm ở giữa data engineer và data scientist chứ không hề liên quan tới software engineer. Điều này có nghĩa là bạn có thể yên tâm phát triển trên con đường trở thành software engineer mà không cần phải bận tâm tới big data nếu như bạn không thích nó.
Đương nhiên là bức ảnh trên cũng có phần quá đát (date) rồi, nhưng mình cũng có tham khảo qua nhiều tin tuyển dụng về software engineer thì không thấy có yêu cầu nào cần skill liên quan tới big data cả. Nếu như có tin tuyển dụng nào cần tới big data hay các công cụ xoay quanh big data như hadoop thì đều là tin tuyển data engineer.
Software engineer vs Data engineer
Software engineer là người tạo ra phần mềm, mỗi phần mềm sẽ đóng góp dữ liệu trong quá trình nó hoạt động vào một cái ao chung tạm gọi là Big data. Data engineer sẽ sử dụng dữ liệu trong cái ao này để thực hiện một yêu cầu nào đó dạng như: xuất báo cáo, vẽ biểu đồ, trích xuất dữ liệu để train cho model AI, trích xuất dữ liệu để làm database cho một ứng dụng khác,…
Data engineer sẽ sở hữu một ngách skill của software engineer và có không ít trong số họ đi lên từ software engineer.
3. NGHĨ RẰNG NGƯỜI DÙNG THÔNG THƯỜNG SẼ QUAN TÂM TỚI OUTPUT CỦA BIG DATA
Như mình đề cập ở trên, output của việc xử lý big data thường là một báo cáo, một tập tiêu chí và chỉ số, hay một tập dữ liệu nào đó, và dữ liệu khá vô nghĩa với người sử dụng thông thường.
Người dùng thông thường ở đây ám chỉ những người dùng mà cả hệ thống đang hướng tới và khai thác, như khách hàng mua hàng trên tiki, người dùng facebook, người dùng youtube,…
Ví dụ, một khách hàng mua hàng trên tiki sẽ chẳng quan tâm tới biểu đồ doanh thu của công ty trong 1 năm qua, họ chỉ quan tâm tới sản phẩm họ mua có tốt không và nhận hàng trong bao lâu. Một người đi xem phim hành động, họ chẳng quan tâm tới việc thể loại đó được bao nhiêu người khác quan tâm, họ chỉ quan tâm tới việc làm sao để mua vé mà không phải đứng đợi…
Output của big data thường là input của một ứng dụng khác, hoặc được sử dụng để ra quyết định thực hiện một chiến lược kinh doanh nào đó. Khác với output của các phần mềm do software engineer tạo ra, nó hướng trực tiếp tới người sử dụng thông thường.
4. “HIỆU NĂNG” CỦA BIG DATA GIỐNG VỚI “HIỆU NĂNG” CỦA PHẦN MỀM
Khi mình tìm hiểu về các công cụ xử lý big data, thỉnh thoảng sẽ gặp câu kiểu như “công nghệ XXX cho phép xử lý big data với hiệu năng cao“.
Xuất phát từ một software engineer, mình cho rằng hiệu năng ở đây ám chỉ tốc độ thực hiện rất nhanh, nhưng không phải. Cụm từ “hiệu năng” trong big data sẽ được hiểu là có khả năng tính toán trên một tập dữ liệu rất lớn, với độ chính xác cao, còn thời gian thì có thể là một vài tiếng hoặc thậm chí cả ngày mới ra kết quả cũng không sao.
Khi xử lý big data, yếu tố chính xác và đầy đủ thông tin được đặt lên hàng đầu, thời gian thì có thể đợi được.
5. TỔNG KẾT
Kể ra mới thấy góc nhìn của software engineer khác rất nhiều so với góc nhìn của data engineer, nhưng cũng may là mình đã sớm nhận ra chứ không cứ lao đầu học data theo hướng học lập trình thì có ngày sẽ đi vào ngõ cụt mất.
Một bài viết ngắn ngọn chia sẻ tới các bạn, hy vọng sẽ giúp ích cho cộng đồng.