Home Blog Page 134

3 chỉ số quan trọng đánh giá quy trình tuyển dụng

tuyen dung it
tuyen dung it

Tuyen dung IT đang có nhiều thách thức lớn. Dù là Freelancer IT hay những ứng viên inhouse đều có những khó khăn. Thực tế cho thấy việc đánh chuyên sâu; thiết lập chiến lược tuyen dung it phù hợp rất quan trọng. Đó là lý do các chỉ số đo lường hiệu quả trở nên cần thiết hơn. Cùng TopDev điểm qua 3 chỉ số chuẩn mà các nhà tuyển dụng cần theo dõi và xem xét.

1. Chất lượng tuyển dụng IT (Quality of Hire)

Đây được xem là chỉ số quan trọng, viết tắt là QoH. Nó là chỉ số ảnh hưởng trực tiếp đến quá trình tuyen dung it. Tuy nhiên, chỉ số về chất lượng tuyển dụng phụ ứng theo quan niệm riêng của từng doanh nghiệp. Do vậy, việc thiết lập các công thức để mô tả quy trình tuyen dung it cũng khác nhau. 

tuyen dung it
Chỉ số đánh giá thứ 1

Điều đó cũng giống như tùy vị trí ứng tuyển như freelancer IT, Senior Developer,… Đối với freelancer IT, CV cần chất lượng hơn để cạnh tranh trên thị trường tuyen dung it.

Công thức tính chuẩn

QoH = (Hiệu quả công việc + Thời gian nhân viên thích ứng và bộc lộ khả năng + Mức độ hài lòng của nhân viên với công việc mới + Mức độ phụ ứng của nhân viên mới với văn hóa doanh nghiệp) / 4

Lưu ý, hệ giá trị về tính hiệu quả công việc được đánh giá bằng chỉ số khách quan từ nhân viên hoặc từ cấp trên.

Và mức độ phụ ứng với văn hóa doanh nghiệp dựa trên các đánh giá từ đồng nghiệp; cấp trên của nhân viên ấy.

2. Chỉ số % tuyển mới hỏng (New Hires failure rate)

Tuyển hỏng được hiểu là:

+ Thời gian khi nhân viên mới chủ động nghỉ việc

+ Hoặc thời gian bị công ty cho nghỉ việc trong vòng 6 tháng từ khi nghỉ việc. 

Xem thêm các việc làm UTrend tuyển dụng

tuyen dung it
Chỉ số thứ 2 -% tuyển hỏng

Không những tổn thất về các chi phí; thời gian lưu động nhận sự tuyen dung it, việc phát sinh những chi phí là điều khó tránh khỏi. Vì thế, nhà tuyen dung it cần tập trung theo dõi chỉ số này. Điều này giúp phát hiện và cập nhật tình hình đo lường một cách nhanh chóng. Từ đó, đề ra những giải pháp cụ thể, kịp thời.

Công thức tính chuẩn

Tỷ lệ tuyển mới hỏng = (Số lượng nhân viên mới nghỉ việc trong vòng 6 tháng/ Tổng số lượng nhân sự nghỉ việc) x 100%

Bạn có thể phối hợp với phòng kế toán để nắm bắt hệ giá trị quy đổi. Điều này giúp giá trị có thể quy đổi giá trị thành mức độ thiệt hại tương ứng. Từ đó xây dựng các giải pháp hiệu quả hơn, giảm thiểu mức độ ảnh hưởng từ thực tế nghỉ việc.

3. Chi phí tuyển dụng IT (Cost Per Hire)

Chi phí tuyen dung it giúp nhà tuyển dụng hiểu được những vấn đề trọng tâm của quy trình tuyen dung it. Đồng thời, cùng với các hệ giá trị khác, hiệu suất thực tế của quy trình sẽ được bôc lộ rõ nét hơn. Hơn thế nữa, chi phí tuyển dụng có liên quan trực tiếp đến khía cạnh tính toán và quản lý ngân sách tuyen dụng it.

tuyen dung it
Chỉ số thứ ba – Chi phí tuyển dụng

Công thức tính

CPH = Tổng chi phí tuyển dụng/ Tổng số lượng ứng viên tuyển được = (Tổng chi phí tuyển dụng nội bộ + Tổng chi phí tuyển dụng bên ngoài)/ Tổng số lượng ứng viên tuyển thành công

Cụ thể, trong đó:

  • Chi phí tuyển dụng nội bộ: Mức chi trả cho đội ngũ khai thác và sàng lọc các ứng viên tiềm năng. Cùng với đó là các chi phí quản lý, phát triển đào tạo
  • Chi phí tuyển dụng bên ngoài: Chi phí liên hệ book từ các agency, phí từ gói tuyển dụng, các bài test đầu vào,…


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

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

Xem thêm việc làm Developers hàng đầu tại TopDev

Trách nhiệm chính của một Product Manager xuất sắc là gì?

product manager là gì
Trách nhiệm chính của một Product Manager xuất sắc là gì?

Tác giả: Annie Dunham

Công việc của Product Manager là gì?

  • Là người thiết lập kế hoạch và tầm nhìn dài hạn cho các sản phẩm công ty thực hiện.
  • Truyền đạt phương pháp thực hiện kế hoạch cho tất cả các bên liên quan.
  • Tài liệu chính được sử dụng là “lộ trình sản phẩm”, nó chứa đựng các tài liệu trực quan  về chuyên môn và những lý do vận hành đằng sau một sản phẩm thành công.
product manager
Product Manager phải làm việc với nhiều bên liên quan

4 trách nhiệm chính của Product Manager là gì?

1. Minh bạch thông tin về các vấn đề liên quan đến thứ tự ưu tiên của lộ trình sản phẩm

Giải đáp các vấn đề liên quan đến câu hỏi “Tại sao” với các bên làm việc chính là vai trò và trách nhiệm của một Product Manager. Tại sao bạn lại ưu tiên tính năng này hoặc chủ đề này cho bản phát hành tiếp theo mà không phải cái khác? Tại sao bạn lại chọn tập trung nhiều hơn vào một mục tiêu cụ thể trong hai quý tiếp theo thay vì một mục tiêu khác?…

Xem thêm các việc làm tuyển dụng product manager hấp dẫn tại TopDev

Sẽ có rất nhiều các câu hỏi như thế và cách tốt nhất là Product Manager phải thuyết phục được khách hàng, lãnh đạo công ty, các bộ phận liên quan và thành viên trong team hiểu và tham gia vào tư duy chiến lược của bạn. Hãy rõ ràng và cởi mở với họ về lý do bạn đưa ra những quyết định đó.

2. Bạn có thể nói “Không”, nhưng hãy giải thích lý do bằng các thuật ngữ mà các bên liên quan đều hiểu được

CEO của công ty có thể sẽ yêu cầu một tính năng mới mà theo họ cho rằng sẽ rất tuyệt vời, một dev trong team sẽ đề xuất kết hợp phát triển một bộ tính năng để tiết kiệm thời gian cho lần chạy nước rút tiếp theo, còn khách hàng của bạn thì yêu cầu kết hợp thêm một công cụ cụ thể vào bản phát hành tiếp theo vì một khách hàng tiềm năng của họ đã hứa sẽ mua nếu nó được bao gồm.

Nhưng, nếu làm theo tất cả những yêu cầu này sẽ làm giảm khả năng đạt các mục tiêu chiến lược mà Product Manager đặt ra từ đầu.

Vậy nên bạn có quyền từ chối!

>>> Xem thêm Một ngày làm việc của Google Product Manager

Nhưng chìa khóa để thuyết phục họ là bạn phải nói được lý do tại sao bạn không thể đáp ứng những yêu cầu của họ. Vận dụng các thuật ngữ chuyên ngành mà bạn cảm thấy dễ hiểu nhất với họ, để chứng minh rằng điều đó sẽ gây ảnh hưởng không tốt đến sản phẩm cuối cùng. Với một câu trả lời có đầy đủ chiến lược và minh chứng cụ thể, bạn sẽ thuyết phục được đối phương.

3. Hãy là người “ưu tiên tàn nhẫn” để cân bằng nhu cầu của khách hàng và các bên liên quan

Có một sự thật là bất kể quy mô và ngân sách của công ty bạn đang bao nhiêu, Product Manager sẽ luôn phải đối mặt với tình trạng thiếu nguồn lực để phát triển sản phẩm. Điều đó đồng nghĩa với việc bạn sẽ luôn cần tìm ra thứ tự ưu tiên và phải cân nhắc các yếu tố cạnh tranh trong mục tiêu đối với sản phẩm, giữa nguồn lực hạn chế của công ty và nhu cầu từ các bên liên quan khác nhau.

product manager tuyển dụng
Các yếu tố giúp Product Manager làm việc hiệu quả

Nếu bạn không chắc chắn về cách sắp xếp mức độ ưu tiên hoặc cách cân nhắc các yếu tố khác nhau trong việc phát triển lộ trình sản phẩm của mình, Product Manager có thể tham khảo ý kiến từ những người đi trước hoặc sử dụng nền tảng Kano model để tính toán và sắp xếp.

4. Cách đưa ra quyết định của Product Manager là gì? Dựa trên các bằng chứng và khả năng giao tiếp của bạn

Đặc điểm chính của những Product Manager là gì để đạt được thành công? Đó là họ có thể trả lời được câu hỏi “Tại sao” của các bên liên quan. Một trong những cách hiệu quả để thuyết phục họ là đưa ra các bằng chứng. Hơn cả quan điểm cá nhân hay suy nghĩ của bạn, bằng chứng là một sự thật rõ ràng và có thể nhìn thấy được những ảnh hưởng của nó, khiến các bên liên quan không thể bỏ qua.

Những bằng chứng thuyết phục đó là gì?

Nếu bạn có dữ liệu người dùng trong thực tế, phản hồi của khách hàng và chỉ số về sản phẩm đã được phát hành trước đó thì đây chính là một nguồn thông tin kinh doanh tuyệt vời. Nó là dữ liệu xác thực nhất để cung cấp thông tin về cách làm việc hiệu quả và xây dựng lộ trình phát triển sản phẩm của bạn. Số liệu sẽ nói lên vấn đề mà bạn cần giải quyết.

Còn nếu sản phẩm mà bạn đang làm việc là sản phẩm mới và chưa có dữ liệu thực tế thì cũng đừng quá lo lắng. Có rất nhiều cách khác để thu thập thông tin hữu ích về sản phẩm, khách hàng và thị trường. Product Manager có thể hỏi trực tiếp khách hàng, nghiên cứu thêm các sản phẩm của đối thủ cạnh tranh, đọc các review trên cộng đồng trực tuyến hoặc nhận xét của khách hàng trên blog của công ty bạn. Đây được xem là nơi khách hàng tiềm năng hoặc khách hàng đã sử dụng sản phẩm của bạn thảo luận về sản phẩm của công ty hoặc sản phẩm của đối thủ cạnh tranh.

Bên cạnh đó, khả năng trình bày và thuyết phục của Product Manager cũng được yêu cầu rất cao. Đây là một trong những yếu tố được đánh giá khắt khe khi bạn ứng tuyển cương trình Product Manager tuyển dụng. Khi giám đốc điều hành hoặc đại diện bán hàng hỏi tại sao bạn lại chọn hướng đi này thay vì hướng đi khác cho lần lặp lại tiếp theo của sản phẩm, thì với khả năng trình bày một lời giải thích thuyết phục được minh chứng bởi các dữ liệu thực sẽ giúp bạn kiếm được nhiều nguồn lợi từ khách hàng.

Kết luận

Rõ ràng để có thể trở nên xuất sắc ở bất cứ vị trí nào đi chăng nữa, bạn cũng cần phải có chuyên môn và sự đam mê với ngành nghề đó. Dù là bạn ứng tuyển vào tester chưa có kinh nghiệm hay muốn làm việc với tư cách là một Product Manager, tất cả đều yêu cầu bạn học hỏi nhiều hơn để nâng cao kỹ năng của mình. Đó cũng là cơ hội để bạn có thể làm việc trong những môi trường chuyên nghiệp như KMS Technology tuyển dụng chẳng hạn.

Nguồn productplan.com

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

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

5 quy tắc binding trong JavaScript

Quy tắc Binding

Tác giả:TAPAS ADHIKARY

Khái niệm “this” trong JavaScript là một trong những khía cạnh khó hiểu nhất của ngôn ngữ này. Tuy nhiên lại là nhân tố quan trọng để viết code “advance”, nâng cao hơn. Trong JS, this cho phép:

  • Sử dụng lại function trong các ngữ cảnh khác nhau
  • Xác định tập trung vào object nào khi gọi method.

Nhắc tới this thì điều đầu tiên phải làm đó là biết gọi funtion nào, bởi sẽ không biết cái gì ở bên trong this cho tới khi function được gọi. Và các trường hợp của this có thể chia thành 5 khía cạnh binding khác nhau.

Tuyển dụng lập trình JavaScript mới nhất

Binding là gì

Trong JavaScript thì Lexical Environment hay nói theo nghĩa đen là môi trường từ vựng, cũng như là môi trường chứa đựng code được viết ra theo mặt vật lý (physically) . Hãy xem ví dụ bên dưới, tên biến bên trong function sayName()lexically

function sayName() {
  let name = 'someName';
  console.log('The name is, ', name);
 }

Trong đó Execution Context thì liên quan đến code hiện thời đang chạt và những thứ giúp code hoạt động. Có thể có nhiều lexical environments có sẵn nhưng chỉ có một cái đang chạy lúc này, và được Execution Context quản lý.

Mỗi Execution Context lại chứa một Environment Record. Binding trong JavaScript có nghĩa là ghi lại identifier (variable và tên function) trong một Environment Record cụ thể.

Lưu ý: Binding giúp kết hợp identifier (variable và tên function) với this cho  một execution context.

Khúc này còn hơi rắc rối nhưng những phần sau sẽ giải thích rõ hơn.

Quy tắc 1: Implicit Binding hoạt động như thế nào?

Trong implicit binding thì cần phải xem đối tượng bên trái của hàm sử dụng dấu chấm (dot operater), từ đó xác định được this đang tham chiếu tới cái gì.

let user = {
    name: 'Tapas',
    address: 'freecodecamp',
    getName: function() {
        console.log(this.name);
    }
};

user.getName();

Ví dụ này thì this đang trỏ tới user object, bởi vì bên trái hàm dấu chấm là function getName (), ta thấy có user object, vì vậy this.name trong console sẽ hiển thị Tapas.

Một ví dụ khác

function decorateLogName(obj) {
      obj.logName = function() {
          console.log(this.name);
      }
  };

  let tom = {
      name: 'Tom',
      age: 7
  };

  let jerry = {
      name: 'jerry',
      age: 3
  };

  decorateLogName(tom);
  decorateLogName(jerry);

  tom.logName();
  jerry.logName();

Trong ví dụ này thì có 2 object, tomjerry. Và chúng được decorate (nâng cao) bằng cách đính kèm method logName().

Khi gọi hàm tom.logName()., object tom nằm bên trái dấu chấm cuả function logName(). Nên this sẽ trỏ tới object tom và nhận giá trị của tom (this.name tương đương với tom). Tương tự khi gọi hàm jerry.logName().

>>> Xem thêm: Hướng dẫn cơ bản để làm việc với Javascript regular expression

Quy tắc 2: Explicit Binding hoạt động như thế nào?

JavaScript tạo môi trường để thực hiện code mà chúng ta viết. Trong đó nó đảm nhiệm memory creation (dành cho variables, functions, objects) trong creation phase. Cuối cùng nó chãy code trong execution phase. Lúc này môi trường mới được Execution Context.

Có nhiều loại environment trong JavaScript application. Mỗi execution context thực hiện, chạy lệnh độc lập với nhau. Nhưng sẽ có lúc cần sử dụng thứ này trong execution context này trong cái context khác. Lúc này explicit binding sẽ pht huy công dụng.

Trong explicit binding, chúng ta có thể gọi function với object khi funtion đó nằm ngoài execution context của object đó.

Để thực hiện explicit biding thì có 3 method đó là call(), apply()bind().

Hàm call() hoạt động thế nào

Với phương thức call() thì context với function được gọi sẽ được chuyển tới called dưới dạng tham số (parameter).

let getName = function() {
     console.log(this.name);
 }
 
let user = {
   name: 'Tapas',
   address: 'Freecodecamp'  
 };

getName.call(user);

Ở đây, hàm call() sẽ được gọi trên function getName(). Function getName() nhận giá trị this.name. Nhưng this trong đây là gì? Lúc này sẽ được quyết định bởi giá trị đã được chuyển đến hàm call().

Với trường hợp này, this sẽ được bind đến user object bởi vì đã chuyển user như một tham số đến hàm call(). Vì vậy this.name sẽ nhận giá trị của thuộc tính name của user object, Tapas. 

Ví dụ trên mới chỉ chuyển một argument tới hàm call(), nhưng thực tế có thể chuyển nhiều argument như sau:

let getName = function(hobby1, hobby2) {
     console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
 }

let user = {
   name: 'Tapas',
   address: 'Bangalore'  
 };

let hobbies = ['Swimming', 'Blogging'];
 
getName.call(user, hobbies[0], hobbies[1]);

Argument đầu tiên đuợc chuyển tới call() là object context với function đã được gọi. Những thuộc tính khác chỉ có thể là giá trị được sử dụng. Ở đây mình đang chuyển Swimming và Blogging là 2 thuộc tính tới function getName().

Tuy nhiên sẽ có trường hợp cần pass từng argument một trong call() thì sao? Lúc này sẽ tới lượt của apply().

Hàm apply() hoạt động thế nào

Cách mà apply() thực thi lệnh cũng tương đồng với call() nhưng cho phép pass argument thuận tiện hơn.

let getName = function(hobby1, hobby2) {
     console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
 }
 
let user = {
   name: 'Tapas',
   address: 'Bangalore'  
 };

let hobbies = ['Swimming', 'Blogging'];
 
getName.apply(user, hobbies);

Lúc này ta có thể chuyển một mảng (array) chứa argument luôn, tiện hơn nhiều so với việc chuyển từng cái.

Tips: nếu bạn cần chuyển một giá trị argument hay argument mà không có giá trị thì hãy dùng call(). Còn nếu chuyển nhiều giá trị argument thì dùng apply().

Hàm bind() hoạt động thế nào

Method bind() cũng tương tự như call() nhưng có 1 điểm khác biệt nhỏ. Thay vì gọi function trực tiếp thì bind() trả về một hàm mới.

let getName = function(hobby1, hobby2) {
     console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
 }

let user = {
   name: 'Tapas',
   address: 'Bangalore'  
 };

let hobbies = ['Swimming', 'Blogging'];
let newFn = getName.bind(user, hobbies[0], hobbies[1]); 

newFn();

Ở đây getName.bind() không gọi function getName() trực tiếp, nó trả về function mới, newFn và chúng ta đang gọi hàm dưới dạng newFn().

Quy tắc 3: New Binding

Từ khóa new được dùng để tạo một object mới từ constructor function.

let Cartoon = function(name, animal) {
     this.name = name;
     this.animal = animal;
     this.log = function() {
         console.log(this.name +  ' is a ' + this.animal);
     }
 };

Bạn có thể tạo nhiều object với từ khóa new như sau:

 let tom = new Cartoon('Tom', 'Cat');
 let jerry = new Cartoon('Jerry', 'Mouse');

Với quy tắc new biding, khi một function được gọi với từ khóa new, thì this bên trong function sẽ tham chiếu tới cái object mới được lập.

let tom = new Cartoon('Tom', 'Cat');

Đây là function Cartoon được gọi với từ khóa new. Thì this sẽ tham chiếu tới object mới, tom.

>>> Xem thêm: JavaScript là gì? Làm thế nào để trở thành lập trình viên JavaScript?

Quy tắc 4: Global Object Binding

Đoạn code dưới đây có kết quả thế nào? this tham chiếu tới cái gì?

let sayName = function(name) {
    console.log(this.name);
};

window.name = 'Tapas';
sayName();

Nếu this không giải quyết được với một trong những quy tắc binding, implicit, explicit hay new, thì this lúc này tham chiếu tới object window(global).

Tuy nhiên quy tắc strict mode của JavaScript sẽ không cho phép default binding như sau:

"use strict";
function myFunction() {
  return this;
}

Trường hợp này thì thisundefined.

Quy tắc 5: HTML Event Element Biding

Trong HTML event handlers, this tham chiếu tới element HTML nào nhận event đó.

<button onclick="console.log(this)">Click Me!</button>

Đây là giá trị trả về console khi click vào button:

"<button onclick='console.log(this)'>Click Me!</button>"

Bạn có thể thay đổi style của this:

<button onclick="this.style.color='teal'">Click Me!</button>

Nhưng hãy cẩn thận khi call function trên button click và dùng this bên trong function đó.

<button onclick="changeColor()">Click Me!</button>

và JavaScript:

function changeColor() {
  this.style.color='teal';
}

Đoạn code trên sẽ không trả kết quả như ý được, bởi theo như rule số 4 thì this sẽ tham chiếu tới object global, và không có object style để set màu.

Tổng kết

  • Với implicit binding thì this trỏ tới object bên trái của hàm dấu chấm.
  • Với explicit binding, chúng ta có thể gọi function với object khi function đó nằm ngoài execution context của object, có thể dùng các hàm như call(), apply(), bind().
  • Khi một function được gọi với từ khóa new thì this bên trong dunction sẽ trỏ tới object mới được lập.
  • Khi this không được giải quyết với implicit, explicit hay new thì this trỏ tới object window(global). Với strict mode của JavaScript, this trở nên undefined.
  • Trong HTML event handler thì this trỏ tới HTML element nào nhận event đó.

Tham khảo bài viết gốc tại freeCodeCamp

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

Xem thêm việc làm Front-End Developer hấp dẫn tại TopDev

Cú pháp cơ bản trong lập trình Python

Cú pháp cơ bản trong lập trình Python

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

Trong bài này tôi sẽ trình bày khái quát cho bạn về cú pháp Python cơ bản. Mục đích của bài này là giúp bạn làm quen dần các khái niệm và thuật ngữ được sử dụng trong Python từ đó bạn có thể rút ra điểm giống và khác nhau với một số ngôn ngữ lập trình khác.

Đặt tên (identifier) trong Python

Một định danh (identifier) trong Python là một tên được sử dụng để nhận diện một biến, một hàm, một lớp, hoặc một đối tượng. Một định danh bắt đầu với một chữ cái từ A tới Z hoặc từ a tới z hoặc một dấu gạch dưới (_) được theo sau bởi 0 hoặc nhiều ký tự, dấu gạch dưới hoặc các chữ số (từ 0 tới 9).

Python không hỗ trợ các ký tự đặc biệt chẳng hạn như @, $ và % bên trong các định danh. Python là một ngôn ngữ lập trình phân biệt chữ hoa- chữ thường, do đó định danh UCODE và ucode là hai định danh hoàn toàn khác nhau trong lập trình Python. Dưới đây là một số qui tắc nên được sử dụng trong khi đặt tên các định danh:

Tên có thể là một dãy ký tự hoặc 1 dãy số bắt đầu bằng ký tự hoặc dấu gạch dưới
Không được phép sử dụng ký tự đặc biệt để đặt tên (ngoại trừ dấu gạch dưới). Ký tự đầu tiên có thể là chữ cái, dấu gạch dưới, nhưng không được sử dụng chữ số làm ký tự đầu tiên.

Khi đặt tên không nên đặt trùng với từ khóa trong Python (phần dưới sẽ trình bày về khác từ khóa này).

Tên lớp bắt đầu với một chữ cái hoa. Tất cả tên khác bắt đầu với một chữ cái thường.

Một tên được bắt đầu với một dấu gạch dưới đơn chỉ ra rằng tên (định danh) đó là private.
Bắt đầu một định danh với hai dấu gạch dưới chỉ rằng định danh đó thực sự là private.
Nếu định danh cũng kết thúc với hai dấu gạch dưới, thì định danh này là một tên đặc biệt được định nghĩa bởi ngôn ngữ (ví dụ như init chẳng hạn).

  Hàm trong Python - Cú pháp và một số hàm phổ biến

Các từ khóa trong Python

Bảng dưới liệt kê các từ khóa trong ngôn ngữ lập trình Python. Đây là các từ dành riêng và bạn không thể sử dụng chúng như là các hằng, biến hoặc cho bất kỳ tên định danh nào. Tất cả từ khóa trong lập trình Python là chỉ ở dạng chữ thường.

Cú pháp cơ bản trong lập trình Python

Tuyển dụng python các công ty lớn

Dòng lệnh và độ thụt dòng lệnh trong Python

Python không cung cấp các dấu ngoặc ôm ({}) để chỉ các khối code cho định nghĩa lớp hoặc hàm hoặc điều khiển luồng. Các khối code được nhận biết bởi độ thụt dòng code (indentation) trong lập trình Python và đây là điều bắt buộc.

Số khoảng trống trong độ thụt dòng là biến đổi, nhưng tất cả các lệnh bên trong khối phải được thụt cùng một số lượng khoảng trống như nhau. Ví dụ:

if True:
	print "True"
else:
	print "False"

Tuy nhiên, khối lệnh sau sẽ tạo ra một lỗi:

if True:
	print "Answer"
	print "True"
else:
	print "Answer"
  print "False"

Do đó, trong lập trình Python thì tất cả các dòng liên tiếp nhau mà được thụt đầu dòng với cùng lượng khoảng trống như nhau sẽ tạo nên một khối. Trong ví dụ tiếp theo sẽ có các khối lệnh đa dạng:

Ghi chú: Bạn không cần cố hiểu vấn đề này ngay lập tức, bạn chỉ cần hiểu các khối code khác nhau ngay cả khi chúng không có các dấu ngoặc ôm. Đây chính là điểm khác nhau giữa Python và ngôn ngữ khác.

import sys
try:
	# open file stream
	file = open(file_name, "w")
except IOError:
	print "There was an error writting to", file_name
	sys.edit()
print "Enter '", file_finish,
print "' When finished"
while file_text != file_finish:
	file_text = raw_input("Enter text: ")
	if file_text == file_finish:
		# close the file
		file.close
		break
	file.write(file_text)
	file.write("\n")
file.close()
file_name = raw_input ("Enter filename: ")
if len(file_name) == 0:
	print "Next time please enter something"
	sys.exit()
try:
	file = open(file_name, "r")
except IOError:
	print "There was an error reading file"
	sys.exit()
file_text = file.read()
file.close()
print file_text

Xem thêm việc làm Python tại TP Hồ Chí Minh mới nhất trên TopDev

Các lệnh trên nhiều dòng trong Python

Các lệnh trong Python có một nét đặc trưng là kết thúc với một newline (dòng mới). Tuy nhiên, Python cho phép sử dụng ký tự \ để chỉ rõ sự liên tục dòng. Ví dụ:

total = item_one + \
		item_two + \
		item_there

Các lệnh được chứa bên trong các dấu ngoặc [], {}, hoặc () thì không cần sử dụng ký tự /. Ví dụ:

days = ['Monday', 'Tuesday', 'Wednesday',
		'Thursday', 'Friday']
  Str trong Python là gì? Kiểu dữ liệu chuỗi và định dạng chuỗi trong Python

Trích dẫn trong Python

Python chấp nhận trích dẫn đơn (‘), kép (“) và trích dẫn tam (”’ hoặc “””) để biểu thị các hằng chuỗi, miễn là các trích dẫn này có cùng kiểu mở và đóng.

Trích dẫn tam được sử dụng để trải rộng chuỗi được trích dẫn qua nhiều dòng. Dưới đây là tất cả các trích dẫn hợp lệ:

word = 'word'
sentence = "This is a sentence."
paragraph = """This is a paragraph. It is
made up of multiple lines and sentences."""

Comment trong Python

Python hỗ trợ hai kiểu comment đó là comment đơn dòng và đa dòng. Trong lập trình Python, một dấu #, mà không ở bên trong một hằng chuỗi nào, bắt đầu một comment đơn dòng. Tất cả ký tự ở sau dấu # và kéo dài cho đến hết dòng đó thì được coi là một comment và được bỏ qua bởi trình thông dịch. Ví dụ:

# First comment
print "Hello, Python!" # second comment

Chương trình trên sẽ cho kết quả:

Hello, Python!

Bạn cũng có thể gõ một comment trên cùng dòng với một lệnh hoặc biểu thức như sau:

name = "Madissetti" # This is again comment

Bạn có thể comment trên nhiều dòng như sau:

# This is a comment
# This is a comment, too.
# This is a comment, too.
# I said that already.

Python cũng hỗ trợ kiểu comment thứ hai, đó là kiểu comment đa dòng được cho bên trong các trích dẫn tam, ví dụ:

#single line comment
print "Hello Python"
"""This is 
multiline comment"""

Python tuyển dụng khu vực Hà Nội lương cao

Sử dụng dòng trống trong lập trình Python

Một dòng mà chỉ chứa các khoảng trống trắng whitespace, có thể với một comment, thì được xem như là một dòng trống và Python hoàn toàn bỏ qua nó.

Trong một phiên thông dịch trong chế độ tương tác, bạn phải nhập một dòng trống để kết thúc một lệnh đa dòng.

Các lệnh đa dòng trên một dòng đơn trong Python

Dấu chấm phảy “;” cho phép xuất hiện nhiều lệnh trên một dòng đơn. Tất cả các lệnh được cung cấp này không bắt đầu một khối code mới. Dưới đây là ví dụ:

import sys; x= 'foo'; sys.stdout.write(x + '\n')

Các nhóm lệnh đa dòng (còn được gọi là suite) trong lập trình Python

Một nhóm các lệnh đơn, mà tạo một khối code đơn, được gọi là suite trong Python. Các lệnh phức hợp như if, while, def, và class cần một dòng header và một suite.

Các dòng header bắt đầu lệnh (với từ khóa) và kết thúc với một dầu hai chấm “:” và được theo sau bởi một hoặc nhiều dòng để tạo nên một suite. Ví dụ như:

if expression :
	suite
elif expression :
	suite
else :
	suite

Tham số dòng lệnh trong lập trình Python

Nhiều chương trình có thể được chạy để cung cấp cho bạn một số thông tin cơ bản về cách chúng nên được chạy. Python cho bạn khả năng để làm điều này với -h:

$ python -h
usage: pythn [opption] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-c cmd : program passed in as string (terminates option list)
-d     : debug output from parser (also PYTHONDEBUG=x)
-E     : ignore environment variables (such as PYTHONPATH)
-h	   : print this help message and exit

[ etc. ]

Bạn cũng có thể lập trình cho script của mình theo cái cách mà nó nên chấp nhận các tùy chọn khác nhau tùy theo cách bạn thiết lập. Để tìm hiểu thêm về tham số dòng lệnh, bạn có thể tham khảo bài Tham số dòng lệnh trong Python. (uCode đề nghị bạn nên tìm hiểu chương này sau khi bạn đã tìm hiểu qua về các khái niệm còn lại của Python.)

Ngoài ra, một điều cần nói đến đó là khi bạn gặp phải trường hợp chương trình hiển thị dòng nhắc sau:

raw_input("\n\nPress the enter key to exit.")

Lệnh này nói rằng bạn hãy nhấn phím Enter để thoát. Ở đây, “\n\n” là để tạo hai newline (dòng mới) trước khi hiển thị dòng thực sự. Khi người dùng nhấn phím enter, thì chương trình kết thúc. Lệnh này sẽ đợi cho đến khi nào bạn thực hiện một hành động nào đó, và điều này giữ cho cửa sổ console của bạn mở tới khi bạn tiếp tục thực hiện hành động.

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

Xem thêm Việc làm it không cần kinh nghiệm hấp dẫn trên TopDev

Singleton Pattern là gì? Khái niệm và ứng dụng

Giới thiệu Singleton Pattern

Bài viết được sự cho phép của BQT Kinh nghiệm lập trình

Singleton Pattern là gì? Singleton là 1 trong 5 design pattern của nhóm khởi tạo (Creational Design Pattern). Vậy cấu trúc của Singleton ra sao và cách triển khai như thế nào, cùng mình tìm hiểu nội dung dưới đây.

Định nghĩa

Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.

Single Pattern là một design pattern trong số 5 design pattern thuộc nhóm Creational Design Pattern (Theo Gang of Four patterns, một cuốn sách rất nổi tiếng về design pattern).

Xem thêm Design Pattern là gì?

Creational Structure Behavioral
Abstract factory Adapter Chain of responsibility
Builder Bridge Command
Factory Composite Interpreter
Prototype Decorator Iterator
#Singleton Facade Mediator
Flyweight Memento Memento
Proxy Observer
Strategy
Template Method
Visitor

Singleton là một design pattern mà: Đảm bảo rằng một class chỉ có duy nhất một instance, và cung cấp một cách để truy cấp tới instance đó.

Singleton giải quyết bài toán nào?

Singleton Pattern giải quyết 2 vấn đề dưới đây cùng 1 lúc:

  • Đảm bảo rằng 1 lớp (class) sẽ chỉ có 1 instance duy nhất: Sẽ có 1 số trường hợp mà bạn cần kiểm soát việc truy cập đến các tài nguyên dùng chung ví dụ như database hay 1 file nào đó; lúc này bạn cần kiểm soát được số lượng instance mà 1 class đó có.

Cách nó hoạt động sẽ như sau: Thử tưởng tượng rằng bạn đã tạo 1 object rồi, tuy nhiên sau đó bạn lại quyết định tạo thêm 1 object mới. Lúc này thay vì việc nhận được 1 object mới thì bạn sẽ nhận về object mà bạn tạo ra lúc trước.

Lưu ý rằng hành vi này không thể thực hiện với 1 phương thức khởi tạo thông thường (như sử dụng new), vì nó sẽ luôn trả về 1 object mới. Ở đây khách hàng có thể không nhận ra rằng họ đang làm việc với cùng 1 đối tượng.

  • Cung cấp 1 điểm truy cập global đến instance đó: Biến global (toàn cục) thường được sử dụng để lưu trữ 1 số đối tượng thiết yếu. Mặc dù nó rất là tiện dụng, nhưng chúng cũng rất không an toàn vì bất cứ đoạn code nào trong chương trình cũng có thể ghi đè nội dung của những biến đó khiến cho ứng dụng của chúng ta bị crash. Singleton cũng giống như biến toàn cục, nó cho phép bạn truy cập đến 1 số object ở bất kỳ đâu trong chương trình, tuy nhiên nó cũng bảo vệ instance đó tránh khỏi việc bị ghi đè bởi code khác.

Cấu trúc của Singleton Pattern

Giới thiệu Singleton Pattern

Để biến một class thành Singleton, cần đảm bảo rằng:

  • Định nghĩa một attribute là private static và đó là thể hiện duy nhất của class này
  • Định nghĩa public static getInstance() dùng để khởi tạo đối tượng (hàm accessor)
  • Thực hiện lazy-init trong hàm accessor (chỉ khi gọi mới khởi tạo thể hiện)
  • Constructor (hoặc các constructor) là private hay protected, vì bạn không muốn client tạo nhiều thể hiện
  • Client chỉ có thể gọi hàm accessor khi muốn có thể hiện của class

Code ví dụ

Code ví dụ này được viết bằng Java, các ngôn ngữ khác cũng sẽ tương tự.

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Lưu ý

Các pattern khác có thể dùng cùng Singleton. Chẳng hạn, Abstract Factory, Builder, Prototype (sẽ trình bày cụ thể trong từng bài riêng cho mỗi pattern). Các đối tượng Facade và State cũng thường là Singleton.

Không nên hiểu máy móc rằng Singleton nghĩa là tồn tại chính xác 1 thể hiện. Có thể có những thể hiện khác nhau cho những mục đích khác nhau. Đây cũng là ưu điểm của Singleton so với việc dùng biến toàn cục (global variable).

Singleton là toàn cục. Vì vậy, khi đơn giản là muốn truyền một đối tượng A cho đối tượng B xử lý, hãy cân nhắc xem bạn có thật sự cần một đối tượng toàn cục hay không. Giống như thời trang vậy, che bao nhiêu, khoe bao nhiêu, bạn phải tự tìm lấy một điểm cân bằng.

Thận trọng với đa luồng (multithreading). Hai luồng khác nhau có thể gọi phương thức khởi tạo ở cùng một thời điểm và sinh ra hai thể hiện. Trong khi đó, đồng bộ (synchronized) phương thức khởi tạo lại ảnh hưởng tới hiệu suất.

Singleton: pattern hay anti-pattern?

Singleton có phải anti-pattern hay không? Tức là, nó nên tránh hay không? Điều này còn phụ thuộc nhiều thứ và sẽ gây nhiều tranh cãi. Dưới đây là một ý kiến mà bạn nên cân nhắc.

Giới thiệu Singleton Pattern

Có bốn đại cao thủ, mà đồng đạo code lâm thường gọi là Tứ Nhân Bang (Gang of Four), gồm Erich Gamma, Richard Helm, Ralph Johnson, và John Vlissides. (Đừng nhầm với “bè lũ bốn tên” bên Tung Của nha). Ngày 21/10/1994, họ có tung vào trong giang hồ một cuốn bí kíp, gọi là “Design Patterns: Elements of Reusable Object-Orientated Software”. Nó cổ vũ người người, nhà nhà sử dụng design pattern theo một cách “có quy củ”.

Bên cạnh các chiêu thức khác, Singleton được yêu mến hết mực, thậm chí so bề tài sắc lại là phần hơn. Ấy cũng vì nó dễ hiểu ý tưởng, dễ đem vào sử dụng. Nhưng hỡi ôi, kiếm tốt rồi cũng đứt tay người. Singleton trở thành con dao hai lưỡi đâm vào sau lưng nhiều đại hiệp. Những người thiết kế hệ thống quá lạm dụng nó đã khiến họ phải nhận lời cay đắng từ các lập trình viên hì hụi refactor.

Và miệng đời gán cho Singleton cái danh ác quỷ.

Tại sao lại có lời cay nghiệt như vậy? Vì những người thiết kế phớt lờ bốn lưu ý ở trên. À quên, hồi ấy đã làm gì có mấy lưu ý đó. Họ đã làm thế này. Đầu tiên, họ ép những đối tượng có thể có nhiều thể hiện trở nên chỉ có một thể hiện. Và code bỗng không còn dễ thay đổi, dễ cập nhật nữa, không còn flexible. Và tiếp theo, tai hại hơn, họ khiến chương trình khó test, khó debug hơn bằng cách dùng chiêu thức singleton mà họ ưa thích. Rất khó để viết unit test cho những đoạn code dùng chiêu thức ấy.

Chính vì những tác hại khôn lường mà singleton có thể đem lại, chúng ta nên nghiêm túc nhìn nhận rằng: nó quả thực là một anti-pattern. Khi muốn áp dụng nó, hãy chắc chắn rằng bạn đã xem xét các lưu ý bên trên. Dấu hiệu để lựa chọn Singleton chính là: liệu việc tạo ra nhiều thể hiện của class có gây nguy hiểm hay không (chẳng hạn một công ty mà có nhiều giám đốc điều hành). Đồng thời, cũng cần đảm bảo việc dùng Singleton không ảnh hưởng đến việc thực thi, khả năng nâng cấp, bảo trì của chương trình.

Mong rằng bài viết này sẽ giúp ích cho việc lập trình của bạn. Và hãy giữ cho việc áp dụng design pattern là “healthy & balance”.

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

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

Xem thêm IT Jobs Developer hấp dẫn trên TopDev

4 ngộ nhận về công việc kiểm thử phần mềm – Trở thành Tester hay Developer?

Trở thành Tester hay Developer?

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

Vũ Phạm – Delivery Director tại KMS Technology Vietnam

Nếu đặt câu hỏi này cho sinh viên sắp và mới tốt nghiệp ra trường cách đây 5-10 năm có lẽ sẽ nhận không ít những ánh mắt ngạc nhiên. Không ngạc nhiên sao được khi kiểm thử phần mềm là chủ đề được đề cập thoáng qua theo kiểu “cưỡi ngựa xem hoa” trong môn “Công nghệ phần mềm”. Điều này phần nào phản ánh đúng thực trạng là công việc kiểm thử phần mềm tại thời điểm đó chưa nhiều và chưa có những đòi hỏi chuyên sâu và thử thách thật sự. Đó là lý do tại sao kiểm thử phần mềm là chọn lựa đứng sau công việc lập trình, phân tích yêu cầu v.v Cùng với sự phát triển của ngành CNTT và nhu cầu nhân lực, kiểm thử phần mềm dần đã trở thành môn học chính thức trong một số trường ĐH. Tuy nhiên với thời lượng 4 tín chỉ cũng chỉ đủ để cung cấp cho sinh viên những khái niệm cơ bản nhất về kiểm thử phần mềm. Những thông tin cụ thể hơn về xu hướng phát triển, công nghệ, quy trình làm việc, cơ hội nghề nghiệp và những thử thách cũng như những vấn đề khác liên quan tới công việc của một kiểm thử viên (còn gọi là QC, QA hoặc Tester tùy theo công ty) thì vẫn chưa được chia sẽ nhiều trong quá trình học.

Tìm hiểu công việc tester

Kiểm thử phần mềm (KTPM) và đặc biệt là thị trường dịch vụ KTPM trong khoảng 5 năm qua đã có những chuyển biến rất tích cực tại Việt Nam. Thành phố Hồ Chí Minh hiện tại là một trong những địa điểm được lựa chọn và đánh giá cao, khi các doanh nghiệp ở các nước Âu, Mỹ muốn gửi công việc KTPM sang gia công ở một nước thứ ba. Ngoài ra nhiều công ty khi mở chi nhánh nghiên cứu và phát triển ở Việt Nam cũng thường bắt đầu bằng việc chuyển giao công việc KTPM dưới dạng này hoặc dạng khác. Theo ước tính thì thị trường nhân lực KTPM ở Việt Nam cho tới năm 2020 sẽ cần thêm khoảng trên dưới 10,000 chuyên viên kiểm thử (Tester), trong đó khoảng 50% là chuyên viên KTPM cao cấp trở lên.

Thông qua bài viết này tôi muốn chia sẻ một số quan điểm và nhận xét liên quan tới công việc KTPM nhằm giúp các bạn sinh viên mới ra trường cũng như các bạn đang đứng ở “ngã ba đường” chọn lựa nghề nghiệp (trở thành tester hay developer) tránh được những “ngộ nhận” về công việc KTPM. Qua đó, tôi hy vọng giúp các bạn có một cái nhìn thấu đáo hơn về ngành này để có thể có được một chọn lựa phù hợp với khả năng, mong muốn và nhu cầu thực tế của thị trường nhân lực IT ở Việt Nam. Dưới đây là bốn ngộ nhận phổ biến trong cách nhìn nhận về ngành KTPM.

Định hướng nghề nghiệp – Trở thành Tester hay Developer?

1. “Ai cũng có thể làm KTPM thậm chí không cần phải qua đào tạo”

Nếu chỉ nhìn vào khoảng thời gian 1 người mới tiếp cận với KTPM cho đến khi họ có thể làm được việc thì đúng là không lâu. Chung quy bạn sẽ được trang bị những kiến thức cơ bản về KTPM như đọc/hiểu yêu cầu của ứng dụng; lập kế hoạch và chiến lược kiểm thử; kỹ thuật phân tích thiết kế test cases dựa trên yêu cầu của phần mềm và các quy trình cơ bản để thực thi kiểm thử. Tuy nhiên đó mới chỉ là những kiến thức cơ bản nhất để bạn có thể bước đi trên con đường nghề nghiệp này.

Thực tế không ít công ty thậm chí còn không có giai đoạn đào tạo cơ bản này. Họ sẽ giao việc cho Tester ngay sau khi cung cấp những thông tin về dự án và sản phẩm đang được phát triển. Tester sẽ phải tự học và nhiều người đã có thể kiếm ra được kha khá lỗi bằng cách làm như vậy. Điều này tạo nên ngộ nhận là công việc KTPM khá dễ, ai cũng có thể làm được và có thể trở thành chuyên gia trong thời gian ngắn hoặc lâu lắm là 2-3 năm. Thực tế thì khoảng cách về kỹ năng và hiệu quả công việc giữa Tester làm được việc và Tester xuất sắc là khá lớn.

Kiểm thử phần mềm đòi hỏi những kỹ năng chuyên môn mà không phải ai cũng có thể sở hữu hoặc trang bị trong một sớm một chiều. Sự đam mê công nghệ, mong muốn đóng góp để cho ra đời một sản phẩm phần mềm với chất lượng hoàn hào, khả năng tư duy sáng tạo, quan sát, trình bày, phân tích và lập trình v.v là những kỹ năng cốt yếu để một Tester có thể làm tốt công việc. Trên thực tế, quá trình đào tạo trong ngành KTPM chủ yếu là quá trình tự học để hoàn thiện kỹ năng.

  "Cơ hội phát triển sự nghiệp AI với các ngành nghề là tương đồng" - Bảo Đại, AI Researcher tại Knorex
  "Mẹo bỏ túi" cho dân coder mới vào nghề

2. “Công việc KTPM không đòi hỏi kỹ năng lập trình”

Không ít người nghĩ rằng KTPM được thực hiện theo kiểu thủ công (manual testing hay kiểm thử bằng tay). Như vậy thì những kiến thức như phân tích thiết kế, lập trình, cơ sở dữ liệu, quản lý dự án v.v. được học trong mấy năm ĐH sẽ bị mai một.

Trên thực tế, kiến thức và kỹ năng bạn trang bị trong những năm ĐH sẽ giúp bạn trau dồi khả năng tư duy, phân tích để giải quyết vấn đề trong lĩnh vực CNTT. Cho dù bạn chọn lựa trở thành lập trình viên (Developer), người phân tích yêu cầu (Business Analyst) hoặc kiểm thử viên (Tester) thì đó là những kiến thức nền tảng để tiếp cận được công việc trong 1 dự án phát triển phần mềm. Hiên nay, công việc KTPM đòi hỏi Tester phải làm nhiều việc hơn so với trước đây.

Ngoài việc kiểm thử các chức năng (functional testing), họ còn phải ít nhiều biết làm kiểm thử tự động (automation testing) và kiểm thử hiệu năng (performance testing) cho sản phẩm. Một người Tester ngày nay sẽ cần phải tìm hiểu và xây dựng giải pháp/công cụ phục vụ kiểm thử tự động/hiệu năng. Và đó có lẽ đã trở thành “chuyện thường ngày ở huyện” cho một Tester.

Để đánh giá được công cụ nào “ngon, bổ, rẻ” đòi hỏi người Tester phải nắm vững kỹ thuật, phải biết lập trình để xây dựng thêm tính năng cho phù hợp với nhu cầu dự án. Cho công việc này, ngoài việc đòi hỏi kỹ năng lập trình như phân tích thiết kế, ngôn ngữ lập trình Java, .NET v.v nó còn đòi hỏi sự tập trung cao độ cho chất lượng. Vì bạn đang phát triển giải pháp nhằm được sử dụng để kiểm tra một sản phẩm phần mềm khác.

3. “Công việc KTPM không đòi hỏi nhiều khả năng phân tích, sáng tạo”

Thống kê cho thấy nếu chỉ dựa vào tài liệu về yêu cầu của ứng dụng (requirements) để tiến hành việc kiểm thử (cho dù các tài liệu này được viết ở mức tốt nhất có thể) thì kết quả cũng chỉ có thể kiếm được khoảng 70% những lỗi có thể xảy ra của ứng dụng. Trách nhiệm của Tester là làm sao phát hiện thêm được càng nhiều càng tốt, trong số 30% lỗi còn lại.

Họ phải phân tích xem với công nghệ và phương pháp cài đặt hiện tại có những rủi ro gì về chất lượng. Họ phải vượt ra khỏi các suy nghĩ thông thường (“think out-of-the-box”) về môi trường người dùng cuối; về những kịch bản sử dụng ứng dụng có thể dẫn đến những vấn đề không mong muốn; về những rủi ro khi phần mềm này tương tác với những phần mềm khác v.v. Hơn nữa, một sản phẩm phần mềm có thể được phát triển bằng công nghệ mới nhất, có nhiều tính năng và rất ít lỗi, nhưng lại không giúp người dùng giải quyết một cách hiệu quả các vấn đề của người dùng trong công việc của họ thì đó vẫn là sản phẩm kém chất lượng.

Tester thời hiện đại cần tham gia rất sớm vào dự án phát triển phần mềm mà không làm nhiều việc liên quan tới “kiểm thử” trong thời gian khởi đầu dự án. Trong giai đoạn này, họ sẽ cùng làm việc với nhóm phát triển phân tích, đánh giá yêu cầu, phân tích các sản phẩm tương tự và đưa ra những đề xuất để cải thiện tính năng của sản phẩm mà cả nhóm đang cùng thực hiện. Thông qua việc đánh giá công nghệ, kiến trúc họ sẽ phải xác định các rủi ro về chất lượng (quality), bảo mật (security), hiệu năng (performance), tính dễ sử dụng (usability) v.v.

Khi nhóm phát triển bắt đầu cài đặt là lúc họ lập ra chiến lược kiểm thử, chuẩn bị môi trường sao cho càng giống với môi trường thật càng tốt, nghiên cứu công cụ v.v . Thực tế có tương đối ít công cụ giúp bạn làm tốt những việc này. Vậy nên khả năng phân tích càng tốt và tính sáng tạo càng cao thì công việc của bạn sẽ càng hiệu quả và lý thú.

Định hướng nghề nghiệp – Trở thành Tester hay Developer?

4. “Công việc KTPM không có nhiều thử thách và cơ hội phát triển nghề nghiệp”

Cùng với sự suy thoái kinh tế toàn cầu, cạnh tranh trong lĩnh vực CNTT có lẽ hiện nay đang ở mức cao nhất. Một công ty đưa ra sản phẩm hoặc dịch vụ phần mềm sẽ ít có cơ hội sửa sai nếu như sản phẩm hoặc dịch vụ đó không đáp ứng được nhu cầu của người dùng. Điều này đặt một gánh nặng rất lớn lên đội ngũ phát triển phần mềm nói chung và Tester nói riêng. Cụ thể hơn là chất lượng công việc phải cao hơn; thời gian dành cho kiểm thử ít đi; kiểm thử sẽ phải được thực hiện trên nhiều môi trường và tình huống khác nhau; Tester sẽ phải toàn diện hơn để có thể đảm nhận nhiều loại công việc khác nhau trong từng giai đoạn của dự án v.v. Điều này đòi hỏi Tester phải tận dụng cơ hội và thời gian nhàn rỗi để trau dồi thêm kiến thức, kỹ năng nhằm chuẩn bị cho những thử thách sắp tới hơn là chỉ “đóng khung” trong công việc của dự án hiện tại.

Với đà phát triển của CNTT và tầm quan trọng ngày càng tăng của sản phẩm phần mềm trong công việc và cuộc sống, KTPM càng ngày càng trở nên quan trọng và có thể thấy được điều này thông qua sự phát triển qui mô nhanh chóng của những công ty chuyên cung cấp dịch vụ KTPM tại Việt Nam trong thời gian qua. Tester bây giờ có nhiều chọn lựa hơn trong công việc, ví dụ như trở thành chuyên gia tư vấn hoặc chuyên gia kỹ thuật cho KTPM. Và bạn chắc chắn sẽ luôn có cơ hội để trở thành chuyên gia hay nhà quản lý cấp cao trong công ty.

Lời Kết

Một điều không thể phủ nhận là tất cả những người thành công trong nhiều lĩnh vực khác nhau đều có một điểm chung là bắt nguồn từ sự đam mê, tâm huyết với ngành nghề mà họ theo đuổi. Và nghề KTPM cũng không phải là ngoại lệ. Công việc KTPM đã, đang và sẽ luôn tồn tại những việc có phần đơn giản, lặp đi lặp lại và cũng không thiếu những việc thử thách thực sự.

Có thể thấy ở người Tester giỏi dáng dấp của một Tester “truyền thống” với kỹ năng và kiến thức nền tảng về kiểm thử; dáng dấp của một Business Analyst với kỹ năng phân tích và đánh giá yêu cầu; dáng dấp của một người Developer với kỹ năng thiết kế và lập trình cơ bản; dáng dấp của Expertise End-User (Người dùng cuối thành thạo) với khả năng đánh giá rủi ro và chất lượng sản phẩm trước khi đưa ra thị trường. Điều này có thể là khó khăn nhưng cũng là thử thách hấp dẫn cho những Tester muốn thăng hoa trên con đường nghề nghiệp.

Trên con đường trở thành chuyên gia, thử thách lớn nhất đối với Tester không phải là công nghệ mới hay vấn đề kỹ thuật hóc búa mà là vượt qua chính bản thân mình để luôn giữ được “lửa nghề”.

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

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

Xem thêm tuyển dụng tester hà nội, đà nẵng, hcm hấp dẫn trên TopDev

Distributed cache là gì? – điều gì khiến nó trở nên mạnh mẽ?

Distributed cache là gì? – điều gì khiến nó trở nên mạnh mẽ?

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

Ngày nay, một ứng dụng web, một app trên mobile cũng có thể có cả triệu user sử dụng, truyền đạt (gửi đi tới cả tera bytes). Rõ ràng là thế, vậy mới đẻ ra nghành Khoa Học Dữ Liệu (Data Scientist), càng ngày càng hot.

Để đảm bảo cho trải nghiệm của người dùng, cache chưa bao giờ là sự lựa chọn tồi. Tuy nhiên, chỉ mỗi cache không là chưa đủ.

  Buffer là gì? Hiểu về Buffer và Cache
  Speed up Microservices 2: Tận dụng trình duyệt và cache

Số lượng request nhiều -> một server cache sẽ chịu tải quá lớn -> nếu đầu tư quá nhiều phần cứng -> lãng phí. Đây là nguyên nhân chính ta cần phải biết thêm về Distributed cache (Cache phân tán).

1. Distributed cache là gì?

Distributed cache thì có 2 chữ, chữ Distributed và chữ Cache. Giờ focus vô chữ Cache trước

Caching is a commonly used technology to boost application performance as well as reduce costs

Caching là công nghệ thường được sử dụng để tăng hiệu năng của ứng dụng, giảm chi phí xuống ở mức tối thiểu

Bằng cách sử dụng cache và CDN, user không còn phải chờ tải nội dung từ hơn 1000km hoặc từ châu lục này tới châu lục khác.

By caching frequently accessed data in memory, rather than the backend database, applications can deliver highly responsive experiences.

Bằng cách thường xuyên sử dụng cache để lưu trữ data trong bộ nhớ, hơn là sử dụng Backend DB, ứng dụng có thể cung cấp những trải nghiệm tuyệt vời hơn tới User.

Khác với CDN, thay vì mỗi khu vực sẽ có vài ba máy chủ chịu trách nhiệm cache dữ liệu. Distributed ở đây được hiểu như “phân tán, phân phối”, giảm tải nội dung cần cache cho server.

Distributed caching is simply an extension of this concept, but the cache is configured to span multiple servers.

Distributed caching là phần mở rộng phần định nghĩa về cache, nội dung cần cache ở đây sẽ được cấu hình cho nhiều servers khác nhau.

Distributed caching thường được sử dụng cho cloud computing và virtualised environments (môi trường ảo hóa)

2. Sức mạnh tới từ đâu?

2.1 Hiệu năng – Performance

Không thể phủ nhận là khi sử dụng cache, phần hiệu năng cho end user được tăng lên một cách rõ rệt. Trải nghiệm tuyệt vời hơn, không còn thời gian chờ đợi

For a given workload, the cache must meet and sustain the application’s required steady-state performance targets for latency and throughput.

Đối với một công việc nhất định, cache giúp đáp ứng và duy trì các mục tiêu về hiệu năng (độ trễ là mức tải)

2.2 Mở rộng – Scalability

Càng ngày cành nhiều user, nhưng do điều chuyển linh hoạt, không quá focus tập trung vào một server nên thời gian cache không còn là vấn đề.

Không những đáp ứng được về khả năng mở rộng, Distributed cache còn có thể mở rộng nhanh chóng, không ảnh hưởng về mặt tính năng

2.3 Sẵn sàng – Availability

Sử dụng cache còn giúp đỡ về tính sẵn sàng cho toàn hệ thống. Thay vì server down, đang pending chưa thể update hoặc get dữ liệu mới nhất từ server. Có thể lấy từ cache

The cache must ensure the availability of data 24/7 so that data is always available during planned and unplanned downtime.

Cache để giúp đảm bảo tính sẵn sàng 24/7 của data.

2.4 Chi phí – Affordability

Tất nhiên rồi, dùng gì mà chẳng quan tâm tới chi phí. Tuy nhiên, thay vì nâng cấp server, chi phí bỏ ra để thuê các server tỏ ra linh hoạt và tiện dụng hơn.

Distributed cache là gì? – điều gì khiến nó trở nên mạnh mẽ?
Hiện tại cũng không thiếu các dịch vụ CDN, giá cũng không quá chát. Anh em không cần lo lắng quá

3. Đọc thêm về Distributed cache

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

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

Xem thêm IT Jobs for Developer hấp dẫn trên TopDev

Top 5 việc làm tuyển dụng IT Developer nổi bật

tuyen dung it
tuyen dung it

Nhiều ứng viên IT đang quan tâm nhiều đến tuyen dung it Developer. Với một thị trường cạnh tranh, các ứng viên có nhiều sự lựa chọn hơn. Bài viết sau đây sẽ chia sẻ với các bạn Top 5 việc tuyen dung it Developer hot nhất tại TopDev.

Các Job tuyển dụng IT HCM

tuyen dung it

1. PHP Developer  

Location: Quận Phú Nhuận, Hồ Chí Minh

Yêu cầu công việc:

  • Kinh nghiệm làm việc với PHP ít nhất 2 năm.
  • Có kinh nghiệm làm việc với Laravel từ phiên bản 6 ít nhất 1 năm.
  • Nắm vững kiến thức về OOP, MVC trong PHP.
  • Có khả năng viết được PHP Packages và quản lý package bằng Composer.
  • Nắm được cấu trúc hoạt động của Laravel.
  • Có kinh nghiệm xây dựng RESTful API với Laravel Framework.
  • Kinh nghiệm xây dựng package cho Laravel Framework.
  • Có kiến thức về cơ bản về JS/ES.
  • Có kiến thức/ biết sử dụng 1 trong các Framework Frontend như: VueJS, React, Angular,…; là một lợi thế.
  • Nhiệt huyết, chủ động, sáng tạo, có trách nhiệm cao trong công việc.
  • Có khả năng làm việc độc lập, làm việc nhóm.

Tìm việc làm IT HCM ngay trên TopDev

2. Tuyen dung IT Junior/ enior Developer (ASP.NET)

Location: Quận Tân Bình, Hồ Chí Minh

Yêu cầu công việc:

  • Học vấn và kinh nghiệm
    • Tốt nghiệp Cao đẳng/ Đại học chuyên ngành CNTT, Toán Tin, Viễn thông…
    • Có tối thiểu 1 năm kinh nghiệm trở lên ở vị trí Developer
  • Trình độ chuyên môn
    • Có kinh nghiệm làm việc với: C#, ASP.Net, Javascript, React, Jquery, Kendo, MVC,…
    • Có kinh nghiệm về CSOM, JSOM & REST, Jquery với Ajax.
    • Trải nghiệm cơ sở dữ liệu MS SQL Server.
    • Có kiến thức về lập trình hướng đối tượng tốt.
  • Kỹ năng mềm
    • Có tư duy logic tốt + khả năng tự học
    • Khả năng chịu áp lực công việc tốt
    • Có trách nhiệm, nhiệt huyết với công việc
    • Khả năng đọc hiểu tài liệu tiếng Anh.

Ít ứng viên biết được tầm quan trọng của tuyen dung it. Chẳng hạn như bạn biết apply vị trí tuyển dụng ASP.NET, bạn cần đánh giá lại năng lực của mình.

Hoặc ít nhất là biết cách chọn lọc các năng lực phù hợp để CV IT Developer của mình tạo ấn tượng mạnh với nhà tuyển dụng. Từ Fresher, Junior là gì, Senior là gì? Hay freelancer it đến các vị trí cấp cao hơn.  

3. Front-end Developer 

Location: Quận Bình Thạnh, Hồ Chí Minh

Yêu cầu công việc:

  • Thành thạo hệ thống Vue và React, các thuộc tính tính toán, quản lý vòng đời sản phẩm (product life cycle), giao tiếp lệnh Vue, Vuex,…
  • Nắm rõ các kĩ thuật Web Front-End, bao gồm: echart, CSS3, HTML5, Ajax, JQuery, XML, JSON,…
  • Thành thạo công nghệ phát triển Web trên nền tảng AMP/LNMP, thành thạo phát triển PHP, Java
  • Thành thạo các công cụ kỹ thuật như: Webpack, Grunt, Gulp, Git.
  • Có khả năng phân tích giải quyết vấn đề một cách đc lập, coi trọng tinh thần đồng đội, giao tiếp tốt, có tinh thần trách nhiệm.

Xem thêm việc làm Golang HCM cho bạn

Các Job tuyen dung IT ở khu vực Hà Nội

tuyen dung it
tuyen dung it da nang

4. Java Developer (Junior/Mid/Senior)

Location: Quận Cầu Giấy, Hà Nội

Yêu cầu công việc:

  • Thành thạo lập trình hướng đối tượng, nền tảng lập trình, cấu trúc dữ liệu và giải thuật tốt, có kiến thức về design parterns.
  • Sử dụng thành thạo 1 trong các ngôn ngữ lập trình như Java
  • Nắm vững các công nghệ webservices (SOAP/RESTful), websocket, Hibernate, Struts, JPA, HTML5/CSS3, Spring MVC, Microservices, Primefaces, Javascript,….
  • Thành thạo & có kinh nghiệm làm việc với một trong các hệ quản trị CSDL như Oracle/ MSSQL/ MySQL, Maria DB, …
  • Nắm rõ quy trình phát triển phần mềm, ưu tiên ứng viên đã có kinh nghiệm với Agile, Scrum và ứng viên có các chứng chỉ lập trình như Java SE, EE, …
  • Cẩn thận, tỉ mỉ, chịu được áp lực công việc cao, có khả năng làm việc độc lập và làm việc nhóm hiệu quả.)

5. Senior Unity Developer (Sign-on bonus)

Location: Quận Thanh Xuân, Hà Nội

Yêu cầu công việc:

  • Tốt nghiệp đại học chuyên ngành CNTT
  • Kinh nghiệm Unity 4 năm trở lên, hiểu rõ về Unity (AssetBundle, Plugin, Editor, Anim, …).
  • Hiểu và vận dụng tốt lập trình hướng đối tượng.
  • Thành thạo C#, biết review code, refactoring code.
  • Hiểu về các Design Pattern.
  • Sử dụng thành thạo GIT/SVN
  • Có kinh nghiệm dev native plugin cho Unity
  • Có tư duy logic tốt, chủ động trong công việc, có tinh thần trách nhiệm cao
  • Yêu thích làm game, đã hoàn thiện 1 game phức tạp về kỹ thuật
  • Chi tiết công việc trao đổi khi phỏng vấn

Xem thêm các việc làm it tại Hà Nội cho bạn

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

Xem thêm Việc làm Developer trên TopDev

Viettelimex – Kiến tạo tương lai từ nguồn nhân lực công nghệ tài năng

Viettelimex - Kiến tạo tương lai từ nguồn nhân lực công nghệ tài năng

Hướng đến mục tiêu giúp Viettel đẩy mạnh chuyển đổi số và thực hiện sứ mệnh tiên phong kiến tạo Xã hội số tại Việt Nam, Viettelimex đã và đang làm tốt nhiệm vụ của mình. Đây cũng chính là cơ hội cho các lập trình viên trẻ phát triển và tỏa sáng tài năng. Hãy cùng khám phá những điều thú vị đang chờ các nhân tài công nghệ tại Viettelimex.

Viettelimex - Kiến tạo tương lai từ nguồn nhân lực công nghệ tài năngViettelimex – Ra đời với mục tiêu cùng tập đoàn Viettel chinh phục kỷ nguyên số

Viettelimex với tên đầy đủ là Công ty TNHH Nhà nước Một Thành viên Thương mại và Xuất nhập khẩu Viettel, là đơn vị hạch toán độc lập trực thuộc Tập đoàn Viễn Thông Quân Đội. Trải qua hơn 20 năm xây dựng và phát triển kể từ năm 1997, công ty đã khẳng định được vị thế của mình với mức tăng trưởng vượt bậc và những đóng góp không nhỏ vào sự lớn mạnh của Tập đoàn.

Viettelimex là đơn vị đa ngành, với 5 trung tâm bao gồm hoạt động trên nhiều lĩnh vực: bán lẻ, phân phối, xuất nhập khẩu, in ấn và dịch vụ CNTT. Trong đó lấy việc sản xuất và gia công phần mềm làm mũi nhọn, thực hiện nhiệm vụ chuyển dịch số cho tập đoàn với sự xuất hiện của “gương mặt mới” mang tên Viettel Software Service.

Sự ra đời của Viettelimex – Viettel Software Service: Bước chuyển mình cho sứ mệnh tiên phong kiến tạo Xã hội số tại Việt Nam

Chính thức góp mặt vào cuộc đua công nghệ từ tháng 8 năm 2020, Trung tâm Dịch vụ Công nghệ Thông tin (Viettel Software Service) là công ty trực thuộc Viettelimex (đơn vị hạch toán độc lập trực thuộc Tập đoàn Viễn Thông Quân Đội từ năm 2006), hoạt động với mục tiêu thực hiện sứ mệnh chuyển dịch ngành nghề và chuyển dịch số của Công ty Viettelimex và Tập đoàn Viettel.

Được thừa hưởng những thế mạnh từ Viettel và Viettelimex, ngay từ khi ra đời, Viettelimex – Viettel Software Service đã cho thấy sức mạnh nội tại của bản thân với:

  • Hạ tầng Viễn thông, Công nghệ Thông tin trải khắp 11 quốc gia;
  • Số lượng khách hàng khổng lồ, lên đến 100 triệu người dùng trong và ngoài nước;
  • Nguồn nhân lực số, chất lượng cao cùng đội quân tinh nhuệ với kinh nghiệm cung cấp dịch vụ khách hàng chuyên nghiệp,…

Mang trong mình ngọn lửa quyết tâm, Viettelimex – Viettel Software Service đang từng ngày phát triển vượt trội từ những thế mạnh sẵn có và không ngừng trở nên lớn mạnh, đem đến các dịch vụ gia công phần mềm, dịch vụ nhân sự Công nghệ Thông tin chất lượng cao, dịch vụ managed services, các sản phẩm công nghệ thông tin theo nhu cầu của khách hàng và thị trường,… hướng đến mục tiêu trở thành doanh nghiệp cung cấp sản phẩm, dịch vụ Công nghệ Thông tin hàng đầu tại Việt Nam. 

Chiêu mộ nhân tài, từng bước xây dựng đội quân tinh nhuệ phát triển Viettelimex – Viettel Software Service

Viettelimex - Kiến tạo tương lai từ nguồn nhân lực công nghệ tài năng

Với tham vọng trở thành công ty sản xuất và gia công phần mềm thuộc top 10 tại Việt Nam, Viettelimex – Viettel Software Service đang tìm kiếm những người cộng sự tài năng cho các vị trí:  

  • Java Developer (Junior/Mid/Senior) – VIETTEL COMMERCE (A MEMBER OF VIETTEL GROUP)
  • Kỹ sư Quản trị dự án phần mềm (Software Project Manager)
  • Kỹ sư Quản lý chất lượng phần mềm (QA)
  • Mobile Developer (Android/iOS/React Native/Flutter)

Thừa hưởng những giá trị cốt lõi từ tập đoàn mẹ – Viettel, Viettelimex – Viettel Software Service đem đến cho mỗi “phần tử” của mình cơ hội được phát triển và tỏa sáng tài năng:

  • Tại Viettelimex – Viettel Software Service, mỗi nhân viên là một “ngôi sao” với những thế mạnh của riêng mình.
  • Viettelimex – Viettel Software Service luôn tạo mọi điều kiện cho các nhân viên của mình phát huy khả năng sáng tạo, phát triển bản thân và cảm thấy hạnh phúc khi tham gia vào từng công việc. 
  • Đăc biệt, các tài năng công nghệ sẽ có cơ hội thử sức ở nhiều lĩnh vực, sản phẩm tại Tập đoàn tiên phong mở đường , kiến tạo tương lai thông minh, gắn kết của Việt Nam.

Với mục tiêu vì con người, vì Viettel hùng mạnh, vì Việt Nam thịnh cường, Viettelimex – Viettel Software Service không chỉ mở ra cơ hội nghề nghiệp hấp dẫn cho các tài năng, mà còn sẵn sàng mang đến cho mỗi nhân viên những phúc lợi đặc sắc:

  • Thu nhập hấp dẫn cho các Junior lên đến $1,500;
  • Hưởng lương thứ 13, 14 cực “ngầu” cùng loạt thưởng hấp dẫn, cạnh tranh với thị trường;
  • Được chăm sóc sức khỏe tận tình, đảm bảo quyền lợi đầy đủ;
  • Được “trao” quyền cho mọi sự “đột phá” và phát triển, công ty sẵn sàng nâng bước bạn vươn xa;
  • Nâng trình chuyên môn với các chương trình đào tạo, trao dồi kỹ năng định kỳ và nhu cầu thực tế;

Ngoài môi trường làm việc năng động và sáng tạo, đến với Viettelimex – Viettel Software Service, bạn được thoải mái phát triển cùng những người đồng đội tuyệt vời, luôn hỗ trợ bạn trên con đường phát triển sự nghiệp.

Và nếu bạn đã sẵn sàng viết nên câu chuyện thành công của riêng mình thì Viettelimex tuyển dụng Software Service chính là điểm đến đầy hứa hẹn cho tài năng của bạn.

Đừng bỏ lỡ cơ gia nhập đội ngũ công nghệ tại Viettelimex – Viettel Software Service

Apply ngay hôm nay!

Product Manager là gì? Có gì khác biệt trong công việc của Owner Product với Product Manager?

product manager là gì
Product Manager là gì? Có gì khác biệt trong công việc của Owner Product với Product Manager?

Tác giả: Jay LeBoeuf

Trách nhiệm của một Product Manager là gì?

1. Công việc chính với Product Manager là gì?

Với những người đang quan tâm đến vị trí này thì thắc mắc về công việc hàng ngày của Product Manager là gì là điều đương nhiên. Product Manager là người chịu trách nhiệm toàn bộ với quá trình làm việc và xử lý một sản phẩm. Họ sẽ là người hướng dẫn team của mình, xác định các vấn đề cần ưu tiên trước sau, xác định lộ trình làm việc với sản phẩm. Product Manager cũng là người theo dõi và giám sát toàn bộ quá trình đó trong một bức tranh tổng thể nhất. Nghe thì có vẻ đơn giản nhưng thật sự việc “giám sát” này không đơn thuần như theo dõi những thứ khác.

Sau khi thông qua chương trình Product Manager tuyển dụng của bất cứ công ty nào bạn cũng cần biết rằng, quá trình làm việc của Product Manager có ảnh hưởng trực tiếp đến sự hình thành của sản phẩm và sự phát triển của cả công ty. Với những ứng viên đã ứng tuyển chương trình tuyển dụng PM chưa có kinh nghiệm, họ sẽ là người hiểu rất rõ vấn đề hiểu được cấu tạo sản phẩm sẽ giúp ích cho công việc của chúng ta như thế nào. Một sản phẩm tốt về cơ bản cần đảm bảo đáp ứng được các yêu cầu của người dùng nên Product Manager cần hiểu rõ mục đích tạo ra sản phẩm. Quá trình làm việc sẽ đi đến đâu và team cần phải làm gì để đạt được mục tiêu, Product Manager phải truyền đạt những vấn đề này với team như thế nào để mọi người hiểu rõ và làm theo cho hiệu quả.

product manager
Product Manager phải phụ trách khá nhiều công việc

2. 3 đối tượng trọng yếu trong công việc của Product Manager

Công việc hàng ngày của Product Manager sẽ xoay quanh 3 đối tượng sau: thứ nhất, công ty sẽ có khách hàng nên Product Manager phải hiểu được suy nghĩ và mong muốn của khách hàng. Thứ hai, họ phải tính toán về những vấn đề mà cần giải quyết vì họ đã có một team gồm các dev, tester và tất cả bên liên quan đang làm việc để build sản phẩm. Vấn đề thứ ba liên quan đến doanh nghiệp vì công ty hay bất cứ ai mà bạn làm việc cùng đều có những lợi ích nhất định, đó có thể là một định hướng chiến lược hoặc mục tiêu về doanh thu. Product Manager sẽ là người làm việc liên tục với cả ba đối tượng này và cố gắng cân bằng tốt nhất các mục tiêu mà họ đặt ra.

Product Owner sẽ đảm nhận những công việc gì?

Trước đây, Product Manager sẽ tạo một tài liệu gọi là yêu cầu thị trường. Kho thông tin khổng lồ này sẽ cho các PM biết về mục đích sử dụng của phần mềm và ai sẽ sử dụng những phần mềm này. Với sự phát triển nhanh chóng của công nghệ ngày nay, hầu hết các công ty liên quan đến công nghệ, vai trò chính của Product Manager được chuyển sang một team gọi là Product Owner.

Nhiệm vụ của Product Owner

Bởi vì Product Owner có vai trò thật sự cụ thể trong việc xây dựng và phát triển sản phẩm. Họ cũng chịu nhiều trách nhiệm khác nhau với sản phẩm mà công việc chính của họ là đảm bảo rằng team của mình biết họ cần phải làm gì tiếp theo để tạo nên sản phẩm. Team sẽ chia nhỏ các ý tưởng lớn thành những phần nhỏ và thực hiện dưới sự hỗ trợ lẫn nhau, nhờ đó họ sẽ biết được đâu là thứ tự ưu tiên cho các task mình đang làm.

product manager tuyển dụng
Product Owner chịu trách nhiệm cụ thể với từng task

Trong quá trình làm việc, Product Manager và Product Owner sẽ phải đảm bảo rằng các task còn tồn đọng phải được ưu tiên và họ sẽ làm việc với bộ phận marketing để lên phương án quảng cáo cho sản phẩm. Bên cạnh đó họ cũng cần đưa ra những feedback cho team development, kể cả nếu cần thiết, họ phải ra ngoài và test sản phẩm trực tiếp với khách hàng.

Xem thêm Những mẹo hay để trở thành một Product Manager giỏi nhất

Với những công ty lớn như KMS Technology thì chương trình KMS Technology tuyển dụng về cơ bản các công việc liên quan cũng sẽ được thực hiện như thế. Vậy nên nếu muốn tích lũy thêm nhiều kinh nghiệm hơn cho bản thân, ngay từ bây giờ các bạn sinh viên nên tham khảo các chương trình KMS tuyển dụng Intern để ứng tuyển và có cơ hội làm việc tại môi trường chuyên nghiệp này.

Bài viết được transcript từ video gốc

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

Xem thêm việc làm tuyển dụng lập trình viên hấp dẫn tại TopDev

Log MySQL Query

Log MySQL Query

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

Đôi khi bạn sẽ cần monitor tất cả các query được gửi tới server trong một khoảng thời gian, mysql cung cấp cho ta vài phương pháp để làm chuyện này, cho phép chúng ta xem log của các query và log của các query chậm. Nếu ta kích hoạt tính năng log query thì những log được tạo sẽ sẽ nằm ở một tront hai chỗ file hoặc 2 bảng general_log và slow_log của database có tên là mysql.

  Kinh nghiệm vận hành MySQL - Chú ý khi chọn MySQL làm database

  MySQL Newbie: Quên mật khẩu Root?

1. Đầu tiên, kiểm tra nếu database mysql chưa có hai bảng sau slow_log và general_log thì tạo nó.

  • Để tạo bảng general_log
CREATE TABLE `general_log` (
   `event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
                          ON UPDATE CURRENT_TIMESTAMP,
   `user_host` mediumtext NOT NULL,
   `thread_id` bigint(21) unsigned NOT NULL,
   `server_id` int(10) unsigned NOT NULL,
   `command_type` varchar(64) NOT NULL,
   `argument` mediumtext NOT NULL
  ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='General log'

Bảng genera_log là mt bản ghi ghi lại những gì mysqld đã làm, mysql server ghi thông tin đến log này khi client kết nối hay ngắt kết nối, log lại mỗi câu truy vấn nhận được từ client. Log này rất hữu dụng khi bạn nghi ngờ có sự sai sót trong truy vấn và muốn truy được client nào đã gửi truy vấn tới.

  • Và để tạo bảng slow_log
CREATE TABLE `slow_log` (
   `start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP 
                          ON UPDATE CURRENT_TIMESTAMP,
   `user_host` mediumtext NOT NULL,
   `query_time` time NOT NULL,
   `lock_time` time NOT NULL,
   `rows_sent` int(11) NOT NULL,
   `rows_examined` int(11) NOT NULL,
   `db` varchar(512) NOT NULL,
   `last_insert_id` int(11) NOT NULL,
   `insert_id` int(11) NOT NULL,
   `server_id` int(10) unsigned NOT NULL,
   `sql_text` mediumtext NOT NULL,
   `thread_id` bigint(21) unsigned NOT NULL
  ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log'

Log này lưu lại nhưng câu query mất nhiều thời gian để hoàn thành, bao lâu là lâu được định nghĩa ở biến long_query_time, giá trị mặc định là 10s, slow_log rất quan trọng trong việc xác định query để tối ưu hóa hóa, nhầm tăng hiệu suất của hệ thống.

2. Mở tính năng ghi nhật kí log trên database

set global log_output = 'table';
set global general_log = 1;
set global slow_query_log = 1;

3. Định nghĩa thế nào là slow query

# Mình set 1s là lâu
set global long_query_time = 1;

4. Bây giờ để xem log bạn có thể dùng query này.

select * from mysql.general_log;
select * from mysql.slow_log;

5. Nếu bạn muốn tắt log query vào table

set global general_log = 0;
set global slow_query_log = 0;

Nguồn : TablePlus

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

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

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

Con trỏ và cấp phát động trong C++

Con trỏ và cấp phát động trong C++

Bài viết được sự cho phép của tác giả Khiêm Lê

Con trỏ (pointer) là một khái niệm quan trọng và khó nhất trong C++, nó thường được dùng để đánh giá mức độ thành thạo C++ của bạn. Việc sử dụng thành thạo con trỏ đi cùng với việc thành thạo các thao tác cấp phát động, quản lý bộ nhớ một cách chặt chẽ trong C++.

Kiến trúc máy tính

Để hiểu được bài này, chúng ta cần biết được kiến thức cơ bản về bộ nhớ máy tính, cụ thể là RAM. Chúng ta sẽ không tìm hiểu quá sâu mà chỉ ở mức cơ bản, đủ để có thể hiểu được con trỏ hoạt động như thế nào.

  Code game rắn săn mồi trên console bằng C++
  Hàm bạn và lớp bạn trong C++

RAM (Random Access Memory) là bộ nhớ được dùng để lưu trữ dữ liệu tạm thời để xử lý và dữ liệu sẽ mất đi khi ngưng cấp điện cho RAM. RAM chứa rất nhiều ô nhớ và mỗi ô nhớ có kích thước là 1 byte (1 byte = 8 bit). Các ô nhớ có địa chỉ duy nhất và được đánh số từ 0 trở đi.

Khi trình biên dịch thực hiện biên dịch code, nó sẽ dành riêng một vùng nhớ cho biến được khai báo, liên kết địa chỉ ô nhớ đầu tiên của vùng nhớ đó với tên biến và mỗi khi gọi đến biến đó, nó sẽ tự truy xuất đến vùng nhớ đã được liên kết với tên biến đó. Vùng nhớ của một biến là tập các ô nhớ liền kề nhau. Các biến khác nhau không nhất thiết các vùng nhớ của nó phải liền kề nhau.

Con trỏ và cấp phát động trong C++

Tùy vào kích thước của kiểu dữ liệu mà trình biên dịch sẽ cấp phát số ô nhớ liền kề khác nhau tương ứng. Ví dụ như kiểu char có kích thước 1 byte thì sẽ cấp cho biến kiểu char 1 ô nhớ, kiểu int có 4 byte thì sẽ cấp cho 4 ô nhớ liền kề nhau và địa chỉ của biến đó là địa chỉ của ô nhớ đầu tiên của vùng nhớ đó. Ví dụ như hình trên thì ta có biến x kiểu int được cấp phát 4 ô nhớ và địa chỉ của biến x chính là địa chỉ của ô nhớ đầu tiên của vùng 4 ô nhớ đó chính là 0x0B.

Cấp phát bộ nhớ trong C++

Cấp phát bộ nhớ tĩnh (Static memory allocation)

Biến tĩnh hay biến được cấp phát tĩnh là biến được khai báo bằng cú pháp khai báo biến, có tên và được cấp phát một vùng nhớ cố định trước khi sử dụng. Vùng nhớ cố định ở đây nghĩa là vùng nhớ đó luôn tồn tại khi chương trình thực thi, không thể được xóa đi (tức trả lại cho hệ điều hành) hoặc là thay đổi kích thước (đối với mảng), sau khi kết thúc chương trình sẽ tự động trả vùng nhớ đó lại cho hệ điều hành.

Chính việc cấp phát vùng nhớ cố định cho biến tĩnh gây chiếm dụng bộ nhớ nếu ta không có nhu cầu sử dụng biến đó nữa, hoặc ta không thể thay đổi kích thước nếu dữ liệu vượt quá kích thước lưu trữ của biến (đối với mảng). Đây chính là lúc chúng ta sử dụng biến động.

Cấp phát bộ nhớ động (Dynamic memory allocation)

Biến động hay biến được cấp phát động là biến thuộc một kiểu dữ liệu đã định nghĩa, không có tên, không được khai báo trong phần khai báo biến. Điều này có nghĩa là biến động là một biến được cấp phát một vùng nhớ trong bộ nhớ RAM, không được liên kết với tên biến do đó nó không có tên, nó chỉ là một vùng nhớ. Việc quản lý biến động được thực hiện qua con trỏ.

Cấp phát bộ nhớ tự động (Automatic memory allocation)

Cấp phát bộ nhớ tự động là quá trình cấp phát và giải phóng bộ nhớ diễn ra tự động cho các tham số hàm và biến cục bộ trong một chương trình. Khi chương trình đi vào một khối lệnh, vùng nhớ cho các biến cục bộ và tham số hàm sẽ được cấp phát. Khi khối lệnh kết thúc hoặc bị thoát, vùng nhớ này sẽ tự động được giải phóng. Điều này giúp giảm thiểu lỗi do quản lý bộ nhớ thủ công và tăng cường hiệu suất lập trình.

Con trỏ trong c++ là gì

Để hiểu nắm được kiến thức về cấp phát động, trước tiên, hãy điểm qua một số kiến thức về con trở (pointer).

Con trỏ (pointer) là một biến đặc biệt trong C++ được sử dụng để lưu trữ địa chỉ của một biến khác. Con trỏ không chỉ lưu trữ giá trị của biến mà còn có thể trỏ đến địa chỉ bộ nhớ nơi biến đó được lưu trữ. Con trỏ là một công cụ mạnh mẽ trong lập trình C++ vì chúng cung cấp một cách linh hoạt để truy cập và thao tác dữ liệu trong bộ nhớ.

Biến con trỏ

Biến con trỏ hay thường gọi là con trỏ là biến dùng để lưu trữ giá trị là địa chỉ ô nhớ. Nghĩa là bản thân con trỏ là một biến thông thường nhưng mà nó chứa địa chỉ của biến tĩnh hoặc biến động. Như đã trình bày ở trên, biến động không có tên do đó chỉ có thể được quản lý qua con trỏ, do đó, con trỏ thường được dùng để chứa địa chỉ của biến động, lúc này ta nói con trỏ này trỏ đến hoặc con trỏ này tham chiếu đến biến hoặc vùng nhớ đó. Do con trỏ chỉ chứa địa chỉ nên mọi con trỏ đều có kích thước như nhau.

Do con trỏ liên quan đến việc tham chiếu đến địa chỉ của biến, ta phải tìm hiểu các toán tử & và *. Toán tử & và * là toán tử một ngôi, toán tử & (address-of operator) được đặt trước tên biến và cho biết địa chỉ ô nhớ đầu tiên trong vùng nhớ của biến đó. Toán tử * (dereferencing operator hay indirection operator) được đặt trước một địa chỉ để lấy giá trị lưu trữ tại địa chỉ đó. Ví dụ:

int a = 2409; // Giả sử a được cấp phát vùng nhớ có địa chỉ 0x50
cout << &a; // lấy địa chỉ của biến a tức là 0x50
cout << *&a; // in ra giá trị được lưu trữ tại địa chỉ của biến a, tức là 2409
// hay nói cách khác *&a ~ a

Để tạo một con trỏ, ta sử dụng cú pháp như sau:

<kiểu_dữ_liệu> *<tên_biến_con_trỏ>;
// Trong đó <kiểu_dữ_liệu> là kiểu dữ liệu của biến mà con trỏ này trỏ tới
// Ví dụ
int *ptr_a; // Khai báo con trỏ có tên là ptr_a

Nhớ là kiểu con trỏ là kiểu gì thì ta chỉ được trỏ tới biến kiểu đó, ví dụ không thể đem một con trỏ int mà trỏ vào biến kiểu double được. Biến con trỏ không có kiểu riêng mà chỉ phụ thuộc vào đối tượng mà nó trỏ đến, do đó khi chưa xác định được kiểu dữ liệu của đối tượng trỏ đến, ta dùng kiểu void.

void *p;
int a = 2, *pt;
p = (void *) &a;
pt = (int *) p;
*pt += 3;      // a = 5

Dấu * khi khai báo con trỏ nên được đặt trước và sát vào tên biến:

int *ptr_1, *ptr_2;
// LƯU Ý
int* ptr1, ptr2; // không phải là 2 con trỏ mà chỉ ptr1 là con trỏ

Lưu ý: để phân biệt con trỏ và biến thường, ta thường sử dụng prefix ptr để phân biệt con trỏ.

Một con trỏ lưu giá trị là địa chỉ, vậy nên để trỏ đến một biến, ta dùng toán tử & như sau:

int a = 2409;    // giả sử địa chỉ của biến a là 0x50
int *ptr_a = &a; // trỏ con trỏ ptr_a đến địa chỉ của biến a, tức lúc này con trỏ ptr_a mang giá trị 0x50
cout << *ptr_a;  // in ra giá trị lưu trữ tại địa chỉ con trỏ trỏ tới, tức là 2409

Do con trỏ trỏ tới biến là trỏ vào vùng nhớ mà biến đó được cấp nên khi ta thay đổi giá trị của vùng nhớ đó thì giá trị của biến cũng thay đổi theo. Quan sát ví dụ sau bạn sẽ thấy rõ hơn, biến a có giá trị là 2409, địa chỉ ô nhớ giả sử là 0x50, sau đó tạo con trỏ ptr_a trỏ tới biến a. Khi ta thực hiện thay đổi giá trị tại địa chỉ con trỏ đang giữ qua toán tử *, tức là đang thay đổi giá trị tại ô nhớ 0x50 do con trỏ đang trỏ tới a, mà 0x50 lại là địa chỉ của biến a, do đó giá trị của biến a cũng bị thay đổi theo.

int a = 2409;    // giả sử địa chỉ ô nhớ của a là 0x50
int *ptr_a = &a; // trỏ con trỏ ptr_a tới biến a
*ptr_a = 2001;   // thay đổi giá trị tại ô nhớ mà con trỏ ptr_a đang giữ
cout << a;       // 2001

Tóm lại về con trỏ, bạn cần nhớ được:

Một số bạn sẽ hơi thắc mắc dấu * trong lúc khai báo con trỏ và dấu * trước con trỏ. Dấu * trong lúc khai báo con trỏ chỉ là cú pháp để khai báo con trỏ mà thôi. Còn dấu * trước con trỏ là toán tử *, dùng để lấy giá trị lưu trữ tại địa chỉ mà con trỏ trỏ tới.

  • *ptr_a và a đều là chỉ giá trị của a
  • ptr_a và &a đều là địa chỉ của biến a
  • Không thể thay đổi hay tự quyết định địa chỉ của biến (việc này do hệ điều hành thực hiện)
  • Con trỏ chỉ có thể tham chiếu đến đối tượng có kiểu dữ liệu tương thích
  • Không thể tham chiếu con trỏ đến một biểu thức hay hằng (vì biểu thức, hằng làm gì có địa chỉ)

Hằng con trỏ và đối tượng hằng

Như đã trình bảy ở trên, biến con trỏ cũng giống như một biến bình thường nhưng dùng để lưu trữ địa chỉ, con trỏ cũng có hằng con trỏ như hằng bình thường. Hằng con trỏ sẽ được khởi tạo giá trị một lần duy nhất và không được gán lại giá trị mới, hay nói cách khác là chỉ trỏ đến một đối tượng duy nhất mà thôi. Cú pháp khai báo tương tự con trỏ nhưng có từ khóa const phía trước tên biến:

int a = 2409;
int b = 2001;
int *const ptr_a = &a; // con trỏ ptr_a trỏ đến biến a
ptr_a = &b; // lỗi vì ptr_a là hằng con trỏ, không thể gán giá trị khác được

Đối tượng hằng tức là một con trỏ mà ta không thể sử dụng toán tử * để gán lại giá trị tại vùng nhớ mà nó trỏ tới. Đối tượng hằng vẫn có thể trỏ đến đối tượng khác được.

int a = 2409;
const int *ptr_a = &a; // con trỏ otr_a trỏ đến biến a
*ptr_a = 2001; // lỗi vì ptr_a là đối tượng hằng, không thể gán giá trị qua toán tử *

Bạn không muốn thay đổi giá trị và cũng không muốn tham chiếu lại vào biến khác bạn có thể kết hợp cả hai như sau:

int a = 2409;
const int *const ptr_a = &a;

Con trỏ hàm

Ngoài những loại con trỏ trên, ta còn có một loại con trỏ đặc biệt nữa đó chính là con trỏ hàm (function pointer). Để khai báo một con trỏ hàm, ta sử dụng cú pháp sau:

<kiểu_dữ_liệu> (*<tên_con_trỏ>)([các_tham_số]);
// Ví dụ
int (*funcPtr_sum)(int a, int b);
// Bạn có thể đặt tên tham số hoặc là không như thế này
int (*funcPtr_sum)(int, int);

Bạn có thể hiểu đơn giản rằng <kiểu_dữ_liệu> chính là kiểu dữ liệu mà hàm trả về, <tên_con_trỏ> là tên hàm, dấu ngoặc tròn “()” bên ngoài là bắt buộc để nói cho compiler biết đó là một con trỏ hàm và các tham số truyền vào giống như tham số truyền vào hàm vậy thôi và được đặt trong dấu ngoặc tròn.

Để trỏ tới một hàm, các bạn làm như sau:

int sum(int a, int b)
{
	return a + b;
}

funcPtr_sum = sum;     // nên dùng
// Hoặc có thể dùng toán tử &
funcPtr_sum =// Cả hai đều tương đương như sau
// Tại sao thì sẽ được giải thích bên dưới

Các kiểu dữ liệu và tham số của con trỏ hàm phải tương đương với các tham số và kiểu dữ liệu trả về của hàm mà các bạn muốn trỏ tới. Như trong ví dụ trên kiểu trả về của hàm sum là int thì kiểu dữ liệu khai báo con trỏ cũng phải là int và kiểu dữ liệu tham số của con trỏ cũng giống với tham số hàm sum. Và để gọi hàm mà con trỏ đó trỏ tới, bạn chỉ cần thực hiện như cách sau:

funcPtr_sum(4, 5);    // được 9, nên dùng cách này
// Hoặc dùng theo cách "con trỏ style"
(*funcPtr_sum)(3, 4); // được 7

Có thể nhiều bạn sẽ hỏi tại sao phải sử dụng con trỏ hàm cho mất công vậy, mình có thể gọi trực tiếp hàm đó mà. Đúng thật là vậy nhưng khi sử dụng con trỏ hàm, bạn sẽ làm được một thứ mà không cách nào thực hiện được nếu không có con trỏ hàm đó chính là truyền tham số là hàm.

Để thực hiện truyền con trỏ hàm cho hàm, ta cũng thực hiện truyền như truyền một con trỏ thông thường. Hãy xem ví dụ sau để hiểu rõ hơn:

int sum(int a, int b)
{
	return a + b;
}

void myFunc(int (*func_ptr)(int, int))
{
	func_ptr(4, 5);
}

// bên trong hàm main
int (*funcPtr_sum)(int, int) = sum;
myFunc(funcPtr_sum);        // được 9

Một số bạn quen với một số ngôn ngữ hiện đại thì sẽ quen cách truyền hàm cho hàm, tuy nhiên có thể bạn không hiểu được bản chất tại sao nó như vậy. Qua con trỏ hàm bạn có thể hiểu được cách thức mà một hàm có thể được truyền cho một hàm.

Có một só điều bạn cần lưu ý về con trỏ hàm như sau:

  • Không như con trỏ thông thường, con trỏ hàm không phải là con trỏ trỏ vào vùng nhớ mà nó trỏ vào code. Có thể hiểu là nó trỏ vào điểm bắt đầu của một hàm hoặc là nó đang tham chiếu đến hàm đó và nó là nick name của hàm đó, do đó ta có thể gọi nó thay vì trực tiếp gọi hàm.
  • Con trỏ hàm không cần cấp phát hay giải phóng vì nó không trỏ vào vùng nhớ.
  • Bạn có thấy ở ví dụ trên mình có thể sử dụng toán tử & hoặc không, và cũng tương tự đối với việc sử dụng toán tử *. Đó là do tên hàm có thể được sử dụng để lấy địa chỉ (hay điểm bắt đầu) của hàm, do đó tên hàm giống như đã bao gồm toán tử & rồi.
  • Giống như một con trỏ thông thường, ta cũng có thể có một mảng con trỏ hàm. Con trỏ hàm cũng có thể được sử dụng như để rẽ nhánh như sau:
  • Giống như con trỏ thông thường, con trỏ hàm có thể được truyền cho hàm, giống như ví dụ bên trên. Loại con trỏ này rất thường đường sử dụng trong C++.

Con trỏ NULL

Con trỏ NULL (NULL pointer) hay con trỏ trỏ vào NULL là con trỏ không trỏ vào đâu cả, nó khác với con trỏ chưa được khởi tạo. Bởi vì khi được khai báo, con trỏ không được khởi tạo giá trị thì sẽ mang giá trị rác. Do đó, khi làm việc với con trỏ, khi chưa trỏ vào đâu cả thì ta nên khởi gán con trỏ đó bằng NULL (vì nếu không may, ta thực hiện truy xuất đến vùng nhớ rác không tồn tại sẽ gây ra kết quả không mong muốn).

int *ptr_a = 0;    // ptr_a là con trỏ NULL
int *ptr_b = NULL; // NULL là macro định nghĩa sẵn bằng 0, tức là NULL

Ngoài ra, C++ 11 còn cung cấp một từ khóa mới là nullptr, cũng là dùng để chỉ con trỏ NULL.

int *ptr_a = nullptr;

Để kiểm tra xem một con trỏ có NULL hay không ta dùng câu lệnh if.

if (my_ptr) // true nếu con trỏ không NULL, false nếu con trỏ NULL
	cout << "NOT NULL";
else
	cout << "NULL ptr";

Xem thêm các vị trí tuyển dụng lập trình C++ lương cao tại Topdev.

Con trỏ trỏ vào con trỏ

Con trỏ cũng giống như một biến thông thường nên nó sẽ có địa chỉ, do đó, một con trỏ có thể được một con trỏ khác trỏ tới. Ví dụ;

int *a = new int(2409);
int **ptr_a = &a; // con trỏ ptr_a trỏ vào con trỏ a

Khi một con trỏ trỏ vào con trỏ, con trỏ đó sẽ giữ giá trị là địa chỉ của con trỏ mà nó trỏ tới, vậy nên nếu muốn lấy giá trị của biến của con trỏ mà nó đang trỏ tới ta phải dùng hai lần toán tử *, lần 1 là để lấy địa chỉ của con trỏ nó trỏ tới đang giữ, lần 2 là để lấy giá trị được lưu trữ tại địa chỉ mà biến của con trỏ mà nó trỏ tới đang giữ.

int *a = new int(2409); // giả sử biến động có địa chỉ 0x50 và con trỏ có địa chỉ 0x70
int **ptr_a = &a; // con trỏ ptr_a trỏ vào con trỏ a tức ptr_a mang giá trị 0x70
cout << *ptr_a;   // được 0x50 là giá trị của con trỏ nó trỏ tới đang giữ tức địa chỉ của a đang giữ
cout << **ptr_a;  // được 2409 do nó lấy giá trị lưu trữ tại địa chỉ a đang giữ chính là 0x50

Con trỏ trỏ vào con trỏ được ứng dụng để xây dựng mảng hai chiều như sau:

// Tạo ra mảng 2 chiều 10x10
int** arr = new int* [10];
for (auto i = 0; i < 10; i++)
	arr[i] = new int[10];

// truy xuất phần tử giống như mảng bình thường

// Giải phóng vùng nhớ
for (auto i = 0; i < 10; i++)
	delete[] arr[i];
delete[] arr;
arr = nullptr;

Việc sử dụng con trỏ đối với mảng một chiều đã khá rắc rối rồi nên mình sẽ không đi sâu vào mảng nhiều chiều mà chỉ giới thiệu cho các bạn biết vậy thôi. Bạn nên sử dụng vector như trong bài giới thiệu ở trên.

Con trỏ và hàm

Con trỏ là một kiểu dữ liệu, do đó nó có thể được sử dụng trong lúc truyền tham số cho hàm hoặc kiểu dữ liệu trả về.

int* doSomething(int* n)
{
	cout << *n; // giá trị tại địa chỉ mà con trỏ n đang trỏ tới
	cout << n;  // địa chỉ con trỏ n đang lưu trữ
	return n;
}

Để truyền đối số vào tham số trong hàm, ta phải truyền cùng kiểu pointer hoặc là một địa chỉ như khi bạn tham chiếu con trỏ vậy.

int n = 5;
doSomething(&n);

// Hoặc
int *pt = new int(5);
doSomething(pt);

Lưu ý do thao tác trên con trỏ là thao tác trên địa chỉ ô nhớ, vậy nên thay đổi giá trị tại địa chỉ ô nhớ cũng làm thay đổi luôn giá trị của biến nó tham chiếu tới tương tự như biến tham chiếu vậy.

>> Đọc tiếp: Chi tiết về Con trỏ và hàm trong C++

Con trỏ và đối tượng

Con trỏ không có kiểu dữ liệu cụ thể mà phụ thuộc vào đối tượng nó trỏ vào, do đó nó có thể là bất kỳ bao gồm kiểu dữ liệu do người dùng định nghĩa như struct hay class. Ví dụ:

struct MyStruct
{
	int count;
};

MyStruct mStruct;
mStruct.count = 5;

MyStruct *ptr = &mStruct;

Thông thường để gọi một thuộc tính hay một phương thức của một đối tượng, ta sử dụng dấu chấm (.), nhưng đối với con trỏ khi đã trỏ đến đối tượng, ta sử dụng dấu mũi tên (->) như sau:

// Biến thông thường
cout << mStruct.count; // 5
// thông qua con trỏ
cout << ptr->count;      // 5

Tương tự đối với class:

class MyClass
{
public:
	int count;

	MyClass(int n)
	{
		count = n;
	}

	void print() const
	{
		cout << count << endl;
	}
};

MyClass mClass(5);
MyClass *ptr = &mClass;
cout << ptr->count;  // 5
ptr->print();        // 5

Ta cũng có thể thực hiện cấp phát động như sau:

MyStruct *ptr_struct = new MyStruct;
MyClass *ptr_class = new MyClass(5);

Chi tiết về cấp phát động

Ở phần đầu ta đã nắm khái quát về khái niệm cấp phát động, tiếp tục tìm hiểu chi tiết về Cấp phát bộ nhớ động.

Một số bạn sẽ hỏi tại sao lại dùng con trỏ chi cho mệt vậy, cứ biến tĩnh mà dùng, sao phải dùng rồi lại thêm toán tử &, * cho rối. Tất cả những ví dụ ở trên chỉ để cho bạn hiểu được con trỏ mà thôi, sức mạnh thực sự của con trỏ nằm ở chỗ nó được sử dụng để quản lý biến động.

Để cấp phát vùng nhớ cho một biến động ta làm như sau:

new <kiểu_dữ_liệu>;
// Ví dụ
new int;
new float;

Nếu như cấp phát vùng nhớ thành công, toán tử new sẽ trả về một con trỏ trỏ tới địa chỉ của vùng nhớ mới. Và như đã nói ở trên, biến động không có tên do đó nó được quản lý bằng con trỏ, vậy nên khi tạo biến động ta gán luôn địa chỉ của nó cho con trỏ như sau:

int *ptr = new int; // con trỏ ptr lúc này đang trỏ tới biến động kiểu int đã được tạo
// Bạn cũng có thể khởi tạo giá trị ngay khi khai báo như sau
int *ptr1 = new int(2409);

Bây giờ bạn có thể thao tác trên biến động vừa cấp phát thông qua con trỏ như sau:

*ptr = 2001;

Cấp phát động là yêu cầu cấp phát một vùng nhớ, do đó sẽ có thể xảy ra trường hợp không đủ bộ nhớ để cấp phát, lúc này toán tử new sẽ trả về con trỏ NULL, bạn có thể kiểm tra như sau:

int* myPtr = new int;
if (myPtr != nullptr)
	cout << "Memory allocated";
else
	cout << "Bad allocate";

Sau khi đã sử dụng xong, dữ liệu trong vùng nhớ của biến động nên được xóa đi và trả lại cho hệ điều hành. Việc này rất quan trọng, việc không giải phóng sau khi sử dụng sẽ khiến cho vùng nhớ đó tồn tại nhưng hệ điều hành không được sử dụng do nó đã được cấp phát cho chương trình của chúng ta, dẫn đến việc rò rỉ bộ nhớ.

Hiện nay, hầu hết các hệ điều hành hiện đại quản lý việc cấp phát bộ nhớ một cách triệt để, mỗi khi chương trình kết thúc bộ nhớ đã được cấp phát sẽ được thu hồi lại, tuy nhiên dữ liệu trong vùng nhớ đó không được xóa, việc này cũng dẫn đến rò rỉ bộ nhớ. Do đó, bạn vẫn nên xóa và giải phóng biến động mỗi khi kết thúc chương trình hoặc sử dụng xong.

Việc xóa và giải phóng vùng nhớ của biến động được thực hiện qua toán tử delete. Cú pháp như sau:

delete <tên_biến_con_trỏ>;
// ví dụ
delete ptr_a;

Sau khi xóa đi, vùng nhớ đó đã được xóa dữ liệu và trả lại cho hệ điều hành quản lý, tuy nhiên, con trỏ mà đang trỏ đến vùng nhớ đó vẫn đang chứa địa chỉ đó. Việc sử dụng con trỏ này sẽ gây ra hậu quả không mong muốn do biến động nó trỏ tới không còn tồn tại, do đó khi delete biến động, ta nên gán lại con trỏ NULL.

int *ptr = new int(1);
delete ptr;
ptr = nullptr;

Mảng động

Mảng động là một topic quan trọng trong C++, việc sử dụng mảng C++ thông thường, bạn sẽ không thể thay đổi kích thước của mảng (thêm khi cần và xóa khi không cần), mảng động sẽ giải quyết việc này. Để cấp phát một mảng động, ta sử dụng toán tử new và sau kiểu dữ liệu phải cung cấp số lượng phần tử [size]:

new <kiểu_dữ_liệu_của_mỗi_phần_tử>[size];

// ví dụ
new int[100];

Hệ điều hành sẽ cấp phát cho biến động một dãy các vùng nhớ liền kề nhau, mỗi vùng nhớ có kích thước bằng với kích thước của phần tử của mảng đó. Tương tự với mảng thông thường bạn vẫn phải cung cấp kích thước mảng và kích thước đó phải là hằng. Và cũng tương tự như biến động thông thường, bạn vẫn quản lý thông qua con trỏ như sau:

int *myArr = new int[100];

Đối với mảng động, toán tử new sẽ trả về con trỏ trỏ vào ô nhớ đầu tiên của vùng nhớ được cấp phát cho mảng đó. Việc thao tác với mảng động thực hiện qua con trỏ cũng tương tự như đối với mảng thông thường như sau:

myArr[0] = 1;
myArr[1] = 2;

Hoặc thao tác theo cách “con trỏ style” như sau:

*(myArr + 0) = 1;
*(myArr + 1) = 2;

Việc thực hiện myArr + i tức là lấy địa chỉ của con trỏ myArr (tức địa chỉ phần tử đầu tiên) rồi cộng thêm i lần kích thước của mỗi vùng nhớ. Ví dụ như kiểu byte có kích thước là 4 byte, myArr đang trỏ vào phần tử đầu tiên giả sử địa chỉ 0x50, khi gọi đến myArr là phần tử đầu tiên nghĩa là không cộng thêm gì hết là chính nó do con trỏ trỏ vào phần tử đầu tiên của mảng tức là 0x50. Gọi myArr + 1 tức là địa chỉ của phần tử đầu tiên cộng với 1 lần kích thước của kiểu int là 4 byte, tức là phần tử 0x54, sau đó dùng toán tử * để lấy giá trị như ví dụ bên trên.

Và cũng để tránh rò rỉ bộ nhớ, không dùng đến nữa thì ta cũng phải xóa mảng động đi. Để xóa mảng động đi nó có hơi khác một chút là có dấu [] sau toán tử delete.

delete[] ptr;

Vậy thì việc thay đổi kích thước mảng thực hiện ra làm sao? Bạn sẽ không có phương thức hỗ trợ nào mà phải làm thủ công. Tức là bạn sẽ phải tạo một mảng mới với kích thước phần tử mới, sau đó copy phần tử sang mảng mới và xóa mảng cũ đi như sau:

// Mảng ban đầu
int size = 3;
int* arr = new int[size] {1, 2, 3};

// Tạo mảng mới
int newSize = 5;
int* newArr = new int[newSize];

// Copy phần tử từ mảng cũ sang mảng mới
for (auto i = 0; i < size; i++)
	newArr[i] = arr[i];

// Xóa mảng cũ đi
delete[] arr;
arr = nullptr;

Về bản chất, mảng thông thường thật ra chính là một mảng động được một hằng con trỏ trỏ tới:

int *const arr = new int[10];

Do nó hơi phức tạp nên người ta thường sử dụng lớp vector cũng được dựa trên con trỏ và mảng động. Bạn có thể xem bài viết về vector trong C++.

Sử dụng con trỏ là một kĩ thuật rất quan trong, bạn sẽ cần sử dụng nó thật thành thạo để có thể học tốt các môn như Lập trình hướng đối tượng, Cấu trúc dữ liệu và giải thuật…

Lợi ích của cấp phát động

Linh Hoạt Trong Quản Lý Bộ Nhớ

Cấp phát động cho phép chương trình quản lý bộ nhớ một cách linh hoạt, đáp ứng nhu cầu thay đổi liên tục về dung lượng bộ nhớ trong suốt quá trình thực thi. Điều này đặc biệt hữu ích khi làm việc với các cấu trúc dữ liệu động như danh sách liên kết, cây, hoặc các mảng có kích thước thay đổi.

Tiết Kiệm Bộ Nhớ

Với cấp phát động, bộ nhớ chỉ được cấp phát khi cần thiết và có thể được giải phóng khi không còn sử dụng nữa. Điều này giúp tiết kiệm tài nguyên hệ thống và tránh lãng phí bộ nhớ.

Tối Ưu Hóa Hiệu Suất

Bằng cách cấp phát đúng lượng bộ nhớ cần thiết tại thời điểm sử dụng, chương trình có thể hoạt động hiệu quả hơn. Điều này giúp cải thiện hiệu suất chương trình, đặc biệt là trong các ứng dụng yêu cầu nhiều bộ nhớ hoặc có cấu trúc dữ liệu phức tạp.

Hỗ Trợ Các Cấu Trúc Dữ Liệu Động

Cấp phát động hỗ trợ việc tạo và quản lý các cấu trúc dữ liệu động như danh sách liên kết, ngăn xếp, hàng đợi, và các cấu trúc phức tạp khác một cách hiệu quả. Điều này giúp lập trình viên dễ dàng triển khai các thuật toán phức tạp mà không cần biết trước kích thước của dữ liệu.

Hạn chế của cấp phát động

Rò Rỉ Bộ Nhớ (Memory Leak)

Một trong những hạn chế lớn nhất của cấp phát động là nguy cơ rò rỉ bộ nhớ. Nếu bộ nhớ cấp phát động không được giải phóng đúng cách sau khi không còn sử dụng, nó sẽ dẫn đến rò rỉ bộ nhớ, làm tiêu tốn tài nguyên hệ thống và có thể gây ra lỗi chương trình hoặc làm chậm hệ thống.

Quản Lý Bộ Nhớ Phức Tạp

Quản lý bộ nhớ động đòi hỏi lập trình viên phải cẩn thận trong việc cấp phát và giải phóng bộ nhớ. Sai sót trong quản lý bộ nhớ có thể dẫn đến các lỗi nghiêm trọng như truy cập bộ nhớ không hợp lệ, lỗi phân đoạn (segmentation fault) hoặc các vấn đề về ổn định chương trình.

Hiệu Suất Giảm Do Quản Lý Bộ Nhớ

Việc cấp phát và giải phóng bộ nhớ động có thể tiêu tốn thời gian và tài nguyên hệ thống. Quá trình quản lý bộ nhớ động, đặc biệt là khi sử dụng các bộ thu gom rác (garbage collector), có thể gây ra độ trễ không mong muốn và ảnh hưởng đến hiệu suất tổng thể của chương trình.

Khó Khăn Trong Việc Gỡ Lỗi

Việc gỡ lỗi các vấn đề liên quan đến bộ nhớ động thường phức tạp hơn so với bộ nhớ tĩnh. Lập trình viên phải sử dụng các công cụ và kỹ thuật đặc biệt để phát hiện và khắc phục các vấn đề như rò rỉ bộ nhớ hoặc truy cập bộ nhớ không hợp lệ.

Vậy là trong bài viết này, mình đã giới thiệu cho các bạn về con trỏ và cấp phát động trong C++, bài viết này khá là dài nhưng có thể vẫn chưa đầy đủ vì con trỏ trong C++ là một kỹ thuật thật sự rất là hay mà nếu bạn giỏi C++, bạn phải nắm được hết tối thiểu những thứ mình đã giới thiệu trong bài viết. Cảm ơn các bạn đã dành thời gian theo dõi bài viết, nếu như bạn thấy hay, đừng quên chia sẻ với bạn bè. Cảm ơn các bạn rất nhiều!

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

Có thể bạn quan tâm: Danh sách liên kết đơn trong C++

Xem thêm vị trí tuyển it chất lượng hấp dẫn trên TopDev

Python: Sự khác nhau giữa List và Tuple?

Python: Sự khác nhau giữa List và Tuple?

Bài viết được sự cho phép của tác giả Phạm Văn Nguyên

Bạn vừa tìm hiểu về lists và tuples và bạn đang tự hỏi làm thế nào chúng khác nhau?

Đây là một câu hỏi phổ biến đáng ngạc nhiên.

Cả 2 cũng khá giống nhau.

Cả lists và tuples là các kiểu dữ liệu chuỗi có thể lưu trữ một bộ sưu tập các item(mục).

Mỗi item được lưu trữ trong một list  hoặc một tuple có thể thuộc bất kỳ loại dữ liệu nào.

Và bạn cũng có thể truy cập bất kỳ item nào theo chỉ mục của nó.

Vì vậy, câu hỏi là, họ có khác nhau không?

Và nếu không, tại sao chúng ta có hai loại dữ liệu hoạt động khá giống nhau?

Chúng ta không thể sống với lists hoặc tuples?

Vâng, chúng ta hãy cố gắng tìm câu trả lời.

Sự khác biệt chính giữa list và Tuple

Sự khác biệt chính giữa list và tuples là thực tế là list có thể thay đổi (mutability) trong khi bộ dữ liệu là bất biến (immutability) .

Điều đó có nghĩa gì, bạn nói gì?

Một kiểu dữ liệu có thể thay đổi có nghĩa là một đối tượng python thuộc loại này có thể được sửa đổi.

Một đối tượng bất biến không thể.

Chúng ta hãy xem điều này có nghĩa là gì.

Hãy tạo một list và gán nó cho một biến.

>>> a = ["apples", "bananas", "oranges"]

Bây giờ hãy xem điều gì xảy ra khi chúng ta cố gắng sửa đổi item(mục) đầu tiên của danh sách.

Chúng ta hãy thay đổi apples thành một berries .

>>> a[0] = "berries"
>>> a
['berries', 'bananas', 'oranges']

Hoàn hảo! mục đầu tiên của a đã thay đổi.

Bây giờ, điều gì sẽ xảy ra nếu chúng ta muốn thử điều tương tự với một tuple thay vì một list? Hãy xem nào.

>>> a = ("apples", "bananas", "oranges")
>>> a[0] = "berries"
Traceback (most recent call last):
    File "", line 1, in
TypeError: 'tuple' object does not support item assignment

Lý do chúng tôi gặp lỗi này là vì các đối tượng tuple, không giống như list, là bất biến, điều đó có nghĩa là bạn không thể sửa đổi một đối tượng tuple sau khi nó được tạo.

Nhưng chờ đã, hãy xem ví dụ sau:

>>> a = ("apples", "bananas", "oranges")
>>> a = ("berries", "bananas", "oranges")
>>> a
('berries', 'bananas', 'oranges')

Ô điều gì đã xảy ra vậy?

Hãy xem, chúng ta có thực sự sửa đổi item đầu tiên trong tuple a với code ở trên không?

Câu trả lời là Không , hoàn toàn không.

Để hiểu tại sao, trước tiên bạn phải hiểu sự khác biệt giữa một biến và một đối tượng python.

  Chuyển đổi Unicode dựng sẵn & tổ hợp với Python

Sự khác biệt giữa một biến và một đối tượng

Bạn có thể nhầm lẫn các biến với các đối tượng. Đây là một quan niệm sai lầm rất phổ biến ở những người mới bắt đầu.

Hãy nhớ rằng một biến không là gì ngoài tham chiếu đến đối tượng python thực tế trong bộ nhớ.

Bản thân biến không phải là đối tượng.

Ví dụ: chúng ta hãy cố gắng hình dung những gì xảy ra khi bạn gán một list (danh sách) cho một biến a .

>>> a = ["apples", "bananas", "oranges"]

Khi bạn làm điều này, một đối tượng python kiểu list kiểu được tạo trong bộ nhớ và biến a tham chiếu đến đối tượng này bằng cách giữ vị trí của nó trong bộ nhớ .

Python: Sự khác nhau giữa List và Tuple?

Trong thực tế, bạn thực sự có thể lấy lại vị trí của đối tượng list trong bộ nhớ bằng cách kiểm tra a  bằng cách sử dụng function  id () .

>>> a = ["apples", "bananas", "oranges"]
>>> id(a)
26953168

Bây giờ nếu bạn sửa đổi chỉ item đầu tiên của danh sách và kiểm tra lại id () , bạn sẽ nhận được cùng một giá trị chính xác vì a vẫn đang tham chiếu đến cùng một đối tượng.

>>> a[0] = "berries"
>>> id(a)
4340729544

Hình dưới đây cho thấy chính xác những gì đã xảy ra sau khi sửa đổi.

Python: Sự khác nhau giữa List và Tuple?

Bây giờ, hãy xem điều gì xảy ra nếu chúng ta thực hiện điều tương tự trên các tuples.

>>> a = ("apples", "bananas", "oranges")
>>> id(a)
4340765824
>>> a = ("berries", "bananas", "oranges")
>>> id(a)
4340765464

Như bạn có thể thấy, hai địa chỉ là khác nhau.

Điều này có nghĩa là sau lần gán thứ hai, a đang đề cập đến một đối tượng hoàn toàn mới.

Con số này cho thấy chính xác những gì đã xảy ra.

Python: Sự khác nhau giữa List và Tuple?

Hơn nữa, nếu không có biến nào khác trong chương trình của bạn đề cập đến bộ dữ liệu cũ hơn thì trình thu gom rác của python sẽ xóa hoàn toàn bộ dữ liệu cũ khỏi bộ nhớ.

Vì vậy, bạn có nó, khái niệm về khả năng biến đổi này là sự khác biệt chính giữa list và tuples.

Mutability (khả năng biến đổi)  không chỉ là một khái niệm python, nó là một khái niệm ngôn ngữ lập trình mà bạn sẽ gặp trong các ngôn ngữ lập trình khác nhau .

Nhưng bây giờ có lẽ toàn bộ cuộc thảo luận này gợi lên một câu hỏi khác trong đầu bạn.

Tại sao chúng ta có các đối tượng có thể thay đổi và bất biến?

  Chạy Python web app

Tại sao chúng ta cần các đối tượng có thể thay đổi và bất biến?

Thật ra, cả hai đều phục vụ các mục đích khác nhau.

Chúng ta hãy thảo luận về một số khía cạnh phân biệt giữa các đối tượng có thể thay đổi và bất biến

1. Hiệu suất bổ sung

Python: Sự khác nhau giữa List và Tuple?

Khả năng tương tác sẽ hiệu quả hơn khi bạn biết bạn sẽ thường xuyên sửa đổi một đối tượng.

Ví dụ: giả sử bạn có một số đối tượng có thể lặp lại (iterable object) (giả sử x) và bạn muốn nối từng phần tử của x vào danh sách.

Tất nhiên bạn chỉ có thể thực hiện L = list (x) kiểu như thế này:

L  = []
for item in x:
    L.append(item)

Điều này hoạt động ổn. Bạn tiếp tục sửa đổi đối tượng danh sách tại chỗ cho đến khi tất cả các yếu tố của x tồn tại trong danh sách L .

Nhưng bạn thậm chí có thể tưởng tượng điều gì sẽ xảy ra nếu chúng ta đã sử dụng một tuple thay thế?

T  = ()
for item in x:
    T = T + (item,)

Bạn có thể hình dung những gì đang xảy ra trong bộ nhớ?

Vì các bộ dữ liệu là bất biến, về cơ bản, bạn đang sao chép nội dung của bộ dữ liệu Tsang một đối tượng bộ dữ liệu mới tại Mỗi lần lặp.

Nếu vòng lặp for lớn, đây là một vấn đề hiệu năng rất lớn.

Trên thực tế, chúng ta hãy sử dụng python để đo hiệu suất của việc thêm vào list so với việc thêm vào một tuple khi x = range (10000).

Bài viết này hướng dẫn bạn cách sử dụng module time để đo thời gian thực hiện của nhiều dòng python .

$ python3 -m timeit \
-s "L = []" \
-s "x = range(10000)" \
"for item in x:" "    L.append(item)"
1000 loops, best of 3: 1.08 msec per loop

Khá tốt, mất khoảng 1,08 mili giây .

Sẽ thế nào nếu chúng ta làm điều tương tự với tuples?

$ python3 -m timeit \
-s "T = ()" -s "x = range(10000)" \
"for item in x:" "    T = T + (item,)"
10 loops, best of 3: 1.63 sec per loop

Một con lớn hơn rất nhiều 1,63 giây ! ( gấp khoảng 1500 lần)

Đây là một sự khác biệt lớn về hiệu suất giữa các list và tuples.

Nếu bạn muốn kiểm tra sự kiên nhẫn của mình, hãy thử x = range(1000000).

Bây giờ khi ai đó nói với bạn việc append nhiều lần vào một đối tượng string(chuỗi) không hiệu quả, bạn sẽ hiểu chính xác lý do tại sao (các đối tượng chuỗi cũng không thay đổi trong python).

    Hiệu suất bổ sung: Thắng: đối tượng có khả năng thay đổi

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

2. Dễ dàng gỡ lỗi

Python: Sự khác nhau giữa List và Tuple?

Mutility (khả năng biến đổi)  là tuyệt vời và tất cả nhưng một điều có thể thực sự gây phiền nhiễu với các đối tượng có thể thay đổi là gỡ lỗi.

Ý tôi là gì?

Chúng ta hãy xem ví dụ rất đơn giản này.

>>> a = [1, 3, 5, 7]
>>> b = a
>>> b[0] = -10
>>> a
[-10, 3, 5, 7]

Lưu ý rằng khi chúng ta gán b = a , chúng ta sẽ không sao chép đối tượng list từ b sang a .

Chúng tôi thực sự nói với python rằng hai biến a và b nên tham chiếu cùng một đối tượng danh sách.

Bởi vì a vị trí có hiệu quả giữ vị trí của đối tượng Python trong bộ nhớ, khi bạn nói b = a bạn sao chép vị trí địa chỉ đó (không phải đối tượng thực tế) vào b .

Điều này dẫn đến việc có hai tham chiếu (a và b) cho cùng một đối tượng list.

Nói cách khác, khi chúng ta thực hiện b [0] = -10 , nó có tác dụng tương tự như a[0] = -10 .

Tất nhiên bạn có thể nhìn vào code và nghĩ đúng rằng nó rất dễ gỡ lỗi.

Chà, bạn đúng với những đoạn code nhỏ như thế này, nhưng hãy tưởng tượng nếu bạn có một dự án lớn với nhiều tham chiếu đến cùng một đối tượng có thể thay đổi.

Sẽ rất khó khăn để theo dõi tất cả các thay đổi đối với đối tượng này bởi vì bất kỳ sửa đổi nào trong số các tham chiếu đó sẽ sửa đổi đối tượng.

Đây không phải là trường hợp với các đối tượng bất biến ngay cả khi bạn có nhiều tài liệu tham khảo đến chúng.

Một khi một đối tượng bất biến được tạo ra, nội dung của nó sẽ không bao giờ thay đổi.

Dễ dàng gỡ lỗi: Thắng: đối tượng bất biến!

3. Hiệu quả bộ nhớ

Python: Sự khác nhau giữa List và Tuple?

Một lợi ích khác của tính bất biến là nó cho phép thực hiện ngôn ngữ để có hiệu quả bộ nhớ cao hơn.

Hãy để tôi giải thích những gì tôi có ý nghĩa bởi điều đó.

Trong CPython  (cách triển khai Python phổ biến nhất) nếu bạn tạo các đối tượng bất biến có cùng giá trị, python (trong một số điều kiện nhất định) có thể bó các đối tượng khác nhau này thành một.

Ví dụ, hãy xem code này:

>>> a = "Nguyenpv"
>>> b = "Nguyenpv"
>>> id(a)
58752160
>>> id(b)
58752160

Hãy nhớ rằng String(Chuỗi) (cũng như Integer, Float và Bools) đều là ví dụ về các đối tượng bất biến.

Như bạn có thể thấy, mặc dù trong chương trình python của chúng tôi, chúng tôi đã tạo rõ ràng hai đối tượng chuỗi khác nhau, python đã bó chúng lại thành một.

Làm thế nào chúng ta biết điều đó?

À bởi vì danh tính của a giống hệt như danh tính của b .

Python đã có thể làm điều đó bởi vì tính bất biến của chuỗi giúp cho việc thực hiện gói này an toàn.

Điều này không chỉ giúp chúng ta tiết kiệm bộ nhớ (bằng cách không lưu trữ chuỗi nhiều lần trong bộ nhớ) mà còn mỗi khi bạn muốn tạo một đối tượng mới có cùng giá trị, python sẽ chỉ tạo một tham chiếu đến đối tượng đã tồn tại trong bộ nhớ chắc chắn hiệu quả hơn.

Khái niệm này được gọi là String Interning , và đây là một bài viết tuyệt vời nếu bạn muốn tìm hiểu sâu hơn .

Không chỉ strings. Điều này cũng áp dụng cho integer (số nguyên) (trong điều kiện nhất định).

>>> a = 1
>>> b = 1
>>> id(a)
1788568672
>>> id(b)
1788568672

Điều đó thật tuyệt phải không?

Thế còn tuples thì sao?

CPython cho đến khi python 3.6 đưa ra quyết định thiết kế không tự động bundle (bó) hai bộ dữ liệu tương đương thành một.

>>> a = (1, 2)
>>> b = (1, 2)
>>> id(a)
58751816
>>> id(b)
58751536

Như bạn có thể thấy, a có bản sắc khác với b .

Quyết định thiết kế này có ý nghĩa bởi vì thực hiện thực tập cho các tuples đòi hỏi phải đảm bảo rằng tất cả các item (mục, phần tử) của tuples là bất biến.

Hiệu quả bộ nhớ: Thắng: bất biến

Phần kết luận

Để hiểu sự khác biệt giữa lists và tuples trong python, trước tiên bạn phải hiểu khái niệm về tính biến đổi (mutability) / immutability  (bất biến).

Lists là các đối tượng có thể thay đổi, có nghĩa là bạn có thể sửa đổi một đối tượng list sau khi nó được tạo.

Mặt khác, các tuple là các đối tượng bất biến, có nghĩa là bạn không thể sửa đổi một đối tượng tuple sau khi nó được tạo.

Cả Mutility(có thể thay đổi) và Immutability (bất biến) đều có những ưu điểm và nhược điểm riêng.

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

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

Tuyển dụng lập trình viên lương cao trên TopDev

Giới thiệu về Reactive Relational Database Connectivity (R2DBC)

Giới thiệu về Reactive Relational Database Connectivity

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

Lúc trước, khi làm việc với các ứng dụng với cơ chế Reactive có sử dụng database, chúng ta thường sẽ sử dụng MongoDB database bởi vì lúc đó rất ít database hỗ trợ cơ chế Reactive ngoại trừ MongoDB. Nhưng hiện tại thì các bạn không cần nhất thiết phải sử dụng MongoDB nữa, Reactive Relational Database Connectivity (R2DBC) sẽ giúp chúng ta làm việc với nhiều relational database system khác theo cơ chế Reactive. R2DBC là một spec định nghĩa cách thức chúng ta sẽ làm việc với các relational database như MySQL, PostgreSQL, … theo cơ chế Reactive là như thế nào. Nó cung cấp cho chúng ta một bộ thư viện Service Provider Interface (SPI) giúp chúng ta có thể implement driver cho từng loại database system tương ứng. Có nhiều driver đã hiện thực bộ SPI này, ví dụ như R2DBC PostgreSQL hiện thực R2DBC cho PostgreSQL database, tương tự chúng ta cũng có R2DBC MySQL hỗ trợ cho MySQL database, … Trong bài viết này, mình sẽ giới thiệu với các bạn về R2DBC với một ví dụ về cách sử dụng R2DBC PostgreSQL driver để thao tác với PostgreSQL database các bạn nhé!

  3 bước tối ưu hiệu năng React App bằng các API mới của React
  Giới thiệu về Reactive Programing trong javascript

Các bạn có thể xem detail về nội dung specs mà R2DBC định nghĩa ở đây.

Đầu tiên, mình sẽ tạo mới một Maven project:

Giới thiệu về Reactive Relational Database Connectivity (R2DBC)

với R2DBC PostgreSQL driver dependency như sau:

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-postgresql</artifactId>
    <version>0.8.6.RELEASE</version>
</dependency>

Tương tự như khi các bạn làm việc với JDBC, điều đầu tiên chúng ta cần làm việc với một database bất kỳ là setup connection tới nó. Với R2DBC, chúng ta sẽ sử dụng đối tượng ConnectionFactory để làm điều này.

Có 2 cách để chúng ta có thể làm được điều này trong R2DBC đó là:

  • Sử dụng connection URL
  • Sử dụng trực tiếp code Java

Với connection URL thì các bạn có thể sử dụng URL sau cho PostgreSQL database (các database system khác thì connection URL sẽ khác một xí các bạn nhé!):

Trong đó:

  • host là PostgreSQL server,
  • port là port PostgreSQL đang chạy. Port mặc định của PostgreSQL là 5432 nha các bạn!
  • database là tên database mà chúng ta sẽ làm việc.
  • username là user đăng nhập vào PostgreSQL database
  • password là mật khẩu của user

Sau đó thì sử dụng đối tượng ConnectionFactories để get đối tượng ConnectionFactory từ connection URL này:

Nếu các bạn sử dụng code Java thì có thể sử dụng đối tượng Builder trong class ConnectionFactoryOptions để làm điều này:

Sau khi đã có đối tượng ConnectionFactory xong, chúng ta có thể tạo một Publisher các connection tới database bằng cách gọi:

Khởi tạo đối tượng Flux của Project Reactor để subcribe đối tượng Publisher này:

Bây giờ thì chúng ta có thể sử dụng connection từ đối tượng Mono này để thao tác với database rồi, ví dụ như:

Ở đây, chúng ta sử dụng phương thức createStatement() để truyền vào câu query mà các bạn muốn sử dụng để đối tượng Connection execute và trả về kết quả.

Giờ ví dụ trong database test của PostgreSQL database server của mình có một table tên là student với các column name và address như sau:

Để insert một record mới vào table này với R2DBC, mình sẽ viết code như sau:

Như các bạn thấy, chúng ta có thể bind giá trị mà chúng ta muốn vào câu query sử dụng phương thức bind() của đối tượng Statement và sau khi đã bind dữ liệu xong, chúng ta có thể gọi phương thức execute() của đối tượng Statement này để chạy câu lệnh SQL.

Xem thêm việc làm React cho SV mới ra trường tại đây

Kết quả sau khi execute câu lệnh SQL sẽ được trả về trong đối tượng Result của R2DBC, chúng ta phải consume đối tượng Result này thì câu lệnh SQL mới fully execute được. Do đó, chúng ta cần viết thêm code như sau:

Phương thức getRowsUpdated() sẽ trả về số record được cập nhập sau khi execute câu lệnh SQL. Đối tượng Result còn chứa một phương thức khác tên là map() giúp chúng ta có thể lấy được data trả về từ database, với câu lệnh SELECT chẳng hạn.

Chúng ta cần gọi phương thức subscribe() để subcribe vào Publisher.

Toàn bộ code có thể viết lại như sau:

package com.huongdanjava.r2dbcpostgresql;

import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.Result;
import reactor.core.publisher.Flux;

public class Application {

public static void main(String[] args) throws InterruptedException {
ConnectionFactory connectionFactory = ConnectionFactories
     .get("r2dbc:postgresql://khanh:123456@localhost:5432/test");

Flux.from(connectionFactory.create())
.flatMap(connection -> connection.createStatement("INSERT INTO student (name, address) VALUES ($1, $2)")
      .bind("$1", "khanh")
      .bind("$2", "Ho Chi Minh")
      .execute())
.flatMap(Result::getRowsUpdated)
.doOnNext(s -> System.out.println(s))
.subscribe();

Thread.sleep(5000);
}

}

Ở đây, mình có sử dụng thêm phương thức doOnNext() để lấy về thông tin số record được cập nhập trong database. Và vì chúng ta cần đợi cho chương trình chạy hoàn thành rồi mới exit nên mình cũng sử dụng thêm Thread.sleep() 5 giây để làm việc này.

Kết quả khi chạy đoạn code trên như sau:

Giới thiệu về Reactive Relational Database Connectivity (R2DBC)

Check database:

Giới thiệu về Reactive Relational Database Connectivity (R2DBC)

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 database, tuyển dụng IT hấp dẫn trên TopDev

Đâu là những thách thức đối với Freelancer IT?

freelancer IT
freelancer IT

Lập trình viên IT luôn có những thách thức tồn tại? Vậy đối với một freelancer IT thì sẽ thế nào? Đâu là trình độ đủ chuẩn của một freelancer IT? Cùng TopDev tìm hiểu bài viết sau đây nhé!

Freelancer là gì? Thế nào là Freelancer IT?

Những người làm công việc với một thời gian không bị bó buộc; tự chủ về không gian được xem là một freelancer. Nhiều freelancer làm việc toàn thời gian hoặc chỉ làm việc vào thời gian rảnh của họ (cam kết với doanh nghiệp/khách hàng).

freelancer IT
Freelancer IT được định nghĩa như thế nào?

Hiểu một cách đơn giản, Freelancer IT (người làm lập trình tự do) với đặc tính trải nghiệm các công việc một cách tự do. Họ thật sự được thỏa mãn về nhu cầu thời gian, môi trường. Họ được trả tiền để đảm bảo các nhiệm vụ về ngành lập trình IT.

Các nhiệm vụ được họ thực hiện trong một thời gian nhất định theo hợp đồng freelancer. Và tất nhiên, nhiệm vụ của họ là đảm bảo hiệu suất; cam kết đầu ra theo các chỉ tiêu đã thỏa thuận trước đó.

Trở thành Freelancer IT – Học lập trình bao lâu là đủ?

Kiến thức bạn tích lũy thật sự có quan trọng hay không? Thực tế cho thấy, việc học tập là việc cả đời, là một quá trình dài. Và nếu bạn muốn đạt trình đủ kiến thức dưới góc độ của sự hoàn hảo, đó là một điều không thể. Ví dụ như một junior là gì, senior là gì? Và họ cần bao nhiêu thời gian để trưởng thành. Vì thế, hãy cứ thử sức lập trình viên freelancer IT nếu cảm thấy bản thân đạt tới mức ổn định.

Không còn là việc làm thế nào để viết CV IT Developer cho tốt, Cover Letter cho Dev, CV Template IT hiệu quả nữa. Bạn cần nhiều hơn thế.

Và khi trải nghiệm freelancer IT, bạn có thể trau đồi chúng. Bạn có thể học thêm các khóa học về kỹ năng lập trình, rèn luyện bài tập đồ án, đi tham gia các workshop – hội thảo khoa học chuyên ngành, tham gia câu lạc bộ có liên quan,… Đừng lo sợ sẽ không có cơ hội trau dồi các kiến thức liên quan đến freelancer IT.

Một điều quan trọng hơn – Đã làm thì phải chuyên

Vậy để chuyên thì thế nào? Kiến thức quan trọng nhưng kỹ năng vẫn không thể thiếu. Thậm chí nó có ý nghĩa lớn trong sự thăng tiến của bạn. Nếu thật sự muốn phát triển, bạn cần biết nhiều kỹ năng hơn. Một freelancer IT thực thụ sẽ không dừng lại ở việc trải nghiệm.

Xem thêm các việc làm NTQ Solution tuyển dụng

Các kỹ năng lập trình theo dự án, phân tích nhu cầu khách hàng, giải quyết vấn đề, nghệ thuật giao tiếp,… tất cả bạn cần phải rèn luyện.

Một khi hiểu được tầm quan trọng của kỹ năng và biết cách lập kế hoạch bồi dưỡng, mức lương xứng đáng sẽ đến với bạn. Niềm vui công việc tất nhiên có vì bạn đã có đam mê. Nhưng để chuyên sâu theo đuổi freelancer IT, phải đầu tư và dành cho nó một sự trân trọng.

Một số khó khăn khi trở thành Freelancer IT

freelancer IT
Nhiều thách thức được đặt ra cho mỗi Freelancer lập trình.

Thách thức tìm kiếm dự án

Bất kỳ ngành nghề, vị trí nào như tuyển dụng Data Scientist, bạn cũng chịu các sức ép cạnh tranh rất lớn để có thể phát triển.freelancer IT cũng không ngoại lệ. Tìm kiếm dự án được xem là một thách thức lớn. Vì vậy, hãy linh động tận dụng những nguồn khai thác việc freelance IT phù hợp. Một số nguồn có thể từ các trang web tìm việc freelance uy tín; thông tin từ các hội nhóm facebook groups, linkedin,… 

Khó khăn trong việc lựa chọn các dự án

Mỗi khi đưa quyết định nhận dự án freelance, bạn cần xem xét “sức nặng” mà mỗi dự án mang lại. Một freelancer IT chuyên nghiệp thì uy tín và trách nhiệm rất quan trọng. Nó là cơ sở để hình thành nên thương hiệu – dấu ấn cá nhân.

Hãy đưa ra các nhận định về kỹ năng, mức độ hoàn thành, quy mô sử dụng các phần mềm – kiến thức công nghệ,… trước khi nhận một dự án. Việc đánh giá là rất quan trọng. Vì nó giúp mỗi freelancer IT đo lường về tính khả thi và phần trăm hiệu suất đạt được của từng dự án.

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

Xem thêm việc làm ngành cntt hàng đầu tại TopDev

Viết một con Bash Script back up Mongo Database đơn giản

Viết một con Bash Script back up Mongo Database đơn giản

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

Chả là mấy ngày nay chả làm ăn được gì, hôm qua mở cửa hàng ra bán thì quản lí thị trường cứ lòng vòng nên cũng đóng luôn, hên sao ông anh rủ làm freelancer cho một project về academy video, hai ngày đầu code ngon lành server chạy mượt mà, nhưng sang ngày thứ 3 không biết vì sao con mongodb server lại không thể truy cập được, cả bọn hoảng hốt vì chưa có một bản backup nào cả. Hên sao 3 tiếng sau nó lại sống lại và rồi mình được ông a giao cho cái công việc viết script backup mongo giá 300k :))

Thực ra thì mình chả biết viết bash nhưng mà sau một vòng lội google thì cũng xong, yêu cầu thì cũng khá đơn giản.

  • Back up database có tên là psdp theo chu kì 1 ngày 1 lần.
  • Lưu vào aws s3 và chỉ giữ lại 5 bản mới nhất.

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

                 Tìm việc MongoBD mới nhất trong tháng

Triển khai

  1. Đầu tiên là làm sao để backup

Mongodb có sẳn tool mongodump để backup database, vậy thì để backup database psdp thì mình chỉ cần chạy lệnh

mongodump --uri=$URI --archive="$OUTPUT"

2. Làm sao để copy file đã backup sang aws s3

Uầy, google một vòng mình thấy aws có cùng cấp aws cli , có hỗ trợ command copy qua s3 luôn. Hướng dẫn cài đặt nằm ở đây.

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 
unzip awscliv2.zip 
sudo ./aws/install

À nếu con server đó chưa cài unzip thì chạy lệnh sudo apt install unzip để cài đặt. Sau khi cài xong aws cli thì phải cấu hình access key và secret key cho nó.

$ aws configure 
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE 
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY 
Default region name [None]: us-west-2 
Default output format [None]: json

Vậy là xong phần setup aws cli , tiếp theo là copy cái file này qua bucket bên s3, giả sửa cái bucket đó có tên là psdp-dev-mongo-backup ,và cái file cần copy qua s3 là psdp

aws s3 cp "psdp" "s3://psdp-dev-mongo-backup"

Về cơ bản là đã xong, nhưng giờ làm để xóa hết mấy cái backup cũ chỉ giữ lại 5,6 cái.

Ý tưởng đơn giản là lặp đi lặp lại đoạn code trên, cứ mỗi lần backup thì lưu lại tên file output đã backup vào trong một mảng, nếu số lần backup lớn hơn 5 thì ta lại xóa cái file đã cũ nhất. File đó có tền là phần tử nằm ở vị trí đầu tiên của mảng, sau khi xóa file rồi thì loại bỏ phần tử đó để cập nhật lại mảng, làm như này thì mảng sẽ luôn duy trì ở mức ≤ 5 giá trị.

Khai báo URI , BUCKETNAME , số lần backup i , mảng lưu tên file output của mongodump arr .

URI="mongodb://admin:123@localhost:27017/psdp?authSource=admin"
BUCKET="psdp-dev-mongo-backup"
declare -a arr=()
i = 0

Và sau đó lặp đi lặp lại thao tác.

  1. Tăng số lần đã backup
((i= i+1))echo "===== Lần thứ $i ======"echo "Backup PSDP NOW"echo "Time is $(date)"

2. Chạy lệnh mongodump xuất ra file, tên file có kèm dấu thời gian, tốt nhất là chuyển qua unix time cho ngắn gọn.

FILENAME=$(date +%s)mongodump --uri=$URI --archive="/home/ubuntu/mongoBackup/psdp-$FILENAME"echo "done => $FILENAME"

3. Copy file đó qua aws s3

aws s3 cp "psdp-$FILENAME" "s3://$BUCKET"echo "done => s3://$BUCKET/psdp-$FILENAME"

4. Thêm tên file vừa tạo vào mảng và liệt kê hiện tại có bao nhiêu file, bao gồm tên.

arr+=("psdp-$FILENAME")for f in ${arr[*]}
   do
      echo $f
done

5. Kiểm tra xem nếu số lần đã backup mà > 5 thì xóa đi file cũ nhất ở cả local và bên s3 . Sau đó xóa phần tử đầu của mảng và cập nhật lại mảng.

if (( $i > 5 ))
then
   echo "delete => ${arr[0]}"
   rm ${arr[0]}
   echo "deleted localfile => ${arr[0]}"   file="s3://$BUCKET/${arr[0]}"
   echo "delete on s3 => $file"
   aws s3 rm $file 
   echo "deleted on s3"   unset arr[0]
   arr=( "${arr[@]}" )
fi

6. Sleep trong vòng 24h = 24*60*60 = 86400 giây

sleep 86400

Và đây là đoạn code hoàn chỉnh.

URI="mongodb://xxx:xxx@xxx:xxx/psdp?authSource=admin"
BUCKET="xxx"
declare -a arr=()
i=0
while
((i= i+1))
echo "=============================== $i ========================================="
echo "Backup PSDP NOW"
echo "Time is $(date)"
FILENAME=$(date +%s)
mongodump --uri=$URI --archive="/home/ubuntu/mongoBackup/psdp-$FILENAME"
echo "done => $FILENAME"
echo "Copy to S3"
aws s3 cp "psdp-$FILENAME" "s3://$BUCKET"
echo "done => s3://$BUCKET/psdp-$FILENAME"
arr+=("psdp-$FILENAME")
for f in ${arr[*]}
do
echo $f
done
if (( $i > 5 ))
then
echo "delete => ${arr[0]}"
rm ${arr[0]}
echo "deleted localfile => ${arr[0]}"
file="s3://$BUCKET/${arr[0]}"
echo "delete on s3 => $file"
aws s3 rm $file
echo "deleted on s3"
unset arr[0]
arr=( "${arr[@]}" )
fi
echo "============================================================================"
sleep 3
do :;done
~

Rồi có code rồi vậy làm sao để chạy, nohup xin hân hạnh tài trợ chương trình này.

nohup bash ./cronbackupmongo.bash &

Toàn bộ stdout sẽ được ghi ra nohup.out và để xem output các bạn chạy lệnh.

sudo tail -f nohup.out

Output sẽ tương tự như sau.

============== Lần thứ 1 ==============
Backup PSDP NOW
Time is Tue Apr 14 06:12:30 UTC 2020
2020-04-14T06:12:30.358+0000 writing psdp.logs to archive '/home/ubuntu/mongoBackup/psdp-1586844750'
2020-04-14T06:12:30.360+0000 done dumping psdp.logs (1 document)
2020-04-14T06:12:30.382+0000 writing psdp.users to archive '/home/ubuntu/mongoBackup/psdp-1586844750'
2020-04-14T06:12:30.383+0000 done dumping psdp.users (1 document)
done => 1586844750
Copy to S3
upload: ./psdp-1586844750 to s3://psdp-dev-mongo-backup/psdp-1586844750
done => s3://psdp-dev-mongo-backup/psdp-1586844750
psdp-1586844750
====================================================================

Và khi đến lần thứ 6 thì nó sẽ delete file cũ.

===================== Lần thứ 6 ==========================
Backup PSDP NOW
Time is Tue Apr 14 11:12:35 UTC 2020
2020-04-14T11:12:35.312+0000 writing psdp.logs to archive '/home/ubuntu/mongoBackup/psdp-1586862755'
2020-04-14T11:12:35.314+0000 done dumping psdp.logs (1 document)
2020-04-14T11:12:35.337+0000 writing psdp.users to archive '/home/ubuntu/mongoBackup/psdp-1586862755'
2020-04-14T11:12:35.338+0000 done dumping psdp.users (1 document)
done => 1586862755
Copy to S3
upload: ./psdp-1586862755 to s3://psdp-dev-mongo-backup/psdp-1586862755
done => s3://psdp-dev-mongo-backup/psdp-1586862755
psdp-1586844750
psdp-1586848351
psdp-1586851952
psdp-1586855553
psdp-1586859154
psdp-1586862755
delete => psdp-1586844750
deleted localfile => psdp-1586844750
delete on s3 => s3://psdp-dev-mongo-backup/psdp-1586844750
delete: s3://psdp-dev-mongo-backup/psdp-1586844750
deleted on s3
====================================================================

Và đây là toàn bộ file đã backup bên s3 . Chỉ có 5 file mới nhất. Đúng 1 tiếng 1 lần.

Viết một con Bash Script back up Mongo Database đơn giản

À còn nữa nếu mà muốn stop script để làm gì đó thì chỉ cần kiếm process id của nó sau đó dùng command kill để giết nó.

ubuntu@ip-172-31-38-138:~/mongoBackup$ ps aux | grep cronbackupmongo
ubuntu    9653  0.0  0.1  14856  1036 pts/0    S+   11:21   0:00 grep --color=auto cronbackupmongo
ubuntu   21435  0.0  0.3  13312  3308 ?        S    06:12   0:00 bash ./cronbackupmongo.sh
ubuntu@ip-172-31-38-138:~/mongoBackup$

Và kill process của nó, có id = 21435

kill 21435 

Vậy là xong rồi, bye bye các bạn, cảm ơn đã ghé đọc bài, happy kiếm tiền :))

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

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

Selenium – Xác Định Đối Tượng UI

Selenium – Xác Định Đối Tượng UI

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

Trong kiểm thử tự động, phần quan trọng là chúng ta phải làm sao cho công cụ kiểm thử nhận biết và phân biệt được các đối tượng UI trên phần mềm mà chúng ta đang kiểm tra. Trong kiểm thử, chúng ta gọi bước này là xây dựng bộ giao diện người dùng – build GUI Repositories. Cơ bản, nó là một bệ từ điển kết nối giữa một cái tên logic – thứ mà chúng ta dùng trong script – và mô tả vật lý – thứ hiển thị trên phần mềm.

  18 designer hàng đầu dự đoán về xu hướng UI/ UX trong năm 2022
  5 bài học quí giá về việc phát triển ứng dụng iOS

Để có thể tạo một GUI Repository tốt, chúng ta cần xác định mô tả vật lý của đối tượng UI một cách chính xác, đơn nhất và ổn định. Tính chính xác, đơn nhất của mô tả vật lý dùng để đảm bảo script của chúng ta có thể chạy một cách đúng đắn. Tính ổn định giúp chúng ta không phải chỉnh sửa nhiều khi phần mềm có sự thay đổi.

Selenium cũng như vậy, bộ GUI Repository cũng đòi hỏi các tính chất như các công cụ kiểm thử khác. Có khác, chỉ là các thuộc tính của UI là đặc thù về Web mà thôi.

Đối Tượng UI – Locators

Selenium hỗ trợ chúng ta xác định UI dựa trên các thuộc tính, và chúng ta gọi một cách xác định đối tượng UI thông qua thuộc tính của nó là locator, như sau:

  • ID
  • Name
  • Link Text
  • CSS Selector
    • Tag/ID
    • Tag/class
    • Tag/attribute
    • Tag/class/attribute
    • Inner text
  • XPath

Đối với ID, đây được coi như là thuộc tính đơn nhất và ổn định nhất của hệ thống phần mềm. Nếu có thể sử dụng ID cho việc xác định UI thì quá tốt. Nhưng, tất cả chúng ta đều biết, hiếm có một nhà phát triển phần mềm nào lại chăm chỉ đến mức gắn ID cho mọi đối tượng UI. Đơn giản là vì ID không phải là một thuộc tính bắt buộc và nó không hiện thị trên giao diện nên bên viết ra phần mềm không chú ý đến nó cũng là hiển nhiên.

Về phần Name. Name là lựa chọn thứ hai sau ID. Tuy nhiên, thuộc tính Name đôi khi không đơn nhất. Không có một ràng buộc nào bắt Name phải đơn nhất cả. Xin lỗi, mình ngoài lề một chút, đối với ID, cũng sẽ không có lỗi systax nào xảy ra nếu nhà phát triển đặt hai đối tượng UI cùng ID, nhưng nó là một bug về ý nghĩa. Nếu bạn làm Unit Test, bạn có thể bắt bug này.

Link Text. Một lựa chọn không tồi nếu bạn không phải kiểm định trên hệ thống với nhiều ngôn ngữ khác nhau. Thuộc tính Link Text là không đơn nhất và cũng không ổn định. Một trang web có thể có nhiều liên kết đến một trang khác hay thay đổi từ ngữ nhưng không đổi ý nghĩa. Thử tưởng tượng, một ngày nào đó, nút Login của chúng ta bị chuyển thành SignIn. Wow….

CSS Selector. Sử dụng CSS là chúng ta phụ thuộc vào cách thiết kế web của nhà phát triển. Và hiển nhiên, CSS Selector không đơn nhất. Không một nhà phát triển nào tạo ra một CSS để dùng cho một đối tượng cả. Cho nên, chúng ta phải kết hợp CSS với Tag/ID, Tag/class, ….

Cuối cùng, XPath. Đây được xem như là thuộc tính hay dùng nhất của Selenium. Tuy nhiên, cách này lại thiếu chính xác, thiếu đơn nhất và thiếu ổn định nhất trong tất cả các cách xác định đối tượng UI.

Xác định đối tượng UI với Selenium IDE

Trong Selenium IDE, khi chúng ta record một test case, IDE đã tự động xác định đối tượng UI cho chúng ta. Nhưng, đôi khi các thuộc tính do IDE lấy ra không thể sử dụng được. Các bạn có thể thử với Google, ở đó có hai đối tượng UI có cùng ID.

Để xác định một đối tượng UI với Selenium IDE, chúng ta làm như sau:

B1: Mở trang web mà chúng ta đang muốn kiểm tra

B2: Mở Selenium IDE

B3: Gõ locator của đối tượng mà chúng ta muốn xác định vào Target textbox. Cấu trúc và ví dụ cho các locators mình liệt kê ở cuối bài.

B4: Click Find button

Selenium – Xác Định Đối Tượng UI

Nếu các locator của đối tượng mà chúng ta điền vào Target là đúng, đối tượng UI sẽ được sáng nhấp nháy trên trang web.

Và đây là cách chúng ta tạo ra các locator với từng phương thức khác nhau:

Phương thức

Cấu trúc

Ví dụ

ID

id= id_của_đối_tượng

id=email

Name

name=name_của_đối_tượng

name=username

Name

name=name_của_đối_tượng

name=tripType

Link Text

link=link_text

link=REGISTER

Tag/ID

css=tag#id

css=input#email

Tag/Class

css=tag.class

css=input.inputtext

Tag/Attribute

css=tag[attribute=giá trị]

css=input[name=lastName]

Tag/Class/Attribute

css=tag.class[attribute=giá trị]

css=input.inputtext[tabindex=1]

XPath

Xpath

//html/body//bookstore/book/title

Để có thể xác định được locator một cách nhanh chóng và chính xác hơn, các bạn có thể sử dụng hai công cụ khá mạnh của FireFox: FireBug và XPath Finder. FireBug hỗ trợ chúng ta đọc mã nguồn của trang web một cách rõ ràng, và XPath Finder giúp chúng ta lấy XPath của đối tượng UI một cách chính xác và đơn giản.

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

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

Xem thêm IT job hấp dẫn trên TopDev

Sự khác nhau giữa code dễ và code đơn giản

Sự khác nhau giữa code dễ và code đơn giản

Bài viết được sự cho phép của tác giả Phạm Bình

Chào các bạn,

Bấy lâu nay, mình và có thể nhiều bạn khác đang có sự nhầm lẫn to lớn giữa hai từ “dễ dàng” và “đơn giản”. Mặc dù nghe qua, chúng có vẻ giống nhau, có vẻ như là từ đồng nghĩa, có vẻ có thể sử dụng thay thế cho nhau được, nhưng thực chất không phải vậy, “dễ dàng” và “đơn giản” là hai từ mang sắc thái ý nghĩa rất khác nhau. Và việc nhầm lẫn giữa “dễ dàng” và “đơn giản” có thể khiến bạn có những suy nghĩ không đúng đắn cả về cuộc sống lẫn … code.

  "Code dễ đọc" là như thế nào?
  Vừa học vừa chơi! Top 15+ game lập trình miễn phí

Vì vậy, bài viết này mình sẽ đi phân tích sự khác nhau giữa “dễ dàng” và “đơn giản” để chúng ta cũng hiểu rõ ý nghĩa của mỗi từ mà nó hướng tới.

I. DỄ DÀNG VS ĐƠN GIẢN

Giả sử mình có câu “Dậy sớm rất đơn giản”.

– Đúng, dậy sớm rất đơn giản, chỉ cần hẹn báo thức và dậy đúng giờ đó, không lằng nhằng nhiều bước, đứa trẻ 5 tuổi cũng làm được.
– Nhưng nó không dễ dàng, bởi bạn có cưỡng lại được sự buồn ngủ, hay cưỡng lại được cái chăn ấm kia. Và thử nghĩ xem, đã bao nhiêu lần bạn đặt mục tiêu dậy sớm và không làm được :D.

Ví dụ ngắn gọn trên có lẽ đã giúp bạn hiểu được sự khác nhau giữa dễ dàng và đơn giản, nhưng hãy cùng phân tích kỹ hơn nhé:

  • Dễ dàng hướng tới việc mô tả một hành động mà không đòi hỏi nhiều công sức, động lực, kiến thức mà vẫn có thể thực hiện.
  • Đơn giản hướng tới việc mô tả một hành động có mà càng ít bước thực hiện, hoặc các bước thực hiện càng rõ ràng càng tốt (xin phép chỉ giới hạn trong phạm vi hành động đơn giản, mà bỏ qua các sự vật, hiện tượng đơn giản).
  • Trái nghĩa với đơn giản là phức tạp, trái nghĩa với dễ dàng là khó khăn.
  • Ranh giới giữa dễ dàng và đơn giản có thể rất mong manh, và bị phụ thuộc nhiều ở góc độ nhìn nhận của các đối tượng khác nhau (mang tính tương đối). Như ví dụ “dậy sớm rất đơn giản” ở trên, chúng ta đều đồng ý là nó đơn giản, nhưng còn dễ dàng thì sao? Với một người lười chảy thây, có thói quen ngủ nướng, hoặc người làm việc nhiều về đêm, thì dậy sớm thật sự khó khăn, nhưng với một người sống trong quân đội lâu năm (hoặc trong tù :D) thì nó quá dễ dàng. Vì thế, để đánh giá một điều là đơn giản (hay dễ dàng) thì cần phải đặt bản thân ở góc độ của đối tượng sẽ nhìn nhận điều đó.

II. CODE DỄ DÀNG VS CODE ĐƠN GIẢN

Dễ dàng và đơn giản khác nhau thế nào thì mình đã phân tích ở trên, vậy code dễ dàng và code đơn giản thì khác nhau thế nào:

  • Code dễ dàng: Là những đoạn code mà để tạo ra nó thì không cần nhiều công sức hay kiến thức về code.
  • Code đơn giản: Là những đoạn code sáng sủa, ít bước xử lý, hoặc có các bước xử lý rất rõ ràng.

Trong lập trình, chúng ta thường hướng tới code đơn giản nhiều hơn là code dễ dàng, và một sự thật rằng “Để tạo ra các đoạn code đơn giản thường không dễ dàng”.

Nguyên tắc KISS.

KISS (không phải là HÔN HÍT) là viết tắt của Keep it simple, Stupid, tạm dịch là Giữ nó đơn giản thôi, đồ ngu – là một nguyên tắc giúp chúng ta hướng tới sự đơn giản, và cho rằng một điều gì đó đơn giản thì sẽ dễ dàng sử dụng hơn là phức tạp (lưu ý là dễ dàng sử dụng, chứ không phải dễ dàng tạo ra nhé).

Để hình dung rõ hơn về code dễ dàng và code đơn giản, chúng ta sẽ cùng làm một ví dụ nhỏ sau:

Xét bài toán

Cho mảng $numbers chứa các số nguyên, hãy viết chương trình in ra màn hình các số nguyên tố có trong mảng $numbers.

Mình sẽ giải bài toán trên theo 2 cách, một cách ưu tiên code dễ, cách còn lại ưu tiên code đơn giản, sau đó chúng ta sẽ cùng rút ra nhận xét về 2 cách này.

Cách 1: Ưu tiên code dễ dàng

// Ban đầu đề bài cho cái mảng này
$numbers = [1, 2, 4, 5, 7, 39, 12, 0];

// Tạo cái mảng để chứa các số nguyên tố
$soNguyenTo = [];

// Bắt đầu đi tìm các số nguyên tố có trong mảng
foreach ($numbers as $n) {
    // Số nguyên tố là số tự nhiên lớn hơn 1
    // và chỉ chia hết cho 1 và chính nó
    // hay nói cách khác, số nguyên tố luôn chỉ có 2 ước
    $soUoc = 1;
    for ($i = 2; $i <= $n; $i++) {
        if ($n % $i == 0) {
            $soUoc = $soUoc + 1;
        }
    }

    // Kiểm tra số ước
    // Nếu bằng 2, thì thêm vào mảng các số nguyên tố
    if ($soUoc == 2) {
        $soNguyenTo[] = $n;
    }
}

// In các số nguyên tố ra màn hình
foreach ($soNguyenTo as $n) {
    echo $n . PHP_EOL;
}

/*
Output

2
5
7
*/

Cách 2: Ưu tiên code đơn giản

/*
 * Đừng quan tâm tới 3 hàm dưới đây viết như thế nào,
 * hãy chỉ quan tâm tới input và output của nó thôi
 */

// Hàm lọc các số nguyên tố có trong $mang
// input: một biến $mang là mảng chứa các số nguyên
// output: trả về một mảng các số nguyên tố có trong biến $mang
function locCacSoNguyenTo($mang) {
    $mangSoNguyenTo = [];

    foreach ($mang as $phanTu) {
        if (laSoNguyenTo($phanTu) == true) {
            $mangSoNguyenTo[] = $phanTu;
        }
    }

    return $mangSoNguyenTo;
}

// Hàm kiểm tra một số $n có phải số nguyên tố hay không
// input: một số $n bất kỳ
// output: trả về true/false. true thì $n là số nguyên tố và ngược lại
function laSoNguyenTo($n) {
    $soUoc = 1;
    for ($i = 2; $i <= $n; $i++) {
        if ($n % $i == 0) {
            $soUoc = $soUoc + 1;
        }
    }

    if ($soUoc == 2) {
        return true;
    }

    return false;
}

// Hàm in các phần tử có trong mảng
// input: một mảng bất kỳ
// output: in ra các phần tử trong mảng
function inPhanTuTrongMang($mang) {
    foreach ($mang as $phanTu) {
        echo $phanTu . PHP_EOL;
    }
}

/*
 * Đoạn này thì hãy quan tâm chi tiết
 */

// Cái mảng đề bài cho này
$numbers = [1, 2, 4, 5, 7, 39, 12, 0];

// Lọc các số nguyên tố có trong mảng ra này
$mangSoNguyenTo = locCacSoNguyenTo($numbers);

// In ra màn hình các số nguyên tố này
inPhanTuTrongMang($mangSoNguyenTo);

/*
Output

2
5
7
*/

Nhận xét

  • Về kết quả, cả 2 cách trên đều cho ra kết quả giống nhau, và đều chính xác so với đề bài.
  • Cách 1 không sử dụng nhiều kiến thức về code, chỉ bao gồm: kiến thức về cách sử dụng biến, cách sử dụng lệnh rẽ nhánh và vòng lặp.
  • Cách 2 thì sử dụng nhiều kiến thức hơn, bao gồm các kiến thức của cách 1, và kiến thức về cách sử dụng function.

Mới chỉ là một bài toán đơn giản, mà hàm lượng kiến thức giữa 2 cách code đã khác nhau rất nhiều (biết sử dụng function và không biết sử dụng function là rất khác nhau). Nếu trong các dự án thực tế, thì sự khác biệt giữa code dễ và code đơn giản còn chênh lệch nhau nhiều nữa.

>> Đọc thêm: Design pattern là gì mà lập trình viên giỏi phải biết

  • Cách 1 code chạy một lèo từ trên xuống dưới, vẫn chia thành các bước nhưng không rõ ràng, nếu không comment có lẽ bạn sẽ không biết các bước được thực hiện như thế nào.
  • Cách 2 code được chia thành 2 bước rõ ràng, bước 1 tìm các số nguyên tố, bước 2 in ra màn hình, mỗi bước được đóng gói thành một function, nếu không có comment, có lẽ bạn sẽ vẫn hiểu code trong cách 2 được chạy như thế nào.
  • Cách 1 code nhanh hơn, do người code chỉ việc viết một chương trình chạy từ trên xuống dưới.
  • Cách 2 code lâu hơn, do người code phải mất công phân tích để tách thành các function.

Trên là một bài toán đơn giản và cũng phổ biến, nên không tốn nhiều thời gian để phân tích xem cần phải tách thành các function nào, còn với các bài toán mới, các bài toán phức tạp trong thực tế thì việc thiết kế code gồm những function nào là cả một vấn đề lớn, và thường được thực hiện bởi những người có kinh nghiệm.

  • Cách 1 không có khả năng tái sử dụng code đã viết.
  • Cách 2 có thể tái sử dụng code, do code được đóng gói thành các function.
  • Cách 1 code khó hiểu hơn, hãy tưởng tượng 3 tháng sau nhìn lại code trong cách 1 thì mất bao lâu để bạn hiểu nó đang thực hiện cái gì. Code khó hiểu hơn, đồng nghĩa với việc khó bảo trì hơn.
  • Cách 2 code dễ hiểu hơn, chắc chắn là vậy, và cũng dễ bảo trì hơn.

Một thực tế rằng, các lập trình viên sẽ dành thời gian để sửa code nhiều hơn là viết code, mà để sửa code thì bạn phải hiểu code hiện tại đang chạy như thế nào, vì thế việc viết code dễ hiểu quan trọng hơn rất nhiều so với việc viết code sao cho nhanh.

  • Để có các dòng code đơn giản, thì không dễ dàng và ngược lại.

III. TỔNG KẾT

Một bài viết ngắn gọn giúp các bạn phân biệt thế nào là code dễ và code đơn giản, và … à mà thôi. Keep it simple, stupid.

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

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

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

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

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

Trong những năm trở lại đây, ngành công nghệ thông tin trở nên vô cùng “hot”. Kéo theo đó là các khái niệm đi cùng như “lập trình”, “ngôn ngữ lập trình”.. xuất hiện một cách dày đặc hơn trên các phương tiện thông tin đại chúng.

Nếu các bạn đang học công nghệ thông tin (IT) thì sẽ biết rằng, số lượng ngôn ngữ lập trình hiện nay đã lên đến con số hàng trăm và nó sẽ còn tiếp tục tăng nữa.

Vậy một câu hỏi đặt ra cho chúng ta là nên chọn học loại ngôn ngữ lập trình nào cho phù hợp với nhu cầu thị trường trong hiện tại và tương lai. Ở đây mình có thống kê một số ngôn ngữ phổ biến như sau:

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

Trong bài viết này mình sẽ giới thiệu với các bạn TOP 5 ngôn ngữ lập trình đáng học nhất năm 2024, những ngôn ngữ lập trình mà mình thấy tiềm năng khá là lớn ở Việt Nam trong khoảng 5-7 năm tới.

#1. Python

Có thể nói Python đang là ngôn ngữ được khuyến khích học nhiều nhất, không chỉ ở Việt Nam mà còn cả trên thế giới nữa. Câu hỏi là tại sao? Tại sao lại là Python?

Python được ra đời vào năm 1991, tính đến hiện tại cũng đã khoảng 30 năm tuổi. Nếu xét về tuổi đời thì python cũng không phải là mới.

Nhưng một trong những điểm cộng lớn nhất của python là sự đơn giản, người học dễ dàng tiếp cận, cú pháp khá giống với ngôn ngữ giao tiếp nên python được đánh giá cao trong việc dễ học, dễ sử dụng.

Ngoài ra, python có một số lượng lớn các thư viện giúp cho ngôn ngữ này trở nên rất mạnh (về khía cạnh các thư viện, hàm dựng sẵn). Cùng một công việc, các ngôn ngữ khác có thể mất 5-10 dòng code nhưng với python chỉ mất khoảng 2-3 dòng.

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?
Python Hello World program

Không tin các bạn có thể vào trang https://www.python.org/ để xem cách python giải quyết những bài toán phức tạp mà xem, rất ngắn gọn.

Cuối cùng, không thể không nhắc đến sự phát triển của AI (trí tuệ nhân tạo), Machine Learning (học máy), Deep Learning (học sâu) cũng được viết bằng python rất nhiều…

Tìm việc làm Python mới nhất

#2. JavaScript

Nếu như python nổi lên như một hiện tượng thì JavaScript cũng vậy. Việc JavaScript ra đời và được phát triển là dựa trên sự phát triển của Internet.

Cụ thể hơn là sự phát triển của các ứng dụng web, đặc biệt là phía Client do các máy tính cá nhân ngày càng “khỏe” -> Browser làm được nhiều việc hơn.

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

Số lượng Frameword JavaScript (Front End) cũng nổi lên như nấm sau mưa. Một số cái tên tiêu biểu như ReactJS (Facebook phát triển), AngularJS (Google phát triển), VueJS (Một kỹ sư phần mềm Trung Quốc phát triển).

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

Mình sẽ có một bài riêng nói về các Framework, nhưng các bạn phải luôn nghi nhớ rằng dù là Framework nào thì cũng được xây dựng từ ngôn ngữ lập trình.

Ở đây mình nói đến việc các bạn phải hiểu kỹ các khái niệm của JavaScript trước khi học một Framework.

Quay lại với câu chuyện lập trình web chưa bao giờ hết “hot” do nhu cầu ngày càng nhiều, đặc biệt là các hệ thống web lớn. Vì vậy học JavaScript đã và sẽ chưa bao giờ là quá sớm hay quá muộn.

Tuyển dụng lập trình Javascript lương cao

#3. Java

Java thì là một ngôn ngữ lập trình có thể nói là “huyền thoại”. Sự ra đời của Java đã chuẩn hóa nhiều khái niệm lập trình, cũng như là cảm hứng cho nhiều ngôn ngữ lập trình khác.

Ví dụ như JavaScript đã đặt tên gần giống với Java để “ké” chút danh tiếng, mặc dù hai ngôn ngữ này chả liên quan gì đến nhau cả !

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

Nói về tuổi đời, Java cũng đã khoảng 30 năm kể từ ngày nó được thai nghén bởi James Gosling và bạn đồng nghiệp ở Sun Microsystems.

Vậy tại sao Java cũ nhưng chúng ta vẫn nên học. Cũng đơn giản thôi, thứ nhất là Java là ngôn ngữ thuần hướng đối tượng. Học Java sẽ giúp người học tiếp cận các khái niệm lập trình một cách chuẩn chỉ.

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?
Java Hello World program

Hai nữa là trong suốt gần 30 năm qua, có vô số ứng dụng lớn nhỏ được tạo ra từ Java và cho đến nay vẫn đang hoạt động. Vì vậy học Java sẽ không lo thất nghiệp vì số lượng công việc với Java khá là nhiều, nhưng cũng khó.

Học Java các bạn có thể làm các ứng dụng di động trên hệ điều hành Android, có thể là các ứng web với Framework Spring…

Các vị trí tuyển Java hấp dẫn 2022

#4. C/C++

Nhiều bạn có thể sẽ không đồng ý khi mình đưa C/C++ vào danh sách này vì hai ngôn ngữ một phần khá là khó học, hai nữa tuổi đời cũng lớn, ít dùng…

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

Nhưng có lẽ các bạn không biết về sức mạnh của C/C++ nên mới nghĩ vậy ! Do C/C++ là ngôn ngữ bậc trung nên chúng sẽ tương tác với hệ thống tốt hơn, chính xác là đem lại hiệu năng lớn hơn các ngôn ngữ như Python, Java…

Các ngôn ngữ như Python, NodeJs… đều có bộ nhân được viết từ C/C++ để tối ưu hiệu năng. Vì vậy bạn đừng nghĩ C/C++ “vô dụng” nha.

Ngày nay các job liên quan đến C/C++ phải nói là khá ít vì nhu cầu không nhiều. Đặc biệt là trong các hệ thống nhúng, các hệ thống kết hợp với các thiết bị điện tử thì C/C++ được dùng nhiều hơn.

Nhưng nếu ai giỏi C/C++ thì lương cũng cao lắm đó, cái gì cũng có giá mà các bạn. Vậy nên đừng thấy nó cũ mà nghĩ nó ít dùng nha.

#5. Swift

Nên học ngôn ngữ lập trình nào để sau này dễ xin việc?

Cuối cùng mình muốn đề cập đến một ngôn ngữ lập trình do Apple phát triển. Như các bạn biết thì Táo khuyết từ trước đến nay toàn dùng hàng “độc”. Bạn không thể dùng Java, Kotlin… để viết app iOS được. Thay vào đó bạn phải sử dụng Swift.

Cụ thể về Swift thì mình cũng không hiểu biết nhiều để đánh giá về hiệu năng cũng như cú pháp. Nhưng nếu Apple đã tin dùng thì cũng không phải là hàng “lởm”.

Các bạn cứ yên tâm học nếu muốn đi theo hướng xây dựng, lập trình các ứng dụng iOS hay các ứng dụng của Apple.

#6. Kết luận

Cá nhân mình nghĩ rằng công nghệ có thể thay đổi hàng ngày nhưng nền tảng (người ta hay gọi là core) sẽ không bao giờ thay đổi.

Việc học công nghệ mới là tất yếu nhưng 5 ngôn ngữ mình giới thiệu bên trên không phải là mới. Các bạn cứ học và nắm chắc một hoặc hai trong số chúng là các bạn đã trang bị cho mình một nền tảng vững chắc để tiến tới việc học các Framework, thư viện… rồi đấy.

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

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

Xem thêm Jobs Developer hấp dẫn trên TopDev