Home Blog Page 89

Domain-Driven Design

Domain-Drive Design

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

Tôi đã có một bài viết về Domain Drive Development (DDD) – First thought để “đặt vấn đề” cho DDD, các bạn có thể tham khảo ở đó trước. Trong phạm vi bài viết này, tôi sẽ tham khảo và diễn đạt lại từ bài viết Domain-Drive Design của tác giả herbertograca.

Domain-Drive Design do Eric Evans tạo ra trong cuốn sách nổi tiếng của ông về Domain-Driven Design: Tackling Complexity in the Heart of Software, xuất bản năm 2003. Cuốn sách của Eric Evans là chìa khóa chính thức hóa nhiều khái niệm phát triển phần mềm hiện nay.

  Cách thiết lập một dự án Symfony để làm việc với Docker Subdomains
  System Design Cơ Bản: Domain Name System (DNS)

Tôi không thể đưa ra một đánh giá toàn diện về DDD trong một bài viết trên blog. Có quá nhiều khái niệm quan trọng liên quan đến DDD. May mắn thay, đó cũng không phải là mục tiêu ở đây. Tuy nhiên, những gì tôi sẽ làm là liệt kê các khái niệm DDD mà tôi thấy có liên quan đến cách tôi muốn tổ chức mã và cách tôi nghĩ về Kiến trúc: các khái niệm hệ thống rộng tạo thành nền tảng cho phát triển tính năng.

Trong bài viết, tôi sẽ nói về:

  • Ubiquitous language (Ngôn ngữ chung)
  • Layers Bounded contexts
  • Anti-Corruption Layer
  • Shared Kernel
  • Generic Subdomain

UBIQUITOUS LANGUAGE

Một vấn đề thường xảy ra trong phát triển phần mềm, xoay quanh sự hiểu biết về mã nguồn, nó là gì, nó làm gì, nó như thế nào, tại sao nó lại … nó thậm chí còn phức tạp hơn để hiểu mã nguồn khi nó sử dụng một thuật ngữ khác với thuật ngữ mà các chuyên gia về nghiệp vụ (domain expert) sử dụng. Ví dụ, nghiệp vụ chuyển tiền trong ngân hàng được các chuyên gia gọi là remittance, tuy nhiên khi các developer nghe được từ “chuyển tiền”, họ lại để trong code là money transfer, điều này có thể gây ra nhiều nhầm lẫn khi thảo luận về ứng dụng. Tuy nhiên, hầu hết sự mơ hồ này có thể được giải quyết bằng cách đặt tên đúng các lớp và các phương thức, làm cho chúng thể hiện một đối tượng và phương pháp nào đó trong ngữ cảnh của nghiệp vụ.

Ý tưởng chính của việc sử dụng một Ngôn ngữ chung (Ubiquitous language) là để giúp cho việc không nhầm lẫn mơ hồ giữa các khái niệm về nghiệp vụ với cách đặt tên, ánh xạ các khái niệm đó ở trong mã nguồn. Code, class, method, properties và đặt tên mô-đun phải phù hợp với ngôn ngữ chung. Nếu cần thì ta có thể refactor (tái cấu trúc) lại mã nguồn để đạt được điều đó, với mục đích chung là thống nhất các thuật ngữ giữa code và nghiệp vụ.

LAYERS

Tôi đã nói về Layers trong một bài viết trước, nhưng tôi sẽ viết lại trong bài viết này từ góc nhìn của DDD:

USER INTERFACE

Chịu trách nhiệm render màn hình người dùng sử dụng để tương tác với ứng dụng và dịch các đầu vào của người dùng vào các lệnh ứng dụng. Điều quan trọng cần lưu ý là “người dùng” có thể là con người nhưng cũng có thể là các ứng dụng khác kết nối với API của chúng ta, tương ứng hoàn toàn với các đối tượng Boundary trong kiến trúc EBI;

APPLICATION LAYER

Dàn xếp các đối tượng domain để thực hiện các tác vụ theo yêu cầu của người dùng hay còn gọi là các use-case. Nó không chứa business logic. Điều này giống với Interactors trong kiến trúc EBI, ngoại trừ Interactors là bất kỳ đối tượng nào không liên quan đến User Interfaces hoặc Entity, và trong trường hợp này, Application Layer chỉ chứa các đối tượng có liên quan đến một use-case. Lớp này là nơi mà Application Services thuộc về, vì chúng là nơi tổng hợp, sử dụng tất cả các loại object khác như repositories, Domain Services, Entities, Value Objects hoặc bất kỳ đối tượng Domain nào để đáp ứng yêu cầu của các use-case.

DOMAIN LAYER

Đây là lớp có chứa tất cả logic nghiệp vụ, Domain Services, Entities, Events và bất kỳ loại đối tượng khác có chứa Logic nghiệp vụ. Rõ ràng nó chính là Entity Object của EBI. Đây là trung tâm, là trái tim của hệ thống. Domain Services sẽ chứa các logic nghiệp vụ không phù hợp với một Entity, thường là kết hợp một số Entity trong việc thực hiện một số công việc liên quan đến nghiệp vụ;

INFRASTRUCTURE

Các khả năng kỹ thuật hỗ trợ các Layers ở trên, ví dụ như persistence hoặc messaging.

ddd_layers

Eric Evans, 2003

BOUNDED CONTEXTS

Trong một ứng dụng doanh nghiệp, có thể có rất nhiều model, các khái niệm cũng như số lượng và kích thước của team làm việc trên codebase là rất lớn. Điều này mang lại cho chúng ta hai vấn đề:

  1. Các developer làm việc trên codebase càng lớn thì càng khó nhận thức và hiểu đúng về những gì những đoạn mã đang làm, và do đó có thể tạo ra bug, gây khó khăn trong việc debug, hiểu đúng và ra quyết định được làm theo thế nào.
  2. Các nhiều developer làm việc trên cùng một codebase, càng khó khăn hơn để hợp tác và có một tầm nhìn chung về kỹ thuật và nghiệp vụ của ứng dụng.

Nói cách khác, đó là 2 vấn đề lớn cần giải quyết khi làm việc với các ứng dụng tầm cỡ enterprise.

Giải pháp thông thường cho một vấn đề lớn là chia nhỏ nó thành những phần nhỏ hơn, hay còn gọi là “bounded contexts”. Nơi mỗi phần phục cho những đối tượng người dùng khác nhau. Nói cách khác, trong 1 hệ thống phần mềm doanh nghiệp, nơi mà hệ thống sẽ phục vụ cho rất nhiều đối tượng người sử dụng khác nhau, việc chúng ta nên làm là chia nhỏ hệ thống đó ra thành những hệ thống nhỏ hơn, đơn lẻ về nhiệm vụ, và đối tượng người dùng. Ví dụ hệ thống nhân sự, kế toán, tiền lương. Mỗi hệ thống có 1 ngữ cảnh riêng gọi là “bounded contexts”, nơi mà nó hệ thống con đó chỉ có hiểu biết về ngữ cảnh, nghiệp vụ nó đảm nhiệm.

Two subsystems commonly serve very different user communities.

Eric Evans 2014, Domain-Driven Design Reference

Các bounded contexts xác định một ngữ cảnh áp dụng một phần riêng biệt của mô hình nghiệp vụ. Việc cô lập này có thể đạt được bằng cách tách các logic kỹ thuật, tách biệt codebase, tách biệt giản đồ cơ sở dữ liệu (database schema) và về tổ chức team. Mức độ cô lập bounded context, như thường lệ, phụ thuộc vào tình hình thực tế: nhu cầu và khả năng chúng ta có.

Một điểm thú vị, đây không phải là một khái niệm hoàn toàn mới. Ivar Jacobson đã viết về các hệ thống con (subsystems) trong cuốn sách của mình (Object-Oriented Software Engineering: A Use Case Driven Approach), trở lại vào năm 1992, mười một năm trước Eric Evans!

fig_7_27_subsystems

Ivar Jacobson, 1992

Khi đó, ông đã có một vài ý tưởng rất cụ thể về chủ đề này:

  • Hệ thống do vậy bao gồm một số các hệ thống con có thể chứa các hệ thống con của chính nó. Ở dưới cùng của phân cấp như vậy là các đối tượng phân tích (analysis objects). Các hệ thống con là một cách để cấu trúc hệ thống cho việc phát triển và bảo trì.
  • Nhiệm vụ của các hệ thống con là đóng gói các đối tượng sao để làm giảm đi sự phức tạp.
    Tất cả các đối tượng đảm nhiệm các phần cụ thể của chức năng sẽ được đặt trong cùng một hệ thống con
  • Mục đích là để có một gắn kết chức năng mạnh mẽ trong một subsystem và một sự liên kết lỏng lẽo giữa các subsystem (ngày nay được gọi là low coupling and high cohesion)
  • [Một hệ thống con] nên tốt hơn nên được sử dụng bởi chỉ một actor, vì thay đổi thường được gây ra bởi một actor.
  • […] bắt đầu bằng cách đặt các đối tượng điều khiển trong một subsystem, và sau đó đặt các đối tượng thực thể liên kết chặt chẽ (strongly coupled) và các đối tượng giao diện (interface objects) trong cùng một subsystem.
  • Tất cả các đối tượng có gắn kết chức năng mạnh mẽ (strong mutual functional coupling) sẽ được đặt trong cùng một subsystem […]
    • Liệu những thay đổi trong một đối tượng dẫn đến những thay đổi trong đối tượng kia? (Điều này bây giờ được gọi là Nguyên tắc The Common Closure Principle – Classes được xuất bản bởi Robert C. Martin trong bài báo “Granularity” năm 1996, 4 năm sau cuốn sách Ivar Jacobson)
    • Liệu chúng có giao tiếp với cùng một actor?
    • Có phải cả hai đều phụ thuộc vào một đối tượng thứ ba, chẳng hạn như một interface object hay một entity? Liệu một đối tượng thực hiện một số hoạt động trên đối tượng kia? (Điều này được gọi là Nguyên tắc The Common Reuse Principle – Classes, được sử dụng cùng nhau được đóng gói cùng nhau của Robert C. Martin trong bài báo “Granularity” năm 1996, 4 năm sau cuốn sách Ivar Jacobson)
  • Một tiêu chí khác cho việc phân chia là phải có ít thông tin trao đổi giữa các hệ thống con khác nhau càng tốt (low coupling)
  • Đối với các dự án lớn, có thể có các tiêu chí khác cho phân hệ thống con, ví dụ:
    • Các nhóm phát triển khác nhau có năng lực hoặc nguồn lực khác nhau và có thể phân phối các công việc phát triển phù hợp (các nhóm cũng có thể được tách biệt về mặt địa lý)
    • Trong một môi trường phân tán, một hệ thống phụ có thể được yêu cầu ở mỗi logical node (SOA, web services và micro services) Nếu một sản phẩm hiện có có thể được sử dụng trong hệ thống này, điều này có thể được coi là một subsystem (các libraries mà hệ thống phụ thuộc vào, ví dụ như ORM).

ANTI-CORRUPTION LAYER

Một Anti-Corruption Layer cơ bản là một middleware giữa hai hệ thống con. Nó được sử dụng để cô lập hai hệ thống con, làm cho chúng phụ thuộc vào layer này thay vì phụ thuộc trực tiếp vào nhau. Bằng cách này, nếu chúng ta tái cấu trúc hoặc thay thế hoàn toàn một trong các hệ thống con thì chúng ta sẽ chỉ phải cập nhật layer này để các hệ thống con khác không bị ảnh hưởng.

Điều này đặc biệt hữu ích khi có một hệ thống mới mà chúng ta cần phải tích hợp với một hệ thống có sẵn. Để không để những hệ thống cũ chịu sự ảnh hưởng từ việc thêm mới các hệ thống con mới, chúng ta tạo ra một Anti-Corruption Layer sẽ điều chỉnh API của hệ thống cũ cho các nhu cầu của hệ thống con mới.

Có 3 mối quan tâm chính:

  1. Điều chỉnh các API hệ thống con với những gì các client subsystems cần;
  2. Translate data và commands giữa các hệ thống con;
  3. Thiết lập trao đổi (communication) theo một hoặc nhiều hướng, nếu cần

fig_14_8_anticorruption_layer

Eric Evans, 2003

Đây là một kỹ thuật được sử dụng hợp lý khi chúng ta không kiểm soát một hoặc tất cả các hệ thống con, nhưng cũng có thể sử dụng nó khi chúng ta kiểm soát tất cả các hệ thống con liên quan, ngay cả khi chúng được thiết kế tốt nhưng đơn giản có các model rất khác nhau và chúng ta muốn ngăn chặn sự rò rỉ từ model này sang model khác (thay đổi một hệ thống con để phù hợp với nhu cầu của một hệ thống con khác).

SHARED KERNEL

Trong một số trường hợp, bất chấp mong muốn của chúng ta để có các thành phần tách biệt hoàn toàn và tách rời, vẫn có một số trường hợp buộc ta phải tách một số domain code ra để chia sẻ cho nhiều component khác sử dụng.

Điều này sẽ cho phép các component vẫn giữ được tính phân tách và độc lập với các component khác mặc dù sử dụng chung những mã chia sẻ cùng (shared code), ta gọi các mã chia sẻ này với cái tên “shared kernel”.

Trường hợp ví dụ, với các events được kích hoạt bởi một component và lắng nghe bởi một hoặc một số component khác. Và tương tự cho các service interfaces and thậm chí là các entities.

Tuy nhiên, nên giữ phần Shared Kernel này càng nhỏ càng tốt, và cẩn thận khi thay đổi nó vì chúng ta có thể một cách vô tình gây ảnh hưởng đến những chỗ khác đang sử dụng nó. Điều quan trọng là mã trong Shared Kernel sẽ không nên bị thay đổi nếu không có sự tham gia và hiểu biết của tất cả các nhóm phát triển khác sử dụng nó.

GENERIC SUBDOMAIN

Một Subdomain là một phần rất biệt lập của domain. Generic Subdomain là một Subdomain không liên quan đến ứng dụng của chúng ta mà có thể được sử dụng trong bất kỳ ứng dụng nào tương tự.

Vì vậy, nếu có một ứng dụng có một phần của nó là về finance, có lẽ chúng ta có thể sử dụng một thư viện finance hiện có trong ứng dụng. Nhưng dù sao đi nữa, ngay cả khi không thể sử dụng thư viện hiện có và cần xây dựng riêng của chúng ta, nếu nó là một Generic Subdomain thì đó không phải là hoạt động cốt lõi và nó cần phải được coi là cần thiết nhưng không quan trọng. Đây không phải là phần quan trọng nhất trong ứng dụng nên không phải là nơi các chuyên gia giỏi nhất nên tập trung và thậm chí phải rõ ràng bên ngoài mã nguồn chính, nó có thể được cài đặt với một công cụ quản lý sự phụ thuộc (dependency management tool).

KẾT LUẬN

Các khái niệm DDD tôi đã chọn để tiếp cận ở đây là, một lần nữa, chủ yếu về single responsibility, low coupling, high cohesion, isolating logic để ứng dụng của chúng ta trở nên nhất quán, dễ dàng và nhanh chóng hơn để thay đổi và thích ứng với nhu cầu của doanh nghiệp.

SOURCES

1992 – Ivar Jacobson – Object-Oriented Software Engineering: A use case driven approach

1996 – Robert C. Martin – Granularity

2003 – Eric Evans – Domain-Driven Design: Tackling Complexity in the Heart of Software

2014 – Eric Evans – Domain-Driven Design Reference

Đây là bài viết trong loạt bài viết về “Tổng quan về sự phát triển của kiến trúc phần mềm“. Đây là loạt bài viết chủ yếu giới thiệu về một số mô hình kiến trúc phần mềm hay nói đúng hơn là sự phát triển của chúng qua từng giai đoạn, qua đó giúp chúng ta có cái nhìn tổng quát, up-to-date và là roadmap để bắt đầu hành trình chinh phục (đào sâu) thế giới của những bản thiết kế với vai trò là những kỹ sư và kiến trúc sư phần mềm đam mê với nghề.

Bài viết được tham khảo từ:

Domain-Driven Design

Tổng hợp bởi edwardthienhoang

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Nên học lập trình web cơ bản ở nhà hay ở trung tâm?

Nên học lập trình web cơ bản ở nhà hay ở trung tâm?

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

Học lập trình web cơ bản ở đâu?

Với mỗi sinh viên IT, việc đưa ra quyết đưa ra quyết định học lập trình web ở đâu là một lựa chọn ảnh hưởng lớn tới tương lai sau này. Tại các trường đại học ở Việt Nam, lập trình web thường chỉ dừng ở mức cơ bản, đủ để sinh viên nắm bắt được lý thuyết chứ chưa thực sự làm chủ được các ngôn ngữ lập trình, chưa được trực tiếp làm các công việc thực tế. Cho nên việc lựa chọn giữa tự học hay đi học ở các trung tâm làm cho các bạn sinh viên hết sức băn khoăn. Đây cũng là câu hỏi thường gặp ở nhiều diễn đàn IT và bây giờ, chúng ta hãy cùng đi tìm lời giải cho bài toán này.

Có rất nhiều ý kiến được đưa ra:

Nên đi học ở trung tâm

Nếu bạn chưa quen với việc lập trình thì mình khuyên bạn nên tìm một trung tâm uy tín để học. Dưới đây là một số lý do bạn cần đến trung tâm để học:

Thứ nhất: Học ở trung tâm bạn sẽ được dạy theo một bộ giáo trình nhất định từ cơ bản đến nâng cao. Khi đó bạn sẽ có được những kiến thức cơ bản nhất và tránh được việc bị hổng kiến thức.

Thứ hai: Bạn sẽ có một môi trường làm việc nhóm nơi bạn có thể trao đổi thông tin và rèn luyện kỹ năng một cách tốt nhất. Bạn sẽ không mất nhiều công sức để tìm lời giải thích cho một lỗi sai nào đó hay để tìm tài liệu cho những vấn đề bạn quan tâm.

Thứ ba: Bạn sẽ được làm việc với những người có nhiều kinh nghiệm và được chia sẻ nhiều kiến thức thực tế bổ ích có liên quan trực tiếp tới công việc của bạn sau này.

Thứ tư: Nhiều trung tâm sẽ cấp chứng chỉ sau các khóa học, điều này sẽ góp phần làm đẹp thêm cho bộ hồ sơ xin việc của bạn sau này.

Sau khi có đầy đủ kiến thức về lập trình, các bạn có thể đọc thêm các Ebook hay xem các video trên mạng, tham gia các nhóm lập trình trên Facebook. Bạn hoàn toàn có thể tạo một trang web riêng và code từ một trình soạn thảo, như vậy kỹ năng của bạn sẽ được cải thiện rất nhiều.

  25 thuật ngữ bạn nhất định phải biết khi lập trình web
  8 tools cần có để tăng workflow khi lập trình web

Tự học

Khi bạn đã biết lập trình web cơ bản, bạn làm chủ được kiến thức của mình và đã có một ngôn ngữ lập trình riêng để theo đuổi thì cũng không cần thiết phải tốn tiền vào các trung tâm. Điều quan trọng là bạn cần có khả năng tự học, tự mày mò hoặc đã có chỗ để thực tập trong thời gian còn ngồi trên ghế nhà trường. Một kế hoạch cụ thể sữ giúp cho bạn có được hiệu quả tốt nhất trong việc học.

Đầu tiên bạn cần xác định loại ngôn ngữ bạn sẽ học. Theo mình, nên bắt đầu từ ngôn ngữ R hay Python bởi nó sẽ giúp ích cho ngành học của bạn hơn. Còn nếu bạn muốn làm được nhiều việc thì nên chọn C#. Tiếp đến là Java, PHP hay Ruby tùy theo sở thích của bạn. Sau khi có được kiến thức lập trình cơ bản, bạn cần dành nhiều thời gian cho việc thực hành để tránh các lỗi và để nắm vững ngôn ngữ đó.

Một việc nữa các bạn cần lưu ý khi tự học là cân đối giữa việc học ở trường và việc tự học của bạn. Cần xem trước kế hoạch học ở trường (bạn đang học ngôn ngữ nào, bạn sắp học ngôn ngữ nào,…) để có sự chuẩn bị tốt nhất cho việc tự học. Bên cạnh đó, nếu bạn có một người nhiều kinh nghiệm hướng dẫn là tốt nhất. Đôi khi chúng ta không chạy được chương trình và không biết lỗi ở đâu nên rất cần một người có kinh nghiệm để thường xuyên trao đổi.

Khi bạn đã thành thạo một ngôn ngữ, bạn có thể làm một số công việc thực tế để tích lũy kinh nghiệm đồng thời tìm hiểu sâu hơn về ngôn ngữ đó. Nếu bạn làm tốt thì chỉ cần 2 đến 5 tháng là sử dụng nhuần nhuyễn một ngôn ngữ. Sau đó, bạn có thể chuyển sang một ngôn ngữ mới nhưng đừng quên thường xuyên ôn lại những kiến thức và kỹ năng đã học nếu không bạn sẽ rất chóng quên.

Học theo cách này rất tốn thời gian, không dành cho những người dễ nản chí. Bạn có thể lên rất nhiều kế hoạch nhưng lại không thực hiện được bởi nhiều lý do cho nên hãy cân nhắc kỹ trước khi lựa chọn phương án tự học.

Tự học online

Học lập trình online cũng có rất nhiều chuyên gia truyền đạt lại các kinh nghiệm thực tế. Đây là cách bạn có thể lựa chọn để tiết kiệm cả thời gian và chi phí so với hai cách nêu trên. Các kênh mà bạn có thể lựa chọn là học qua Skype, học qua facebook (những người chuyên viết blog về lập trình, học qua các nhóm,…), học lập trình web từ các diễn đàn,… Ngoài ra bạn có thể đăng ký các khóa học trực tuyến qua các trang imicrosoft.edu.vn; standford.com.vn; itech.edu.vn;…

Dù học theo cách nào thì tự học và tự thực hành là những kỹ năng cần thiết nhất đối với một lập trình viên. Và thêm một lời khuyên cho các bạn sinh viên là nên đi làm thêm vào năm 2 hoặc năm 3 là tốt nhất. Nếu đã có kinh nghiệm, rất dễ để các bạn có được một vị trí “ngon lành” khi mới ra trường.

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Internet vạn vật (IoT) hoạt động như thế nào?

Internet vạn vật (IoT) hoạt động như thế nào?

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

Internet vạn vật – Internet of things hay còn được biết đến với cái tên Internet of Everything (IoE), là tập hợp của tất cả các thiết bị có thể kết nối với các trang web, cho phép thu thập, gửi và xử lý thông tin ở môi trường xung quanh chúng. Các thiết bị này được tích hợp với các bộ cảm biến, bộ xử lý của máy tính và những phần mềm có thể tương tác với nhau. Các nhà khoa học gọi chúng là những thiết bị “được kết nối” hay những thiết bị “thông minh” (connected or smart devices). Dữ liệu từ “smart devices” được truyền tới các thiết bị khác tạo thành một quá trình được gọi là M2M (machine-to-machine).

  1001 Tips: Con trỏ và hàm (Pointer & Function) trong C++
  Cấu hình JSP views trong InternalResourceViewResolver với Spring Boot JAR file

Các thuật ngữ như Internet of Things hay M2M có lẽ không quá xa lạ với dân IT. Có rất nhiều cách định nghĩa về IoT và trên thực tế đây là một cụm từ khá trừu tượng nên không ít người không biết IoT hoạt động thế nào. Chúng ta hãy cùng đi tìm lời giải đáp.

Ban đầu, các chuyên gia sẽ  tương tác với các tiện ích để cài đặt những thiết bị IoT, cung cấp cho các thiết bị đó những hướng dẫn, cách lấy dữ liệu. Các thiết bị sẽ tự hoạt động trong hầu hết các khâu mà không cần tới sự can thiệp của con người. Chẳng hạn một thiết bị thu thập dữ liệu về thời tiết, các chuyên gia sẽ cài đặt làm sao để chúng tự cập nhật được nhiệt độ, độ ẩm, áp suất,… mà người dùng không phải thực hiện bất cứ một thao tác nào khác. Chúng ta chỉ cần bật điện thoại, ấn vào icon là các thông số về thời tiết sẽ hiện ngay ra màn hình.

Sự xuất hiện của những chiếc điện thoại thông minh cũng như mạng lưới internet trực tuyến đã dẫn đến sự ra đời của IoT. IoT tạo ra một lượng dữ liệu lớn trên Internet bao gồm các dữ liệu dùng để tạo ra các thiết bị hữu ích và cả phần lưu lượng được dùng cho những mục đích khác. Công nghệ này cho phép chúng ta tạo ra khối lượng thông tin không lồ trên thời gian thực tế, điều mà các nhà khoa học chưa từng làm được trước đó. Các bạn hoàn toàn có thể yên tâm khi ra khỏi nhà với những thiết bị giám sát; những tổ chức, doanh nghiệp sẽ cải thiện được quy trình làm việc, từ đó nâng cao sản lượng, tiết kiệm chi phí và giảm đáng kể các khoảng thời gian chết. Các thiết bị cảm biến trong thành phố có thể giúp giảm bớt tình trạng tắc nghẽn giao thông và cảnh báo chúng ta khi các công trình có nguy cơ sụp đổ. Hơn thế nữa, các thiết bị còn giúp chúng ta thay đổi điều kiện môi trường, cảnh báo về những thảm họa sắp xảy ra.

Ngày nay, IoT đã xuất hiện ở mọi nơi trên thế giới và chúng ta hoàn toàn có thể đem công nghệ này áp dụng cho các dự án của mình. Bất kể bạn đang hoạt động trong lĩnh vực nào, đang gặp khó khăn gì thì IoT sẵn sàng giúp bạn giải quyết mọi thứ.

 Phạm Bình (Theo howstuffworks.com)

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Mô hình MVC (Model-View-Controller) là gì?

kiến trúc MVC (Model-View-Controller)

MVC được hình thành bởi các nghiên cứu của Trygve Reenskaug vào khoảng các năm 1978-1979. Sau đó nó được điều chỉnh và được cài đặt lần đầu tiên vào các lớp của thư viện Xerox PARC Smalltalk-80. Mô hình MVC cổ điển hiện tại ít được sử dụng trong môi trường lập trình desktop như trước đây nhưng hiện tại nó vẫn được sử dụng cực kì rộng rãi như là kiến trúc cơ bản trong các môi trường lập trình web.

Tổng quan mô hình MVC

Tổng quan mô hình MVC
Tổng quan mô hình MVC

MVC là gì?

Mô hình MVC – Model-View-Controller là phương pháp chia nhỏ các các thành phần dữ liệu (data), trình bày (output) và dữ liệu nhập từ người dùng (input) thành những thành phần riêng biệt.

MVC hoạt động như thế nào?

Thông thường, chúng ta biết rằng mô hình MVC gồm 3 thành phần: Model, View và Controller

  • View – Nơi người dùng tương tác.
  • Controller – Xử lý yêu cầu và gửi phản hồi đến view.
  • Model – Middleware xử lý các thao tác cơ sở dữ liệu.
MVC hoạt động như thế nào?
Mô hình MVC hoạt động như thế nào?

View

Về cơ bản, View đại diện cho cách dữ liệu được trình bày trong ứng dụng (UI). Các view được tạo ra dựa trên dữ liệu thu thập từ model. Bằng cách yêu cầu thông tin từ model, sau đó sẽ trả kết quả tới người dùng. Ngoài việc hiển thị dữ liệu từ các biểu đồ, sơ đồ và bảng, view còn hiển thị dữ liệu từ các nguồn khác. Tất cả các thành phần giao diện người dùng, chẳng hạn như hộp văn bản, menu thả xuống, v.v., sẽ xuất hiện trong bất kỳ view nào của khách hàng.

Controller

Controller là thành phần xử lý tương tác của người dùng. Dữ liệu đầu vào của người dùng được controller phân tích và xử lí, khi người dùng thao tác bất kì với hệ thống controller sẽ gửi thông tin đến model để xử lí và sau đó trả về kết quả view

Người dùng -tương tác-> Controller => Model => View (output) -trả kết quả-> Người dùng

Bằng cách giao tiếp với view liên quan của controller, người dùng có thể thay đổi giao diện của view (ví dụ: cuộn qua một tài liệu) và cập nhật trạng thái của model liên quan (ví dụ: lưu một tài liệu).

Model

Model là nơi lưu trữ dữ liệu và logic. Ví dụ, khi Controller truy xuất thông tin khách hàng từ cơ sở dữ liệu, dữ liệu được chuyển đổi giữa các thành phần controller hoặc giữa các yếu tố logic nghiệp vụ. Nó thao tác dữ liệu và gửi lại cơ sở dữ liệu, hoặc được sử dụng để hiển thị thông tin tương tự.

Ngoài ra, nó phản hồi các yêu cầu từ view và có các chỉ thị từ controller cho phép nó tự cập nhật. Nó cũng là mức thấp nhất của mô hình chịu trách nhiệm duy trì dữ liệu.

Ứng tuyển ngay các vị trí PHP tuyển dụng mới nhất trên TopDev

Ví dụ thực tiễn về mô hình MVC

Khi bạn đến quán trà đá và gọi 1 cốc trà đá. Lúc này:

  • Bạn là “người dùng” và “cốc trà đá” là “yêu cầu từ phía người dùng”.
  • Đối với bà chủ quán, cốc trà đá là một quy trình gồm các bước:
    • Lấy cái cốc
    • Cho đá vào
    • Cho trà vào
    • Cho thêm nước lọc cho trà loãng bớt
    • Khuấy đều lên
    • Đưa cốc trà cho bạn
    • Thanh toán

Bộ não của bà chủ quán lúc này đóng vai trò Controller. Kể từ thời điểm bạn nói “một cốc trà đá” bằng tiếng Việt và bà chủ quán hiểu được, công việc bắt đầu. Trà đá, nước mía hay cocktail thì cũng như nhau, nhưng nguyên liệu thì hoàn toàn khác biệt. Bà chủ quán chỉ có thể sử dụng những công cụ và nguyên liệu của quán, và những công cụ đó sẽ đóng vai trò Model, bao gồm:

  • Đôi tay của bà chủ
  • Các nguyên liệu pha chế (trà, nước …)
  • Đá lạnh
  • Bia, nước ngọt, thuốc lá…
  • Chanh, sấu, gừng…
  • Các ly cốc để đựng đồ uống

Những nguyên liệu và công cụ này, thông qua một loạt các bước, đã trở thành cốc trà đá mát lạnh đến tay bạn. Cốc trà đá lúc này đóng vai trò View. “View” được làm nên từ những công cụ, nguyên liệu trong “Model”, được chế biến và bàn giao tới tay bạn thông qua “Controller” (chính là bộ não của bà chủ quán).

>> Xem thêm: Mô hình MVC trong php là gì? Hướng dẫn chi tiết

Ưu điểm của Kiến trúc MVC

Mô hình MVC ra đời giúp các lập trình viên giải quyết nhiều vấn đề lúc bấy giờ, cùng điểm qua các ưu điểm của MVC:

  • Mã nguồn dễ bảo trì và mở rộng: Kiến trúc MVC giúp mã nguồn dễ bảo trì, có thể mở rộng và phát triển một cách dễ dàng.
  • Các thành phần có thể kiểm tra độc lập: Các thành phần của nó có thể được kiểm tra riêng biệt từ các thành phần người dùng.
  • Hỗ trợ các loại khách hàng mới dễ dàng hơn: Kiến trúc này hỗ trợ việc thêm các loại khách hàng mới một cách dễ dàng.
  • Phát triển song song các thành phần: Có thể phân chia phát triển các thành phần khác nhau một cách song song.
  • Giảm bớt độ phức tạp: Bằng cách chia ứng dụng thành ba phần, bạn có thể tránh được sự phức tạp.
  • Sử dụng mô hình Front Controller: Chỉ sử dụng mô hình Front Controller, trong đó mỗi yêu cầu được xử lý bởi một bộ điều khiển duy nhất.
  • Hỗ trợ phát triển theo hướng kiểm thử tối đa: Hỗ trợ phát triển theo hướng kiểm thử (TDD) một cách tối đa.
  • Dễ dàng cho các nhóm phát triển lớn: Một nhóm lớn các nhà thiết kế và phát triển web có thể tạo và duy trì các ứng dụng web với nó.
  • Kiểm tra từng lớp và đối tượng riêng biệt: Bạn có thể kiểm tra tất cả các lớp và đối tượng riêng lẻ vì chúng đều độc lập với nhau.
  • Hành động của bộ điều khiển có thể nhóm logic: Các hành động của bộ điều khiển có thể được nhóm lại một cách logic bằng cách sử dụng các mẫu thiết kế MVC.

Nhược điểm của Kiến trúc MVC

Bên cạnh ưu điểm MVC cũng tồn tại một số nhược điểm:

  • Khó đọc, thay đổi và kiểm thử đơn vị: Khó đọc, thay đổi và kiểm thử đơn vị mô hình này vì không có thành phần riêng biệt để xử lý giao diện người dùng (UI). Tất cả phải được thực hiện trên lớp view. Vì vậy, cần phải phụ thuộc vào một framework khác để làm điều đó.
  • Không hỗ trợ xác thực chính thức: Kiến trúc MVC không cung cấp hỗ trợ xác thực chính thức. Vì vậy, việc xác thực cần phải được thực hiện rõ ràng.
  • Xử lý dữ liệu có thể không hiệu quả: Có thể dẫn đến quá trình xử lý dữ liệu không hiệu quả vì nó làm cho logic thực thi trở nên phức tạp.
  • Khó sử dụng với giao diện người dùng hiện nay: Việc sử dụng MVC khó khăn với giao diện người dùng hiện đại. Hầu hết các framework UI phổ biến hiện nay đều có kiến trúc triển khai riêng của chúng và không thể nhúng vào MVC.
  • Cần duy trì nhiều mã trong bộ điều khiển: Cần duy trì nhiều mã trong các bộ điều khiển, gần như cho mỗi hành động khác nhau trên trang, chúng ta cần khai báo các phương thức Action riêng biệt.

Nhầm lẫn về MVC

Một nhầm lẫn thường gặp trong quan hệ giữa các thành phần MVC là khi xem mục đích của Controller như là thành phần trung gian để tách rời View khỏi Model. Trong khi thực tế, kiến trúc MVC tách rời dữ liệu và xử lý trung tâm khỏi phần trình bày thông qua cơ chế là Observer Pattern chứ không phải Controller. Nhiệm vụ của Controller là cần nối giữa người dùng là ứng dụng, không phải giữa View và Model.

Trong khi mục đích chính của MVC là tách rời trình bày và các xử lý bên trong. Việc phân rõ vai trò xử lý ouput (View) và input (Controller) là một hệ quả nhằm hoàn thiện cơ chế cho ý tưởng trên. Hiện nay trong nhiều môi trường lập trình hiện đại, nhiều control được cung cấp và hoàn thiện hơn (nhiều xử lý sự kiện cơ bản đã được hỗ trợ sẵn) so với trước đây nên việc khoáng tất cả các xử lý sự kiện cho một thành phần độc lập như Controller không còn là vấn đề quan trọng nữa.

Trong khi đó, những cơ chế như Observer Pattern cũng có vấn đề của riêng chúng. Trong khi được dùng như phương tiện hiệu quả để loại bỏ sự phụ thuộc của Model vào các thành phần khác, nó có một vấn đề lớn là tại một thời điểm, chúng ta khó có thể xác định điều gì sẽ xảy ra bằng cách đọc code và việc thực hiện các testing cũng khó khăn hơn. Hơn nữa, do Model chỉ liên kết gián tiếp đến View thông qua Observer Pattern, khi sự thay đổi trạng thái của Model cần đến một vài thao tác xử lý phức tạp để áp dụng lên giao diện thì với mô hình cổ điển sẽ gặp khó khăn.

Một vấn đề khác là chúng ta cần lưu trữ tình trạng hiện tại của UI (UI state), ví dụ trong danh sách sinh viên thì chúng ta cần biết sinh viên nào đang được chọn. Trong khi thành phần UI nắm giữ dữ liệu trình bày đang được chọn thì dữ liệu sinh viên thuộc về Model, như vậy dữ liệu về sinh viên được chọn sẽ được lưu trữ ở đâu khi cần truy xuất đến?

Vì những lí do trên, MVC sau này đã có những thay đổi và bổ sung nhất định (như khái niệm Application Model). Kiến trúc MVP chúng ta sẽ bàn dưới đây cũng dựa trên tư tưởng cơ bản của MVC nhưng với cách tiếp cận khác nhằm mục đích khắc phục các khuyết điểm đã có.

Tài liệu tham khảo: https://www.interviewbit.com/blog/mvc-architecture/

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Todo List: Kiểm tra dữ liệu với Laravel Validation

Todo List: Kiểm tra dữ liệu với Laravel Validation

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

Video trong bài viết

Ứng dụng Todo List đã gần thành hình với các chức năng tạo bản ghi mới, xem danh sách bản ghi và xem chi tiết từng bản ghi. Tiếp theo, trong bài học hôm nay chúng ta cùng tìm hiểu về một công việc rất quan trọng: Kiểm tra dữ liệu. Tại sao kiểm tra dữ liệu quan trọng? chúng ta cùng tìm hiểu nhé.

  Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream
  Cách sử dụng Laravel với Socket.IO

Kiểm tra dữ liệu

Tại sao cần kiểm tra dữ liệu

Người dùng không hề biết phải nhập dữ liệu như thế nào vào ứng dụng, đặc biệt khi mới lần đầu sử dụng và như vậy việc nhập liệu có thể dẫn đến lỗi ứng dụng. Hơn nữa, dữ liệu là một tài nguyên quan trọng trong ứng dụng, muốn khai thác tài nguyên này chúng ta cần có tài nguyên được sắp xếp và phân loại một cách rõ ràng. Ví dụ như một ô nhập liệu về ngày sinh, có rất nhiều các giá trị như 01/02/1983, 19830201, Feb 01 1983, Ngày 01 tháng 02 năm 1983… tuy nhiên, máy tính lại không thể hiểu tất cả các kiểu nhập liệu này, đặc biệt khi khai thác dữ liệu chúng ta rất dễ bị nhầm lẫn, vậy nên việc kiểm soát dữ liệu và bắt người dùng thực hiện các quy tắc nhập liệu ngay từ đầu là một việc rất quan trọng.

Tham khảo thêm tuyển dụng Laravel hấp dẫn cho bạn

Kiểm tra dữ liệu với Laravel Validation

Các controller trong Laravel đều được kế thừa từ class App\Http\Controllers\Controller, mà class này sử dụng trait ValidateRequests cho phép chúng ta kiểm tra dữ liệu thông qua gọi phương thức validate().

Trở lại đoạn code lưu dữ liệu xuống database trong bài trước, chúng ta cần kiểm tra dữ liệu trước khi thực hiện lưu dữ liệu, cùng xem đoạn code thực hiện như sau:

public function store()
{
    $this->validate(request(), [
        'name' => 'required|min:6|max:12',
        'description' => 'required'
    ]);

    $data = request()->all();

    $todo = new Todo();
    $todo->name = $data['name'];
    $todo->description = $data['description'];
    $todo->completed = false;

    $todo->save();
    return redirect('/todos');
}

validate() có hai tham số:

  • Tham số thứ nhất là đối tượng request chứa thông tin về dữ liệu được gửi lên từ người dùng.
  • Tham số thứ hai là một mảng các quy tắc kiểm tra cho từng trường nhập liệu có trong request.
    • Trường name: bắt buộc nhập, tối thiểu 6 ký tự, tối đa 12 ký tự.
    • Trường description: bắt buộc nhập.

Như vậy chúng ta đã thực hiện kiểm tra dữ liệu trước khi lưu trữ xuống database. Tuy nhiên nếu chúng ta thực hiện nhập liệu sai thì không thấy ứng dụng có phản hồi gì cả, đó là do chúng ta chưa hiển thị thông báo lỗi nhập liệu ra ứng dụng.

Hiển thị thông báo lỗi kiểm tra dữ liệu

Mặc định trong Laravel, khi kiểm tra dữ liệu gặp lỗi, hệ thống tự động truyền đến trang (view) hiện tại một biến là $errors. Biến này chứa thông tin về các lỗi xảy ra khi kiểm tra dữ liệu.

Trong view hiện hành, ở đây là create.blade.php, để hiển thị các lỗi kiểm tra dữ liệu nếu có, chúng ta sử dụng một vòng lặp và hiển thị các lỗi là một thành phần của danh sách:

@if($errors->any())
    <div class="alert alert-danger">
        <ul class="list-group">
            @foreach($errors->all() as $error)
                <li class="list-group-item">
                    {{ $error }}
                </li>
            @endforeach
        </ul>
    </div>
@endif

Phần hiển thị lỗi này sẽ đặt phía trên form nhập liệu, như vậy nội dung của create.blade.php sau khi hoàn thành như sau:

@extends('layouts.app')

@section('content')
    <h1 class="text-center my-5">Create Todos</h1>

    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card card-default">
                <div class="card-header">Create new todo</div>
                <div class="card-body">
                    @if($errors->any())
                        <div class="alert alert-danger">
                            <ul class="list-group">
                                @foreach($errors->all() as $error)
                                    <li class="list-group-item">
                                        {{ $error }}
                                    </li>
                                @endforeach
                            </ul>
                        </div>
                    @endif
                    <form action="/store-todos" method="POST">
                        @csrf
                        <div class="form-group">
                            <input type="text" class="form-control" placeholder="Name" name="name">
                        </div>
                        <div class="form-group">
                            <textarea name="description" placeholder="Description" cols="5" rows="5" class="form-control"></textarea>
                        </div>
                        <div class="form-group text-center">
                            <button type="submit" class="btn btn-success">Create Todo</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
@endsection

Quay lại ứng dụng Todo List của chúng ta, tạo thử một todo với tên để trống xem thế nào:

Thông báo lỗi kiểm tra dữ liệu

Các lỗi đã hiển thị cho người dùng, như vậy người dùng luôn biết được trạng thái hiện tại của ứng dụng.

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

[Thảo Luận] Ngành IT liệu có hết “HOT” trong vài năm tới?

Ngành IT liệu có hết “HOT” trong vài năm tới?

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

Chào các bạn, một vài hôm trước mình có đọc được một bài báo thống kê rằng năm học 2021 ngành IT nói riêng và khối ngành công nghệ kỹ thuật nói chung là một trong những ngành có tỷ lệ chọi cao nhất (khoảng 1/64).

Nói đến đây đủ để thấy rằng trong những năm trở lại đây thì ngành công nghệ thông tin đã trở nên “hot” như thế nào!

Một câu hỏi đặt ra là liệu với số lượng lớn học sinh có ý định theo học ngành IT như vậy thì trong một vài năm tới, ngành IT có còn “hot” như vậy được nữa hay không? Liệu nhân lực ngành này có bị bão hòa hay không?

  "Ngành IT này học rất dễ, tài liệu ko bao giờ thiếu. Quan trọng là phải có đam mê và chịu cày"
  10 Add-on Google Sheets phải có dành cho các Recruiters

Trong bài viết này mình sẽ chia sẻ một vài quan điểm của cá nhân mình dưới cái nhìn của một người đang học và làm về IT về hai câu hỏi trên.

#1. Nhiều người  đang “ảo tưởng” về ngành IT

Có lẽ những câu chuyện về sinh viên IT mới ra trường làm lương 2000 – 3000$ một tháng, hay những bài báo giật tít về mức thu nhập “khủng” của dân IT đã khiến nhiều người ảo tưởng về ngành này.

nganh-it-lieu-co-het-hot-trong-vai-nam-toi (1)

Điều này cũng dễ hiểu thôi vì người dân Việt Nam mình thường có xu hướng làm theo đám đông. Nhiều người không chịu bỏ thời gian tìm hiểu gốc rễ vấn đề mà luôn tin vào hiệu ứng đám đông, ủng hộ số đông. Sợ thật ◔◡◔

Là một người học và làm IT thì mình hiểu rất rõ về vấn đề thu nhập, để đạt được mức lương ngàn đô hay mấy chục triệu một tháng khi mới ra trường (như lời đồn) là điều rất khó, thực sự khó.

Trừ khi bạn là người có năng lực thực sự, kiểu mấy ngàn người mới có một người ấy. Mà các bạn biết đấy, con số này rất ít.

Và mình tin là có rất nhiều bạn học sinh khi đăng ký học công nghệ thông tin thì ít nhiều đã bị tác động bởi những con số có chút “ảo” này, mà không chịu tìm hiểu kỹ càng.

Bạn nên nhớ là, bạn nên theo đuổi những cái mà bạn thực sự thích và muốn tìm hiểu. Giỏi thì nghề nào cũng giàu thôi. Và có như vậy mới bền lâu được !

Nhưng cũng chẳng thể trách được, vì công tác hướng nghiệp của Việt Nam chúng ta còn quá yếu kém, trong khi báo chí thì lại thường xuyên thổi phồng dẫn đến nhiều bạn học sinh bị “ảo tưởng” và hệ lụy là tỷ lệ chọi cao ngất ngưởng.

Quay lại với câu hỏi thì “ảo tưởng” như thế thì có ảnh hưởng gì đến độ “hot” của ngành này trong vài năm tới hay không?

Các bạn cứ hình dung thử xem, bạn bị lừa thì cùng lắm là chỉ bị lừa một lần thôi chớ. Ví dụ như trong nhà bạn có ông anh làm IT lương tháng cũng 9 – 10 triệu, trong khi đó phải làm sờ mờ lờ (sấp mặt luôn) suốt ngày…

Thử hỏi nếu bạn định đăng ký học IT thì ông anh bạn có nói như báo đài nói hay không? Có thể ông ý vẫn bảo bạn thích thì học, lương cũng không tệ nhưng chắc chắn là bạn sẽ phải suy nghĩ lại một cách thấu đáo hơn.

Tham khảo các vị trí HOT trong IT:

#2. Số lượng không đi đôi với chất lượng

Số lượng và chất lượng là những con số thường tỷ lệ nghịch đối với lao động Việt Nam.

nganh-it-lieu-co-het-hot-trong-vai-nam-toi (1)

Mình không hề có ý chê bai chất lượng lao động của người Việt Nam thấp, nhưng thực tế cho thấy, với các ngành đòi hỏi phải làm việc trí óc nhiều như làm IT thì hiệu suất làm việc của chúng ta rất thấp, đây là một sự thật mà chúng ta cần phải nhìn nhận để cố gắng hơn.

Khi mà số lượng rất lớn người đổ xô đi học công nghệ thông tin nhưng chất lượng đào tạo lại chưa được tốt, đào tạo ra nhưng không làm được việc, vẫn nặng về lý thuyết, thiếu thực hành, thiếu tính thực tế…

Mà các bạn biết đó, bất cứ thị trường nào cũng có tính đào thải, đặc biệt là những thị trường lao động đòi hỏi trí óc. Mà với dân IT, 90% kiến thức là tự học, công nghệ thay đổi theo từng giờ..

Nhà tuyển dụng họ sẵn sàng loại đi 9 trong 10 người, chỉ để lấy một người có năng lực thực sự, phù hợp và đáp ứng được công việc mà họ cần.

Điều này đồng nghĩa với việc nhiều người nghĩ cứ học công nghệ thông tin ra trường kiểu gì cũng có việc là hoàn toàn sai lầm. Việc thì có đấy, nhưng quan trọng bạn kiếm được bao nhiêu tiền..

Bạn phải hiểu rằng, số lượng càng nhiều thì mức độ cạnh tranh càng khốc liệt, mà cạnh tranh càng khốc liệt thì mức độ đào thải càng cao.

Và đến một thời điểm, mức độ đào thải đủ để cân bằng cung và cầu về nhân lực ngành IT thì lúc đó ngành IT sẽ không còn “hot” như hiện tại nữa.

#3. Sự phát triển của nhiều ngành nghề mới

Nếu bạn để ý thì có thể thấy ngành IT bây giờ cũng giống như ngành kế toán ngày trước. Từng có thời kỳ rất nhiều người đổ xô đi học kế toán, ngân hàng… các thứ !

nganh-it-lieu-co-het-hot-trong-vai-nam-toi (2)

Ngành IT nói riêng và các khối ngành kỹ thuật nói chung chịu ảnh hưởng tích cực từ cuộc cách mạng 4.0 nên nhu cầu nhân lực chất lượng đang rất hiếm.

Nhưng rõ ràng, cuộc cách mạng 4.0 không chỉ ảnh hưởng đến lĩnh vực công nghệ không đâu, mà nó còn tác động rất nhiều đến các ngành nghề khác nữa.

Nhiều ngành nghề phát triển rất nhanh nhờ áp dụng chính thành quả của các sáng tạo công nghệ. Ví dụ như bán hàng online, Maketing online, nuôi trồng áp dụng công nghệ cao…

Bất cứ ngành nghề nào cũng có thể áp dụng các tiến bộ khoa học kỹ thuật được. Nhưng điều đó không có nghĩa là những ngành nghề đó phụ thuộc vào công nghệ.

Công nghệ mở ra một hướng đi mới để từ đó người ta phát triển ngành đó theo một hướng đi mới. Tốt hơn và hiệu quả hơn !

Và tất nhiên, như một điều tất yếu thì người lao động sẽ có thêm những lựa chọn mới, những hướng đi mới cho những gì họ vẫn đang làm.

Cá nhân mình thấy thì đây chính là nguyên nhân có tác động mạnh mẽ nhất đến mức độ “hot” của ngành IT trong vài năm tới, vì theo thống kê có một số lượng lớn người học IT sau khi học xong ra trường lại không làm IT.

Họ chuyển hướng sang làm những công việc không liên quan trực tiếp đến công nghệ, hay nói cách khác là họ chuyển hướng sang làm các công việc có ứng dụng công nghệ. Hai cái này khác nhau nhé, các bạn đừng nhầm lẫn !

#4. Lời kết

Tóm lại, theo nhận định của cá nhân mình thì trong một vài năm tới, ngành IT vẫn sẽ rất “hot” bởi vì thị trường lao động này chưa đạt đến độ bão hòa về số lượng, trong khi chất lượng chưa thay đổi nhiều.

Nhưng sau một vài năm nữa mình tin chắc mức độ “hot” sẽ giảm dần do có nhiều ngành nghề mới ra đời.

Và thực tế đã co thấy, làm việc trong ngành IT không phải “ngồi mát ăn bát vàng” như nhiều người vẫn nghĩ. Đến một lúc nào đó, nhiều người nhận ra được sự thật này thì họ sẽ tự động thay đổi quan điểm và ngành IT sẽ hết “hot” mà thôi.

Mình chỉ có một lời nhắn nhủ chân thành như thếnày: Nếu các bạn thực sự đam mê với ngành IT, một lĩnh vực nào đó trong ngành IT thì wellcome, bạn nên tham gia ngay. Còn nếu bạn chỉ học IT vì bạn đọc đâu đó, hoặc nghe đâu đó vì có lương cao thì nên cân nhắc lại.

Vâng, đó là những quan điểm của cá nhân mình, không biết các bạn nghĩ thế nào. Nếu có quan điểm gì khác thì hãy comment bên dưới nha. Xin chào và hẹn gặp lại các bạn trong các bài viết tiếp theo.

CTV: Nguyễn Đức CảnhBài viết gốc tại blogchiasekienthuc.com

Sử dụng Fanout Exchange trong RabbitMQ

Sử dụng Fanout Exchange trong RabbitMQ

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

Trong các bài viết trước, chúng ta đã cùng tìm hiểu về Direct Exchange. Trong bài này, tôi sẽ giới thiệu với các bạn một loại exchange khác là Fanout Exchange.

  RabbitMQ là gì? Hướng dẫn sử dụng RabbitMQ chi tiết
  Sử dụng binding Exchange to Exchange trong RabbitMQ

Flow của một Message trong Fanout Exchange

Fanout exchange (định tuyến broadcast – amq.fanout) định tuyến message (copy message) tới tất cả queue mà nó được bind, với bất kể một routing key nào. Giả sử, nếu nó N queue được bind bởi một Fanout exchange, khi một message mới published, exchange sẽ định tuyến message đó tới tất cả N queues. Fanout exchange được sử dụng cho định tuyến message broadcast (quảng bá).

Flow của một Message trong Fanout Exchange như sau:

  • Một Producer sẽ tạo một Message và publish tới Exchange.
  • Một hoặc nhiều Queue bind tới Fanout Exchange không cần thông tin routing key.
  • Một Message tới Exchange sẽ được chuyển tiếp đến tất cả các Queue mà không có bất kỳ điều kiện kiểm tra nào.

Exchange Fanout hữu ích với trường hợp ta cần một dữ liệu được gửi tới nhiều ứng dụng khác nhau với cùng một message nhưng cách xử lý ở mỗi ứng dụng là khác nhau.

Ví dụ Fanout Exchange trong RabbitMQ

Trong ví dụ này, tôi tạo một Fanout Exchange có tên GPCoderFanoutExchange, tạo 3 Queue binding tới Fanout Exchange này: QDeveloper, QManagerQGeneral.

Một số class của chương trình:

  • ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
  • FanoutExchangeChannel :  class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, publish/ subscribe message, …
  • Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue.
  • Producer: để gửi Message đến Exchange.
  • Consumer: để nhận Message từ Queue.
  • App: giả lập việc gửi nhận Message thông qua Fanout Exchange của RabbitMQ.

ConnectionManager.java

package com.gpcoder.fanoutexchange;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ConnectionManager {

private ConnectionManager() {
super();
}

public static Connection createConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
return factory.newConnection();
}
}

FanoutExchangeChannel.java

package com.gpcoder.fanoutexchange;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class FanoutExchangeChannel {

private String exchangeName;
private Channel channel;
private Connection connection;

public FanoutExchangeChannel(Connection connection, String exchangeName) throws IOException {
this.exchangeName = exchangeName;
this.connection = connection;
this.channel = connection.createChannel();
}

public void declareExchange() throws IOException {
// exchangeDeclare( exchange, builtinExchangeType, durable)
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true);
}

public void declareQueues(String ...queueNames) throws IOException {
for (String queueName : queueNames) {
// queueDeclare - (queueName, durable, exclusive, autoDelete, arguments)
channel.queueDeclare(queueName, true, false, false, null);
}
}

public void performQueueBinding(String queueName) throws IOException {
// Create bindings - (queue, exchange, routingKey)
channel.queueBind(queueName, exchangeName, "");
}

public void subscribeMessage(String queueName) throws IOException {
// basicConsume - ( queue, autoAck, deliverCallback, cancelCallback)
channel.basicConsume(queueName, true, ((consumerTag, message) -> {
System.out.println("[Received] [" + queueName + "]: " + new String(message.getBody()));
}), consumerTag -> {
System.out.println(consumerTag);
});
}

public void publishMessage(String message) throws IOException {
// basicPublish - ( exchange, routingKey, basicProperties, body)
System.out.println("[Send]: " + message);
channel.basicPublish(exchangeName, "", null, message.getBytes());
}
}

Constant.java

package com.gpcoder.fanoutexchange;

public final class Constant {

// Exchange

public static final String EXCHANGE_NAME = "GPCoderFanoutExchange";

// Queue

public static final String DEV_QUEUE_NAME = "QDeveloper";

public static final String MANAGER_QUEUE_NAME = "QManager";

public static final String GENERAL_QUEUE_NAME = "QGeneral";

private Constant() {
super();
}
}

Producer.java

package com.gpcoder.fanoutexchange;

import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import static com.gpcoder.fanoutexchange.Constant.*;

public class Producer {

private FanoutExchangeChannel channel;

public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();

// Create channel
channel = new FanoutExchangeChannel(connection, EXCHANGE_NAME);

// Create fanout exchange
channel.declareExchange();

// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, GENERAL_QUEUE_NAME);

// Binding queues without routing key
channel.performQueueBinding(DEV_QUEUE_NAME);
channel.performQueueBinding(MANAGER_QUEUE_NAME);
channel.performQueueBinding(GENERAL_QUEUE_NAME);
}

public void send(String message) throws IOException {
// Send message
channel.publishMessage(message);
}
}

Consumer.java

package com.gpcoder.fanoutexchange;

import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import static com.gpcoder.fanoutexchange.Constant.*;

public class Consumer {

private FanoutExchangeChannel channel;

public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();

// Create channel
channel = new FanoutExchangeChannel(connection, EXCHANGE_NAME);

// Create fanout exchange
channel.declareExchange();

// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, GENERAL_QUEUE_NAME);

// Binding queues without routing key
channel.performQueueBinding(DEV_QUEUE_NAME);
channel.performQueueBinding(MANAGER_QUEUE_NAME);
channel.performQueueBinding(GENERAL_QUEUE_NAME);
}

public void subscribe() throws IOException {
// Subscribe message
channel.subscribeMessage(DEV_QUEUE_NAME);
channel.subscribeMessage(MANAGER_QUEUE_NAME);
channel.subscribeMessage(GENERAL_QUEUE_NAME);
}

}

App.java

package com.gpcoder.fanoutexchange;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class App {

public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
// Create producer
Producer producer = new Producer();
producer.start();

// Send one message to exchange, it will be forwarded to all queues
producer.send("gpcoder message 1");

// Create consumer
Consumer consumer = new Consumer();
consumer.start();
consumer.subscribe();

TimeUnit.SECONDS.sleep(1);

// Try to send a second message
producer.send("gpcoder message 2");
}
}

Output chương trình:

[Send]: gpcoder message 1
[Received] [QDeveloper]: gpcoder message 1
[Received] [QManager]: gpcoder message 1
[Received] [QGeneral]: gpcoder message 1
[Send]: gpcoder message 2
[Received] [QGeneral]: gpcoder message 2
[Received] [QDeveloper]: gpcoder message 2
[Received] [QManager]: gpcoder message 2

Như bạn thấy, tất cả các Consumer đều nhận được Message.

Lưu ý: Chúng ta có thể sử dụng phương thức queueDeclare() để tạo một Queue non-durable, exclusive, autodelete và tên được tạo một cách ngẫu nhiên.

Tài liệu tham khảo:

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Các loại Client Types trong OAuth 2.0

Các loại Client Types trong OAuth 2.0

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

Trong OAuth 2.0, chúng ta có thể có nhiều loại Client Application khác nhau mà người dùng có thể sử dụng để access tới các resources của resource server. Ví dụ như chúng ta có native mobile application, web application bao gồm cả Single Page Application, console hay backend application, … Những Client Application này sẽ cần phải đăng ký với Authorization Server và sử dụng thông tin Client ID được cung cấp bởi Authorization Server để lấy access token. Một số grant type trong OAuth 2.0 đòi hỏi chúng ta phải cung cấp cả Client Secret, cái mà Client Application được Authorization Server cung cấp cùng với Client Id, ví dụ như grant type Client Credentials chẳng hạn.

  Giới thiệu về OAuth
  OAuth2 là gì? Tìm hiểu về OAuth2

Không phải tất cả các Client Application đều có thể lưu trữ Client Secret một cách bảo mật. Ví dụ như Native mobile application hay Single Page Application. Vì thế, OAuth 2.0 định nghĩa 2 loại Client Types khác nhau bao gồm: các confidential clients và các public clients.

Confidentials clients là những client có thể lưu trữ Client Secret bảo mật, ví dụ như chúng ta có những web application có cả backend và frontend thì Client Secret có thể lưu trữ ở backend side, không ai có thể lấy được thông tin này.

Ngược lại thì public clients là những client không thể đảm bảo việc lưu trữ Client Secret bảo mật, ví dụ như native mobile application hay Single Page Application, người dùng có thể decode hoặc view source code để xem thông tin Client Secret. Đối với những clients này, chúng ta cần sử dụng những grant type mà không cần Client Seret ví dụ như Authorization Code grant type với PKCE các bạn nhé!

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Tạo dự án app-template-lit-element-typescript với snowpack

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

Snowpack là một công cụ xây dựng để xây dựng môi trường phát triển front-end.

Snowpack cung cấp một bộ khởi động độc lập cho mỗi môi trường phát triển được gọi là Tạo ứng dụng Snowpack (CSA). Lần này, tôi sẽ giới thiệu quy trình tạo một Thành phần Web đơn giản bằng cách sử dụng app-template-lit-element là một trong Tạo ứng dụng Snowpack .

  3 bước tối ưu hiệu năng React App bằng các API mới của React
  3 tools giúp bạn tăng hiệu năng của React App một cách bất ngờ

Cài đặt app-template-lit-element-typescript

Để cài đặt app-template-lit-element, trước tiên hãy thực hiện lệnh sau trong bất kỳ thư mục nào.

npx create-snowpack-app [dirname] --template @snowpack/app-template-lit-element-typescript

Sau khi cài đặt xong, di chuyển đến bất kỳ thư mục nào npm startvà thực hiện lệnh sau .

cd [dirname]
npm start

Nếu bạn truy cập http://localhost:8080 và trình duyệt hiển thị bản chụp sau thì việc xây dựng môi trường đã thành công

Tạo component button

Ảnh động GIF của nút bật tắt chuyển đổi giữa BẬT và TẮT khi nút được nhấp

Custom element

Đầu tiên, tạo một tệp cho thành phần.

Template được tạo bởi app-template-lit-element-typescript đều trong folder “src/”

Hãy tạo src/Components/ToggleButton.ts và code sau.

import { customElement, LitElement, html } from 'lit-element';

@customElement('toggle-button')
export class ToggleButton extends LitElement {
  render() {
    return html`
      <button class="c-button">Button</button>
    `;
  }
}

LitElement là class thư viện để tạo WebComponents và sử dụng lit-html.

Tiếp theo, define custom element @customElement của LitElement <toggle-button>

Decorators chưa có sẵn trong JavaScript gốc, nhưng chúng có thể được sử dụng với các plugin của Babel

Bên cạnh việc xác định custom element, hãy xác định mẫu Shadow DOM cho component. Hãy render()triển khai một hàm của lớp LitElement . Call the lit-element htmlfunction in a tagged template literal so that the render()function returns the result.

  // ...
  render() {
    return html`
      <button class="c-button">ボタン</button>
    `;
  }
  // ...

Khi mô tả được hoàn thành cho đến nay, các phần tử của màn hình được hiển thị ./src/app-root.jsvà thành phần đã tạo được importđọc bởi một câu lệnh. Sau đó, ./src/app-root.jscác render()chỉ định nghĩa các chức năng <toggle-button>để mô tả.

import { customElement, property, LitElement, html, css } from 'lit-element';
import './Components/ToggleButton';

// ...
  render() {
    return html`
      <div class="wrapper">
        <h1>LitElement + Snowpack</h1>
        <p>Edit <code>src/app-root.js</code> and save to reload.</p>

        <toggle-button></toggle-button>

        <a
          class="link"
          href="https://lit-element.polymer-project.org/"
          target="_blank"
          rel="noopener noreferrer"
        > ${this.message}</a>
      </div>
    `;
  }
// ...

Nếu bạn thực thi mã cho đến nay trong trình duyệt, nó sẽ giống như hình chụp sau.

Chụp màn hình trình duyệt với thành phần nút bật tắt được thêm vào

Property

Tôi muốn chuyển đổi kiểu nút và văn bản được kích hoạt bởi sự kiện nhấp chuột, vì vậy tôi muốn xác định thuộc tính được đặt tên cho liên kết dữ liệu trong trình trang trí @propertypressed .

Trước hết, tải trình trang trí @property từ thư viện phần tử ánh sáng và viết mã sau.

import { customElement, property, LitElement, html } from 'lit-element';
@customElement('toggle-button')
export class ToggleButton extends LitElement {
  @property({
    type: Boolean,
    reflect: true
  })
  pressed = false;
  // ...
}

Với LitElement, bạn có thể tùy chỉnh hành vi của các thuộc tính, v.v. và nội dung có thể được đặt dưới dạng một tùy chọn. Điểm lần này là cài đặt được gọi là phản chiếu . Điều này reflectcho phép các thay đổi thuộc tính được phản ánh dưới dạng thuộc tính trong các phần tử tùy chỉnh. Kể từ khi tài sản được thiết lập với Boolean thời gian này truefalsecác thuộc tính được thêm vào khi nào, và thuộc tính sẽ bị xóa khi. Bằng cách này, LitElement cho phép bạn tùy chỉnh hành vi của chính thuộc tính .

Tiếp theo pressed, viết một biểu thức để chuyển đổi từ ngữ của nút trong phần tử tùy chỉnh bằng cách sử dụng thuộc tính đã xác định . Ngoài ra, hãy mô tả thuộc tính @click trong mẫu và liên kết hàm pressedchuyển đổi giá trị boolean của thuộc tính toggle().

@customElement('toggle-button')
export class ToggleButton extends LitElement {
  @property({
    type: Boolean,
    reflect: true,
  })
  pressed = false;

  toggle() {
    this.pressed = !this.pressed;
  }

  render() {
    return html`
      <button class="c-button" @click="${this.toggle}">
        ${this.pressed ? 'ON' : 'OFF'}
      </button>
    `;
  }
}

Bằng cách này, toggle()hàm sẽ cập nhật thuộc tính mỗi khi bạn nhấp vào nó và truenếu thuộc tính được nhấn có giá trị, pressedthuộc tính sẽ được thêm vào HTML .

Nếu bạn thực thi mã cho đến nay trong trình duyệt, nó sẽ giống như hình chụp sau.

Chụp màn hình với các thuộc tính chuyển đổi trong Chrome DevTools khi được nhấp vào

Tham khảo việc làm Typescript lương cao hấp dẫn tại Topdev.

Styles

Cuối cùng, hãy thêm một kiểu nút. Tải thư viện LitElement cần thiết cho kiểu và đặt kiểu của nút bạn đã nhấn khi đặt nó làm thuộc tính trước đó.

import { customElement, property, LitElement, html, css } from 'lit-element';

@customElement('toggle-button')
export class ToggleButton extends LitElement {
  @property({
    type: Boolean,
    reflect: true,
  })
  pressed = false;

  static get styles() {
    return css`
      :host {
        --base-color: #c7f8f9;
        --hover-color: #6ab1c9;
      }
      .c-button {
        background-color: transparent;
        color: var(--base-color);
        font-size: 1.5em;
        letter-spacing: 0.1em;
        border: 3px solid var(--base-color);
        margin-bottom: 30px;
        padding: 1rem 3rem;
        width: 200px;
        display: inline-block;
        cursor: pointer;
        border-radius: 5px;
      }
      :host([pressed]) .c-button {
        background-color: var(--base-color);
        color: var(--hover-color);
      }`;
  }
  // ...
}

Nếu bạn thực thi mã cho đến nay trong trình duyệt, nó sẽ giống như hình chụp sau. Điều này hoàn thành một thành phần đơn giản.

Chụp màn hình nút bật tắt đã được tạo kiểu và hoàn thành

Build

Lệnh xây dựng như sau.

npm run build

Tại thời điểm xây dựng, Snowpack dịch các thư viện phụ thuộc vào ứng dụng của bạn để chúng có thể được tải bằng cách nhập ESM. Các thành phần được xuất ra build/_dist_/các thư mục và các gói phụ thuộc build/web_modules/được xuất ra.

Snowpack không có trình gói mô-đun theo mặc định và bằng cách giới thiệu @ snowpack / plugin-webpack và @ snowpack / plugin-parcel , được cung cấp dưới dạng plugin chính thức, nó hỗ trợ các trình duyệt cũ không hỗ trợ nhập ESM. Bạn hoàn toàn có thể làm được. Tuy nhiên, developxin lưu ý rằng ngay cả khi cài đặt Bundle, lệnh sẽ được xuất ra bởi Mô-đun ES.

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Những Câu Hỏi Nên Hỏi Khi Phỏng Vấn Giúp Ứng Viên Ghi Điểm Với Nhà Tuyển Dụng

những câu hỏi nên hỏi nhà tuyển dụng
Những Câu Hỏi Nên Hỏi Khi Phỏng Vấn Giúp Ứng Viên Ghi Điểm Với Nhà Tuyển Dụng

Phỏng vấn là quá trình trao đổi thông tin giữa hai bên: ứng viên và nhà tuyển dụng, để cả hai có thêm hiểu biết về nhau và đánh giá được sự phù hợp giữa hai bên. Một câu hỏi quen thuộc mà các ứng viên thường gặp trong quá trình phỏng vấn là “Bạn có câu hỏi gì muốn hỏi chúng tôi không?”, “Bạn muốn tìm hiểu thêm thông tin gì không?”. Đừng bao giờ trả lời “Tôi không có thắc mắc nào”. Hãy tìm hiểu thêm về những câu hỏi nên hỏi nhà tuyển dụng, chúng sẽ giúp bạn ghi điểm đầy ấn tượng trong mắt nhà tuyển dụng đấy.

những câu hỏi nên hỏi nhà tuyển dụng
Biết cách đặt câu hỏi sẽ giúp ứng viên “ghi điểm” với nhà tuyển dụng

Tại sao cần chuẩn bị trước những câu hỏi nên hỏi nhà tuyển dụng?

Có nhiều lý do khác nhau mà ứng viên nên để tâm đến việc chuẩn bị những câu hỏi sẽ hỏi người phỏng vấn. Cách đặt câu hỏi sẽ phản ánh rất chân thật sự quan tâm của ứng viên đến công việc và vị trí họ ứng tuyển. Người phỏng vấn, nhất là những người trực tiếp quản lý nhân sự trong phòng ban sẽ đánh giá rất cao việc ứng viên đặt những câu hỏi liên quan đến công việc chuyên môn hoặc sắp xếp tổ chức trong quá trình làm việc. Những câu hỏi này cho thấy ứng viên thật sự quan tâm và mong muốn có được công việc, vì đã dành nhiều thời gian để tìm hiểu về nó.

Xem thêm việc làm IT intern hấp dẫn tại TopDev

Thêm vào đó, nhờ đã tìm hiểu thông tin đủ nhiều và đã có một số câu hỏi liên quan, khi nhà tuyển dụng trả lời câu hỏi của mình, ứng viên có thể dựa vào các vấn đề đó để khai thác các thông tin liên quan khác. Suy cho cùng, mục đích của việc đặt câu hỏi là để ứng viên có thêm thông tin về công ty mà các tìm kiếm trên mạng không có kết quả.

Do đó, đừng bao giờ đặt ra những câu hỏi mà bạn hoàn toàn có thể tìm được câu trả lời trên internet nhé. Hãy cố gắng khai thác tối đa các thông tin từ hai phía để buổi phỏng vấn trở nên thú vị và suôn sẻ hơn thay vì ứng viên chỉ trả lời những câu hỏi của người phỏng vấn.

  6 Điều Cần Tránh Khi Phỏng Vấn Để Tăng Cơ Hội Thành Công

Những câu hỏi nên hỏi nhà tuyển dụng trong quá trình phỏng vấn

Như đã đề cập, việc đặt câu hỏi là để khai thác thông tin phục vụ cho công việc và quyền lợi ứng viên. Vậy nên không nên đặt những câu hỏi quá đơn giản, câu trả lời chỉ là có hoặc không.

Đặt những câu hỏi liên quan đến chuyên môn công việc

Việc đặt những câu hỏi về chuyên môn công việc chắc chắn là cần thiết để ứng viên có thể hiểu rõ hơn vị trí mình đang ứng tuyển, bản thân có thể đóng góp gì cho công ty từ kinh nghiệm làm việc của bản thân, và có thể học thêm được những gì mới ở công việc này. Một số mẫu câu hỏi mà bạn có thể tham khảo như:

  • Vị trí này có yêu cầu những kỹ năng đặc biệt gì để hỗ trợ cho công việc không?
  • Ngoài những gì được mô tả trong JD (Job Description), tôi phải làm thêm những việc nào khác nữa không? Nếu có, những việc đó chiếm bao nhiêu phần trăm trong khối lượng công việc tôi phải đảm nhận?
  • Lộ trình công việc của công ty với vị trí này như thế nào?
  • Từ kinh nghiệm của những người đi trước, tôi nên duy trì thói quen nào hoặc chuyên môn nào trong công việc để làm tốt hơn?
  • Thử thách lớn nhất ở vị trí này là gì?

Đặt những câu hỏi liên quan đến công ty

Công ty nào cũng mong muốn có thể tìm được những nhân viên tài năng và thật sự gắn bó với công ty. Đó cũng là lý do mà nhân sự luôn là phòng ban quan trọng trong việc tìm kiếm nhân tài cho công ty. Đưa ra những câu hỏi liên quan đến công ty và phòng ban làm việc sẽ giúp người phỏng vấn đánh giá được chính xác mong muốn gắn bó của ứng viên với công ty. Và đương nhiên cũng sẽ giúp ứng viên xem xét những mục tiêu của công ty với mục đích nghề nghiệp của bản thân có tương xứng.

Một số câu hỏi ứng viên có thể đặt ra với người phỏng vấn như:

  • Xu hướng phát triển của công ty trong 5 năm tới như thế nào? Các sản phẩm mà công ty hướng đến nằm trong lĩnh vực chính yếu nào?
  • Điểm mạnh của công ty là gì?
  • Phòng ban tôi làm việc chịu trách nhiệm với những công việc nào trong công ty?
  • Quy mô nhân sự của phòng ban hiện đang như thế nào?
  • Quản lý đánh giá như thế nào về môi trường là việc tại phòng ban cũng như chất lượng nhân sự?
lưu ý khi đặt câu hỏi
Có một số câu hỏi mẫu để ứng viên tham khảo

Đặt câu hỏi về quy trình làm việc tiếp theo sau buổi phỏng vấn

Đây chắc chắn là phần quan trọng mà bất cứ ứng viên nào cũng không nên bỏ qua ở cuối mỗi buổi phỏng vấn. Hãy hỏi người phỏng vấn:

  • Nếu thông qua buổi phỏng vấn này thì bước tiếp theo của quy trình tuyển dụng là gì?
  • Sau bao lâu tôi sẽ nhận được kết quả phỏng vấn?
  • Tôi nên giữ liên lạc với ai để nắm được các thông tin sau buổi phỏng vấn này?
  Những Việc Nên Làm Sau Khi Phỏng Vấn

Yếu tố quan trọng của một buổi phỏng vấn là sự hòa hợp và thái độ giữa hai phía. Ứng viên có thể linh động dựa vào thái độ của người phỏng vấn để hỏi họ những câu hỏi mang tính cá nhân cũng là một lựa chọn không tồi.

  • Cá nhân anh/chị đánh giá như thế nào về môi trường làm việc ở công ty?
  • Anh/chị đã làm việc ở công ty này bao lâu rồi?
  • Anh/chị có nghĩ mình đã thành công và học được nhiều chuyên môn hơn ở công ty không?

Một số lưu ý khi đặt câu hỏi để tránh mất điểm trong mắt nhà tuyển dụng

Bên cạnh những câu hỏi ứng viên nên hỏi nhà tuyển dụng cũng có một số lưu ý trong quá trình đặt câu hỏi khi phỏng vấn để tránh mất điểm. Đừng đặt những câu hỏi mà câu trả lời ứng viên hoàn toàn có thể tìm kiếm trên mạng, đây chắc chắn là yếu tố rất dễ khiến bạn mất điểm trong mắt nhà tuyển dụng. Việc đặt những câu hỏi như thế này không chỉ khiến hai bên làm mất thời gian của nhau mà còn khiến người phỏng vấn đánh giá thấp sự quan tâm và mong muốn của ứng viên với công ty.

Ngoài ra, ứng viên cũng không nên đặt ra những câu hỏi quá sâu về các hoạt động riêng của công ty. Tốt hơn hết chỉ nên hỏi đến những vấn đề như văn hóa làm việc. Cũng không nên đề cập quá nhiều đến vấn đề lương thưởng nếu nhà tuyển dụng chưa nhắc đến nó. Đừng quá nôn nóng với lương thưởng sẽ giúp nhà tuyển dụng đánh giá cao tinh thần làm việc và mong muốn của bạn hơn.

Mặc dù những vấn đề nên hỏi trong khi phỏng vấn được đề cập ở trên khá nhiều, tuy nhiên bạn nên cân nhắc đặt ra những câu hỏi hợp lí và phù hợp với tình hình công ty. Không nên đặt quá nhiều câu hỏi hay hỏi quá lan man. Hãy hỏi những vấn đề mà bạn cảm thấy quan tâm nhất trong số những câu hỏi nên hỏi nhà tuyển dụng ở trên để buổi phỏng vấn diễn ra suôn sẻ nhất.

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Sử dụng Direct Exchange trong RabbitMQ

Sử dụng Direct Exchange trong RabbitMQ

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

Trong các bài viết trước chúng ta đã tìm hiểu về Default Exchange, cách tạo Work Queue với RabbitMQ. Trong bài này, chúng ta sẽ cùng tìm hiểu về Direct Exchange trong RabbitMQ.

  Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud
  Kết nối RabbitMQ sử dụng Web STOMP Plugin

Flow của một Message trong Direct Exchange

Direct Exchange (trao đổi trực tiếp – amq.direct) định tuyến message đến Queue dựa vào routing key.

Một Exchange không xác định tên (empty String), đây là loại Default Exchange, một dạng đặc biệt của là Direct Exchange. Default Exchange được liên kết ngầm định với mọi Queue với khóa định tuyến bằng với tên Queue.

Flow của một Message trong Direct Exchange như sau:

  • Một Producer sẽ tạo một Message và publish tới Exchange.
  • Một Queue sẽ binding tới Exchange sử dụng routing key. Chúng ta có thể tạo nhiều Queue và binding tới Exchange, có thể sử dụng cùng routing key, hoặc các routing key khác nhau.
  • Một Message tới Exchange với thông tin routing key. Dựa vào thông tin routing key, message sẽ được chuyển đến một hoặc nhiều Queue đã binding có cùng routing key với routing key của Message.

Chúng ta đã thấy cách hoạt động của Default Exchange ở các bài viết trước. Trong phần tiếp theo của bài viết này, chúng ta sẽ thấy cách hoạt động của Direct Exchange với routing key cụ thể.

Ví dụ Direct Exchange trong RabbitMQ

Trong ví dụ này, tôi tạo một Direct Exchange có tên GPCoderDirectExchange, tạo 3 Queue binding tới Direct Exchange này với 3 routing key:

  • QDeveloper sẽ binding với routing key devGroup.
  • QManager sẽ binding với routing key managerGroup.
  • QGeneral sẽ binding với routing key generalGroup.

Một số class của chương trình:

  • ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
  • DirectExchangeChannel :  class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, publish/ subscribe message, …
  • Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue, Routing Key.
  • Producer: để gửi Message đến Exchange.
  • Consumer: để nhận Message từ Queue.
  • App: giả lập việc gửi nhận Message thông qua Direct Exchange của RabbitMQ.

ConnectionManager.java

package com.gpcoder.directexchange;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ConnectionManager {

private ConnectionManager() {
super();
}

public static Connection createConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
return factory.newConnection();
}
}

DirectExchangeChannel.java

package com.gpcoder.directexchange;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class DirectExchangeChannel {

private String exchangeName;
private Channel channel;
private Connection connection;

public DirectExchangeChannel(Connection connection, String exchangeName) throws IOException {
this.exchangeName = exchangeName;
this.connection = connection;
this.channel = connection.createChannel();
}

public void declareExchange() throws IOException {
// exchangeDeclare( exchange, builtinExchangeType, durable)
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true);
}

public void declareQueues(String ...queueNames) throws IOException {
for (String queueName : queueNames) {
// queueDeclare - (queueName, durable, exclusive, autoDelete, arguments)
channel.queueDeclare(queueName, true, false, false, null);
}
}

public void performQueueBinding(String queueName, String routingKey) throws IOException {
// Create bindings - (queue, exchange, routingKey)
channel.queueBind(queueName, exchangeName, routingKey);
}

public void subscribeMessage(String queueName) throws IOException {
// basicConsume - ( queue, autoAck, deliverCallback, cancelCallback)
channel.basicConsume(queueName, true, ((consumerTag, message) -> {
System.out.println("[Received] [" + queueName + "]: " + consumerTag);
System.out.println("[Received] [" + queueName + "]: " + new String(message.getBody()));
}), consumerTag -> {
System.out.println(consumerTag);
});
}

public void publishMessage(String message, String routingKey) throws IOException {
// basicPublish - ( exchange, routingKey, basicProperties, body)
System.out.println("[Send] [" + routingKey + "]: " + message);
channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
}
}

Constant.java

package com.gpcoder.directexchange;

public final class Constant {

// Exchange

public static final String EXCHANGE_NAME = "GPCoderDirectExchange";

// Routing key

public static final String DEV_ROUTING_KEY = "devGroup";

public static final String MANAGER_ROUTING_KEY = "managerGroup";

public static final String GENERAL_ROUTING_KEY = "generalGroup";

// Queue

public static final String DEV_QUEUE_NAME = "QDeveloper";

public static final String MANAGER_QUEUE_NAME = "QManager";

public static final String GENERAL_QUEUE_NAME = "QGeneral";

private Constant() {
super();
}
}

Producer.java

package com.gpcoder.directexchange;

import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import static com.gpcoder.directexchange.Constant.*;

public class Producer {
private DirectExchangeChannel channel;

public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();

// Create channel
channel = new DirectExchangeChannel(connection, EXCHANGE_NAME);

// Create direct exchange
channel.declareExchange();

// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, GENERAL_QUEUE_NAME);

// Binding queues with routing keys
channel.performQueueBinding(DEV_QUEUE_NAME, DEV_ROUTING_KEY);
channel.performQueueBinding(MANAGER_QUEUE_NAME, MANAGER_ROUTING_KEY);
channel.performQueueBinding(GENERAL_QUEUE_NAME, GENERAL_ROUTING_KEY);
}

public void send(String message, String routingKey) throws IOException {
// Send message
channel.publishMessage(message, routingKey);
}
}

Consumer.java

package com.gpcoder.directexchange;

import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import static com.gpcoder.directexchange.Constant.*;

public class Consumer {

private DirectExchangeChannel channel;

public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();

// Create channel
channel = new DirectExchangeChannel(connection, EXCHANGE_NAME);

// Create direct exchange
channel.declareExchange();

// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, GENERAL_QUEUE_NAME);

// Binding queues with routing keys
channel.performQueueBinding(DEV_QUEUE_NAME, DEV_ROUTING_KEY);
channel.performQueueBinding(MANAGER_QUEUE_NAME, MANAGER_ROUTING_KEY);
channel.performQueueBinding(GENERAL_QUEUE_NAME, GENERAL_ROUTING_KEY);
}

public void subscribe() throws IOException {
// Subscribe message
channel.subscribeMessage(DEV_QUEUE_NAME);
channel.subscribeMessage(MANAGER_QUEUE_NAME);
channel.subscribeMessage(GENERAL_QUEUE_NAME);
}

}

App.java

package com.gpcoder.directexchange;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import static com.gpcoder.directexchange.Constant.*;

public class App {

public static void main(String[] args) throws IOException, TimeoutException {
// Create producer
Producer producer = new Producer();
producer.start();

// Publish some message
producer.send("This message for all developers", DEV_ROUTING_KEY);
producer.send("This message for all managers", MANAGER_ROUTING_KEY);
producer.send("This message for everyone", GENERAL_ROUTING_KEY);

Consumer consumer = new Consumer();
consumer.start();
consumer.subscribe();
}
}

Output chương trình:

[Send] [devGroup]: This message for all developers
[Send] [managerGroup]: This message for all managers
[Send] [generalGroup]: This message for everyone
[Received] [QDeveloper]: amq.ctag-F_4tm402_GYRx8FBn6rLPw
[Received] [QDeveloper]: This message for all developers
[Received] [QManager]: amq.ctag-DbVV5-XhzLyFtFd5Kij8DQ
[Received] [QManager]: This message for all managers
[Received] [QGeneral]: amq.ctag-feJgzR4t-P2tjvBWEb13yA
[Received] [QGeneral]: This message for everyone

Như bạn thấy,  Consumer/ Producer chỉ gửi/nhận đúng Message ở Queue mà nó binding.

Tài liệu tham khảo:

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Authorization Code grant type với Proof Key for Code Exchange (PKCE) trong OAuth 2.1

Authorization Code grant type với Proof Key for Code Exchange (PKCE) trong OAuth 2.1

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

Các public client như Native mobile application or Single Page Application không thể lưu trữ Client Secret trong Authorization Code grant type một cách bảo mật được. Decode source của mobile application hay view source code của Single Page Application, chúng ta có thể xem được thông tin Client Secret này. Vì thế OAuth giới thiệu Proof Key for Code Exchange (PKCE) extension hỗ trợ cho Authorization Code grant type để giải quyết vấn đề này. Cụ thể như thế nào? Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu về Authorization Code grant type với Proof Key for Code Exchange (PKCE) trong OAuth 2.1 các bạn nhé!

  Building Microservices Application - Phần 3: Xác thực API bằng Oauth 2.0
  OAuth2 là gì? Tìm hiểu về OAuth2

Ý tưởng của PKCE là sử dụng 1 cặp secret code bao gồm Code Challenge và Code Verifier, được generate bởi ứng dụng thứ ba hay còn gọi là Client Application. Client Application sẽ gửi Code Challenge đi cùng với request để lấy authorization code. Authorization server sẽ lưu Code Challenge này lại. Trong request get access token, Client Application sẽ gửi Code Verifier, authorization server sẽ verify Code Challenge và Code Verifier để issue access token.

Workflow của Authorization Code grant type với PKCE, mình có thể diễn đạt như sau:

Authorization Code grant type với Proof Key for Code Exchange (PKCE) trong OAuth 2.1

Như các bạn thấy, Client Secret đã không còn được sử dụng trong grant type Authorization Code với PKCE nữa!

Hướng dẫn tạo mock API Server với Post Man

Hướng dẫn tạo mock API Server với Post Man

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

Bước 1: Import Collection có kèm theo sample response.

Bước 2: Chọn Mock collection

  Cài đặt PostgreSQL server sử dụng Docker
  Chạy Postgresql trong Docker container

Bước 3: Config và create mock server.

Bước 4: Get URL Mock Server.

Bước 4: Update config để sử dụng URL Mock Server.

     
Bài viết gốc được đăng tải tại lcdung.top
Có thể bạn quan tâm:
Xem thêm Việc làm IT hấp dẫn trên TopDev

Cấu trúc dữ liệu Set trong Python

Cấu trúc dữ liệu Set trong Python

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

Python khác với các ngôn ngữ lập trình khác, nó đưa ra rất nhiều các cấu trúc dữ liệu dạng đa giá trị, trong bài trước chúng ta đã học về List và Tuple, bài này chúng ta tìm hiểu về hai cấu trúc dữ liệu tiếp theo của Python là Set (tập hợp).

1. Tập hợp (Set)

Set trong Python là một cấu trúc dữ liệu liên quan đến toán tập hợp hay còn gọi là lý thuyết tập hợp do nhà toán học người Đức Georg Cantor đề xuất. Set có thể chứa nhiều các phần tử và các phần tử này không có thứ tự, vị trí của nó hỗn loạn trong tập hợp. Bạn có thể duyệt qua các phần tử trong tập hợp, có thể thêm hoặc xóa đi các phần tử và thực hiện các phép toán tập hợp như phép hợp (union), phép giao (intersection), phép hiệu (difference)…

Các phần tử của tập hợp phải là các dữ liệu không thể thay đổi như một số (int), một chuỗi (string), hoặc một Tuple.

1.1 Khai báo tập hợp

Tập hợp (Set) trong Python có một số tính chất mà bạn cần nhớ:

  • Các phần tử trong tập hợp không có thứ tự.
  • Các phần tử này là duy nhất, không cho phép lặp lại.
  • Set có thể thay đổi (thêm bớt phần tử) nhưng các phần tử của tập hợp phải ở dạng không thể thay đổi (tức là xác định được dung lượng bộ nhớ ngay khi khai báo).

Chúng ta sử dụng các dấu ngoặc nhọn trong khai báo Set, ví dụ:

friends = {"Rolf","Bob","Anne"}
print(friends)

Chú ý:

  • [] sử dụng khai báo List
  • () sử dụng khai báo Tuple
  • {} sử dụng khai báo Set
  Biến và kiểu dữ liệu cơ bản trong Python

1.2 Thay đổi tập hợp

Các phần tử trong tập hợp có thể thêm hoặc loại bỏ. Python hỗ trợ rất nhiều các phương thức để thực hiện thao tác thay đổi tập hợp.

1.2.1 Phương thức .add()

Phương thức sử dụng để thêm một phần tử vào tập hợp.

Ví dụ:

friends = {"Rolf","Bob","Anne"}
friends.add("Jen")
print(friends) # Kết quả là {"Bob","Jen","Anne","Rolf"}

Chú ý, kết quả có thể khác đi do Set không sắp xếp các phần tử theo một trật tự nào cả.

1.2.2 Phương thức .remove()

Loại bỏ một phần tử trong tập hợp.

Ví dụ:

friends = {"Rolf","Bob","Anne"}
friends.remove("Anne")
print(friends) # Kết quả là {"Rolf","Bob"}
friends.remove("Jen")
print(friends) # Kết quả là lỗi KeyError: "Jen"

Khi loại bỏ một phần tử, nếu phần tử đó không tồn tại trong tập hợp, chương trình sẽ dừng và một thông báo lỗi KeyError xuất hiện.

1.2.3 Phương thức .discard()

Giống như phương thức .remove() loại bỏ phần tử trong tập hợp, tuy nhiên nếu phần tử đó không tồn tại thì nó không báo lỗi gì cả.

friends = {"Rolf","Bob","Anne"}
friends.discard("Anne")
print(friends) # Kết quả là {"Rolf","Bob"}
friends.discard("Jen")
print(friends) # Kết quả là {"Rolf","Bob"}

1.2.4 Phương thức .pop()

Loại bỏ một phần tử ngẫu nhiên khỏi tập hợp.

friends = {"Rolf","Bob","Anne"}
friends.pop()
print(friends) # Kết quả là {"Bob","Rolf"}

Bạn cần chú ý về thứ tự các phần tử trong tập hợp, nó không được sắp xếp theo bất kỳ quy tắc nào.

1.2.5 Phương thức .clear()

Loại bỏ tất cả các phần tử trong tập hợp, khi đó tập hợp được gọi là tập rỗng.

friends = {"Rolf","Bob","Anne"}
friends.clear()
print(friends) # Kết quả là set()

1.2.6 Phương thức .update()

Phương thức .add() ở trên chỉ thêm được 1 phần tử vào tập hợp với 1 câu lệnh, để thêm nhiều phần tử, chúng ta sử dụng .update(). Chú ý, đầu vào của .update() có thể là một Set, một List hoặc một Tuple.

friends = {"Rolf","Bob","Anne"}
friends.update(["Jen","Charlie"],{"Jonhny", "Sara"},("Laura","Elite"))
print(friends) # Kết quả là {'Anne', 'Laura', 'Elite', 'Rolf', 'Jonhny', 'Charlie', 'Bob', 'Sara', 'Jen'}

Kết quả của bạn có thể có thứ tự khác đi, một chú ý nữa là không sử dụng chuỗi để cập nhập vào tập hợp mà các phần tử là chuỗi bởi vì chuỗi sẽ được coi là một danh sách các ký tự, ví dụ:

friends = {"Rolf","Bob","Anne"}
friends.update("Jen")
print(friends) # Kết quả là {'n', 'e', 'Rolf', 'Bob', 'Anne', 'J'}

Không như mong đợi phải không, bạn có thể sử dụng phương thức .add() hoặc có thể chuyển chuỗi thành Set, List hoặc Tuple có 1 phần tử:

friends = {"Rolf","Bob","Anne"}
friends.update(("Jen",))
# hoặc
friends.update(["Jen"])
# hoặc
friends.update({"Jen"})

Có một số phương thức update khác như .difference_update(), .symmetric_difference_update() hay .intersection_update() nó có liên quan đến các phép toán trong tập hợp nên sẽ trình bày ở phần tiếp theo.

  Hàm Python tích hợp sẵn

1.3 Các phép toán trong tập hợp

Các tập hợp có lợi thế hơn các cấu trúc dữ liệu khác ở chỗ nó thực hiện được các phép toán tập hợp như hợp, hiệu, giao… Để mô tả dễ hiểu hơn, chúng ta có hai tập hợp art_friends và science_friends là tập hợp các bạn trong lớp Mỹ thuật và tập hợp các bạn trong lớp Khoa học.

art_friends = {"Rolf", "Anne", "Jen"}
science_friends = {"Jen", "Charlie"}

Các phép toán được mô tả như hình sau:

Các phép toán - phương thức sử dụng với tập hợp

1.3.1 Phép hợp (Union)

Hợp của hai tập hợp cho kết quả là tất cả các phần tử trong hai tập hợp, chú ý phần tử nào lặp lại sẽ chỉ xuất hiện 1 lần trong tập kết quả. Trong Python, để thực hiện phép hợp, chúng ta sử dụng phương thức .union(). Chú ý, sử dụng tập hợp nào trước cũng cho kết quả như nhau, art_friends.union(science_friends) cũng cho kết quả như science_friends.union(art_friends).

art_friends = {"Rolf", "Anne", "Jen"}
science_friends = {"Jen", "Charlie"}
all_friends = art_friends.union(science_friends)
print(all_friends) # Kết quả {'Rolf', 'Anne', 'Jen', 'Charlie'}

Chú ý, “Jen” có mặt trong cả hai lớp nhưng với tập kết quả cuối cùng thì “Jen” chỉ xuất hiện 1 lần.

1.3.2 Phép trừ (Difference)

Hiệu của một tập A trừ đi một tập B cho kết quả là tất các phần tử thuộc A nhưng không thuộc B. Sử dụng phương thức .difference() để thực hiện phép trừ hai tập hợp.

art_friends = {"Rolf", "Anne", "Jen"}
science_friends = {"Jen", "Charlie"}
art_but_not_science = art_friends.difference(science_friends)
science_but_not_art = science_friends.difference(art_friends)

print(art_but_not_science) # Kết quả {'Rolf', 'Anne'}
print(science_but_not_art) # Kết quả {'Charlie'}

Trong ví dụ trên, tập hợp art_but_not_science chứa các bạn học lớp Mỹ thuật nhưng không học lớp Khoa học, chú ý “Jen” học cả hai lớp nên không có mặt trong tập hợp này.

1.3.3 Hiệu đối xứng của hai tập hợp (Symmetric difference)

Hiệu đối xứng của hai tập A và B được kết quả là tập hợp các phần tử thuộc cả A và B nhưng không đồng thời thuộc cả tập A và B. Phương thức .symmetric_difference() cho kết quả là hiệu đối xứng của hai tập hợp. Chú ý, do tính chất đối xứng nên art_friends.symmetric_difference(science_friends) và science_friends.symmetric_difference(art_friends) cho kết quả như nhau.

art_friends = {"Rolf", "Anne", "Jen"}
science_friends = {"Jen", "Charlie"}

not_in_both_1 = art_friends.symmetric_difference(science_friends)
print(not_in_both_1) # Kết quả {'Rolf', 'Charlie', 'Anne'}

not_in_both_2 = science_friends.symmetric_difference(art_friends)
print(not_in_both_2) # Kết quả {'Rolf', 'Anne', 'Charlie'}

1.3.4 Phép giao (Intersection)

Phép giao hai tập hợp cho kết quả là các phần tử đồng thời thuộc cả hai tập hợp. Trong Python sử dụng phương thức .intersection() để thực hiện phép giao, chú ý tập hợp nào đứng trước cũng được, do đó kết quả art_friends.intersection(science_friends) và science_friends.intersection(art_friends) là như nhau.

art_friends = {"Rolf", "Anne", "Jen"}
science_friends = {"Jen", "Charlie"}

art_and_science = art_friends.intersection(science_friends)
print(art_and_science) # Kết quả là {"Jen"}

Tập hợp art_and_science chứa các bạn học đồng thời cả lớp Mỹ thuật và lớp Khoa học, do đó kết quả chỉ có “Jen” học cả hai lớp này.

Tuyển dụng lập trình viên python Hồ Chí Minh trên TopDev

1.3.5 Thay đổi tập hợp dựa trên phép toán tập hợp

Trong phần trước chúng ta đã biết đến phương thức .update() để thêm nhiều phần tử vào một tập hợp. Dựa vào các phép toán tập hợp, Python cung cấp một số các phương thức khác để thay đổi tập hợp như sau:

.difference_update()

Phương thức này là sự kết hợp của .difference() và .update(). Nó thực hiện phép trừ tập hợp trước, được kết quả như thế nào sẽ cập nhật vào tập hợp đích.

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

A.difference_update(B)
print(A) # Kết quả {1, 2}

.symmetric_difference_update()

Phương thức này là sự kết hợp của .symmetric_difference() và .update(). Nó thực hiện phép trừ đối xứng 2 tập hợp trước, được kết quả như thế nào sẽ cập nhật vào tập hợp đích.

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

A.symmetric_difference_update(B)
print(A) # Kết quả là {1, 2, 5, 6}

.intersection_update()

Tương tự, Python thực hiện .intersection() trước sau đó thực hiện .update():

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

A.intersection_update(B)
print(A) # Kết quả là {3, 4}

1.3.6 Một số các phép toán khác

.isdisjoint() Trả về True nếu hai tập hợp không giao nhau, tức là hai tập hợp không có phần tử chung.

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

print(not A.isdisjoint(B)) # Kết quả là True

Ở đây, chúng ta sử dụng toán tử logic not, để thực hiện trả về True khi hai tập giao nhau, nghe nó thuận tai hơn :D.

.issubset() Trả về True nếu tập này còn tập con của tập đích (tập trong ngoặc).

A = {3, 4}
B = {3, 4, 5, 6}

print(A.issubset(B)) # Kết quả là True

.issuperset() Trả về True nếu tập này là tập cha của tập đích (tập trong ngoặc).

A = {3, 4}
B = {3, 4, 5, 6}

print(A.issuperset(B)) # Kết quả là False

Ngoài ra chúng ta có thể sử dụng các ký hiệu phép toán so sánh thông thường để kiểm tra xem là tập con, tập cha hay hai tập bằng nhau với >, >=, ==, <, <=.

A = {3, 4}
B = {3, 4, 5, 6}

print(A.issubset(B)) # Kết quả là True

# Tương đương với 
print(A <= B) # Kết quả là True

Ứng tuyển vị trí việc làm python lương cao trên TopDev

1.4 “Đóng băng” tập hợp (Frozen Set)

Python cung cấp một hàm tên là frozenset(), kết quả trả về là một tập hợp (Set) không thể thay đổi. Khi đó, nếu bạn thực hiện các phương thức .add(), .remove(), .update()… sẽ báo lỗi.

“Đóng băng” tập hợp sẽ làm cho tập hợp đó giống như cấu trúc Tuple trong Python.

friends = {"Rolf", "Anne", "Jen"}

frozen_friends = frozenset(friends)
frozen_friends.add({"Jen", "Charlie"})
print(frozen_friends) # Kết quả lỗi: AttributeError: 'frozenset' object has no attribute 'add'

Đóng băng một tập hợp rất hữu ích trong trường hợp bạn muốn tập hợp đó không thể thay đổi. Ví dụ khi dùng một tập hợp làm key cho một từ điển (Dictionary), sẽ được giới thiệu trong phần tiếp theo.

A = {1, 2, 3}
B = {'a', 'b', 'c'}

C = {x: 'foo', y: 'bar'} # Kết quả lỗi: TypeError: unhashable type: 'set'

Tuy nhiên nếu bạn đóng băng các tập hợp này, sẽ không có lỗi nào xảy ra.

A = frozenset({1, 2, 3})
B = frozenset({'a', 'b', 'c'})

C = {x: 'foo', y: 'bar'} # Không có lỗi

2. Tập hợp sử dụng khi nào?

Toán tập hợp hay lý thuyết tập hợp là một trong những phần quan trọng của Toán học mà Khoa học dữ liệu (data science) và Machine Learning sử dụng kiến thức Toán rất nhiều, do vậy toán tập hợp trong Python là một phần không thể thiếu. Ngay từ đầu Python đã được phát triển cho mục đích Khoa học và Giáo dục, do vậy bạn có thể thấy các thiết kế mang hơi hướng Khoa học.

Tập hợp cũng là một nhóm các dữ liệu giống như với ListTuple nhưng nó lại được xây dựng sẵn các phép toán tập hợp, do vậy tập hợp sẽ được sử dụng khi chương trình của bạn cần các phép toán tập hợp như .union(), .difference(), .intersection()… Để lựa chọn cấu trúc dữ liệu phù hợp, bạn cần nắm rõ những đặc điểm của từng loại, ở đây Set có những đặc điểm mà tôi đã đưa ra ở phần đầu, dưới đây chỉ nhắc lại:

  • Set có các phần tử là không được thay đổi, do vậy khả năng tìm dữ liệu sẽ nhanh hơn.
  • Set cần thiết cho các logic liên đến các cặp (key:value) trong cấu trúc Dictionary của Python.
  • Các phần tử là duy nhất, do đó nếu bạn có một dữ liệu tương tự thì Set là một lựa chọn.

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Lit HTML là gì?

Lit HTML là gì?

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

Trước khi giới thiệu về Lit-HTML (Lit) mình tìm hiểu xíu về Web components cái đã.

Từ khi khái niệm Web components được giới thiệu bởi Alex Russel vào năm 2011 trong một hội thảo về front-end. Google đã tích cực phát triển công nghệ này với dự án mã nguồn mở Polymer (https://lit-html.polymer-project.org), và Lit-html (Lit.dev) đã mang lại một đột phá trong việc phát triển Web đặt biệt là về Front-end.

  Cách để tạo một Switch trên iPhone bằng HTML và CSS
  Cấu trúc trang HTML cơ bản

Web components là gì?

Web components là một tập các quy chuẩn công nghệ dùng cho việc xây dựng các thành phần web được đóng gói (tách biệt với phần code còn lại của ứng dụng như View / Back For Front của server side reder) và có thể tái sử dụng cho client side (nôm na là nén thành cục javaScript for browser và tự render linh hoạt) thường sẽ áp dụng SPA.

Về cơ bản web components sử dụng 3 công nghệ cốt lõi:

  • Custom Elements: là tập hợp các API của Javascript để cho phép tạo ra các thành phần web tuỳ chỉnh.
  • Shadow DOM: là tập hợp các API của Javascript để gắn một cây DOM “shadow” (như shadow object trong javaScript) vào một thành phần của DOM, cây DOM này được quản lý riêng biệt và render riêng với cây DOM chính, do vậy nó có thể được đóng gói lại để sử dụng cho các ứng dụng khác nhau.
  • HTML templates: Dùng để tạo ra các template từ đó được render ra trang web. Khái niệm này được một số framework như Angular (Google) hay React (Facebook) phát triển.

Vậy là Lit đã được tạo ra để tích hợp các công nghệ Web component đúng không? Ummmm

Lit (Lit html) là gì?

Trích dẫn từ nhà phát triển từ câu đầu tiên https://lit.dev/docs/

Repo: https://github.com/lit/lit

Lit is a simple library for building fast, lightweight web components.

At Lit’s core is a boilerplate-killing component base class that provides reactive state, scoped styles, and a declarative template system that’s tiny, fast and expressive.

Như mình hiểu thì Lit là một thư viện cho việc xây dựng nhanh gọn web components tiêu chuẩn của google đề xuất và phát triển.

Với thư viện gọn nhẹ thì bạn sẽ dễ dàng tích hợp cho các framework hiện tại của bạn mà không phải tốn quá nhiều effort cho việc chuyển đổi công nghệ qua Vue, ReactJS mà vẫn có các tính năng linh hoạt cao như Vue hoặc ReactJS.

Điều khác biệt mà Lit mang lại so với Vue, ReactJS là gì??? Sau thời gian trải nghiệm các dự án sử dụng Vue, React và Lit thì thấy ở Lit nổi bật là giảm thiểu tình trạng lock-in, tối đa được tính linh hoạt và khả năng maintain tốt hơn với việc sử dụng mô hình browser’s native. Với site render lượng lớn component kết hợp server site render của React sẽ thấy khác biệt rõ rệt sau khi chuyển qua Lit… oh my goodnessoh my goodness gọi ajax rồi render page nhanh quá dậy.

Khi bạn phát triển ứng dụng bằng Lit, bạn có thể dễ dàng tích hợp các web components libraries khác. Bạn thậm chí có thể update version mới của Lit hoặc chuyển sang thư viện khác lúc phát triển mà không ảnh hưởng nhiều tới quá trình phát triển product. Còn dùng Vue hay React thì migration version thôi cũng phát ngán rồi, còn các tính năng và lifecycle mới phải học nửa…

Tuy nhiên đó chỉ là quan điểm cá nhân mình trên dự án có business phức tạp thôi còn tuỳ mức độ và business của dự án nửa mới phán đoán Lit có phù hợp hay không 😀

Giới thiệu tới đây thôi còn muốn đào sâu hơn thì mình sẽ có bài viết khác về migration hệ thống dùng Lit.

React

Lit-HTML

Bài viết gốc được đăng tải tại lcdung.top
Có thể bạn quan tâm:
Xem thêm Việc làm IT hấp dẫn trên TopDev

Truy vấn dữ liệu MongoDB

Truy vấn dữ liệu MongoDB

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

Trong phần này, SmartJob sẽ giới thiệu đến bạn nhiều cú pháp truy vấn khác nhau, theo một hay nhiều điều kiện.

  Kiểm thử qui hồi – Cơn ác mộng của kỹ sư kiểm thử
  MongoDB là gì? Cơ sở dữ liệu phi quan hệ

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

CHUẨN BỊ DỮ LIỆU

Làm việc với MongoDB, thông thường là làm việc với dữ liệu lớn, do đó cần chuẩn bị cơ sở dữ liệu có kích thước tương đối lớn để sát với thực tế sử dụng. Tải về từ server SmartJob 2 cơ sở dữ liệu mẫu, bấm vào để download
– zips: Cơ sở dữ liệu về mã số bưu cục tại Mỹ (file download 567 KB, sau giải nén 3,03 MB, dữ liệu thật, hoàn toàn sát với thực tế).
– dataset: Cơ sở dữ liệu thông tin về quán ăn, nhà hàng (file donwload 1,5 MB, sau giải nén 11,6 MB)Sau khi download về, bạn giải nén ra.

Import collection có tên zips vào vào CSDL test (test là CSDL có sẵn sau khi cài đặt MongoDB)

mongoimport --db test --collection zips --drop --file "C:\zips.json"

Khi sử dụng tiện ích RockMongo để import dữ liệu, thời gian import rất lâu và dễ bị lỗi. Việc sử dụng câu lệnh là rất tốt, nhanh, tránh được lỗi.

Trong Hệ quản trị Cơ sở dữ liệu MongoDB sẽ có nhiều CSDL, do đó bạn cần khai báo sẽ sử dụng CSDL nào. Gõ lệnh

use test;

Để sử dụng CSDL test

Gõ lệnh dưới đây để xem nội dung collection “zips” của Cơ sở dữ liệu “zips”. (Đây chính là câu lệnh hiển thị toàn bộ nội dung của collection zips)

db.zips.find().pretty();

Do lượng dữ liệu lớn, cửa sổ dòng lệnh không thể liệt kê hết. Để xem thêm trang sau, bạn gõ

it

rồi nhấn phím Enter.

Công tác chuẩn bị dữ liệu đã sẵn sàng để bạn tạo các truy vấn từ đơn giản đến phức tạp.

TRUY VẤN THEO ĐIỀU KIỆN

Tìm bưu cục có id = 01002, do tìm theo id, nên sẽ trả về không quá 1 document

db.zips.find(

     {"_id": "01002"}

);

Để tìm document chứa dữ liệu về bưu cục vill

db.zips.find(

     {"city": "GILBERTVILLE"}

);

Các bạn tham khảo mã nguồn sử dụng Spring Data Mongo để C.R.U.D. (Create – Read – Update – Delete) dữ liệu MongoDB tại: https://github.com/SmartJobVN/MongoDB_SpringDataMongo

Đỗ Như Vý – Bài viết gốc được đăng tải tại smartjob.vn

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Giới thiệu về Docker Compose

Giới thiệu về Docker Compose

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

Chúng ta sử dụng Dockerfile để cấu hình và build các Docker Image. Để run các Docker container từ các Docker Image này, các bạn có thể sử dụng command line hoặc Docker Compose. Lợi ích của Docker Compose là giúp các bạn có thể định nghĩa và run nhiều Docker container cùng một lúc. Cụ thể như thế nào? Mình sẽ hướng dẫn các bạn cách định nghĩa và run các Docker container với Docker Compose các bạn nhé!

  20 trường hợp sử dụng lệnh Docker cho developer
  Cách tạo một Docker đơn giản cho Node.JS

Ứng dụng ví dụ

Để làm ví dụ cho bài viết này, mình sẽ tạo mới một Spring Boot project đơn giản, có khai báo sử dụng database nhưng sẽ không có thao tác nào đến database cả, tương tự như bài viết Deploy ứng dụng Spring Boot sử dụng Docker, như sau:

Giới thiệu về Docker Compose

Kết quả:

Giới thiệu về Docker Compose

SpringBootDockerComposeApplication:

package com.huongdanjava.springboot;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.jdbc.metadata.HikariDataSourcePoolMetadata;
import org.springframework.web.bind.annotation.RequestMapping;

import com.zaxxer.hikari.HikariDataSource;

@SpringBootApplication
public class SpringBootDockerComposeApplication {

@Autowired
private DataSource dataSource;

@RequestMapping("/hello")
public String helloDockerCompose() {
Integer idleConnection = new HikariDataSourcePoolMetadata((HikariDataSource) dataSource).getIdle();

return "Hello Docker Compose! Idle connection to database is " + idleConnection;
}

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

}

application.properties

spring.datasource.url=jdbc:postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}
spring.datasource.username=${DATABASE_USERNAME}
spring.datasource.password=${DATABASE_PASSWORD}

Kết quả khi chạy ứng dụng:

Giới thiệu về Docker Compose

Xây dựng Dockerfile

Để có thể sử dụng Docker Compose, chúng ta cần build Docker Image cho ứng dụng của mình. Điểm khác biệt ở đây là chúng ta không cần phải khai báo biến môi trường của ứng dụng trong Dockerfile, chúng ta sẽ sử dụng Docker Compose để làm điều này.

Mình sẽ tạo mới một tập tin Dockerfile trong project ví dụ:

Giới thiệu về Docker Compose

Nội dung tập tin Dockerfile cho ứng dụng của mình như sau:

FROM openjdk:11.0.11-jre

VOLUME /tmp

ADD target/spring-boot-docker-compose-0.0.1-SNAPSHOT.jar app.jar

ENTRYPOINT exec java -jar app.jar

Sau khi đã có Docker Image cho ứng dụng của mình, các bạn có thể sử dụng Docker Compose để deploy ứng dụng của mình. Như mình đã nói, lợi ích lớn nhất của Docker Compose là giúp chúng ta có thể cấu hình và chạy nhiều Docker Image cùng 1 lúc.

Để minh hoạ điều này, mình sẽ không sử dụng PostgreSQL ở máy local của mình nữa. Mình sẽ viết một Docker Compose có thể cài đặt mới một PostgreSQL và sau đó sẽ chạy ứng dụng của mình lên.

Nhưng trước tiên, chúng ta hãy nói một ít về Docker Compose đã các bạn nhé!

Docker Compose thông thường sử dụng tập tin .yml để định nghĩa.

Mặc định, Docker sử dụng tập tin có tên là docker-compose.yml để chạy Docker Compose, nên mình sẽ tạo mới một tập tin docker-compose.yml trong project ví dụ như sau:

Giới thiệu về Docker Compose

Đầu tiên, các bạn có thể sử dụng thuộc tính version để định nghĩa version của Docker Compose mà chúng ta sẽ sử dụng. Các bạn có thể tham khảo về version của Docker Compose tại đây.

Mình sẽ khai báo sử dụng Docker Compose version 3.8 như sau:

version: '3.8'

Mỗi Docker Container là một service trong tập tin Docker Compose và chúng ta sẽ sử dụng thuộc tính services cùng với khai báo tên của từng service cho các Docker Container của chúng ta. Mình sẽ khai báo như sau:

services:
postgresql: 

spring_boot_docker_compose:

Nhiệm vụ của chúng ta là khai báo thông tin cho mỗi service. Các bạn có thể sử dụng một số thuộc tính phổ biến sau để khai báo cho mỗi service như sau:

  • image: Docker Image mà chúng ta sẽ sử dụng để chạy container.
  • container_name: tên của container.
  • restart: các bạn xem thêm ở đây. Thông thường chúng ta sẽ cấu hình cho thuộc tính này là on-failure và số lần restart nếu container start failed.
  • environment: khai báo các biến môi trường cần thiết cho mỗi container.
  • volumes: mount thư mục giữa máy chạy Docker và container.
  • ports: mapping port giữa máy chạy Docker và container.
  • depends_on: chỉ ra sự phụ thuộc của service này với service khác trong Docker Compose. Container với name được khai báo trong thuộc tính này bắt buộc phải available trước thì service này mới được chạy.
  • networks: định nghĩa một network dùng chung cho các container. Thông thường chúng ta sẽ sử dụng driver bridge cho phần network này.
Chúng ta sẽ định nghĩa service cho postgresql trước. Mình sẽ định nghĩa như sau:
postgresql: 
image: postgres:13.3
container_name: postgres
restart: on-failure:5
environment:
POSTGRES_PASSWORD: "123456"
POSTGRES_USER: "khanh"
POSTGRES_DB: "test"
volumes:
- /Users/khanh/data:/var/lib/postgresql/data
ports:
- 5432:5432
networks:
- huongdanjava

Các bạn có thể tham khảo thêm bài viết Cài đặt PostgreSQL server sử dụng Docker để hiểu thêm các cấu hình cho service postgresql này.Còn ứng dụng ví dụ, mình sẽ định nghĩa như sau:

spring_boot_docker_compose:
image: spring-boot-docker-compose
container_name: spring_boot_docker_compose
depends_on:
- postgresql
environment:
DATABASE_USERNAME: "khanh"
DATABASE_PASSWORD: "123456"
DATABASE_HOST: "postgresql"
DATABASE_NAME: "test"
DATABASE_PORT: 5432
ports:
- 8080:8080
networks:
- huongdanjava

Vì phải có database thì ứng dụng ví dụ của chúng ta mới chạy được nên mình sử dụng thuộc tính depends_on trong service spring_boot_docker_compose để khai báo sự phụ thuộc này.

Cả 2 service mà mình đã định nghĩa ở trên sử dụng chung networks là huongdanjava với driver bridge. Chúng ta cần định nghĩa phần networks này như sau:

networks:
huongdanjava:
driver: bridge

Toàn bộ nội dung của tập tin docker-compose.yml như sau:

version: '3.8'

services:
postgresql: 
image: postgres:13.3
container_name: postgres
restart: on-failure:5
environment:
POSTGRES_PASSWORD: "123456"
POSTGRES_USER: "khanh"
POSTGRES_DB: "test"
volumes:
- /Users/khanh/data:/var/lib/postgresql/data
ports:
- 5432:5432
networks:
- huongdanjava

spring_boot_docker_compose:
image: spring-boot-docker-compose
container_name: spring_boot_docker_compose
depends_on:
- postgresql
environment:
DATABASE_USERNAME: "khanh"
DATABASE_PASSWORD: "123456"
DATABASE_HOST: "postgresql"
DATABASE_NAME: "test"
DATABASE_PORT: 5432
ports:
- 8080:8080
networks:
- huongdanjava

networks:
huongdanjava:
driver: bridge

OK, đến đây thì chúng ta đã hoàn thành việc viết Docker Compose cho ứng dụng ví dụ này. Giờ chạy xem thế nào các bạn nhé!

Mình sẽ build Docker Image cho ứng dụng ví dụ trước:

mvn clean package -DskipTests && docker build -t spring-boot-docker-compose .

Kết quả:

Giới thiệu về Docker Compose

Bây giờ thì các bạn có thể chạy Docker Compose rồi. Chúng ta sẽ chạy câu lệnh sau:

docker compose up

Kết quả:

Giới thiệu về Docker Compose

Kiểm thử phần mềm trên các thiết bị di động

Kiểm thử phần mềm trên các thiết bị di động

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

“Tôi mới làm quen với kiểm thử di động.  Xin vui lòng cho tôi biết làm thế nào để bắt đầu?”. “Tôi đang làm việc với kiểm định trên web và tôi cần chuyển sang kiểm định trên các thiết bị di động, vui lòng tư vấn để tôi có thể làm tốt trong lĩnh vực này?”. Đối với các bạn mới làm quen với lĩnh vực kiểm thử di động, có một số điểm quan trọng các bạn nên biết và chúng ta hãy cùng bắt đầu từ số ZERO về kiểm thử di động.

  10 lý do kiểm thử phần mềm trở thành một nghề thời thượng
  5 xu hướng của ngành kiểm thử tự động trong năm 2024

Xem thêm Việc làm tester lương cao hấp dẫn trên TopDev

1. Cơ bản về kiểm thử phần mềm.

Cho dù bạn có kinh nghiệm hay mới vào nghề, kiến thức cơ bản về kiểm thử phần mềm luôn luôn là cần thiết trong mọi loại kiểm thử mà các bạn sẽ thực hiện. Các bạn tham khảo thêm ở bài viết này nhé.

2. Cơ bản về viễn thông.

Những am hiểu cơ bản về viễn thông luôn là lợi thế. Bạn chắc chắn sẽ nhận được lợi thế nếu bạn biết được những thứ khác trong lĩnh vực này xoay quanh thử nghiệm các sản phẩm di động. 2G, 3G, CDMA, GPRS, GSM, HSCSD, SIM, tin nhắn SMS, WAP là một số điều cơ bản về viễn thông mà bạn cần phải biết.

3. Các kiến thức về hệ điều hành/nền tảng di động.

Có rất nhiều các hệ điều hành dành cho di động hiện nay trên thị trường như Android, iOS, Blackberry, J2ME, Symbian, Palm, Windows phone, Samsung Bada, Nokia Meego…Kiến thức về các hệ điều hành cho di động thực sự rất quan trọng để bạn trở thành 1 kỹ sư kiểm thử di động giỏi. Những hiểu biết về khả năng và hạn chế của từng hệ điều hành sẽ cho bạn sự tự tin để phân biệt được đâu là lỗi ứng dụng và đâu là giới hạn của hệ điều hành.

4. Làm quen với điện thoại di động của chính bạn.

Tôi có thể chắc chắn một điều là rất nhiều trong số các bạn thậm chí không biết một cách đầy đủ về chiếc điện thoại mà các bạn đang sử dụng như mẫu điện thoại (model), phiên bản phần mềm, hệ điều hành, tên mã…Nếu bạn mới bắt đầu với kiểm thử di động, hãy bắt đầu khám phá chiếc dế của mình. Hãy thử truy cập Internet bằng chiếc điện thoại thông minh của bạn, sử dụng wifi, GPRS. Kiểm tra xem làm cách nào để đưa điện thoại của mình về trạng thái ban đầu (Factory reset – nhớ cẩn thận nhé các bạn, sao lưu dữ liệu…) Thử kiểm tra số IMEI của điện thoại, thử nâng cấp phiên bản của hệ điều hành, thử những cài đặt khác (settings). Nói một cách ngắn gọn hãy làm sử dụng hết tất cả các chức năng của chiếc điện thoại. Chắc chắn là bạn sẽ nghĩ ra thêm vài tình huống trong khi các bạn thực hiện kiểm thử?

5. Tìm hiểu kiến thức chuyên ngành kiểm thử di động (mobile testing).

  • Kiểm thử di dộng các ứng dụng có thể tải về: một vài ứng dụng được cài sẵn khi được sản xuất, và một số khác có thể được tải về từ kho ứng dụng (Apple App store, Android Market, Gẹtar, Nokia Ovi Store, Blackberry App world…)
  • Kiểm thử di động các thiết bị cầm tay: Tương tự như các tổ chức phát triển ứng dụng tải về di động, có rất nhiều công ty phát triển thiết bị cầm tay di động hoàn chỉnh. Bộ phận bảo đảm chất lượng ở đây có thể cần phải kiểm tra các ứng dụng hoặc các tính năng có sẵn trong điện thoại. SMS, MMS, Voice Call, MMS, danh bạ điện thoại, Máy tính, Bluetooth và tính năng di động khác. Bao gồm đa phương tiện (máy ảnh, Video, Media player, nhạc chuông) và thử nghiệm giao thức ngăn xếp điện thoại di động.
  • Kiểm thử trang web trên di động (WAP Sites): không như các ứng dụng có thể tải về, web trên di động có thể được truy cập thông qua trình duyệt, không cần tải về. Kiểm thử này có những thách thức riêng của nó. Chuyển hướng đúng đắn, giao diện người dùng tốt (thiết kế), an toàn, hiệu quả và khả năng tưng thích trình duyệt di động là những lĩnh vùng quan trọng.

6. Nhận biết các loại kiểm thử ứng dụng di động.

Cũng giống như kiểm thử phần mềm, kiểm thử ứng dụng di động cũng bao gồm

  • Kiểm thử giao diện (màu sắc, phong cách Menu, nhất quán của giao diện người dùng trên thiết bị khác nhau)
  • Kiểm thử chức năng (kiểm tra chức năng chính của ứng dụng di động theo đặc điểm kỹ thuật)
  • Kiểm thử hiệu suất và chịu tải (Hành vi của ứng dụng di động trong các nguồn tài nguyên thấp (bộ nhớ / không gian lưu trữ), hành vi của trang web điện thoại di động khi nhiều người sử dụng điện thoại di động đồng thời truy cập vào trang web di động)
  • Kiểm tra khả năng sử dụng (kiểm tra các khía cạnh khả năng sử dụng các ứng dụng di động)
  • Thử nghiệm tương thích: Kiểm tra khả năng tương thích của ứng dụng của bạn với các tính năng thiết bị gốc (tức là để đảm bảo rằng ứng dụng của bạn không cản trở chức năng thiết bị gốc)
  • Kiểm tra gián đoạn (cuộc gọi thoại, tin nhắn SMS, sạc, thông báo bộ nhớ thấp) trong khi ứng dụng đang chạy.
  • Monkey Testing (không biết dịch sao luôn – kiểm tra khỉ haha): bấm bàn phím liên tục thông qua các công cụ để kiểm tra sự ổn định ứng dụng với sự kiện nhấn phím khác nhau.

7. Tham khảo các trường hợp thử nghiệm mẫu cho ứng dụng di động.

Đối với các bạn mới tiếp cận kiểm thử di động, sẽ là rất thông minh khi các bạn tham khảo các trường hợp thử nghiệm mẫu cho thiết bị di động. Tham khảo chúng từ những dự án đã hoàn thành trước kia hoặc hỏi các anh chị đi trước. Ngoài ra các bạn có thể tìm những thử nghiệm mẫu này từ Internet.

8. Khám phá những khả năng của trình mô phỏng.

Trình mô phỏng đóng vai trò đặc biệt quan trọng khi không có thiết bị thật cho việc kiểm thử. Mặc dù thử nghiệm trên thiết bị luôn được ưu tiên vì nó tái lập hành động của người dùng cuôi, tầm quan trọng của trình mô phỏng không thể bỏ qua. Để có thử nghiệm hiệu quả trên Simulator, tôi đề nghị khám phá tất cả các khả năng của trình mô phỏng.

9. Sử dụng các dịch vụ cho thuê thiết bị từ xa.

Vì có qúa nhiều loại thiết bị di động trên thị trường, nên chúng ta không thể mua tất cả chúng. Trong khi đó, trình mô phỏng không thể tin tưởng 100% để phát hành ứng dụng. Các dịch vụ thiết bị từ xa có thể là giải pháp tốt để đối phó với thách thức này. Là một kỹ sư kiểm thử di động giỏi, bạn nên biết các loại dịch vụ này và đề nghị với người quản lý của bạn về các dịch vụ này khi cần thiết

  • Device Anywhere (Paid)
  • Perfecto Mobile (Paid)
  • Nokia RDA (Free, For Symbian Phones)

*** Lợi ích của dịch vụ

  • Bạn không phải mua thiết bị
  • Người dùng có thể lựa chọn nhà mạng (Verizon, T-Mobile, AT&T)
  • Độ tin cậy cao hơn sử dụng trình mô phỏng bởi vì chúng là thiết bị thật
  • Một số dịch vụ thiết bị từ xa như Device Anywhere còn hỗ trợ kiểm thử tự động

*** Bất lợi

  • Mất thời gian để các sự kiện gõ phím, hành động được truyền tải tới thiết bị và phản hồi lại cho người dùng
  • Đôi khi những thiết bị bạn cần không có sẵn (vì người khác đã đặt trước rồi)
  • Chi phí khá cao.

10. Khám phá công cụ và tiện ích.

Có nhiều công cụ và tiện ích có trên thị trường, chúng có thể giúp ích cho các bạn trong việc kiểm thử ứng dụng di động hiệu quả. Một số công cụ có sẵn trong bộ SDK. Tuy nhiên bạn có thể sẽ tìm kiếm thêm trên Internet cho những nền tảng/hệ điều hành khác nhau

  • Công cụ kiểm tra sự tiêu thụ pin trong khi ứng dụng đang chạy Nokia Energy Profiler.
  • Công cụ/phần mềm chụp màn hình
  • Công cụ tạo ra những file giả để kiểm tra sự phản ứng của ứng dụng
  • Công cụ lấy logs file

11. Khám phá các công cụ kiểm thử tự động cho thiết bị di động.

  • TestComplete
  • M-Eux
  • TestQuest Countdown
  • Test Quest Pro
  • Robotium
  • VNC
  • Sikuli
  • Deviceanywhere
  • FoneMonkey (iPhone)
  • Eggplant (iPhone)
  • TestiPhone( For iPhone Mobile Web)
  • IBM® Rational® Performance Tester (RPT)
  • 3P Mobile
  • Expertest
  • MITE

12. Khám phá các cộng đồng, diễn dàn, blogs.

Càng khám phá các bạn sẽ thấy có rất nhiều thứ để học. Hơn nữa, công nghệ luôn luôn thay đổi và ngày càng nhanh hơn. Giải pháp là tham gia các cộng đồng, diễn đàn để học từ kiến thức và kinh nghiệm của những người khác. Bắt đầu và thảo luận, bạn sẽ tìm thấy rất nhiều những chia sẻ và kinh nghiệm.

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Cài đặt Visual Studio: Công cụ lập trình mạnh mẽ của Microsoft

Cài đặt Visual Studio: Công cụ lập trình mạnh mẽ của Microsoft

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

Chào các bạn, khi nói đến những sản phẩm phần mềm, dịch vụ phần mềm hay hạ tầng phần cứng, thiết bị công nghệ… thì không thể không nhắc tới ông trùm Microsoft  được.

Với ngôn ngữ lập trình C# (C Sharp) được đánh giá thuộc TOP các ngôn ngữ lập trình nên học năm 2021 thì Microsoft cũng cho ra đời một công cụ lập trình rất mạnh đó là Visual Studio.

  Hướng dẫn tạo Copyright Header trên từng file .cs trong Visual Studio

Visual Studio được thiết kế để phù hợp với các dự án sử dụng ngôn ngữ lập trình C# và các công nghệ .NET do Microsoft phát triển.

Trong bài viết này, mời các bạn hãy cùng mình đến với nội dung hướng dẫn cài đặt Visual Studio (phiên bản 2019 – LTS) trên hệ điều hành Windows 10. Các hệ điều hành khác cũng hoàn toàn tương tự nha các bạn !

#1. Tải và cài đặt Visual Studio

+ Bước 1: Các bạn có thể download file cài đặt tại đây: https://visualstudio.microsoft.com/

cai-dat-visual-studio-tren-windows (1)

+ Bước 2: Sau khi có file cài đặt thì các bạn kích đúp chuột để bắt đầu cài đặt. Visual Studio sẽ download một vài thông tin trên mạng nên các bạn nhớ giữ Internet ổn định.

cai-dat-visual-studio-tren-windows (2)

+ Bước 3: Tiếp theo các bạn sẽ chọn các option để cài đặt, Visual Studio đã hỗ trợ khá nhiều công nghệ.

Ở đây để nhanh mình chỉ chọn mỗi ASP.NET and web deveopment. Yêu cầu về không gian lưu trữ là 9.28 GB => Sau đó các bạn bấm Install để bắt đầu cài đặt.

cai-dat-visual-studio-tren-windows (3)

Quá trình cài đặt tương đối lâu (5-7 phút) do dung lượng cài đặt lớn.

cai-dat-visual-studio-tren-windows (4)

+ Bước 4: Sau khi cài đặt xong thì sẽ có một popup như hình bên dưới để các bạn đăng ký tài khoản. Các bạn có thể bấm vào Not now, maybe later để đăng ký sau.

cai-dat-visual-studio-tren-windows (5)

+ Bước 5: Tiếp theo các bạn sẽ chọn theme cho Visual Studio (có 4 theme cơ bản, bạn chọn theme nào cũng được). Ở đây mình chọn Blue => rồi bấm chọn Start Visual Studio.

cai-dat-visual-studio-tren-windows (6)

#2. Tạo một Project mới trên Visual Studio

+ Bước 1: Đây chính là giao diện đầu tiên khi các bạn sử dụng Visual Studio. Có một vài lựa chọn như:

  • Clone a repository: Là bạn lấy code từ các repos trên mạng như Github về.
  • Open a project or solution: Mở một Project có sẵn trên máy của bạn.
  • Open a local folder: Mở một Folder trong máy.
  • Create a new project: Tạo mới một Project mới.

+ Bước 2: Ở đây mình chọn Create a new project để tạo mới một Project đơn giản nha các bạn.

cai-dat-visual-studio-tren-windows (7)

Có rất nhiều loại Project khác nhau và để cho đơn giản thì mình sẽ tạo một Console Project với ngôn ngữ C# và có chức năng in ra màn hình chữ Hello World như hình bên dưới.

=> Sau đó bấm Next để tiếp tục.

cai-dat-visual-studio-tren-windows (8)

+ Bước 3: Một vài thông tin các bạn phải điền đó là:

  • Project name: Tên Project
  • Location: Vị trí lưu Project
  • Solution name: Nó giống như tên Package vậy, nhưng trong Visual Studio gọi là Solution.

=> Sau đó bấm Next để tiếp tục.

cai-dat-visual-studio-tren-windows (9)

+ Bước 4: Tiếp theo các bạn phải chọn Target Framework, ở đây mình chọn .NET Core 3.1 (LTS) => rồi bấm Create để tạo mới một Project.

cai-dat-visual-studio-tren-windows (10)

Và đây chính là project cũng như đoạn code C# mặc định được Visual Studio tạo ra cho bạn.

  • (1) – Các bạn có thể bấm vào biểu tượng này để chạy project.
  • (2) – Đoạn mã C# sẽ in ra màn hình console dòng chữ “Hello World”
  • (3) – Logs trong quá trình chạy chương trình

cai-dat-visual-studio-tren-windows (11)

Ok, và đây chính là dòng chữ được in sau khi chạy chương trình.

cai-dat-visual-studio-tren-windows (12)

#3. Lời kết

Okay, như vậy là trong bài viết mình đã cùng với các bạn cài đặt thành công Visual Studio trên máy tính Windows – một công cụ lập trình mạnh mẽ của Microsoft, hỗ trợ rất tốt cho ngôn ngữ lập trình C# rồi nhé.

Phiên bản mình sử dụng để cài đặt và hướng dẫn trong bài viết là phiên bản cộng đồng, phiên bản dành cho cá nhân nên nó miễn phí và tất nhiên là nó sẽ ít chức năng hơn các phiên bản dành cho doanh nghiệp.

Hi vọng là bài viết này sẽ hữu ích với bạn. Hẹn gặp lại các bạn trong các bài hướng dẫn tiếp theo nha.

CTV: Nguyễn Đức Cảnh – Bài viết gốc tại blogchiasekienthuc.com

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

Xem thêm Việc làm IT hấp dẫn trên TopDev

Java Stream – Collectors và Statistics

Java Stream – Collectors và Statistics

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

Làm việc với Stream Collector/ Collectors đã lâu, liệu rằng bạn có biết ngoài Grouping và Partitioning, Stream còn hỗ trợ cả thống kê (Statistics). Bất ngờ chưa, Collector thật sự còn ẩn chứa nhiều điều mà anh em còn chưa biết tới.

Hãy cùng tìm hiểu collector và các methods của nó qua bài viết dưới đây. Cuộc đời anh em developer chúng ta sẽ bớt khổ.

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

  10 Java Web Framework tốt nhất
  10 lý do cho thấy tại sao bạn nên theo học ngôn ngữ lập trình Java

Trường hợp muốn tìm hiểu sâu hơn về stream và stream how it’s works, các bạn có thể khảo bài viết này.

Thực sự những method được cung cấp bởi collectors như Partitioning hay Grouping có sức hấp dẫn lạ kì.

1. Collectors.

A Collector represents a way to combine the elements of a Stream into one result.

Collector đại diện cho cách kết hợp tất cả các đối tượng trong Stream thành một kết quả duy nhất.

Để hoàn thành nhiệm vụ của mình, collectors sẽ chịu trách nhiệm thực thi 3 nhóm tác vụ chính:

  • A supplier of an initial value. Cung cấp một giá trị ban đầu.
  • An accumulator which adds to the initial value. – Nơi tổng hợp một loạt các giá trị ban đầu.
  • A combiner which combines two results into one. – Kết hợp hai kết quả thành một.

Collector được sử dụng thường xuyên khi ta sử dụng Stream API, nhưng ta ít khi để ý tới. Có hai cách để sử dụng:

// Phương thức ngắn gọn.
collect(Collector) (types left off for brevity)
// Các phương thức chi tiết để có một collection.
collect(supplier,accumulator,combiner).
Hình ảnh mô tả các bước để có một Collection thông qua Collector. Giá trị trả về sẽ là Optional.

Phương thức import để sử dụng collectors là:

import staticjava.util.stream.Collectors.*;

1.1 Collectors đơn giản.

Hai methods đơn giản và thường được sử dụng nhiều với collectors là toList() và toCollection().

// Lấy danh sách tên toàn bộ gái xinh, chuyển qua List.
List<String> listGaiXinh = Gai.stream() .map(Gai::getName)
.collect(toList());

// Lấy danh sách tên toàn bộ gái xinh, chuyển qua TreeSet.
Set<String> setGaiXinh = Gai.stream() .map(Gai::getName)
.collect(toCollection(TreeSet::new));

1.2 Joinning.

Trước đây, muốn Joining chuỗi trong Java, thường ta sử dụng Apache Common StringUtils.join. Khi sử dụng collector, ta có thể gọi phương thức joining. Khi gọi phương thức này, bản thân collectors lúc này biến thành một thùng chứa, kết nối các chuỗi String nhỏ, cho ra một String lớn.

// Tạo chuỗi gái xinh, cách nhau bởi dấu phẩy.
gaiXinhStr = gai.stream() .map(Gai::getName)
.collect(joining(","));
stream-collector-ho-tro-joining-chuoi-string-cuc-totXưa rồi nhé StringUtils của Apache. Stream nay hỗ trợ joining chỉ trong một nốt nhạc.

2. Statistics.

Ngoài công việc kết nối hoặc tổng hợp một loạt các giá trị. Collector còn cung thêm cả chức năng thống kê (statistics).

// Đọc file Rio, tính trung bình các dòng khác rỗng.
System.out.println(
Files.lines(Paths.get("Rio.java"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(averagingInt(String::length))
);

2.1 Summarizing.

Summarizing là một method của collector, phương thức này trả về một lớp đặc biệt chứa thông tin thống kê (statistical information). Thông tin này thống kê dữ liệu cuối đã được trích xuất khỏi Stream.

// Lấy danh sách gái xinh có số đo vòng một cực lớn.
// Ôi, tao thích Collectos quá đi thôi. =))) 
int soEmVongMotLon = gaiXinh.stream()
.collect(summarizingInt(Gai::vongMotLon));

2.2 Averaging.

Phương thức averaging trả về giá trị trung bình của các đối tượng.

// Tính tuổi trung bình danh sách gái xinh.
// Có collectors, đã không còn cực như xưa.
int tuoiTrungBinh = gaiXinh.stream()
.collect(averagingInt(Gai::getAge));

2.3 Summing.

Tương tự như tính giá trị trung bình, Summing trả về tổng các đối tượng trong Stream.

// Tính tổng số tuổi danh sách gái xinh.
// Có collectors, đã không còn cực như xưa.
int tongSoTuoi = gaiXinh.stream()
.collect(SummingInt(Gai::getAge));
Stream-collector-summing-co-che-congLuồng thực hiện summing ở Stream. Các phép cộng được thực hiện tuần tự ở mỗi lần gọi method getCalories.

2.4 MaxBy / MinBy.

MaxBy/MinBy collectors return the biggest/the smallest element of a Stream according to a provided Comparator instance.

MaxBy/MinBy collectors trả về giá trị lớn nhất/ nhỏ nhất của Stream theo thể hiện được cung cấp bởi Comparator.

// Lấy 
// Có collectors, đã không còn cực như xưa.
Optional<String> result = gaiXinh.stream()
.collect(maxBy(Comparator.getAge()));

Có một lưu ý nhỏ là giá trị trả về khi gọi phương thức MaxBy và MinBy phải được đóng gói theo kiểu Optional instance.

2.5 Grouping By.

GroupingBy collector is used for grouping objects by some property and storing results in a Map instance.

GroupingBy collector được sử dụng để gom một số đối tượng bởi thuộc tính của chúng, kết quả trả về sẽ được lưu vào một thể hiện (instance) của Map.

Map<Integer, Set<String>> result = givenList.stream()
.collect(groupingBy(String::length, toSet()));
Java-Stream-Collectors-Grouping-ByCơ chế groupingBy ở Stream tương tự như GROUP_BY ở SQL, các nhóm sẽ gom lại với nhau tùy theo điều kiện được thiết lập ở Stream.

2.6 Partitioning By.

PartitioningBy is a specialized case of groupingBy that accepts a Predicate instance and collects Stream elements into a Map instance that stores Boolean values as keys and collections as values.

PartitioningBy là một trường hợp đặc biệt của groupingBy, nó chấp nhận thêm Predicate Instance và Stream elements bên trong Map, các giá trị này sẽ được lưu kiểu Boolean giống như một keys.

Lý thuyết như thế này thì hơi khó hiểu. Ví dụ như sau: Nếu bạn có một nhóm bạn năm người. Trong đó, ba người ăn chayhai người còn lại thì không. Ta sẽ sử dụng partitioning để chia nhóm người thành 2 vùng (ăn chay và không).

Hai nhóm người được phân như sau:

  • Ăn chay: Hạnh, Hải, Anh, Ẩn.
  • Ăn mặn: Hậu, Vinh, Tú, Tùng.
Map<Boolean, List<Dish>> partitionedMenu = menu.stream()
.collect(partitioningBy(Dish::isVetetarian));

Kết quả trả về sẽ là:

  • True: [Hạnh, Hải, Anh, Ẩn].
  • False: [Hậu, Vinh, Tú, Tùng].

Chỉ nên sử dụng partitioningBy trong trường hợp phép so sánh để phân các đối tượng thành vùng là phức tạp. Ngược lại, đơn giản ta chỉ cần sử dụng Stream.filter().

List<Dish> vegetarianDishes = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());

3. Kết luận.

Stream và Collectors kết hợp lại với nhau thật sự là một cặp đôi song sát. Hữu hiệu và mạnh mẽ. Việc ghi nhớ các method được cung cấp bởi Collector sẽ rất hữu hiệu cho anh em trong quá trình code. Sử dụng linh hoạt collector giúp code trở nên đơn giảndễ đọc, dễ mantainance.

Ngoài ra, collectors còn trở nên mạnh mẽ hơn khi ta có thể custom một class của nó, tạo riêng cho mình một collector.

Khi cần thiết, hãy sử dụng Java Stream Collectors, cuộc đời bạn sẽ bớt khổ.Khi cần thiết, hãy sử dụng Java Stream Collectors, cuộc đời bạn sẽ bớt khổ.

Mong rằng bài viết này sẽ giúp đỡ các bạn phần nào.

Thanks and love u so much!.

4. Tham khảo.

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

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

Xem thêm Việc làm IT hấp dẫn trên TopDev