Trợ lý giọng nói (voice assistant) và tìm kiếm bằng giọng nói (voice search) đang dần trở thành một xu hướng mới. Trong số những trợ lý ảo phổ biến nhất hiện nay thì Google Assistant là cái tên bạn không nên bỏ qua. Hiện tại, Google Assistant đang hỗ trợ hơn 19 ngôn ngữ, trên hơn 80 quốc gia và có mặt trên nhiều loại thiết bị khác nhau như các thiết bị Smart Display, các dòng điện thoại Android, Iphone, Google Home, loa thông minh,…
Bạn có thể tận dụng Google Assistant để tiếp cận lượng người dùng lớn của trợ lý ảo này bằng cách tạo ra các action thông qua nền tảng Actions on Google. Bài viết sẽ hướng dẫn chi tiết về nền tảng này thông qua topic “Multiple ways to build for Google Assistant” của Ms. Jessica Dene Earley-Cha – Developer Advocate @Google tại sự kiện Vietnam Web Summit 2020 LIVE – biên tập bởi TopDev.
Bài viết sẽ phù hợp nếu bạn:
Là một Web Content Owner, hãy tìm hiểu về Content Actions: các loại công thức, những nội dung hướng dẫn, podcast, tin tức,…
Là một Android App Developer, hãy tìm hiểu về App Actions.
Là một người thích tìm hiểu về công nghệ giao tiếp bằng giọng nói, hãy tìm hiểu về Conversational Actions với Interactive Canvas.
Là một Hardware Developer, hãy tìm hiểu về Smart Home SDK.
Google Assistant hỗ trợ người dùng, khiến đời sống và công việc trở nên hiệu quả hơn
Content Action – Khiến nội dung của bạn xuất hiện trên Google Assistant
Nếu bạn đã có một website, một podcast hay một danh sách công thức thì bạn có thể đưa những nội dung trên của bạn vào trong Google Assistant.
Tương tự như việc tối ưu website cho Google Search, bạn cần cấu trúc website nhằm giúp Google Search có thể hiểu được dữ liệu đó và có thể mang chúng đến và phục vụ cho user thì với Google Assistant điều này cũng diễn ra tương tự.
Tất cả những việc bạn cần làm là thêm vào scheme.org và cấu trúc các trang làm sao để Google Assistant có thể nhận biết, hiểu và mang những nội dung của bạn đến người dùng.
Các loại nội dung trên Google Assistant
Thường những nội dung trong Google Assistant được phân loại thành: news; podcast; recipes; FAQs; how-to.
Việc bạn cần làm là hãy thêm vào Structured Data Markup (Đánh dấu dữ liệu có cấu trúc) để content của bạn có thể được tìm thấy bởi nhiều công cụ tìm kiếm hơn, trong đó có cả Google Assistant.
Đây là một ví dụ về một quyển sách nói. Họ chỉ sử dụng RSS, đồng thời có thể thêm vào cấu trúc dữ liệu ấy, từ đó mà có thể được tối ưu hoạt động trên cả Search và Assistant.
App Action – Kích hoạt chức năng giọng nói cho các ứng dụng Android
App Action là một hành động giúp bạn gửi một ứng dụng Android đến người dùng thông qua câu thoại lệnh “Hey Google, open *your app*”.
Thách thức “Tái tương tác ứng dụng”
Thông thường doanh nghiệp sẽ phải tốn rất nhiều chi phí để có thể khiến người dùng cài đặt app cũng như khiến họ tương tác, sử dụng ứng dụng một cách thường xuyên. Một trong số những lý do tại sao khiến chi phí này lại trở nên đáng kể là bởi trên thiết bị người dùng thường có hàng tá icon khiến ứng dụng của bạn bị lu mờ (mình biết điều này vì trên điện thoại mình cũng thế :P)
Bởi thế, việc mà chúng ta cần làm chính là tìm cách xem rằng làm thế nào để vượt qua những rào cản trên, giúp người dùng không cần phải nhớ xem app của bạn trông như thế nào và họ có thể dễ dàng tìm thấy nó.
Với App Action, công nghệ này cho phép Android Developer sử dụng Google Assistant như một công cụ để mang ứng dụng của bạn nhanh chóng đến người dùng thông qua giọng nói.
Để hiểu rõ hơn, chúng ta cùng xem một ví dụ được gọi là SmoresSmores.
Hoàn tất ‘chiếc bánh’ S’more theo cách cũ
Bước 1: Tìm ứng dụng SmoreSmores bên trong mớ icon hỗn độn trên thiết bị.
Bước 2: Chọn loại bánh quy.
Bước 3: Chọn loại marshmallow.
Bước 4: Chọn loại chocolate.
Bước 5: Xác định độ nóng giòn.
Bước 6: Xác định số lượng.
Bước 7: Xem lại order.
Bước 8: Hoàn tất order.
Bạn có thể hình dung một số bước có thể diễn ra để hoàn tất một chiếc bánh S’more trên ứng dụng theo cách thông thường:
Đầu tiên, người dùng sẽ phải tìm kiếm icon của ứng dụng trong số mớ icon hỗn độn trong thiết bị của họ. Sau đó, họ sẽ phải lựa chọn từng option cho mình như: loại bánh quy nào , loại marshmallow nào, có thêm chocolate hay không, độ chính và số lượng bánh,…
Người dùng sẽ phải trải qua nhiều bước mới có thể hoàn tất order của mình. Điều này sẽ mất khá nhiều thời gian. Tuy nhiên, có một phương pháp khác để hoàn tất order và tiết kiệm công sức hơn cho người dùng, đó là triển khai thực hiện App Action. Người dùng sẽ có thể nói chuyện và thực hiện order một cách tự nhiên nhất thông qua chính cách nói chuyện của họ.
Hoàn tất S’more bằng giọng nói với App Actions
Với giải pháp này, người dùng chỉ cần thực hiện 2 bước đơn giản.
Bước 1: Thao tác bằng giọng
Bước 2: Xác nhận order
Theo cách này, tất cả thông tin sẽ được đưa vào ứng dụng và ứng dụng mở lên trang xác nhận thông tin mà người dùng đã cung cấp. Điều này sẽ khiến cho người dùng dễ dàng thao tác hơn.
Với App Actions, tất cả thông tin sẽ được đưa vào ứng dụng và ứng dụng sẽ mở lên đến ngay trang xác nhận thông tin mà người dùng đã cung cấp bằng chính giọng nói của họ (loại bánh, marshmallow, chocolate, độ nóng và số lượng,…)
Một ví dụ khác, hãy tham khảo trường hợp của Nike
Bạn có thể nói chuyện với ứng dụng của Nike, yêu cầu mở ứng dụng Nike Run Club.
Ngoài ra bạn còn có thể thực hiện những thao tác khác như chuyển tiền hay đặt hàng thông qua ứng dụng.
Tất cả những tác vụ như trên có thể được thực hiện là bởi Google Assistant sử dụng một thứ được gọi là built-in intents.
Built-in intents
Hãy để Google biết rằng bạn có những tính năng nào, chẳng hạn như tính năng đặt đồ ăn hay bắt đầu một buổi tập luyện, từ đó giúp Google có thể biết được đâu là nơi mà nó cần kết nối đến.
Hãy cho Google biết đâu là những tính năng mà người dùng có thể sử dụng; các loại parameter mà bạn cung cấp, đâu là các fulfillment URL, đâu là trang đích khi tôi tổng hợp tất cả các parameter đó lại.
Bạn có thể sử dụng bao nhiêu built-in intent và thêm vào bao nhiêu mà bạn muốn.
Các intent google assistant đang hỗ trợ
Và đây là một file XML để ví dụ cho việc sử dụng built-in intent cho hành động gọi món.
Một file XML để ví dụ cho việc sử dụng built-in intent cho hành động gọi món
Bạn có thể thấy trên ví dụ sử dụng:
intentName=“actions.intent.ORDER_MENU_ITEM”
Đây là một built-in intent mà Google đã cung cấp và bạn có thể sử dụng nó và người dùng sẽ có thể gọi món từ menu.
Android Slices
Slices là các UI template có thể hiển thị các nội dung tương tác động từ ứng dụng của bạn bên trong Google Assistant.
Hẳn rằng nhiều lần bạn muốn cung cấp cho người dùng một thông báo nhanh hay một xác nhận đơn hàng nào đó. Bạn có thể sử dụng Android Slice để cung cấp những thông tin ấy một cách đơn giản và thân thiện đến người dùng và giúp họ cập nhật thông tin một cách nhanh chóng.
Một lưu ý nhỏ: khi bắt đầu với các built-in intent và item này, Google Assistant đã kiểm soát và quản lý tất cả NLU (Natural Language Understanding) nên bạn không cần phải lo về cách mà người dùng nói chuyện khi bạn sử dụng built-in intent trong Android Slices.
Smart home – Control hardware with just your voice
Smart Home Device Integration
Khi nhắc về Smart Home, tất cả những gì chúng ta cần nói đến đều chỉ là về tích hợp với phần cứng (integrating with hardware).
Nếu bạn quan tâm đến việc tự động hóa ngôi nhà của mình, bạn có thể hiện thực hóa điều ấy thông qua giải pháp Smart Home Integration của Google.
Giải pháp này sẽ cung cấp cho bạn một Home Graph, giúp ngôi nhà nắm được trạng thái và khả năng thực hiện của tất cả các thiết bị kết nối bên trong ngôi nhà. Người dùng có thể đưa ra yêu cầu như “làm mờ đèn một chút” và ngôi nhà sẽ biết rằng chúng cần làm gì.
NLU
Phần hay nhất nội dung này chính là Natural Language Understanding. Giống với Android và built-in intents, Google Assistant sẽ kiểm soát tất cả vấn đề này cho bạn. Nếu ai đó nói “làm ấm phòng” hay “bật/tắt đèn” hay nói về một nhiệt độ cụ thể như “tôi muốn 24 độ”, nó sẽ chuyển dữ liệu đó như một parameter và thực hiện yêu cầu đó.
Types & traits
Type thực chất là những thiết bị, phần cứng (hardware). Bạn có thể nghĩ đến những thứ như cửa sổ, đèn, lò sưởi, rèm che,…
Trait là thuộc tính hay đặc điểm, chức năng của type. Bạn có thể hình dung về những yêu cầu đơn giản như bật/tắt đèn, nhưng cũng có một số loại đèn có thể điều chỉnh độ sáng.
Google có đề cập rất nhiều về nội dung này trong tài liệu, bạn có thể kiểm tra chúng tại đây.
Smart Home Integration
Để có thể tích hợp thiết bị của bạn với Google Assistant, nhà phát triển cần cung cấp cloud service của chính họ cho người dùng để đăng ký và quản lý thiết bị, điện thoại thông minh và Action Platform. Sau đó, hãy tích hợp với cloud service thông qua một chuỗi các webhook để truy cập và kiểm soát các thiết bị đó.
Một thứ mà chúng tôi tự hào khi bắt đầu nó từ năm ngoái chính là local SDK. Local home SDK của Google được thực hiện thông qua wifi. Các thiết bị trên local network có thể được phát hiện và kiểm soát với local protocols, giúp giảm chi phí giao dịch đám mây.
Conversation
Cuối cùng, nếu bạn muốn xây dựng một trải nghiệm phong phú cho người dùng, bạn nên xây dựng các conversation action.
Tương tự như cách smart home được cấu trúc và những thứ tương tự, người dùng sẽ gửi một request và Google Assistant tiến hành xử lý.
Tuy nhiên, không giống như với Android hay với Smart Home – chúng đã có các built-in intents hay types of traits, nơi mọi thứ đã được định nghĩa và Google đã xử lý phần natural language processing, tại đây, Google Assistant sẽ xử lý phần text-to-speech và cho bạn text của người dùng. Sau đó, tùy vào action mà bạn muốn thực hiện mà hệ thống sẽ xác định đâu là thứ cần gửi lại, nhưng việc xác định đâu là thứ mà cần hệ thống gửi lại bạn vẫn là người quyết định. Hãy gửi một response tới Google Assistant và Google Assistant sẽ chuyển nó đến cho người dùng.
Natural Language Understanding (NLU)
Giả sử, nếu người dùng chỉ nói “yes” thì việc phân tích cú pháp sẽ tiến hành khá nhanh phải không?
Tuy nhiên, nếu người dùng nói “yes” theo nhiều cách (biến/variation) khác nhau như: “Perfect”, “Let’s do it”, “Go ahead”, “Sure”, “Yep”, “oke”,… thì bây giờ phải làm sao?
Lúc này, bạn sẽ cần một công cụ để quản lý tất cả các biến và đó chính là NLU.
Để giúp xử lý thông tin của người dùng, bạn hãy xác định các intent khác nhau mà người dùng có thể sẽ thể hiện một “yes” intent hay “no” intent, hay khi người dùng muốn chơi một trò chơi nào đó.
Khi bạn đã xác định những thứ trên thì hãy đưa ra những hướng dẫn (training phrases) cho các intent ấy, từ đó Google Assistant có thể hiểu được và biết được rằng khi người dùng nói về các intent đó, như “yes” chẳng hạn, Google sẽ đóng gói tất các các biến của “yes” và tiếp tục xây dựng thêm dựa trên đó.
Nhưng ai sẽ làm việc đó?
Cuộc đối thoại hai chiều với action của bạn được thực hiện theo cách sau.
Input của người dùng sẽ được gửi đến, và sau đó, Google Assistant sẽ tiến hành xử lý nó bằng NLU và gửi thông tin đó đến Action.
Tiếp đến, action sử dụng chúng để tạo ra một prompt và gửi prompt ấy đến Google Assistant. Cuối cùng, Google Assistant sẽ chuyển nó đến người dùng.
Nhưng làm sao để bạn biết được khi nào người dùng nói “yes”? Họ sẽ nói “yes” ở vị trí nào trong câu thoại? Có phải họ đang nói “yes, I want to play the game” hay “yes, I want to drink the magic potion in the game”. Bạn không thể xây dựng các intent kiểu như “fly around”. Bạn cần phải tổ chức nó theo một cách nào đó.
Quản lý mô hình hội thoại (conversational model) cho action chính là chìa khóa để tạo ra các prompt phù hợp ấy và bạn có thể sử dụng Actions Builder để thực hiện điều này.
Actions Builder
Actions Builder là một web-based IDE được tích hợp hoàn toàn vào Action Console.
Hiện tại bạn chỉ có duy nhất một window để xây dựng các action. Bạn có thể thiết kế các luồng hội thoại một cách trực quan và đồng thời hỗ trợ và cải tiến NLU khi nó tiến hành xử lý các input của người dùng. Đồng thời, nó cũng bao gồm một inline editor vì thế bạn cũng có thể xây dựng các phản hồi động bằng cách sử dụng webhook.
Với Actions Builder, nó sẽ xử lý input của người dùng và cũng làm như thế với intent matching hoặc slot filling khi nó đến các types. Bạn cũng có thể lấy các phần dữ liệu ra khi nó đến users responses.
Tương tự, chúng ta có khái niệm scenes để quản lý logic. Nếu nó thực sự bắt đầu và người dùng nói “yes”, intent sẽ khớp với “Yes” intent, nhưng bởi vì nó đang ở start scene, hệ thống biết “oh, họ đang nói yes để bắt đầu trò chơi chứ không phải nói yes để sử dụng item hồi máu”. Từ đấy, nó có thể chuyển tiếp bạn tới scene kế tiếp để bắt đầu trò chơi, và bạn cũng có thể xây dựng các prompt động hoặc tĩnh bằng cách sử dụng công cụ này.
Scenes
Scene là một khái niệm mới khi “Action Developer” sử dụng công cụ cũ của Google.
Scenes là những khối logic (logical chunks) của Mô hình thoại hội của bạn và chúng cũng là thứ sẽ thực thi các action của bạn. Chúng sẽ xử lý những phần nặng nhọc, xử lý các logic cần thiết để định hướng cuộc trò chuyện. Đây là một cách thức để modularize action của bạn.
Bạn có thể nhận được một welcome scene, và ở scene này, bạn sẽ được hỏi rằng liệu bạn có muốn làm cái này, làm cái kia hay không. Chúng có mối liên hệ chặt chẽ với nhau.
Nếu một người dùng nói gì đó, hệ thống có thể sẽ chuyển tiếp họ đến scene tiếp theo. Điều thú vị là scene của bạn có thể hỏi và thu thập dữ liệu thông qua các slot. Nếu socket của bạn được fill, một scene có thể chuyển tiếp đến scene tiếp theo. Tương tự, bạn có thể liên tưởng đến việc order, bạn có thể lấp đầy hoặc chúng ta có thể có tất cả thông tin như mua một chiếc đầm dạ hội, chúng ta sẽ biết được họ muốn mua size nào, sau đó chuyển tiếp bạn đến nơi có thể mua hàng hoặc nơi cung cấp thêm thông tin.
Vòng đời thực thi của scene
Các scene rất linh hoạt, chúng cho phép bạn thực hiện những thứ rất tuyệt và tất cả các scene chạy bên trong vòng lặp, mọi thứ điều tuyệt vời, thế nên, vòng đời thực thi (executor lifecycle) là một thứ gì đó mà chúng ta nên thử phân tích cú pháp và đào sâu thêm vào nội dung này. Hãy xem video ngắn bên dưới.
Action SDK
Bổ sung thêm cho Actions Builder là Actions SDK. Action SDK sẽ cho bạn một file-based representation của action và khả năng sử dụng các local IDE của chính bạn. Phần hay nhất là nó hoạt động song song với Actions Builder. Nó bao gồm tất cả các tài nguyên cấu hình của action của bạn, gồm có khả năng hỗ trợ và quốc tế hóa.
Bạn có thể build bằng cách sử dụng Actions Builder, sau đó kéo dự án xuống local, thay đổi và đẩy những thay đổi mới ấy lên Action Builder và cuối cùng là cho phép team làm việc với công cụ yêu thích của họ.
Google cũng đã cải thiện phần giả lập – thứ cung cấp cho bạn các execution log cùng với các dữ liệu về request và response để giúp bạn dễ dàng debug các action của mình. Chúng được tổ chức theo scene, theo sự tương tác giữa người dùng và Google Assistant.
Sau đó, bạn có thể click lên nó và bạn có thể nhìn thấy dữ liệu của scene.
Bạn có thể thấy những parameter nào đang được chuyển qua khi các webhook đang bị gọi.
Google cũng đã thêm state editor để khiến nó dễ dàng để update và debug content của bạn.
Thông qua việc tạo ra các thay đổi trong scene, trong phần scene editor, bạn không nhất thiết phải trải qua tất cả flow, bạn có thể kết nối tới các thứ dễ dàng hơn hoặc khiến nó như kiểu người dùng đã hoàn thành việc xyz và theo cách đó, bạn có thể đi trực tiếp đến nơi mà bạn muốn test bên trong luồng hội thoại.
Gia tăng trải nghiệm cho người dùng
Action Builder và Action SDK cung cấp các thành phần hội thoại (conversational components) của action nhưng bạn cũng có thể thêm vào những thành tố (element) khác để gia tăng trải nghiệm cho người dùng.
Bạn có thể sử dụng Interactive Canvas để build những trải nghiệm game thú vị, cung cấp những hình ảnh trực quan sinh động cho cuộc trò chuyện của bạn.
Một điều rất tuyệt chính là Interactive Canvas chỉ sử dụng như web app, vì thế HTML, CSS và JavaScript là tất cả những gì bạn cần để có thể tạo ra một web app ấy. Interactive Canvas sẽ kết nối trang web với cuộc hội thoại của người dùng và sẵn sàng để thực hiện trải nghiệm giọng nói khi bắt đầu action này.
Bạn có thể tìm hiểu chi tiết về công nghệ Interactive Canvas tại bài viết này.
Continuous Match Mode
Một điều khác là bạn có thể gia tăng thêm trải nghiệm người dùng với chế độ khớp liên tục (Continuous Match Mode). Thứ công nghệ này cho phép người dùng có thể nói nhiều từ hoặc cụm từ khác nhau nhất quán.
Một ví dụ điển hình là khi bạn muốn có một bản đồ quốc gia với tất cả các vùng, hoặc bản đồ thế giới có tất cả các quốc gia bên trong bản đồ đó, việc bạn cần làm là hiển thị chúng thông qua interactive canvas. Nhờ đó, bạn có thể trực quan hóa chúng và cho phép người dùng nói tên các quốc gia và thậm chí nói một cách nhanh chóng. Người dùng sẽ chỉ cần gọi tên từng quốc gia liên tục và chúng – hình ảnh các quốc gia sẽ được hiển thị liên tục thông qua công nghệ Continuous Match Mode.
Continuous Match Mode Developer
Ở góc độ của một lập trình viên, tất cả việc bạn cần làm là cung cấp cấu hình bao gồm danh sách các cụm từ chính xác mà người dùng có thể sử dụng / nói.
Trở lại ví dụ về bản đồ thế giới với khả năng hiển thị tất cả các quốc gia ở trên, bạn sẽ cần liệt kê ra tất cả các quốc gia và cung cấp thông tin đó đến action. Lúc này, interactive canvas web app có thể tiến hành xử lý chúng một cách nhanh chóng cho người dùng.
Bài viết được trích dẫn từ phần trình bày của Jessica Dene Earley-Cha tại sự kiện Vietnam Web Summit 2020 LIVE do TopDev tổ chức
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Qua 5 bài trước, chúng ta đã hiểu các khái niệm cơ bản về RxSwift. Và bây giờ, chúng ta sẽ bắt đầu làm quen với Observables, khái niệm quan trọng của RxSwift. Dịch sát nghĩa là có thể quan sát được. Ở Rx, nó là 1 lớp cho phép bạn quan sát bất cứ thứ gì mà có sự thay đổi về trạng thái, như trạng thái bấm nút, ô text,..
Sau gõ mở terminal, cd vào thư mục source code và gõ:
pod install
để cài đặt RxSwift cho project.
Vậy 1 observable là gì?
observable là trái tim của RxSwift. Chúng ta sẽ giành thời gian để nghiên cứu, thử nghiệm với nó.
Bạn sẽ nghe các khái niệm observable, observable sequence hay sequence trong Rx. Thậm chí bạn sẽ nghe các khái niệm stream từ các developer khác bằng các khái niệm ở môi trường phát triển khác tương tự. Tuy nhiên ở Rx nó không phải là stream, mà mọi thứ đều là sequence.
Nghĩa là trong Rx mọi thứ đều là luồng quan sát – dịch sát nghĩa là đối tượng quan sát sự kiện có thứ tự.
stream: sự kiện phát liên tục
sequence: sự kiện phát không đồng bộ, tùy ý
Sự kiện luồng phát 3 sự kiện 1, 2, 3 trong thời gian thực
Mũi tên từ trái sang phải mô tả thời gian, vòng tròn đánh số thể hiện sự kiện phát ra. Đầu tiên sự kiện 1 phát ra, tiếp theo lần lượt sự kiện 2, và cuối cùng là 3. Vậy lúc nào thì phát ra sự kiện? Nó có thể bất kỳ lúc nào ở trong vòng đời của 1 observable.
Vòng đời của 1 obserbvable
Sự kiện bấm vào nút
Những thứ có thể quan sát được và phát ra các sự kiện như nút bấm, được thể hiện bằng sơ đồ trên. Nút được phát ra 3 lần bấm, và kết thúc.
Tuy nhiên đôi khi sự kết thúc có thể trước 3 lần, do trục trặc lỗi.
Sự kiến phát ra có lỗi thể hiện màu đỏ
Khi 1 sự kiện lỗi phát ra, nó kết thúc luôn vòng đời của observable.
Một observable có thể quan sát và phát ra các sự kiện tiếp theo cho đến khi tạo ra một sự kiện lỗi và kết thúc
Hoặc phát ra 1 sự kiện hoàn thành và kết thúc
Một khi observable kết thúc, nó sẽ không thể phát ra thêm sự kiện nào nữa
Ví dụ trực tiếp từ mã nguồn RxSwift:
/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\* (error | completed)**
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
Như ta thấy ở đây:
Sự kiện Next chứa instance của Element
Sự kiện error chứa lỗi Swift.Error
Sự kiện completed đơn giản là dừng và không có bất kỳ dữ liệu nào
Tạo observables
Quay lại với project các bạn vừa tạo ở đầu bài, chúng ta thêm đoạn code sau vào file playground:
example(of: "just, of, from") {
// 1
let one = 1
let two = 2
let three = 3
// 2
let observable: Observable<Int> = Observable<Int>.just(one)
}
Ở đoạn code trên:
Tạo 3 biến one, two, three
Tạo 1 observable và phát ra sự kiện one – tức là phát ra số nguyên one
just là hàm phát ra 1 sự kiện, nó là phương thức của observable. Tuy nhiên trong Rx, nó được gọi là toán tử.
Thêm tiếp mã sau vào cuối chương trình:
let observable2 = Observable.of(one, two, three)
Lần này, chúng ta không chỉ định rõ ràng kiểu cho observable, tuy nhiên chúng ta hiểu đây là 1 observable kiểu int, và đây không phải là mảng. Bấm Option + click chuột để xem:
observable tự chuyển về kiểu int
Nếu bạn muốn tạo 1 observable kiểu mảng, chúng ta dùng toán tử of như sau:
let observable3 = Observable.of([one, two, three])
Còn khi dùng toán tử from, chúng ta tạo 1 observable như sau:
let observable4 = Observable.from([one, two, three])
Toán tử from nhận các giá trị từ mảng, và tạo 1 observable riêng rẽ, nó có kiểu int, chứ không phải kiểu mảng [int].
from tạo ra 1 observable kiểu int, từ 1 mảng kiểu int
Subscribing tới 1 observable
Là một nhà phát triển IOS, chắc các bạn đã quen thuộc với NotificationCenter, nó phát ra 1 notification tới observers. Dưới đây là ví dụ về xử lý sự kiện UIKeyboardDidChangeFrame, được xử lý ở hàm closure:
let observer = NotificationCenter.default.addObserver(
forName: .UIKeyboardDidChangeFrame,
object: nil,
queue: nil
) { notification in
// Handle receiving notification
}
Việc đăng ký – subscribing tới 1 observable cũng tương tự. Thay vì dùng hàm addObserver(), thì ta dùng hàm subscribe().
Tuy nhiên việc đăng ký các observable hợp lý hơn. Như đã trình bày, các observable phát ra các sự kiện next, completed và error. Để hiểu hơn, chúng ta thêm ví dụ sau vào cuối chương trình:
example(of: "subscribe") {
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
observable.subscribe { event in
print(event)
}
}
Kết quả:
--- Example of: subscribe ---
next(1)
next(2)
next(3)
completed
Observable phát ra các sự kiện 1, 2, 3 và completed để kết thúc.
Thường chúng ta sẽ quan tâm giá trị phát ra từ các sự kiện đó hơn là chính nó. Chúng ta sửa ví dụ trên sau:
observable.subscribe { event in
if let element = event.element {
print(element)
}
}
Kết quả:
--- Example of: subscribe ---
1
2
3
Sự kiện phát ra chứa giá trị, và là 1 giá trị optional. Vì vấy chúng ta check trước giá trị đó để tránh nil. Các sự kiện không chứa phần tử(ở đây là completed) sẽ không được in ra. Như ta đã biết, các sự kiện onNext đều có phần tử.
Thay thế đoạn code trên như sau:
observable.subscribe(onNext: { element in
print(element)
})
Vậy là bạn đã biết cách tạo các observable có phần tử. Vậy muốn tạo 1 observable rỗng(empty) thì làm thế nào? Xem ví dụ sau:
example(of: "empty") {
let observable = Observable<Void>.empty()
observable
.subscribe(
// 1
onNext: { element in
print(element)
},
// 2
onCompleted: {
print("Completed")
} )
}
Một observable phải được định nghĩa 1 kiểu có thể quan sát được. Trong trường hợp này nó là kiểu void.
Giống như sự kiện onNext bạn xem ở ví dụ trước
sự kiện onCompleted không trả ra phần tử nào mà chỉ đơn giản là phát sự kiện hoàn thành.
--- Example of: empty ---
Completed
Nhưng mục đích của việc sử dụng 1 observable empty là gì? Nó thật sự hữu ích khi bạn muốn trả về 1 sự kiện kết thúc ngay lập tức và không trả về giá trị.
Trái với toán tử empty, toán tử never không emit ra sự kiện nào và không bao giờ kết thúc. Cùng xem ví dụ sau:
example(of: "never") {
let observable = Observable<Any>.never()
observable
.subscribe(
onNext: { element in
print(element)
},
onCompleted: {
print("Completed")
}
) }
Nó không in ra bất cứ thứ gì kể cả Completed, vậy làm sao để biết là nó hoạt động? Hãy giữ vững tinh thần học hỏi cho đến phần Challenges sau này nhé.
Cho đến bây giờ, bạn đang làm việc với các observable có thể quan sát được. Nhưng bạn cũng có thể tạo ra observable từ 1 loạt các giá trị. Cùng xem ví dụ sinh dãy Phibonaci sau:
example(of: "range") {
// 1
let observable = Observable<Int>.range(start: 1, count: 10)
observable
.subscribe(onNext: { i in
// 2
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) /
2.23606).rounded())
print(fibonacci)
})
}
Tạo 1 observable từ các số có phạm vi từ 1 đến 10
Tính toán và in ra số fibonacci thứ n cho mỗi phần tử được emit ra.
hàm pow(x, y) trả về x^y.
Trên thực tế, có 1 nơi tốt hơn ngoài xử lý onNext, để đặt mã biến đổi emit sự kiện. Chúng ta sẽ tìm hiểu vấn đề này sau ở bài Transforming Operators.
Ngoài trừ ví dụ về never(), cho đến thời điểm này bạn đang làm việc với các observables có thể quan sát tự động phát ra sự kiện completed để kết thúc. Điều này giúp bạn tập trung vào cơ chế đăng ký observables, tuy nhiên lại gạt đi 1 khía cạnh quan trọng trong việc đăng ký đã xử lý như nào. Khi cấp phát thì nghĩa là cần phải giải phóng cho nó. Đã đến lúc chúng ta nghiên cứu việc giải phóng 1 observable như nào trong bài tiếp theo.
Trong bài viết này, mình sẽ hướng dẫn các bạn cài Flutter SDK lên Linux, cụ thể mình sử dụng distro phổ biến nhất của Linux là Ubuntu để hướng dẫn các bạn. Trước khi bắt đầu, do mình đã có video trên Youtube, và trong bài hướng dẫn cho Windows mình cũng đã hướng dẫn khá chi tiết nên trong bài viết này, những thao tác nào giống mình sẽ nói nhanh qua thôi. Con bây giờ thì bắt đầu thôi!
Đầu tiên bạn truy cập trang web flutter.dev, bấm vào Get Started, tiếp theo bạn chọn Linux. Ở trang này, bạn tìm đến mục “Get the Flutter SDK”, sau đó bấm vào nút download Flutter SDK ngay bên dưới mục 1 của mục “Get the Flutter SDK” để tải Flutter SDK về. Các bạn chờ tải về xong và mở file bằng Archive Manager hoặc chọn Open with Archive Manager cũng được.
Sau khi mở file bằng Archive Manager, các bạn chọn Extract.
Tiếp tục, các bạn chọn đường dẫn để giải nén folder “flutter” ra. Mình sẽ chọn giải nén ra thư mục “/home/khiemle/Development”. Sau khi giải nén xong, các bạn mở Terminal (ctrl + alt + T), nhập lệnh “sudo gedit ~/.bashrc” và enter, sau đó nhập mật khẩu. Một cửa sổ editor sẽ hiện lên để bạn chỉnh sửa file .bashrc.
Các bạn thêm đoạn sau vào cuối file .bashrc: export PATH=”$PATH:[PATH_TO_FLUTTER_GIT_DIRECTORY]/flutter/bin”. Các bạn sửa [PATH_TO_FLUTTER_GIT_DIRECTORY] thành đường dẫn chứa folder flutter của bạn. Ví dụ trong trường hợp của mình là: export PATH=”$PATH:/home/khiemle/Development/flutter/bin”.
Mục đích của việc trên là để thêm flutter vào trong $PATH để có thể sử dụng command flutter trong terminal mà không cần mỗi lần mở terminal mới phải export mất thời gian, và cũng để cho các ứng dụng khác ví dụ như VS Code có thể sử dụng được command flutter. Sau khi đã thêm flutter vào $PATH xong, các bạn nhấn Save để lưu lại và tắt nó đi.
Tiếp theo, các bạn tắt các cửa sổ Terminal hiện tại đi, mở một cửa sổ terminal mới, gõ lệnh sau: flutter –version. Nếu như nó hiện thông tin về Flutter SDK, Dart version… thì bạn đã cài thành công SDK. Nếu như máy bạn chưa cài đặt Git, khi bạn chạy lệnh trên, nó sẽ báo là “Unable to find git in your PATH”, lúc này, bạn chỉ cần chạy câu lệnh “sudo apt-get install git -y” là được. Sau khi cài git, bạn có thể chạy câu lệnh flutter bình thường.
Lời kết
Vậy là qua bài viết này, mình đã hướng dẫn các bạn cài đặt Flutter SDK trên Linux (Ubuntu distro). Nếu các bạn có thắc mắc gì có thể để lại bình luận bên dưới video hoặc bài viết này để mình có thể giúp các bạn nha. Bạn nào chưa biết cách cài trên Windows đừng quên tham khảo bài viết này nha. Đừng quên chia sẻ cho mọi người nếu bạn thấy hay! Cảm ơn các bạn đã theo dõi bài viết!
Nếu các bạn thích đọc hướng dẫn và tài liệu bằng tiếng anh và ví dụ cụ thể thì có thể tham khảo các nguồn sau đây mình cũng học và tham khảo trên các trang này:
Để download gói cài đặt các bạn vào trang này để lấy gói phù hợp với máy các bạn: https://golang.org/dl/
Cài đặt Golang trên Windows.
Cài đặt
Các bạn vào trang download của Golang mình đã để link phía trên và tím đến chỗ dowload gói cài đặt cho Microsoft Windows, tiến hành download về thôi.
Cũng như cài đặt các chương trình khác các bạn nếu không muốn điều chỉnh gì thì cứ nhấn Next, next nhé.
Chấp nhận điều khoản, nhấn Next.
Ở bước này các bạn có thể điều chỉnh lại đường dẫn mà chúng ta muốn cài đặt Golang trên máy, sau đó nhấn Next.
Nhấn Install nào hỏi nhiều quá đấy.
Các bạn chờ trong giây lát nhé, 1 xí nữa thôi là xong rồi.
Ok, nhấn Finish để hoàn thành việc cài đặt.
Như vậy chúng ta đã hoàn tất cả các bước cài đặt, bước tiếp theo là setup biến môi trường GOROOT và GOPATH.
Thiết lập biến môi trường.
Các bạn nhấn nút Windows và gõ tìm đến cài đặt biến môi trường như hình bên dưới.
Chọn vào Environment Variables.
Sau đó chọn New và thêm một biến (variable) tên là GOPATH với đường dẫn đến thư mục chúng ta muốn tạo các project Golang, đây là nơi làm việc với code Go của chúng ta.
Và nhìn xuống biến có tên Path để xem đã dẫn tới thư mục bin của Go mà ban đầu chúng ta đã cài đặt chưa nhé.
Mình thấy nó đã có rồi nên là ok, nếu chưa có thì mình sẽ thêm vào.
Sau đó restart lại máy và kiểm tra Go đã được setup đúng chưa.
Kiểm tra version của Go bằng command go version
Kiểm tra cấu hình cài đặt biến môi trường lưu ý 2 biến là GOROOT và GOPATH.
Cài đặt Golang trên Ubuntu.
Truy cập vào máy Ubuntu thực hiện việc update apt.
sudo apt-get update
sudo apt-get -y upgrade
Tải Golang về máy.
Vào thư mục mà chúng ta muốn ở đây mình sẽ bỏ vào thư mục /home/vxphong/go và thư mục này mình đã tạo sẵn trên máy.
Sau khi đã tải được gói cài đặt xuống ta tiến hành giải nén (extract).
Ở đây mình cái nhiều phiên bản nên mình sẽ đổi tên thư mục go vừa extract ra thành số version nha.
mv go 1.12.7
Để gọn trông nhìn sạch hơn mình xóa luôn file tar.gz nha, và đây là thư mục mà mình đã cài đặt Go.
cd 1.12.7
pwd
Chúng ta sẽ nhớ đường dẫn này để tí nữa cài đặt biến môi trường GOROOT nha.
/home/vxphong/go/1.12.7
Tạo ra thư mục làm việc với code Go
mkdir /home/vxphong/gopath
Tiếp theo chúng ta hãy thiết lập GOPATH và GOROOT
nano /home/vxphong/.bashrc
Và export các đường dẫn để thiết lập biến môi trường GOPATH, GOROOT và PATH.
export GOROOT=/home/vxphong/go/1.12.7
export GOPATH=/home/vxphong/gopath
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
Như vậy là đã xong kiểm tra version của Go nào các bạn, chúng ta tắt terminal đang làm việc đi và mở lại.
go version
Kiểm tra các biến môi trường đã thiết lập trước đó.
go env
Hello world trong Golang.
Quay trở lại với máy Windows của mình, các bạn nhớ lúc trước mình đá thiết lập biến môi trường GOPATH không? Trong thư mục GOPATH thì chúng ta tạo thêm thư mục src nữa nha. nơi làm việc để tạo ra các project sẽ nằm ở đó.
mkdir src
cd src
mkdir hello-world
Tạo file main.go trong thư mục hello-world với nội dung sau:
package main
import "fmt"
func main(){
fmt.Println("Hello World")
}
Bây giờ chúng ta tiến hành code và chạy chương trình Hello-World thôi nào
Dùng câu lệnh sau đây để chạy chương trình.
go run main.go
Tóm Lại:
Thư mục làm việc sẽ nằm trong thư mục src nằm ở đường dẫn GOPATH do chúng ta đã cài đặt trước đó. Các bạn có thấy các thư mục như github.com, golang.org không đó là nơi chứa các thư viện mà chúng ta sẽ cài đặt để sử dụng cho project. Có thể những bài tiếp theo mình sẽ hướng dẫn các bạn cái thư viện như thế nào nha, nhưng thật ra nó rất đơn giản thôi à chỉ cần 1 câu lệnh là nó sẽ tự động cài và bỏ vào trong các thư mục tương ứng, chỉ cần import để lấy ra sử dụng thôi.
Bài viết được sự cho phép của tác giả Chung Nguyễn
Hôm nay, nhóm Laravel đã phát hành một phiên bản chính mới của “laravel/installer” bao gồm hỗ trợ khởi động nhanh các dự án Jetstream. Với phiên bản mới này khi bạn chạy laravel new project-name, bạn sẽ nhận được các tùy chọn Jetstream. Ví dụ:
React Props Cheatsheet: 10 Patterns mà bạn nên biết (Phần 2)
Tác giả: Reed Barger
6. Cập nhật giá trị của React prop thông qua thanh trạng thái
Để chuyển một giá trị prop cho một thành phần, trong các thành phần của hàm đó cần cố gắng thay đổi ngay lập tức giá trị của prop.
Giá trị prop phải là giá trị thuần túy. Nói cách khác, chúng không thể bị biến dạng hoặc thay đổi trực tiếp. Trong React nếu chúng ta muốn thay đổi các giá trị theo thời gian, thì phương tiện thích hợp để làm điều đó là với status – trạng thái.
Nếu chúng ta muốn chuyển một giá trị prop vào một thành phần và thay đổi nó sau này, có thể đưa nó vào một hook React trạng thái để lưu trữ giá trị đó dưới dạng một biến. Sau đó, cập nhật bằng cách sử dụng chức năng setter thích hợp. Ví dụ, chúng ta có thể làm như vậy với useStatehook hoặc useReducerhook.
Điều gì sẽ xảy ra nếu chúng ta có một đối tượng gồm các thuộc tính mà chúng ta muốn chuyển xuống dưới dạng các giá trị prop độc lập? Tuy nhiên, đối tượng này có rất nhiều thuộc tính. Chúng ta có cần tạo thủ công các đạo cụ riêng lẻ và đặt giá đỡ thành object.propertyName không?
Thay vì làm điều đó cho mọi thuộc tính, chúng ta có thể rất dễ dàng lấy đối tượng và trải rộng các thuộc tính của nó xuống một thành phần dưới dạng các giá trị chống đỡ riêng lẻ bằng cách sử dụng toán tử mở rộng đối tượng {...myPropObject}.
Bằng cách sử dụng cú pháp này – một tính năng trong ES7, cho phép tự động tạo các đạo cụ riêng lẻ theo tên thuộc tính của đối tượng mà không cần phải viết tất cả các tên thuộc tính đó. Nó rất thuận tiện khi làm việc với các đối tượng lớn với nhiều thuộc tính mà chúng ta muốn chuyển dưới dạng các đạo cụ riêng lẻ cho một thành phần.
8. React props có thể tạo ra default value nếu không có giá trị nào được cung cấp
Điều gì sẽ xảy ra nếu ta đã biết rằng chúng mình đang chuyển một chỗ dựa cho một thể hiện của một thành phần, nhưng chúng ta không chuyển chỗ dựa đó cho một instance khác của nó?
Hoặc có thể biết rằng chỗ dựa có thể không có giá trị. Cần làm gì để cung cấp cho nó một giá trị mặc định thay vì chỉ một giá trị undefined nếu không có giá trị prop nào được chuyển cho nó?
Nếu chúng ta đang sử dụng hàm destructuring để truy cập prop đó trong thành phần hàm của mình, chúng ta có thể sử dụng toán tử bằng để cung cấp cho nó một giá trị mặc định. Vì vậy, nếu không có giá trị prop nào được chuyển cho prop đó, chúng ta có thể sử dụng toán tử bằng bên cạnh nó và đặt nó thành giá trị mặc định tốt hơn.
Đặt một giá trị mặc định là rất quan trọng vì giá trị mặc định bình thường cho một giá trị là undefined. Điều này có thể giúp tránh các lỗi có thể xảy ra do default value không ở đó.
9. React props có thể đặt lại tên để tránh errors
Ngoài ra, nếu có xung đột khi đặt tên với một trong các đạo cụ thì sao? Điều gì sẽ xảy ra nếu chúng ta sử dụng một tên prop trên nhiều thành phần và thấy rằng có một giá trị khác trong thành phần của chúng ta có cùng tên biến?
Thay vì phải đi vòng quanh và đổi tên tất cả các giá trị prop trên tất cả các phiên bản của các thành phần, chúng ta chỉ cần sử dụng dấu hai chấm sau tên prop đó, nếu chúng ta đang destructure nó, để đặt cho nó một tên gọi riêng biệt.
Nói cách khác, bạn có thể đặt cho nó một cái tên khác chỉ trong trường hợp đó. Điều này sẽ tránh được xung đột đặt tên cũng như lỗi.
10. Không nên destructure React props nhiều lần
Nếu chúng ta đang destructure một đối tượng từ các props, hãy lưu ý rằng có thể destructure prop đó sâu hơn nữa, tùy vào các thuộc tính cấu thành của nó. Tuy nhiên, thông thường không nên làm như vậy trừ khi bạn rất tin tưởng rằng đối tượng đó sẽ luôn có những thuộc tính đó.
Nếu một trong những thuộc tính đó bị thiếu và bạn cố gắng destructure nó ở nhiều cấp độ chuyên sâu, nó có thể gây ra cho bạn một lỗi khá khó chịu khi bạn đang cố gắng truy cập vào một thuộc tính không tồn tại.
Lưu ý rằng bạn có thể sử dụng destructure bao nhiêu tùy thích, nhưng nó có thể khó đọc đến một điểm nhất định và nó cũng có thể không đáng tin cậy. Nếu bạn đang cố gắng truy cập một thuộc tính trên một đối tượng, có thể không tồn tại, nó sẽ gây ra lỗi.
Trong các bài viết trước chúng ta đã cùng tìm hiểu về các loại Exchange trong RabbitMQ. Có một câu hỏi đặt ra là có thể thực hiện binding một Exchange đến Exchange khác hay không? Câu trả lời là có và chúng ta sẽ thấy cách thực hiện như thế nào trong phần tiếp theo của bài viết này.
Sơ đồ bên dưới là sự kết hợp của sơ đồ ở bài viết Topic Exchange và Header Exchange. Điểm khác biệt duy nhất là cách mà Header Exchange binding tới Topic Exchange với routing key pattern “#.gpcoder.com“.
Một Producer publish một Message đến source Exchange với một routing key dựa trên loại của Exchange. Trong trường hợp này là GPCoderTopicExchange.
Có 2 Queue: QJava và QAll được binding tới Exchange GPCoderTopicExchange lần lượt với routing key patter:
QJava : Queue này sẽ nhận tất cả message có Key match với routing key “java.*.gpcoder.com“. Nghĩa là chỉ nhận các message cho một topic Java cụ thể từ gpcoder.com, chẳng hạn: java.core.gpcoder.com, java.collection.gpcoder.com.
QAll : Queue này nhận tất cả message có Key match với routing key “#.gpcoder.com“. Nghĩa là nhận tất cả message từ gpcoder.com, chẳng hạn: design-pattern.gpcoder, java.gpcoder.com, creational.design-pattern.gpcoder.com.
Một Exchange khác là GPCoderHeadersExchange binding tới Exchange GPCoderTopicExchange với routing key pattern #.gpcoder.com.
Có 3 Queue: QDeveloper, QManager và QPublished được binding tới Exchange GPCoderHeadersExchange với các header:
QDeveloper : Queue này sẽ nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”}.
QManager : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”} hoặc {“manager”, “Manager Channel”}.
QPublished : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} và {“access”, “publish”}.
Bây giờ nếu một Message được gởi tới Exchange với routing key là design-pattern.gpcoder.com và header là {“dev”, “Developer Channel”} sẽ được chuyển tới Queue QAll và GPCoderHeadersExchange. Sau đó GPCoderHeadersExchange sẽ chuyển đến Queue QDeveloper và QManager dựa vào header {“dev”, “Developer Channel”}.
Tương tự vậy, chúng ta cũng có thể định nghĩa một hay nhiều Exchange khác, chẳng hạn GPCoderDirectExchange binding tới GPCoderHeadersExchange và GPCoderTopicExchange.
Một Exchange nhận Message từ publisher được gọi là Source Exchange. Trong sơ đồ trên, GPCoderTopicExchange là Source Exchange.
Một Exchange nhận Message từ một Exchange khác gọi là Destination Exchange. Trong sơ đồ trên, GPCoderHeadersExchange là Destination Exchange.
Ví dụ binding Exchange to Exchange trong RabbitMQ
Một số class của chương trình:
ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
ExchangeChannel : class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, binding Exchange đến Exchange, publish/ subscribe message, …
Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue.
HeadersExchangeProducer : để gửi Message đến GPCoderHeadersExchange.
TopicExchangeProducer : để gửi Message đến GPCoderTopicExchange.
HeadersExchangeConsumer : để nhận Message từ Queue được binding đến GPCoderHeadersExchange.
TopicExchangeConsumer : để nhận Message từ Queue được binding đến GPCoderTopicExchange.
App: giả lập việc gửi nhận Message thông qua Topic Exchange của RabbitMQ.
ConnectionManager.java
package com.gpcoder.exchange2exchange;
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();
}
}
package com.gpcoder.exchange2exchange;
public final class Constant {
// Exchange
public static final String HEADERS_EXCHANGE_NAME = "GPCoderHeadersExchange";
public static final String TOPIC_EXCHANGE_NAME = "GPCoderTopicExchange";
// Queue
public static final String DEV_QUEUE_NAME = "QDeveloper";
public static final String MANAGER_QUEUE_NAME = "QManager";
public static final String PUBLISHED_QUEUE_NAME = "QPublished";
public static final String JAVA_QUEUE_NAME = "QJava";
public static final String ALL_QUEUE_NAME = "QAll";
// Routing key pattern
public static final String JAVA_ROUTING_KEY = "java.*.gpcoder.com";
public static final String GPCODER_ROUTING_KEY = "#.gpcoder.com";
// Message key
public static final String JAVA_CORE_MSG_KEY = "java.core.gpcoder.com";
public static final String JAVA_MSG_KEY = "java.gpcoder.com";
public static final String DESIGN_PATTERN_MSG_KEY = "design-pattern.gpcoder.com";
public static final String NOT_MATCHING_MSG_KEY = "java.collection.gpcoder.com.vn";
private Constant() {
super();
}
}
HeadersExchangeProducer.java
package com.gpcoder.exchange2exchange;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.exchange2exchange.Constant.*;
public class HeadersExchangeProducer {
private ExchangeChannel channel;
public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();
// Create channel
channel = new ExchangeChannel(connection, HEADERS_EXCHANGE_NAME);
// Create headers exchange
channel.declareExchange(BuiltinExchangeType.HEADERS);
// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, PUBLISHED_QUEUE_NAME);
// Binding queues with headers
Map<String, Object> devGroup = new HashMap<>();
devGroup.put("x-match", "any"); // Match any of the header
devGroup.put("dev", "Developer Channel");
devGroup.put("general", "General Channel");
Map<String, Object> managerGroup = new HashMap<>();
managerGroup.put("x-match", "any"); // Match any of the header
managerGroup.put("dev", "Developer Channel");
managerGroup.put("manager", "Manager Channel");
managerGroup.put("general", "General Channel");
Map<String, Object> publishedGroup = new HashMap<>();
publishedGroup.put("x-match", "all"); // Match all of the header
publishedGroup.put("general", "General Channel");
publishedGroup.put("access", "publish");
channel.performQueueBinding(DEV_QUEUE_NAME, "", devGroup);
channel.performQueueBinding(MANAGER_QUEUE_NAME, "", managerGroup);
channel.performQueueBinding(PUBLISHED_QUEUE_NAME, "", publishedGroup);
}
}
package com.gpcoder.exchange2exchange;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.exchange2exchange.Constant.DESIGN_PATTERN_MSG_KEY;
public class App {
public static void main(String[] args) throws IOException, TimeoutException {
HeadersExchangeProducer producer1 = new HeadersExchangeProducer();
producer1.start();
TopicExchangeProducer producer2 = new TopicExchangeProducer();
producer2.start();
// Publish some messages
Map<String, Object> devHeader = new HashMap<>();
devHeader.put("dev", "Developer Channel");
producer2.send("[1] Head First Design Pattern", DESIGN_PATTERN_MSG_KEY, devHeader);
HeadersExchangeConsumer consumer1 = new HeadersExchangeConsumer();
consumer1.start();
consumer1.subscribe();
TopicExchangeConsumer consumer2 = new TopicExchangeConsumer();
consumer2.start();
consumer2.subscribe();
}
}
Output của chương trình:
[Send] [{dev=Developer Channel}]: [1] Head First Design Pattern
[Received] [QDeveloper]: amq.ctag-QVREG0uBg6XsIebvbecqCg
[Received] [QDeveloper]: [1] Head First Design Pattern
[Received] [QManager]: amq.ctag-vLHleyPNedl2ZiMYukIing
[Received] [QManager]: [1] Head First Design Pattern
[Received] [QAll]: amq.ctag-GymeB0uwpALjgoM3RP_cvg
[Received] [QAll]: [1] Head First Design Pattern
Như bạn thấy, Message được gởi tới Exchange với routing key là design-pattern.gpcoder.com và header là {“dev”, “Developer Channel”} sẽ được chuyển tới Queue QAll và GPCoderHeadersExchange. Sau đó GPCoderHeadersExchange sẽ chuyển đến Queue QDeveloper và QManager dựa vào header {“dev”, “Developer Channel”}.
Tóm lại, Exchange Binding trong RabbitMQ là một tính năng vô cùng mạnh mẽ, giúp chúng ta kết hợp nhiều loại Exchange lại với nhau, giúp hệ thống mở rộng tốt hơn và đáp ứng được hầu hết yêu cầu phức tạp của hệ thống.
p5.js là một thư viện Javascript, thường thì nó sẽ sử dụng để dùng làm những thứ linh tinh với đồ họa các thứ sử dụng canvas. Các bạn có thể sử dụng p5.js bằng p5 Web Editor ở đây.
Ở trong p5.js thì sẽ có hai function quan trọng. Đó là setup() và draw().
setup()
Đây là function sẽ chạy ngay lập tức khi chạy. Thường thì hay dùng để config trước khi chạy thực tế.
draw()
Function này sẽ chạy ngay sau thằng setup() ở trên. Đây là function chính của p5.js.
ml5.js là gì?
ml5.js là một thư viện bao gồm các thuật toán và pre-trained models cho browser. ml5.js được build trên nền của tensoeflow.js. Vì thế, mình có thể dùng ml5.js để build một số thứ hay ho dựa trên các pre-trained models có sẵn.
Lý do mình dùng p5.js trong bài này là vì thằng ml5.js này chơi thân với thằng p5.js nên nó dễ sử dụng hơn.
Ví dụ
Bây giờ thì mình làm linh tinh một cái gì đó để ví dụ về cái ml5.js này cho vui. Mình ở đây sẽ làm một cái project vui vui sử dụng poseNet pre-trained model.
poseNet pre-trained model
poseNet pre-trained model là một pre-trained model dùng để mô phỏng vị trí tay, chân, mắt, mũi, miệng, bla bla bla của con người.
Cấu trúc Project
Về cấu trúc thì đơn giản như sau.
index.html
nancy.mp4
script.js
Bash
Trong file index.html thì import p5.js và ml5.js vào thôi
<!DOCTYPE html><htmllang="en"><head><title>Getting Started with ml5.js</title><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script><scriptsrc="https://unpkg.com/ml5@0.4.1/dist/ml5.min.js"></script></head><body><scriptsrc="script.js"></script></body></html>
HTML
Load Video bằng function createVideo của p5.js
Để load Video thì trong p5.js mình dùng function createVideo(). Vì là load video thì mình viết nó trong setup(), sau khi load xong rồi thì mới hiển thị đó ra ở draw() function.
let video;let poses =[];functionsetup(){createCanvas(406,720);
video =createVideo(["nancy.mp4"],()=>{
video.loop();
video.volume(0);});
video.size(width, height);
video.hide();}functiondraw(){image(video,0,0, width, height);}
JavaScript
Lúc này kết quả như sau.
Load poseNet pre-trained model
Công việc tiếp theo là mình load tiếp pre-trained model vào. Mình sẽ viết tiếp trong function setup() như sau.
poseNet = ml5.poseNet(video,()=>{console.log("Model is ready");});// Listen to new 'pose' events
poseNet.on("pose",function(results){
poses = results;});
JavaScript
Vẽ skeleton lên Video
Sau khi lắng nghe sự kiện pose ở trên. Nó sẽ trả một mảng các pose, bao gồm các properties như sau:
// A function to draw the skeletonsfunctiondrawSkeleton(){// Loop through all the skeletons detectedfor(let i =0; i < poses.length; i++){let skeleton = poses[i].skeleton;// For every skeleton, loop through all body connectionsfor(let j =0; j < skeleton.length; j++){let partA = skeleton[j][0];let partB = skeleton[j][1];stroke(255,0,0);strokeWeight(4);line(
partA.position.x,
partA.position.y,
partB.position.x,
partB.position.y
);}}}
JavaScript
Sau đó để function drawSkeleton() này vào function draw()
Trên đây là một ứng dụng cơ bản của thằng ml5.js. Ngoài poseNet pre-trained model, thằng ml5.js còn cung cấp một số pre-trained model khác cũng hay ho, ví dụ như là Image Classifier sử dụng Mobilet Net, FaceApi, hay YOLO. Các bạn cứ ghé vào trang chủ của ml5.js để ngó qua nhé.
Bài viết được sự cho phép của tác giả Trần Khôi Nguyên Hoàng
Ngày nay, xử lý dữ liệu lớn đã trở thành bài toán tiếp xúc hằng ngày đối với Kỹ sư phần mềm, Distributed Data Processing dựa trên MapReduce mà một kỹ thuật được sử dụng để giải quyết những bài toán đó.
Thông qua kiến trúc và cách xử lý của MapReduce. Dữ liệu lên tới hàng Petabyte cũng có thể xử lý linh hoạt và nhanh chóng.
Còn chần chừ gì nữa mà không tìm hiểu xem ta có thể thực hiện như thế nào?. Bắt dầu thôi!
1. Distributed Data Processing
Processing đối với data được đề cập trong bài viết này là đề cập tới dữ liệu lớn (large data). Con số lên tới petabytes.
Ngoài ra, process cũng bao gồm các vấn đề khó nhắn như: Search LIKE, trả về tất cả dữ liệu có chứa dòng chữ “hello”. Sẽ không có gì để bàn nếu ta đang muốn giữ cho peformance ở mức tốt cho dù dữ liệu rất lớn. Bằng cách sử dụng hệ cơ sở dữ liệu phân tán (Distributed Database) kết hợp với kiến trúc MapReduce
Bắt đầu với MapReduce, vậy MapReduce là gì?
1.1 MapReduce = Map + Reduce.
Map, written by the user, takes an input pair and produces a set of intermediate key/value pairs. The MapReduce library groups together all intermediate values associated with the same intermediate key I and passes them to the Reduce function
Map, thường được viết bởi user, nhận đầu vào là một cặp key value, tạo ra một tập hợp các key-value trun gian. Thư viện MapReduce sẽ gom tất cả các cặp key value trung gian cùng với khóa tạm I và gửi chúng tới Reduce function
Túm váy lại thì Map sẽ tạo ra các cặp key-value, nó là input cho Reduce, khi đã có danh sách key-value trung gian, ta sẽ gọi Reduce function.
The Reduce function, also written by the user, accepts an intermediate key I and a set of values for that key. It merges together these values to form a possibly smaller set of values. The intermediate values are supplied to the user’s reduce function via an iterator. This allows us to handle lists of values that are too large totinmemory
Reduce function, cũng được viết bởi user, nhận vào khóa tạm I và danh sách giá trị cho khóa đó. Merge chúng lại với nhau để tạo thành một tập giá trị có thể nhỏ hơn. Các giá trị trung gian được tạo ra thông qua quá trình lặp (interator). Điều này cho phép chúng ta xử lý danh sách các giá trị quá lớn để vừa trong bộ nhớ
Đọc hơi khó hiểu, tuy nhiên chỉ cần nhớ đơn giản rằng MapReduce cho phép lặp các giá trị tuần tự trong MapReduce, đem ra xử lí. Quá trình xử lí này là tuần tự cho từng phần tử. Trường hợp dữ liệu cực lớn, không cần phải có toàn bộ dữ liệu mới thực hiện xử lý (data processing)
2. MapReduce Architecture
Với lượng dữ liệu khổng lồ, nếu muốn xử lí dữ liệu theo hướng thông thường như lưu trên disk, ram, thực hiện tuần tự là điều không khả thi. Chính vì vậy, ta cần một kiến trúc mới, xử lí tuần tự như MapReduce. Lúc này Distributed Data Processing cho dữ liệu lớn trở nên khả thi hơn bao giờ hết.
Bài toán thực tế cho thấy ta có:
Documents: file cần xử lý với dung lượng khổng lồ, cỡ Petabyte
Master: thực hiện tính toán phân chia cho các worker trên system. Dựa vào tài nguyên thì mỗi worker sẽ có khả năng xử lý nhất định, (MB/s)
Worker: Xử lí nội dung file theo yêu cầu từ phía master, sức mạnh của các worker là ngang bằng nhau.
Áp dụng nguyên lý và ý tưởng từ Distributed Storage và Distributed Database, ta sẽ chia Documents thành các phần nhỏ. Mỗi phần sẽ vừa đủ để một Worker có thể xử lí
Trường hợp mỗi Worker có thể xử lý 256MB, ta sẽ chia document thành 4000 phần nhỏ. Nếu có 1000 workers, mỗi worker sẽ chịu trách nhiệm xử lý cho 4 Splits.
Lúc này, vai trò của master trở thành người giám sát, ghi lại tiến trình của từng worker. Phân phối Splits nếu worker đã không còn gì để mà xúc.
Để đảm bảo tính phân tán và stateless của mỗi worker, splits không phân chia tuần tự tới từng worker
Các splits không phân chia tuần tự tới từng worker
3. Handle khi MapReduce fail
Trong quá trình processing data, không thể tránh khỏi các trường hợp bị fail. Lúc này, khi một worker bị fail, master sẽ ghi nhận và đổi trạng thái của worker thành failed
Any map task or reduce task in progress on a failed worker is also reset to idle and becomes eligible for rescheduling
Bất kì Map task hay reduce task đang có trạng thái in progress hay failed đều sẽ reset về trạng thái idle và có thể rescheduling lại
Về phía master
If the master task dies,a new copy can be started from the last check pointed state. However, given that there is only a single master, its failure is unlikely ;therefore our current implementation aborts the MapReduce computation if the master fails. Client scan check for this condition and retry the MapReduce operation if they desir
Nếu master tiêu, một bản copy khác có thể bắt đầu lại từ trạng thái kiểm tra cuối cùng. Tuy nhiên, do chỉ có một master duy nhất, nên việc thất bại ở master thường không xảy ra. Do đó, trường hợp master fail, ta đơn giản hủy bỏ tính toán của MapReduce hiện tại, phía client có thể kiểm tra điều kiện này và thử lại ở MapReduce nào bị fail
React Props Cheatsheet: 10 Patterns mà bạn nên biết (Phần 1)
Tác giả: Reed Barger
Props là công cụ mạnh mẽ được sử dụng trong React, nhưng làm thế nào để bạn sử dụng chúng một cách hiệu quả để viết các thành phần và ứng dụng React mạnh mẽ, đáng tin cậy?
1. React props có thể pass một cách có điều kiện
Các đạo cụ được chuyển cho các thành phần có thể được coi như các đối số được truyền cho một hàm. Nếu các giá trị prop không được thông qua một thành phần nhất định, error sẽ không xuất hiện. Thay vào đó, trong thành phần prop sẽ có giá trị là undefined.
Nếu bạn muốn được cảnh báo về thời điểm một giá trị không được chuyển làm hỗ trợ cho một thành phần, bạn có thể sử dụng một công cụ như prop-type hoặc TypeScript bằng các công cụ này.
Trong React đơn giản, hãy lưu ý rằng bạn thường quên việc chuyển các đạo cụ. Ngoài ra, bạn có thể không chuyển một chỗ dựa nhất định cho một thành phần, nếu bạn chọn.
2. React props được thông qua khi tên chúng có giá trị đúng
Mỗi prop phải được cung cấp một giá trị liên quan được cung cấp sau toán tử bằng. Nhưng điều gì sẽ xảy ra khi chúng ta không cung cấp toán tử bằng cũng như một giá trị?
Nếu bạn chỉ cung cấp tên riêng cho một thành phần mà không có gì khác, bạn sẽ chuyển một giá trị boolean là true cho phần hỗ trợ đó cho thành phần đó. Không cần phải viết rằng một prop bằng true. Thay vào đó, bạn chỉ có thể bao gồm giá trị prop và nó sẽ được cung cấp giá trị boolean true khi bạn sử dụng nó trong một thành phần mà bạn chuyển nó vào.
3. React props có thể truy cập như một object hoặc tái cấu trúc
Có một số mẫu mà chúng ta có thể sử dụng để truy cập các giá trị prop trong các thành phần của chúng ta. Props có thể được truy cập dưới dạng toàn bộ đối tượng thường được gọi là “props”. Hoặc chúng có thể tái cấu trúc, vì đạo cụ sẽ luôn là một đối tượng, thành các biến riêng biệt. Nếu bạn có nhiều đạo cụ đang chuyển cho thành phần của mình, tốt nhất có thể đưa chúng vào toàn bộ đối tượng đạo cụ và truy cập chúng bằng cách nói props.propName.
Tuy nhiên, nếu bạn chỉ có một vài đạo cụ mà bạn đang chuyển xuống thành phần của mình, bạn có thể hủy cấu trúc chúng ngay lập tức trong các tham số của thành phần chức năng của bạn.
Bạn có thể sử dụng cấu trúc đối tượng ES6 để bao gồm một tập hợp các dấu ngoặc nhọn trong các tham số của thành phần hàm của bạn và ngay lập tức lấy ra các thuộc tính của đối tượng, cũng như khai báo chúng dưới dạng các biến riêng biệt. Điều này cắt giảm code của chúng tôi và loại bỏ sự cần thiết phải nói props.propName để nhận từng giá trị đạo cụ.
Chúng tôi đã thấy rằng các đạo cụ rất linh hoạt và nếu chúng tôi không chuyển chúng cho một thành phần, error sẽ không xuất hiện. Tính linh hoạt này cũng được mở rộng cho những gì chúng ta có thể vượt qua như một chỗ dựa. Không chỉ các phần tử JSX có thể được chuyển làm đạo cụ cho các thành phần mà chúng ta còn có thể chuyển các thành phần khác làm đạo cụ.
Trên thực tế, có một loại giá đỡ đặc biệt được cung cấp tự động trên đối tượng đạo cụ được gọi là children.
5. Mọi thứ có thể được thông qua như prop trong React
Bất kỳ giá trị JavaScript bình thường nào cũng có thể được chuyển dưới dạng props, bao gồm cả các hàm. Có một số mẫu mạnh mẽ đã xuất hiện, do khả năng chuyển các chức năng làm đạo cụ. Một mẫu rất phổ biến là truyền một hàm xuống một thành phần con như một chỗ dựa, có thể cập nhật trạng thái của thành phần mẹ, và sau đó gọi nó trong thành phần con đó.
Ngoài ra, còn có các mẫu khác, chẳng hạn như mẫu đạo cụ kết xuất, cũng liên quan đến việc chuyển một hàm xuống thành phần con để sau đó được gọi lại và thực hiện một số chức năng thành phần chéo.
Thay vì tạo mỗi observable để chờ đợi các subscribers đăng ký, chúng ta có thể tạo 1 factory observable sinh ra các observable cho mỗi lượt đăng ký mới của chúng.
Tạo 1 factory observable kiểu nguyên bằng toán tử deferred
Nghịch đảo flip, mỗi khi factory observable được đăng ký
Trả về các Observale khác nhau tùy thuộc giá trị của flip
Thêm mã sau vào sau hàm trên:
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)
print()
}
Kết quả như sau:
--- Example of: deferred ---
123
456
123
456
Vậy ta hiểu là, mỗi lần khi ta đăng ký tới factory observable, thì ta nhận các giá trị khác nhau của chúng theo thứ tự xen kẽ nhau. Điều này để thể hiện rằng factory observable có thể tạo ra các observable khác nhau cho các subscriber.
Vậy là bài hôm nay kết thúc rồi. Trong bài tiếp theo chúng ta sẽ tập trung thực hành các khái niệm chính của Rxswift. Cụ thể bài tới là về Subjects.
Windows có lẽ là một hệ điều hành đã quá quen thuộc với người dùng phổ thông rồi, và macOS cũng vậy (vì đã xài Macbook thì mặc định sẽ là macOS mà – tuy nhiên mức độ phổ biến thì không thể bằng Windows được.
Còn với Linux và các phiên bản phát triển từ nhân Linux (như Ubuntu, Kali Linux, Pop!_OS…) thì không phải ai cũng lựa chọn để dùng và biết cách để dùng.
Vậy tại sao mình lại nói các bạn lập trình viên nên sử hệ điều hành này? Vâng, ở trong bài viết này mình sẽ chỉ ra cho bạn 5 lý do mà mình thấy là hợp lý nhất để các bạn chuyển sang sử dụng nền tảng này, các bạn có thể bổ sung thêm dưới phần comment về góc nhìn của bạn nhé
#1. Không phù hợp để chơi game !
Mình tin chắc là sẽ có nhiều bạn nghĩ lý do này là không thuyết phục, vì chuyện chơi game sẽ phụ thuộc vào sở thích cũng như lý trí mỗi người.
Mình không hề phủ nhận điều đó, nhưng một trong những cách để bạn tập trung hơn vào công việc đó là đừng bắt bản thân phải đưa ra lựa chọn. OK !
Ví dụ như khi bạn bạn đang xài hệ điều hành Windows và máy bạn có cài sẵn một số tựa game. Điều này khiến bạn nhiều lúc phải đưa ra lựa chọn giữa làm việc và chơi game để giải trí một lúc.
Tất nhiên rồi, sẽ có lúc bạn không thể cưỡng lại được sự lôi cuốn của mấy con game mình thích, và thế là bạn lại lao vào chơi game mà quên béng đi mất mình đang phải làm việc.
Mình đã từng trong tình trạng này rồi nên mình hiểu rất rõ cảm giác việc phải đưa ra lựa chọn như thế. Về lâu về dài nó sẽ không tốt cho việc hình thành thói quen của bạn.
Đấy là với các bạn làm chủ bản thân kém, còn với các bạn học ra học, chơi ra chơi thì đây có lẽ không phải là một lý do thuyết phục thật.
#2. Linux hoàn toàn miễn phí và Open Source
Windows hay Mac OS đều là những hệ điều trả phí. Và tất nhiên, bạn sẽ phải trả một khoản tiền không hề nhỏ so với thu nhập bình quân đầu người ở Việt Nam để có thể sở hữu bản quyền CHÍNH THỨC.
Mặc dù ở Việt Nam vấn đề bản quyền chưa được đề cao nên số lượng người sử dụng bản quyền lậu vẫn rất nhiều, đặc biệt là với hệ điều hành Windows. Nhưng mình thấy mấy năm trở lại đây chúng ta đã đỡ hơn rất nhiều rồi.
Còn ở nước ngoài thì khác, nơi mà vấn đề bản quyền được đề cao thì việc dùng lậu sẽ mang đến rất nhiều rủi ro cho người dùng. Chúng ta dần dần cũng vậy thôi, chắc chắn là như vậy !
Trong khi đó, lập trình viên lại là những người làm việc thường xuyên với máy tính và hệ điều hành. Ai sẽ là người chịu trách nhiệm khi mà hệ điều hành bạn dùng bị lỗi trong khi bạn đang dùng các phiên bản c.r.a.c.k, các phiên bản bẻ khóa…
Đây chính là lý do tại sao khi bạn hỏi các lập trình viên ở các nước phương tây, họ dùng Linux rất nhiều !
Linux là hệ điều hành mã nguồn mở, hoàn toàn miễn phí và open source. Có nghĩa là bạn không cần phải trả phí để mua bản quyền, mà chỉ cần tải về, cài đặt và dùng thôi.
Vậy một câu hỏi đặt ra là nhỡ có bị lỗi thì ai là người đứng ra chịu trách nhiệm? Vâng, thực ra là Linux được cả cộng đồng xây dựng chung nên rất ít lỗi và nếu có lỗi thì cộng đồng Linux cũng rất đông và sẽ giúp đỡ bạn thôi.
#3. Linux hỗ trợ hầu hết các ngôn ngữ lập trình
Đã là lập trình viên thì phần lớn thời gian họ làm việc với các ngôn ngữ lập trình, mà Linux và các phiên bản của Linux thì lại hỗ trợ hầu hết các ngôn ngữ lập trình: C/C++, Java, Python, PHP…
Không những vậy, việc thao tác dòng lệnh (command line) trên Linux phải gọi là rất sướng, sướng hơn Windows rất nhiều. Các bạn có thể cài đặt mọi thứ, từ ngôn ngữ lập trình cho đến các IDE, tools bằng cách gõ các dòng lệnh. Cảm giác mình sử dụng máy tính ở một cái tầm khác.
Nhiều bạn lập trình viên có tần suất dùng chuột rất ít do là đã quá quen với việc thao tác với dòng lệnh rồi. Tất nhiên khi bạn dùng quen rồi thì bạn cũng có khả năng làm được như vậy thôi.
Hơn nữa, hiện nay hầu hết các ngôn ngữ lập trình đều hỗ trợ CLI (Command Line Interface) nên khi bạn dùng Linux, bạn chỉ cần gõ lệnh là hầu như làm được hết mọi thứ.
#4. Tính bảo mật cao
Trước khi nói về tính bảo mật của Linux thì mình sẽ nói về tính bảo mật của Windows trước. Các bạn vẫn thường nghe về các lần vá lỗi bảo mật hoặc là thông báo lỗ hổng bảo mật trên Windows rất nhiều, đúng chứ.
Đó là khi bạn dùng bản Windows sạch, còn nếu bạn dùng các phiên bản c.r.ac.k, phiên bản lậu thì nguy cơ bị đe dọa bảo mật lại càng nghiêm trọng hơn nữa. Nhiều bạn cẩn thận, máy có nhiều tài liệu quan trọng sẽ còn phát sinh thêm chi phí mua thêm các phần mềm diệt virus.
Nhưng trên Linux thì không, Linux được cả cộng đồng đông đảo chung tay phát triển. Tất nhiên, mình không khẳng định Linux không có lỗi, nhưng nếu xét về tính bảo mật và an toàn thì mình vẫn đánh giá cao Linux hơn.
Vậy tại sao Linux lại bảo mật hơn Windows?
+ Quyền “root”: Gọi nôm na là quyền Admin đấy các bạn. Bình thường khi bạn cài Windows thì mặc định bạn sẽ có quyền này, nó cho phép bạn thực hiện mọi thao tác với hệ thống với quyền hạn cao nhất.
Nhưng trên Linux thì không, người dùng không được cấp quyền này theo mặc định. Nói cách khác thì dù có bị virus xâm nhập thì cũng không có quyền root để mà phá hoại hệ thống.
+ Ít bị dòm ngó bởi các hacker: Vâng, dễ hiểu thôi, mình lấy ngay ví dụ bên trên nhé giả dụ như có cài virus vào được rồi mà không làm gì được thì đương nhiên hacker sẽ tìm những nạn nhân khác dễ nuốt hơn. Mà khi ngoài tầm ngắm rồi thì càng ít bị tấn công, vậy nên vấn đề bảo mật càng ít bị đe dọa.
Còn nhiều nguyên nhân khác nữa, nhưng theo mình thấy đây là hai nguyên nhân tiêu biểu cũng như là dễ thấy nhất.
#5. Khả năng tùy biến cực cao
Như mình đã nói ở trên, Linux miễn phí 100%, có nghĩa là bạn có thể tùy chỉnh mọi thứ từ font chữ, theme, icon… bạn có thể tùy biến rất sâu vào hệ thống, tùy theo ý của bạn.
Thậm chí nhiều phiên bản hệ điều hành còn được xây dựng trên nền tảng các hệ điều hành Linux có sẵn. Khi bạn sử dụng Linux nó sẽ đem lại cho bạn một cảm giác gọi là cảm giác “được kiểm soát”.
Nói nôm na là bạn muốn làm gì thì làm, điều này không giống với Windows hay Mac OS vì hai hệ điều hành này còn liên quan tới vấn đề bản quyền nữa.
Mà các bạn lập trình nếu làm việc can thiệp sâu vào hệ thống, tới tầng hệ điều hành thì điều này lại cực kỳ quan trọng hơn nữa, do phải hiểu thì mới làm được, mới xây dựng sản phẩm trên hệ điều hành đó được.
#6. Lời Kết
Như vậy là trong trong bài viết này mình đã cùng với các bạn điểm qua 5 lý do rất thuyết phục để những bạn đang hoặc sẽ là lập trình viên thì nên sử dụng hệ điều hành Linux rồi nhé.
Có thể đúng nhưng chưa đủ, vậy nên rất mong được các bạn đóng góp thêm ý kiến mới, cũng như là đưa ra góc nhìn của bạn về vấn đề này để anh em cùng trao đổi thêm. Cám ơn các bạn trước nhé
Bài viết được sự cho phép của tác giả Trần Hữu Cương
Giới thiệu lớp Activity trong Android
Lớp Activity là thành phần quan trọng nhất của ứng dụng Android, cách mà chúng hoạt động tạo thành nền tảng cơ bản của mô hình lập trình ứng dụng.
Khi một Activity chỉ định là Activity chính, nó sẽ là màn hình đầu tiên khi khởi chạy ứng dụng. Một Activity này lại có thể gọi và kích hoạt một Activity khác.
Activity chịu trách nhiệm chuyển giao sự kiện cho các view trong nó và quản lý vòng đời của nó. Một ứng dụng Android có thể có một hoặc nhiều Activity.
Một class được gọi là Activity khi nó extend (kế thừa) từ những class cha như : AppCompatActivity, Activity, FragmentActivity
Để tạo một Activity thì bạn phải tạo ra một lớp kế thừa lớp Activity, sau đó triển khai tối thiểu phương thức onCreate(Bundle savedInstanceState), sau đó tùy ngữ cảnh mà khi Activity hoạt động vòng đời của nó diễn ra như mô tả ở hình sau:
onCreate(Bundle savedInstanceState): Được gọi khi hoạt động mới được tạo, tại đây khởi tạo các biến, nạp giao diện layout …, phương thức này cũng nhận dữ liệu lưu lại trạng thái hoạt động trước đó (với mục đích để phục hồi – savedInstanceState).
onStart(): Được gọi ngay trước khi Activity hiển thị trên màn hình.
onResume(): Được gọi ngay khi Activity bắt đầu có thể tương tác với người dùng, và Activity nằm trên cùng trong danh sách các Activity của hệ thống.
onPause(): Được gọi khi hệ thống sắp kích hoạt một Activity khác, nếu bạn quá tải phương thức này, thường để lưu lại dữ liệu thật nhanh để hệ thống còn kích hoạt Activity khác.
onStop(): Được gọi khi nó bị ẩn đi. Sau phương thức này, Activity có thể gọi onRestart() nếu nó được người dùng kích hoạt lại hoặc gọi onDestroy() để hết thúc.
onDestroy(): gọi khi Activity bị hủy hoàn toàn (ví dụ gọi finish(), hoặc người dùng kill Activity)
Trong vòng đời của ứng dụng Android bạn cần phân biệt 2 loại sau: Visible Lifetime và Foreground Lifetime
Visible Lifetime: xảy ra từ sau khi gọi onStart –> cho tới lúc gọi onStop : trong trường hợp này ta vẫn có thể thấy màn hình Activity
Foreground Lifetime: xảy ra từ khi gọi onResume –> cho tới lúc gọi onPause : trong suốt thời gian này Activity luôn nằm ở trên cùng và ta có thể tương tác được với nó
Bài viết được sự cho phép của tác giả Phạm Công Sơn
Sắp xếp chèn (insertion sort) là một thuật toán sắp xếp bắt chước cách sắp xếp quân bài của những người chơi bài. Muốn sắp một bộ bài theo trật tự người chơi bài rút lần lượt từ quân thứ 2, so với các quân đứng trước nó để chèn vào vị trí thích hợp.
var InsertionSort = function ()
{
$.extend(this, new SortX());
this.onCreateScripts = function (array, scripts)
{
var $this = this;
var addScript = function (a1, a2, action)
{
scripts.push($this.createScript0(a1, a2)); // Script lấy ra 2 số cần so sánh
scripts.push($this.createScript1(a1, a2)); // Script để thực hiện so sánh giữa 2 số
return action();
}
var j, last;
for (var i = 1; i < array.length; i++) { last = array[i]; j = i; while ((j > 0) && addScript(array[j - 1], last, () => array[j - 1].number > last.number))
{
var a1 = array[j - 1];
array[j] = a1;
scripts.push($this.createScript2(a1, last));
j = j - 1;
}
array[j] = last;
}
}
}
var pageSortX = new PageSortX();
pageSortX.sortX = new InsertionSort();
pageSortX.start();
Bài viết được sự cho phép của tác giả Phạm Công Sơn
Chọn phần tử nhỏ nhất trong n phần tử ban đầu, đưa phần tử này về vị trí đúng là đầu tiên của dãy hiện hành. Sau đó không quan tâm đến nó nữa, xem dãy hiện hành chỉ còn n-1 phần tử của dãy ban đầu, bắt đầu từ vị trí thứ 2. Lặp lại quá trình trên cho dãy hiện hành đến khi dãy hiện hành chỉ còn một phần tử. Dãy ban đầu có n phần tử, vậy tóm tắt ý tưởng thuật toán là thực hiện n-1 lượt việc đưa phần tử nhỏ nhất trong dãy hiện hành về vị trí đúng ở đầu dãy.
var SelectionSort = function ()
{
$.extend(this, new SortX());
this.name = "SelectionSort";
this.onCreateScripts = function (array, scripts)
{
var $this = this;
var addScript = function (a1, a2, action)
{
scripts.push($this.createScript0(a1, a2)); // Script lấy ra 2 số cần so sánh
scripts.push($this.createScript1(a1, a2)); // Script để thực hiện so sánh giữa 2 số
return action();
}
var min;
for (var i = 0; i < array.length - 1; i++)
{
min = i;
scripts.push(this.createScriptOneItem(array[min]));
for (var j = i + 1; j < array.length; j++) { if (addScript(array[j], array[min], () => array[j].number < array[min].number))
{
min = j;
scripts.push(this.createScriptOneItem(array[min]));
}
}
var b1 = array[i];
var b2 = array[min];
array[min] = b1;
array[i] = b2;
scripts.push(this.createScript2(b1, b2)); // Script thực hiện hoán đổi giữa 2 số với nhau.
}
}
}
var pageSortX = new PageSortX();
pageSortX.sortX = new SelectionSort();
pageSortX.start();
Như vậy là mình đã giới thiệu cho các bạn seri hướng dẫn lập trình theme woocommerce. Seri này mình đã dự định làm cách đây 1 năm trước, nhưng bây giờ mới có thời gian để thực hiện.
Hy vọng với seri này các bạn sẽ biết được cách xây dựng 1 website bán hàng chuyên nghiệp với wordpress.
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Trong bài 9, chúng ta đã tìm hiểu qua cách hoạt động của các loại subjects. Trong bài này, chúng ta sẽ nghiên cứu cách sử dụng các loại subjects trông qua source code cụ thể.
Ở hình trên, chúng ta có 3 đường thẳng thể hiện cho 3 subscribes đăng ký các sự kiện trong thời gian thực. Trong đoạn code bài trước, đường thẳng đầu tiên thể hiện subscribe 1. Sau khi đăng ký, Publish subjects emit 1 sự kiện string là “1”. Lúc đó subscriptionOne nhận được.
Chúng ta thêm đoạn code sau để đăng ký tiếp subscriptionTwo:
example(of: "PublishSubject") {
let subject = PublishSubject<String>()
subject.onNext("Co ai nghe tui noi khong?")
let subscriptionOne = subject
.subscribe(onNext: { string in
print(string)
})
subject.on(.next("1"))
// Add subscription two
let subscriptionTwo = subject
.subscribe { event in
print("2)", event.element ?? event)
}
}
Kết quả chạy PlayGround như sau:
--- Example of: PublishSubject ---
1
Ở đây chúng ta thấy mặc dù đã đăng ký nhưng subscriptionTwo không hề emit ra sự kiện nào. Điều này dễ dàng thấy rằng, 1 PublishSubject sẽ chỉ thực sự phát ra sự kiện cho subscriptionTwo sau khi nó đã đăng ký.
Tiếp tục thêm đoạn code sau:
subscriptionOne.dispose()
subject.onNext("4")
Kết quả chạy chương trình:
--- Example of: PublishSubject ---
1
2) 4
Lúc này subscriptionOne đã dispose, cho nên nó không thể tiếp tục nhận sự kiện từ PublishSubject.
Đầu tiên gửi sự kiện hoàn thành completed cho subject
Gửi 1 sự kiện “5”. Lúc này không có sự kiện nào được in ra, bởi vì subject đã thực sự kết thúc.
dispose subscriptionTwo báo cho nó biết là đã kết thúc việc lắng nghe
Tạo 1 subscription cho subject. Thêm disposeBag cho nó.
Gửi tiếp sự kiện “?”. Thì subject báo nó đã hoàn thành, nên nó không hiển thị thêm “?” nữa. Tuy nhiên subscriptionOne, subscriptionTwo vẫn hiển thị sự kiện đã hoàn thành mặc dù nó đã dispose. Do vậy, bạn phải chú ý khi sử dụng PublishSubject khi xử lý.
Source đầy đủ:
import UIKit
import RxSwift
public func example(of description: String, action: () -> Void) {
print("\n--- Example of:", description, "---")
action()
}
example(of: "PublishSubject") {
let subject = PublishSubject<String>()
subject.onNext("Co ai nghe tui noi khong?")
let subscriptionOne = subject
.subscribe(onNext: { string in
print("1)" + string)
})
subject.on(.next("1"))
// Add subscription two
let subscriptionTwo = subject
.subscribe { event in
print("2)", event.element ?? event)
}
subscriptionOne.dispose()
subject.onNext("4")
// 1
subject.onCompleted()
// 2
subject.onNext("5")
// 3
subscriptionTwo.dispose()
let disposeBag = DisposeBag()
// 4
subject
.subscribe {
print("3)", $0.element ?? $0)
}.disposed(by: disposeBag)
subject.onNext("?")
}
Đôi khi bạn muốn khi 1 subscription đăng ký mới muốn biết các event trước đó là gì. Chúng ta sẽ cùng nghiên cứu trong bài 11 về BehaviorSubjects.
Khi bạn truy cập trang web trên, thì trình duyệt sẽ tự động download file .exe cài đặt. Tuy nhiên bạn cũng có thể download các phiên bản khác như bản 32bit/64bit hay bản portable
Phần Editor mặc định cho Git (phần mềm sử dụng để hiển thị, chỉnh sửa file khi compare file với Git). Mình sử dụng notepad++ vì nó khá dễ nhìn và nhẹ (các bạn có thể chọn editor khác)
Kiểm tra phiên bản của bản git vừa cài đặt bằng cách mở màn hình cmd hoặc powershell và chạy lệnh
Bức ảnh trên là sự khái quát về một lộ trình phổ biến nhất cho các Web Developer hiện nay trên thị trường web development. Trước khi chúng ta đi sâu vào các kỹ năng bạn cần học để trở thành một nhà phát triển web, bạn nên biết rằng phát triển website không phải là một khái niệm hay một chủ đề duy nhất. Nó có rất nhiều vấn đề phát sinh bên trong đó.
Bạn có thể tập trung vào việc học cách phát triển giao diện người dùng, UX UI, thiết kế đồ họa,… Trước hết, bạn có thể bắt đầu với việc phát triển giao diện người dùng. Để phát triển Front-end, bạn cần học HTML, CSS, JavaScript và thư viện hoặc khuôn khổ như React, Angular hoặc Vue.js.
Vì vậy, bây giờ chúng ta hãy nói chi tiết hơn về phát triển giao diện người dùng là gì và những gì bạn cần học.
“Giao diện người dùng của một ứng dụng thường đề cập đến lớp đại diện cho giao diện người dùng (giao diện người dùng). Điều này có thể bao gồm mọi thứ từ một trang web tĩnh với HTML và CSS đến một ứng dụng React đầy đủ hỗ trợ giao diện người dùng” – Colby Fayock.
Có một số kiến thức cơ bản bạn cần biết để phát triển Web Frontend bao gồm: HTML, CSS, JavaScript. Trong đó, HTML và CSS sử dụng để tạo các trang web tĩnh.
Back End Development là gì?
Phát triển back end còn được gọi là phát triển phía máy chủ. Đó là thực hành giao tiếp giữa cơ sở dữ liệu và trình duyệt. Các ngôn ngữ và công cụ của Back-end Development hiện có bao gồm: Node.js, Express, MongoDB và Mongoose.
Những nguồn tài nguyên miễn phí để học cách phát triển website
freeCodeCamp
Đây là trang web quan trọng nhất mà tôi từng truy cập. Họ có một giáo trình về web development đầy đủ kéo dài 3000 giờ và gần đây họ cũng đã thêm các chứng chỉ về tính toán khoa học, phân tích dữ liệu và học máy với Python.
Ngoài chương trình học, freeCodeCamp có một ấn phẩm (nơi bạn đang đọc bài viết này), kênh YouTube và Diễn đàn nơi bạn có thể nhận trợ giúp về các vấn đề lập trình của mình.
Bạn cũng có thể đăng ký với tư cách là người đăng ký và Quincy (người sáng lập freeCodeCamp) sẽ gửi email cho bạn một số bài báo liên quan đến công nghệ mới nhất từ ấn phẩm của freeCodeCamp hàng tuần.
Udemy
Chỉ cần tìm kiếm free courses from Udemy và nó sẽ lọc ra các tùy chọn của bạn. Bạn sẽ thấy cả khóa học trả phí và miễn phí, nhưng bạn chỉ có thể chọn những khóa học miễn phí và tận hưởng.
Nếu bạn còn nhớ, khóa học lập trình đầu tiên của tôi là từ Udemy – khóa học mà bạn tôi đã đăng ký cho tôi. Vì vậy, nó đã giúp tôi bắt đầu.
Tài liệu tham khảo
Khi tôi muốn tìm hiểu hoặc triển khai một khái niệm mới, tôi thường truy cập Google. Và Google thường đề xuất MDN hoặc tài liệu chính thức của một công cụ hoặc ngôn ngữ. Điều này cũng hữu ích khi tôi quên cách hoạt động của các chi tiết của một chủ đề cụ thể (như Flexbox chẳng hạn).
W3Schools cung cấp rất nhiều hướng dẫn tham khảo ngắn cho nhiều chủ đề bao gồm Java, Python, JavaScript, jQuery, React, Angular, AJAX, SQL, Node.js, Raspberry Pi, Artificial Intelligence, Machine Learning, Data Science, NumPy, SciPy, Matplotlib và MongoDB.
Kết luận
Đây chỉ là một hướng dẫn ngắn gọn dành cho các web developer, để giúp bạn bắt đầu trong lĩnh vực web development. Bạn có thể đánh dấu bài viết này để sử dụng thêm hoặc thậm chí chia sẻ nó với bạn bè của bạn, những người cũng muốn bắt đầu sự nghiệp của họ với tư cách là Nhà phát triển web.
Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng
I. Memoization
Memoization không phải là một từ Tiếng Anh có thể tìm thấy trong từ điển Oxford Online . Nó là biến thể của từ gốc Latin “memoradum” với nghĩa “to be remembered” (được nhớ).
Trong lập trình, memoization là một kỹ thuật tối ưu, nhằm tăng tốc chương trình bằng cách lưu trữ kết quả của các câu gọi function và trả về các kết quả này khi function được gọi với cùng input đã gọi.
Hiểu đơn giản, trong python ta có thể implement memoization với dict bằng cách lưu kết quả gọi function f vào một dict theo dạng:
và sửa lại function để nó lấy result1 nếu input1 có trong dict.
Với bài toán tìm số Fibonacci, kết quả của câu gọi function sau bằng tổng kết quả của 2 lần gọi function liền trước, dễ thấy ta có thể sử dụng memoization để tránh việc tính lại (đồng thời tránh luôn cả việc recursive call quá nhiều khiến vượt quá kích thước của stack)
def fib(n):
if n <= 2: return 1
else:
return fib(n - 1) + fib(n - 2)
In [9]: fib(6)
Out[9]: 8
In [12]: fib(25)
Out[12]: 75025
In [13]: fib(75)
... chờ mãi không thấy
In [13]: %time fib(30)
CPU times: user 244 ms, sys: 1.83 ms, total: 246 ms
Wall time: 245 ms
Out[13]: 832040
Nếu dùng memoization để tối ưu việc tính số Fibonacci , ta không phải tính lại các giá trị đã tính rồi:
LRU (least recently used) cache (đọc là /kaʃ/) là một trong các thuật toán cache phổ biến. Cache được dùng để lưu trữ các kết quả tính toán vào một nơi và khi cần tính lại thì lấy trực tiếp kết quả đã lưu ra thay vì thực hiện tính. Cache thường có kích thước nhất định và khi đầy, cần bỏ đi một số kết quả đã tồn tại trong cache. Việc kết quả nào sẽ bị bỏ đi phân loại các thuật toán cache này thành:
LRU (Least Recently Used): bỏ đi các item trong cache ít được dùng gần đây nhất.
MRU (Most Recently Used): bỏ đi các item trong cache được dùng gần đây nhất.
Việc sử dụng kỹ thuật memoization để tối ưu các quá trình tính toán như vậy là chuyện thường ở huyện, vậy nên từ Python 3.2, trong standard library functools đã có sẵn function lru_cache giúp thực hiện công việc này ở dạng decorator. Khi gọi function với một bộ argument, lru_cache sẽ lưu các argument lại thành key của dict, và sử dụng kết quả gọi function làm value tương ứng. lru_cache có option để chỉnh kích thước của cache, phân biệt kiểu của argument.
Một điểm đáng chú ý là các argument được gọi với function đều phải là immutable object bởi chúng được dùng làm key của dict. Khi dùng decorator lru_cache, ta chỉ cần tập trung vào viết function cần viết, lru_cache sẽ lo việc thực hiện caching/memoization.
In [8]: @lru_cache(maxsize=None)
def fib(n):
if n <= 2: return 1
else:
return fib(n-1) + fib(n-2)
...:
In [9]: %time fib(75)
CPU times: user 84 µs, sys: 26 µs, total: 110 µs
Wall time: 114 µs
Out[9]: 2111485077978050