Home Blog Page 89

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

Cài đặt PostgreSQL server sử dụng Docker

Cài đặt PostgreSQL server sử dụng Docker

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

Trong bài viết này, mình hướng dẫn các bạn cách cài đặt PostgreSQL server sử dụng Docker 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

Chúng ta sẽ sử dụng command docker run để chạy một Docker Image của PostgreSQL trên Docker Hub https://hub.docker.com/_/postgres như sau:

docker run --name <container_name> -e POSTGRES_USER=<postgre_user> -e POSTGRES_PASSWORD=<postgre_password> -p 5432:5432 -v <mount_folder>:/var/lib/postgresql/data -d postgres:<tag>

Trong đó:

  • — name <container_name> là tên Docker container mà các bạn muốn đặt cho container của PostgreSQL server.
  • -e POSTGRES_USER=<postgre_user> khai báo biến môi trường POSTGRES_USER, là tên user mà chúng ta sẽ sử dụng để đăng nhập vào PostgreSQL service. Tham số này là optional, nếu các bạn không khai báo thì mặc định, user với username “postgres” sẽ được sử dụng.
  • -e POSTGRES_PASSWORD=<postgre_password> là mật khẩu để đăng nhập với username ở trên. Tham số này là bắt buộc các bạn nhé!
  • -v <mount_folder>:/var/lib/postgresql/data giúp chúng ta mount một thư mục trên máy cài Docker với thư mục /var/lib/postgresql/data của PostgreSQL server, giúp chúng ta có thể synchronize data của PostgreSQL server trong container lên máy cài Docker, phòng trường hợp Docker Container của chúng ta có vấn đề gì, có thể chạy lại mà không mất dữ liệu.
  • -d là tham số giúp chúng ta chạy câu lệnh trong background, giúp chúng ta có thể tiếp tục container khi tắt Terminal hoặc Ctrl+C.
  • postgres:<tag> là tag Docker Image của PostgreSQL server mà các bạn muốn chạy.

Các bạn có thể định nghĩa thêm biến môi trường POSTGRES_DB để định nghĩa tên database mặc định sẽ được tạo khi container chạy. Mặc định, tên database này sẽ có giá trị giống với tên của user đăng nhập.

Ví dụ mình chạy command như sau:

docker run --name postgresql -e POSTGRES_USER=khanh -e POSTGRES_PASSWORD=123456 -p 5432:5432 -v /Users/khanh/data:/var/lib/postgresql/data -d postgres:13.3

Kết quả như sau:

Cài đặt PostgreSQL server sử dụng Docker

Kiểm tra Docker container đang chạy, các bạn sẽ thấy kết quả như sau:

Cài đặt PostgreSQL server sử dụng Docker

Bây giờ thì các bạn có thể sử dụng pgAdmin hoặc một PostgreSQL client tool để kết nối tới PostgreSQL server này và sử dụng rồi.

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

Làm thế nào để truy cập vào BIOS của máy Surface Pro?

Làm thế nào để truy cập vào BIOS của máy Surface Pro?

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

Surface Pro là một đứa “con lai thần thánh” giữa Máy tính bảng và Laptop do chính tay Microsoft sản xuất.

Khi lắp Type cover (bàn phím) vào thì nó là một chiếc Laptop thực thụ với sức mạnh không kém gì một chiếc Laptop đầu bảng.

  Hướng dẫn tạo quyền truy cập quản lý website mà không phải chia sẻ thông tin đăng nhập tại Hostinger
  XMP là gì? Kích hoạt XMP trên BIOS/ UEFI để tối ưu RAM

Còn khi tháo Type Cover ra thì nó là một chiếc máy tính bảng sang, xịn và mịn 🙂 Cá nhân mình thì thấy thiết kế của Surface Pro không thể chê vào đâu được, với khung kim loại từ hợp kim nhôm và Magie..

cach-vao-bios-Surface-Pro

À quên, hơi lan man sang phần giới thiệu sản phẩm rồi 🙂

Ngay bây giờ mình sẽ hướng dẫn cho các bạn cách truy cập vào BIOS để bạn có thể thiết lập/ tùy chỉnh theo nhu cầu sử dụng của bạn.

Hướng dẫn truy cập vào BIOS trên Surface Pro

Thực ra nếu bạn đã biết cách vào Advanced Options trên Windows rồi thì bạn sẽ làm được ngay thôi. Tuy nhiên, nhiều anh em mới sử dụng dòng máy này sẽ hơi bị lúng túng và bỡ ngỡ 🙂

Cách thực hiện như sau:

+ Bước 1: Bạn khởi động đến màn hình đăng nhập, hoặc vào trong màn hình Windows cũng được. Tùy bạn..

Sau đó click vào nút Nguồn => giữ phím SHIFT trên bàn phím => và nhấn vào nút Restart để khởi động lại máy tính.

cach-vao-bios-surface-pro (4)

+ Bước 2: Tiếp theo bạn vào Troubleshoot

cach-vao-bios-surface-pro (3)

Sau đó chọn Advanced Options

cach-vao-bios-surface-pro (2)

+ Bước 3: Và cuối cùng chọn UEFI Firmware Settings như hình bên dưới.

cach-vao-bios-surface-pro (1)

Rồi nhấn vào nút Restart để khởi động lại máy tính, lúc này máy sẽ tự động truy cập vào BIOS cho ban..

cach-vao-bios-surface-pro (6)

Đây là giao diện Surface UEFI (quen gọi là BIOS :D), bạn có thể thiết lập theo nhu cầu của bạn rồi đó.

P/s: Đang nghiên cứu về Windows 11 nên tiện thể viết luôn bài này. May quá Surface Pro 6 có hỗ trợ TPM 2.0 😀 Vậy là có thể nâng cấp lên Windows 11 ngon lành rồi anh em ạ ᵔᴥᵔ

cach-vao-bios-surface-pro (5)

Okay, như vậy là việc truy cập vào BIOS của máy Surface Pro cũng không có gì khó khăn cả đúng không các bạn 🙂 Hi vọng là bài viết này sẽ hữu ích với bạn, chúc các bạn thành công !

Kiên Nguyễn – 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

Tối ưu SQL – Jouney to the West – chap I

Tối ưu SQL – Jouney to the West – chap I

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

1. SQL – tại sao cần tối ưu

Nhận thấy rằng tối ưu SQL thực sự là một vấn đề quan trọng mà các kỹ sư phần mềm cần biết. Vì vậy, mình viết series bài viết này để chia sẻ chút kiến thức nhỏ nhoi về tối ưu SQL.

Có bất cứ thắc mắc hay phản hồi gì, các bạn hãy comment trực tiếp vào dưới bài viết, mình sẽ phản hồi sớm nhất có thể.

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

tối ưu SQL, chứ không phải tăng thời gian timeout của SQLKhi SQL execute time quá cao, tất nhiên, việc của kỹ sử không phải là tăng thời gian setting time out

2. Một vài lưu ý cơ bản

2.1 AND

Khi sử dụng điều kiện AND, hãy nhớ rằng nếu biểu thức có dạng expression 1 AND expression 2, thì expression 2 chỉ thực hiện khi cái đầu tiên là true.

// Nếu column2 != 'B' thì DBMS sẽ không thực hiện kiểm tra tiếp 
// column1 = 'A'
WHERE column2 = 'B' AND column1 = 'A'

Hầu hết các DBMS hiện này đều thực hiện so sánh từ trái qua phải (ngoại trừ Oracle). Vì vậy ta nên viết như sau:

// So sánh column 2 trước column 1
WHERE column2 = 'B' OR column1 = 'A'

2.2 OR

Khi viết toán tử OR, hầu hết các DBMS đều thực hiện kiểm tra các điều kiện này từ trái qua phải. Mẹo nhỏ có thể áp dụng là viết column theo thứ tự trong table trước.

// So sánh column 2 trước column 1
WHERE column2 = 'B' OR column1 = 'A'

Khác với điều kiện AND, điều kiện OR sẽ thực hiện kiểm tra expression thứ hai khi expression thứ nhất false. Vì vậy, để tối ưu SQL, ta nên viết lại thành

// So sánh column 2 trước column 1
WHERE column1 = 'A' OR column2 = 'B'

2.2 NOT

Đầu tiên, chưa bàn tới việc tối ưu SQL. Với các điều kiện khác equals, viết NOT sẽ khó đọc hơn nhiều. Nếu bài toán hiệu năng không bị ảnh hưởng, nên ưu tiên bỏ viết điều kiện NOT. Ví dụ:

// Với những điều kiện đơn giản, sử dụng NOT làm giảm khả năng dễ đọc của SQL. 
WHERE NOT (column1 > 5)

Nên được viết lại thành:

// Rõ ràng dễ đọc hơn. 
WHERE column1 <= 5

Nếu điều kiện NOT quá phức tạp, có thể sử dụng định luật DeMorgan.

NOT (A AND B) = (NOT A) OR (NOT B)

NOT (A OR B) = (NOT A) AND (NOT B)

2.3 IN

Có một số thím, tuy viết SQL nhiều nhưng vẫn không thể phân biệt được sự khác biệt giữa IN và OR. Từ đó, có suy nghĩ chủ quan rằng điều kiện IN và OR không có khác biệt. Vì chúng đều trả về kết quả như nhau. Không đúng, trong một số trường hợp, sử dụng IN sẽ nhanh hơn trong một số trường hợp (IN is faster in some circumstances)

Tuy nhiên, trường hợp cột có đánh index. Hầu như không có sự khác biệt về thời gian khi sử dụng OR hoặc IN. Một thanh niên ở Stackoverflow đã thử trên Mysql (đối với 100000 records).

// Sử dụng IN cho thời gian phản hồi chỉ 1.2679 s. 
SELECT COUNT(*) FROM t_inner WHERE val IN (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);
1 row fetched in 0.0032 (1.2679 seconds)

Trong khi đó, sử dụng OR cho thời gian query chậm hơn. Rõ ràng rằng, tối ưu SQL là giảm thời gian truy xuất. Vì vậy, nên sử dụng IN.

// Sử dụng OR cho kết quả trong thời gian 1.7385s.
SELECT COUNT(*) FROM t_inner WHERE val = 1000 OR val = 2000 OR val = 3000 OR val = 4000 OR val = 5000 OR val = 6000 OR val = 7000 OR val = 8000 OR val = 9000;
1 row fetched in 0.0026 (1.7385 seconds)

Tất nhiên, trực quan mà nói. Kết quả có thể có đôi chút khác biệt giữa các DBMS khác nhau, nhưng vẫn khuyên các thím sử dụng IN khi có thể. Quá nhiều điều kiện OR có thể gây thiếu sót hoặc làm SQL trở nên khó đọc hơn.

Bản thân mình nghĩ rằng, chỉ nên sử dụng OR khi điều kiện so sánh là dưới 5.

  "Làm PM, theo anh không cần biết về code, nhưng phải hiểu về SQL, database, những khái niệm cơ bản của code"
  Các thao tác cơ bản với Database SQL Server (tạo mới database, table,...)

2.4 UNION

Câu lệnh UNION giúp loại bỏ các row trùng lặp trong cả 2 bảng mà chúng ta UNION. Chà, xem ra thì nó là một cách hay để merge data, nhưng nếu bàn về performance. Liệu có gì không ổn?

// Sử dụng OR cho kết quả trong thời gian 1.7385s.
SELECT COUNT(*) FROM t_inner WHERE val = 1000 OR val = 2000 OR val = 3000 OR val = 4000 OR val = 5000 OR val = 6000 OR val = 7000 OR val = 8000 OR val = 9000;
1 row fetched in 0.0026 (1.7385 seconds)

2.5 EXCEPT

2.6 LIKE

2.7 CASE

Trường hợp có SQL có sử dụng function, để SQL được tối ưu, thay vì sử dụng các điều kiện WHERE gọi function nhiều lần. Ta có thể sử dụng CASE WHEN

// Gọi nhiều funciton giống nhau trong 1 câu SQL làm tốc độ giảm đi. Gây lãng phí
.. WHERE function(column1) = 3
OR function(column1) = 5

Để sử dụng được CASE WHEN ở điều kiện WHERE. Một mẹo nhỏ là sử dụng điều kiện 1=1. Điều này cũng có nghĩa rằng điều kiện WHERE chỉ lấy ra các record thỏa mãn khi giá trị CASE được đáp ứng. Cụ thể như sau:

// Thay vào đó, ta có thể sử dụng CASE WHEN.
// Việc sử dụng CASE WHEN giảm đi nhiều lần gọi function
.. WHERE 1 = 
CASE function(column1)
WHEN 3 THEN 1
WHEN 5 THEN 1
END

3. Kết luận

Rõ ràng mà nói, để tối ưu được SQL, lượng kiến thức mà chúng ta cần có là không hề nhỏ. Tất nhiên, phải hiểu thì mới optimize được chứ.

Bài viết này, tuy nội dung đơn giản, nhưng cũng là một phần kiến thức cơ bản cần nắm để có thể tối ưu được những vấn đề lớn hơn. Vì vậy, cảm ơn các bạn đã đọc bài.

Mình sẽ tiếp tục series này, cập nhật nhiều hơn nữa những nội dung chuyển sâu về tối ưu SQL. Các bạn cũng có thể đọc thêm các bài viết về Convert Oracle sang Postgres, hoặc về MyBatis

Tối ưu SQL, SQL performance tuning

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