Home Blog Page 171

Kiểu enum trong TypeScript: làm việc như thế nào, sử dụng ra sao

Chúng ta sẽ trả lời 2 câu hỏi sau: enum của TypeScript làm việc như thế nào? Nó được sử dụng để làm gì. Vỡ lòng cho người mới viết TypeScript

Các khái niệm căn bản của enum

Javascript chỉ có đúng một kiểu mà giá trị bị ràng buộc rất cụ thểboolean, giá trị chỉ được phép là true hoặc false, không chấp nhận một giá trị nào khác. Enum là phiên bản mở rộng với công dụng tương tự như boolean

enum NoYes {
    No,
    Yes
}

2 giá trị No Yes được gọi là thành viên của hội enum NoYes. Dùng nó như một object, chúng ta có thể chấm đến giá trị đó

function toGerman(value: NoYes) {
  switch (value) {
    case NoYes.No:
      return 'Nein';
    case NoYes.Yes:
      return 'Ja';
  }
}

Tất cả thành viên của hội enum đều được cấp một thẻ thành viên gồm name và value. Ở trên chúng ta chỉ đang khai báo phần name cho enum, value lúc đó sẽ lấy mặc định là số từ 0 đi lên

enum NoYes {
  No,
  Yes,
}
// nếu khai báo một cách tường minh hơn
enum NoYes {
  No = 0,
  Yes = 1,
}

assert.equal(NoYes.No, 0);
assert.equal(NoYes.Yes, 1);

Nếu cà khịa, khai báo như thế này

enum Enum {
  A,
  B,
  C = 4,
  D,
  E = 8,
  F,
}

Đồng nghĩa là các giá trị kế tiếp sẽ tự động tăng lên một, so với giá trị khai báo trước đó

assert.deepEqual(
  [Enum.A, Enum.B, Enum.C, Enum.D, Enum.E, Enum.F],
  [0, 1, 4, 5, 8, 9]
);

Về cách đặt tên (name) trong enum, cũng có vài ba lựa chọn

  • Đặt theo kiểu JavaScript, viết hoa hết, Number,MAX_VALUE
  • Đặt theo kiểu symbol, con lạc đà, chữ đầu viết thường, Symbol.asyncIterator
  • Kiểu TypeScript, con lạc đà, chữ đầu viết hoa, Number.MaxValue

Tương tự như object, chúng ta có thể truy xuất đến một thành viên của hội bằng cách viết sau

enum HttpRequestField {
  'Accept',
  'Accept-Charset',
  'Accept-Datetime',
  'Accept-Encoding',
  'Accept-Language',
}
assert.equal(HttpRequestField['Accept-Charset'], 1);

Giá trị value của enum, không bắt buộc là một number, có thể là một string

enum NoYes {
  No = 'No',
  Yes = 'Yes',
}
assert.equal(NoYes.No, 'No');
assert.equal(NoYes.Yes, 'Yes');

Còn một cách khai báo, ít được sử dụng, là kiểu xăng pha nhớt, các giá trị trong enum có thể là số cũng có thể là chữ

enum Enum {
  A,
  B,
  C = 'C',
  D = 'D',
  E = 8,
  F,
}
assert.deepEqual(
  [Enum.A, Enum.B, Enum.C, Enum.D, Enum.E, Enum.F],
  [0, 1, 'C', 'D', 8, 9]
);

Theo như kinh nghiệm từ các bật tiền bối, sử dụng enum thì nên dùng kiểu giá trị string

enum NoYes { No = 'NO', Yes = 'YES' }

Nếu có log ra chúng ta cũng biết được giá trị chính xác là gì, đỡ hack não ngồi đếm số thứ tự

console.log(NoYes.No);
console.log(NoYes.Yes);

Thêm nữa, ràng buộc được luôn kiểu giá trị

function func(noYes: NoYes) {}

func('abc');
func('Yes');

Trường hợp sử dụng enum

hằng số nhiều giá trị

Nếu chúng ta có một mớ các hằng số, có quan hệ họ hàng gần với nhau

const fatal = Symbol('fatal');
const error = Symbol('error');
const warn = Symbol('warn');
const info = Symbol('info');
const debug = Symbol('debug');
const trace = Symbol('trace');

Có thể dùng enum

enum LogLevel {
  fatal = 'fatal',
  error = 'error',
  warn = 'warn',
  info = 'info',
  debug = 'debug',
  trace = 'trace',
}

Lợi ích mang lại: nhóm lại với nhau một cục, TypeScript dễ đàng kiểm tra giúp chúng ta

Tường minh hơn boolean

Chúng ta hay dùng boolean để làm cờ bật tắt hoặc đảo ngược giá trị

class List1 { isOrdered: boolean; }

Nếu dùng enum, nó sẽ rõ nghĩa hơn, nhiều lựa chọn hơn

enum ListKind { ordered, unordered }
class List2 {
  listKind: ListKind;
  
}

String là một hằng số đúng nghĩa, an toàn hơn vì không thể thay đổi được giá trị

Ví dụ hàm bên dưới dùng const

const GLOBAL = 'g';
const NOT_GLOBAL = '';
type Globalness = typeof GLOBAL | typeof NOT_GLOBAL;

function createRegExp(source: string,
  globalness: Globalness = NOT_GLOBAL) {
    return new RegExp(source, 'u' + globalness);
  }

assert.deepEqual(
  createRegExp('abc', GLOBAL),
  /abc/ug);

dùng enum tiện hơn

enum Globalness {
  GLOBAL = 'g',
  NOT_GLOBAL = '',
}

function createRegExp(source: string, globalness = Globalness.NOT_GLOBAL) {
  return new RegExp(source, 'u' + globalness);
}

assert.deepEqual(
  createRegExp('abc', Globalness.GLOBAL),
  /abc/ug);

Enum lúc chạy thì sẽ trở thành gì?

Sau khi TypeScript đã compile, enum sẽ được được chuyển thành javascript object

enum NoYes {
  No,
  Yes,
}
var NoYes;
(function (NoYes) {
  NoYes[NoYes["No"] = 0] = "No";
  NoYes[NoYes["Yes"] = 1] = "Yes";
})(NoYes || (NoYes = {}));

THam khảo các vị trí tuyển lập trình Typescript cho bạn.

TopDev via Vuilaptrinh

Gặp gỡ Sakshi Jawa — Tổng giám đốc nhân sự Tiki

Bài viết được đăng tải lần đầu trên Vietcetera

Là một trong những quốc gia có tốc độ tăng trưởng nhanh nhất châu Á, Việt Nam hiện là vùng đất hứa cho các doanh nghiệp trong và ngoài nước, đặc biệt là các doanh nghiệp trong lĩnh vực thương mại điện tử. Với dân số trẻ và tỉ lệ sử dụng điện thoại thông minh cao, thị trường thương mại điện tử tại Việt Nam là một trong những thị trường phát triển nhanh nhất nhì khu vực.

Thành lập từ tháng 3/2010, Tiki là hệ sinh thái thương mại tất cả trong một tại Việt Nam. Với đội ngũ gồm hơn 4.500 nhân viên, không chỉ là môi trường làm việc vui vẻ, sáng tạo và năng động, Tiki còn mang đến cho nhân viên của mình những cơ hội để phát triển bản thân lẫn thăng tiến trong sự nghiệp.

Và người đứng đằng sau sự phát triển này là Sakshi Jawa, hiện là Tổng giám đốc nhân sự tại Tiki. Gia nhập Tiki cách đây hơn một năm trước, Sakshi Jawa đã có rất nhiều đóng góp trong việc thay đổi các chính sách về nhân sự và con người.

Với nhiều năm kinh nghiệm làm việc cho các thương hiệu toàn cầu như Citibank, Prudential, Amazon và Coupang, động lực nào đã khiến Sakshi chuyển đến Việt Nam và gia nhập Tiki? Thế mạnh nào trong phong cách quản lý của chị đã giúp góp phần giúp Tiki phát triển thành một doanh nghiệp lớn mạnh và năng động như hiện nay?

Hãy ngồi xuống cùng Sakshi và nghe những câu chuyện của chị về phong cách quản lý cũng như văn hoá và bản sắc của Tiki.

tiki 4Sakshi Jawa đã có rất nhiều đóng góp trong việc thay đổi các chính sách về nhân sự và con người.

Điều gì khiến chị quyết định gia nhập Tiki?

Cách đây 10 năm tôi từng sống và làm việc tại Việt Nam, và cảm thấy vô cùng thích trải nghiệm đó. Việt Nam là một nước đang phát triển và thị trường công nghệ ở đây có rất nhiều tiềm năng để lớn mạnh mỗi ngày. Với tốc độ phát triển nhanh chóng trong lĩnh vực công nghệ, Việt Nam đang ngày càng vươn lên chứng tỏ tiềm năng trở thành trung tâm công nghệ và cải tiến. Tôi cực kỳ ấn tượng với sức bật mạnh mẽ này.

Chuyên môn của tôi là quản lý nhân sự, đặc biệt là nhân sự trong ngành công nghệ/thương mại điện tử. Với tôi, để trở thành một người quản lý nhân sự giỏi, chúng ta cũng cần trang bị cho mình kiến thức chuyên sâu về lĩnh vực mà mình đang hoạt động. Lời mời từ Tiki là một lựa chọn lý tưởng, cùng với việc được làm trong một thị trường bùng nổ là một yếu tố quan trọng dẫn đến quyết định của tôi. Tiki đã cho tôi cả hai, nên tôi quyết định tham gia vào đội ngũ ở đây.

Phong cách quản lý của chị là gì? Chị điều hành đội ngũ của mình như thế nào?

Tôi không nghĩ quản lý vi mô là một cách hiệu quả. Tôi không kiểm tra nhân viên của mình mỗi ngày. Phong cách quản lý nhân sự của tôi chú trọng về mặt chất lượng. Mỗi thành viên trong đội ngũ đều có mục tiêu riêng, được đặt ra vào đầu năm. Ở một môi trường làm việc năng động và thay đổi nhanh chóng như Tiki, những mục tiêu này có thể thay đổi tùy thuộc vào sự phát triển của doanh nghiệp trong năm đó. Những lúc như vậy, tôi yêu cầu họ báo cáo tiến độ của họ và cùng thảo luận về hướng đi sắp tới. Mỗi tháng một lần, tôi làm thế này với chính mình cũng như các cá nhân báo cáo trực tiếp cho mình. Khi tuyển dụng đội ngũ làm công tác nhân sự, tôi tin tưởng họ có khả năng và kinh nghiệm để hoàn thành những mục tiêu họ được giao, vì thế, tôi không trực tiếp tham gia vào công việc hằng ngày của họ, trừ khi họ tìm đến tôi để hỏi ý kiến.

Mặc dù tôi đánh giá cao các kinh nghiệm làm việc trước đây của nhân viên, điều tôi quan tâm là liệu họ hơn cả là liệu họ có khả năng để đạt được mục tiêu chung hay không. Tôi đang cố gắng xây dựng một đội ngũ có thể hoàn thành công viên một cách chủ động, chứ không phải là giám sát giờ vào làm, giờ tan ca. Chúng tôi vẫn đang từng bước một xây dựng một văn hoá làm việc như thế.

Hơn nữa, tôi là một người ra quyết định dựa trên những con số, những cơ sở dữ liệu thích hợp chứ không theo cảm tính hay những lý do chủ quan. Vì thế, tôi hy vọng đội ngũ của mình cũng đưa ra quyết định theo cách tương tự.

Chị có thể miêu tả công việc của mình ở Tiki được không?

Tiki hiện sở hữu một trong những nền tảng thương mại đáng tin cậy nhất Việt Nam. Với tốc độ mở rộng nhanh chóng của doanh nghiệp, trách nhiệm của người làm nhân sự là kiểm soát quy mô nhân sự, cả trong quá trình tuyển dụng, sự gắn kết của nhân viên cho đến quy trình tổ chức nhân sự.

Thêm một nhân viên là thêm một công quản lý, thế nên với tốc độ phát triển quy mô doanh nghiệp như tại Tiki, nhân viên nhân sự cần phải tìm những phương pháp mới để quản lý theo quy mô lớn sao cho hiệu quả. Tiki từng quản lý dữ liệu trên một chuỗi bảng tính rườm rà được nhập tay, nên thường dễ xảy ra lỗi thủ công. Sau này, các giải pháp công nghệ lên ngôi, giúp công việc hàng ngày diễn ra hiệu quả hơn, giảm được các sai sót thủ công, đơn giản hoá các tính toán và thiết lập các dự đoán.

Chị sẽ mô tả môi trường làm việc tại Tiki như thế nào?

Đội ngũ Tiki nhìn chung khá trẻ, độ tuổi trung bình vào tầm 27-28 tuổi. Chúng tôi muốn tạo ra một môi trường lành mạnh, nơi mọi người tìm thấy cảm hứng và động lực để làm việc, góp phần vào sự ổn định và thành công lâu dài của cá nhân cũng như tập thể công ty.

Một trong những thế mạnh lớn nhất của tổ chức chúng tôi là khả năng phối hợp trong công việc. Những người làm việc nhóm tốt có xu hướng phát triển trong một mô hình văn phòng mở, nơi mà mọi người cùng cống hiến cho một mục tiêu chung.

Tiki khuyến khích cách làm việc cộng tác, tạo ra một môi trường hoà đồng và thân thuộc giữa các nhân viên.

Chị tìm kiếm những phẩm chất gì ở một người lãnh đạo giỏi?

Không chỉ riêng Tiki mà khi tuyển dụng hoặc tư vấn tuyển dụng cấp lãnh đạo cho các công ty đang phát triển nhanh nói chung, câu hỏi đặt ra là: “Thử thách của chúng ta trong việc tìm kiếm một người lãnh đạo là gì?” và “Nếu tuyển không đúng người, bài học của chúng ta là gì?”

Người lãnh đạo là một người biết “chỉ đạo” và dẫn dắt các cá nhân, đội nhóm hay thậm chí là toàn thể tổ chức. Chính vì họ được xem như một hình mẫu, chúng tôi muốn đảm bảo rằng mình chọn đúng người để đảm nhận vai trò này. Việc này quan trọng cho cả bản thân họ cả sự thành công của công ty.

Phẩm chất đầu tiên mà tôi tìm kiếm ở một người lãnh đạo giỏi là khả năng thích nghi. Trong giai đoạn tăng trưởng liên tục, người đứng đầu phải đối mặt với những thay đổi không ngừng và ngày một phức tạp hơn. Việc này đòi hỏi họ phải có khả năng thích ứng và điều phối tuỳ theo tình huống. Thay đổi, đối với họ, không phải là chướng ngại vật mà là cơ hội tiềm năng để phát triển. Đồng thời, việc lãnh đạo còn yêu cầu kỹ năng đưa ra các quyết định quan trọng. Chúng tôi không muốn thấy một người lãnh đạo tỏ ra mơ hồ, thiếu quyết đoán, ngay cả trong các tình huống vô định. Ngược lại, họ cần làm chủ mọi quyết định của mình, và dẫn dắt đồng đội của mình theo lựa chọn đó.

Thứ hai, họ ý thức và chấp nhận những thất bại của mình. Người lãnh đạo không phải lúc nào cũng đúng. Họ cũng mắc sai lầm như bao người — nhưng thường những sai lầm đó để lại hậu quả lớn hơn. Bất kỳ người lãnh đạo tuyệt vời nào cũng sẽ nói rằng họ đã từng mắc sai lầm trong quá khứ. Những người đứng đầu giỏi sẽ không ngần ngại kể về những thất bại của mình, cũng như những bài học họ rút ra. Hơn nữa, họ cần hiểu rằng mình không thể giỏi toàn diện, có những việc họ cần phải trông cậy vào người khác. Biết thừa nhận những hạn chế của mình và tìm những người có nhiều chuyên môn hơn có lẽ là bí kíp hàng đầu của những người lãnh đạo giỏi.

Cuối cùng, sự khiêm nhường và khả năng tiếp thu là những phẩm chất tôi muốn thấy ở một người đứng đầu. Họ cần liên tục trau dồi kiến thức và kỹ năng của bản thân. Học hỏi là một việc thú vị và nó giúp ta luôn khiêm tốn. Mọi người đánh giá cao việc tôi thích học hỏi từ họ và từ môi trường xung quanh. Nó giúp tôi nhận ra rằng, nhờ việc bày tỏ nguyện vọng được học của mình với người khác, tôi cũng khiến họ cảm thấy quan trọng hơn.

Với số lượng nhân viên tại Tiki hiện giờ, làm thế nào để chị đảm bảo mỗi cá nhân đều đang thấy hạnh phúc và hài lòng với công việc của mình?

Khi đề cập đến sự gắn bó của nhân viên, chúng ta thường nghĩ đến những hoạt động như tiệc tùng hoặc liên hoan. Nhưng xét cho cùng, khi nhân viên rời đi, họ không rời đi vì công ty bạn ít tiệc tùng hơn. Không ai chọn công tác ở một nơi chỉ vì ở đó tiệc tùng nhiều hơn những nơi khác. Đó không phải là yếu tố đo lường mức độ gắn kết với nhân viên.

Thay vào đó, nên đánh giá độ gắn bó của nhân viên qua những lần họ được công nhận, những lần họ đóng góp vào các dự án và những lần họ được giao mục tiêu. Tại Tiki, chúng tôi có một hệ thống quản lý nhân viên chặt chẽ, giúp cả quản lý và nhân viên có thể thảo luận và đánh giá hiệu suất công việc. Chúng tôi còn có chính sách luân chuyển công việc để giúp nhân viên trau dồi các kỹ năng và kinh nghiệm mới ở những khía cạnh khác nhau. Năm nay, chúng tôi tập trung vào các buổi học nghiệp vụ cũng như các kế hoạch đào tạo và phát triển cho nhân viên, giúp họ nâng cao chuyên môn của mình.

Chúng tôi cũng đang nỗ lực hơn trong việc giao tiếp với nhân viên của mình. Với tốc độ phát triển và cải tiến của công ty, chúng tôi muốn đảm bảo sự đồng thuận của mọi người. Tôi nghĩ giao tiếp chính là chìa khóa dẫn tới thành công cho bất kỳ tổ chức nào.

Tiki - SakshiTiki là một trong những nền tảng thương mại điện tử có tốc độ nhanh và uy tín nhất Việt Nam.

Khó khăn lớn nhất trong công việc của chị là gì?

Thử thách xuất hiện mỗi ngày. Điều bạn làm tốt hôm qua chưa chắc vẫn tốt hôm nay. Một chuyên viên nhân sự cần có những đường hướng phù hợp để bổ trợ chiến lược phát triển của công ty. Đôi lúc, sẽ rất khó để vận hành ở cùng một tốc độ. Đôi lúc trở ngại tiếp theo sẽ ập đến bất ngờ. Việc liên tục chuyển mình và đồng thời dẫn dắt cả đội, đôi khi là cả một thách thức.

Lời khuyên của chị dành cho những người chuẩn bị nắm vai trò quản lý hoặc chủ chốt trong công ty là gì?

Dành cho bất kỳ ai đang làm việc tại công ty công nghệ, không chỉ riêng Tiki mà ở bất kỳ đâu trong khu vực Đông Nam Á, hãy nhớ lấy ba điều: hãy sẵn sàng với những điều mơ hồ phía trước, luôn luôn tìm đến những người có thể bù trừ cho điểm yếu của bạn, những người giỏi hơn bạn, và hãy luôn tin rằng vẫn còn rất nhiều kiến thức mà ta cần trau dồi.

Tham khảo thêm các vị trí:

Chị nghĩ xu hướng tuyển dụng của các công ty công nghệ vào năm 2020 là gì?

Có thể ví Việt Nam như “Thung lũng Silicon” của Đông Nam Á với tốc độ tăng trưởng từng ngày của thị trường công nghệ tại đây. Tuy có rất nhiều tài năng tìm đến thị trường đang lớn mạnh này, tôi vẫn thấy rất nhiều người chọn Canada, Nhật Bản hay Đức. Vấn nạn “chảy máu chất xám” đang trở nên nghiêm trọng hơn. Tuy nhiên, tôi chú ý thấy nhiều doanh nghiệp điện tử nước ngoài đang dần dà công nhận nguồn lực tài năng tại Việt Nam và bắt đầu mở văn phòng tại đây. Bản thân Việt Nam cũng đang mở nhiều cơ hội về công nghệ để thúc đẩy các doanh nghiệp cũng như khuyến khích khởi nghiệp. Điều này đồng nghĩa với việc tăng cơ hội việc làm và tăng sự cạnh tranh giữa các tài năng.

Hơn nữa, hiện tại chúng tôi đã có thêm các công cụ tuyển dụng hiệu quả hơn. Ví dụ, nếu muốn chiêu mộ 100-200 người cùng lúc, chúng tôi sẽ không thể chỉ dùng sức người. Chúng tôi cần những công cụ tuyển lọc tự động, giúp đẩy nhanh quá trình như trí tuệ thông minh nhân tạo (AI) và các phần mềm tuyển dụng khác.

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

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

TopDev via Vietcetera

Docker là gì? Kiến thức cơ bản về Docker

Tác giả: Quân Phạm

Tại sao phải dùng Docker?

Việc setup và deploy application lên một hoặc nhiều server rất vất vả từ việc phải cài đặt các công cụ, môi trường cần cho application đến việc chạy được ứng dụng chưa kể việc không đồng nhất giữa các môi trường trên nhiều server khác nhau. Chính vì lý do đó Docker được ra đời để giải quyết vấn đề này.

Docker là gì?

Docker là một nền tảng cho developers và system admin để develop, deploy và run application với container. Nó cho phép tạo các môi trường độc lập và tách biệt để khởi chạy và phát triển ứng dụng và môi trường này được gọi là container. Khi cần deploy lên bất kỳ server nào chỉ cần run container của Docker thì application của bạn sẽ được khởi chạy ngay lập tức.

Lợi ích của Docker

  • Không như máy ảo Docker start và stop chỉ trong vài giây.
  • Bạn có thể khởi chạy container trên mỗi hệ thống mà bạn muốn.
  • Container có thể build và loại bỏ nhanh hơn máy ảo.
  • Dễ dàng thiết lập môi trường làm việc. Chỉ cần config 1 lần duy nhất và không bao giờ phải cài đặt lại các dependencies. Nếu bạn thay đổi máy hoặc có người mới tham gia vào project thì bạn chỉ cần lấy config đó và đưa cho họ.
  • Nó giữ cho word-space của bạn sạch sẽ hơn khi bạn xóa môi trường mà ảnh hưởng đến các phần khác.

Cài đặt

Link download: tại đây

Chọn bản cài đặt tương ứng với hệ điều hành của bạn và tiến hành cài đặt theo hướng dẫn đối với Linux còn Windows và MacOS thì bạn chỉ cần tải bản cài về và cài đặt như mọi application khác.

Sau khi cài đặt xong để kiểm tra xem cài đặt thành công hay không ?

  • Mở command line:
$ docker version$ docker info$ docker run hello-world

Một số khái niệm

Docker là gì? Kiến thức cơ bản về Docker

  • Docker Client: là cách mà bạn tương tác với docker thông qua command trong terminal. Docker Client sẽ sử dụng API gửi lệnh tới Docker Daemon.
  • Docker Daemon: là server Docker cho yêu cầu từ Docker API. Nó quản lý images, containers, networks và volume.
  • Docker Volumes: là cách tốt nhất để lưu trữ dữ liệu liên tục cho việc sử dụng và tạo apps.
  • Docker Registry: là nơi lưu trữ riêng của Docker Images. Images được push vào registry và client sẽ pull images từ registry. Có thể sử dụng registry của riêng bạn hoặc registry của nhà cung cấp như : AWS, Google Cloud, Microsoft Azure.
  • Docker Hub: là Registry lớn nhất của Docker Images ( mặc định). Có thể tìm thấy images và lưu trữ images của riêng bạn trên Docker Hub ( miễn phí).
  • Docker Repository: là tập hợp các Docker Images cùng tên nhưng khác tags. VD: golang:1.11-alpine.
  • Docker Networking: cho phép kết nối các container lại với nhau. Kết nối này có thể trên 1 host hoặc nhiều host.
  • Docker Compose: là công cụ cho phép run app với nhiều Docker containers 1 cách dễ dàng hơn. Docker Compose cho phép bạn config các command trong file docker-compose.yml để sử dụng lại. Có sẵn khi cài Docker.
  • Docker Swarm: để phối hợp triển khai container.
  • Docker Services: là các containers trong production. 1 service chỉ run 1 image nhưng nó mã hoá cách thức để run image — sử dụng port nào, bao nhiêu bản sao container run để service có hiệu năng cần thiết và ngay lập tức.

Dockerfile

– Dockerfile là file config cho Docker để build ra image. Nó dùng một image cơ bản để xây dựng lớp image ban đầu. Một số image cơ bản: python, unbutu and alpine. Sau đó nếu có các lớp bổ sung thì nó được xếp chồng lên lớp cơ bản. Cuối cùng một lớp mỏng có thể được xếp chồng lên nhau trên các lớp khác trước đó.

– Các config :

  • FROM — chỉ định image gốc: python, unbutu, alpine…
  • LABEL — cung cấp metadata cho image. Có thể sử dụng để add thông tin maintainer. Để xem các label của images, dùng lệnh docker inspect.
  • ENV — thiết lập một biến môi trường.
  • RUN — Có thể tạo một lệnh khi build image. Được sử dụng để cài đặt các package vào container.
  • COPY — Sao chép các file và thư mục vào container.
  • ADD — Sao chép các file và thư mục vào container.
  • CMD — Cung cấp một lệnh và đối số cho container thực thi. Các tham số có thể được ghi đè và chỉ có một CMD.
  • WORKDIR — Thiết lập thư mục đang làm việc cho các chỉ thị khác như: RUN, CMD, ENTRYPOINT, COPY, ADD,…
  • ARG — Định nghĩa giá trị biến được dùng trong lúc build image.
  • ENTRYPOINT — cung cấp lệnh và đối số cho một container thực thi.
  • EXPOSE — khai báo port lắng nghe của image.
  • VOLUME — tạo một điểm gắn thư mục để truy cập và lưu trữ data.

Tạo Demo

Tạo file Dockerfile

FROM golang:1.11 AS builderWORKDIR /go/src/docker-demo/COPY . .RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o docker-demo .FROM alpine:latestWORKDIR /root/COPY — from=builder /go/src/docker-demo .CMD [“./docker-demo”]

Tạo file main.go

package mainimport (   “fmt”)func main() {   fmt.Println(“Learning Docker”)}

Tiến hành build file Dockerfile

$ docker build .

Docker là gì? Kiến thức cơ bản về Docker

$ docker run 4cc010d9d657

Docker là gì? Kiến thức cơ bản về Docker

Kết quả in ra dòng chữ Learning Docker đã được code trong file main.go

Các lênh cơ bản trong docker

  • List image/container:
$ docker image/container ls
  • Delete image/container:
$ docker image/container rm <tên image/container >
  • Delete all image hiện có:
$ docker image rm $(docker images –a –q)
  • List all container hiện có:
$ docker ps –a
  • Stop a container cụ thể:
$ docker stop <tên container>
  • Run container từ image và thay đổi tên container:
$ docker run –name <tên container> <tên image>
  • Stop all container:
$ docker stop $(docker ps –a –q)
  • Delete all container hiện có:
$ docker rm $(docker ps –a –q)
  • Show log a container:
$ docker logs <tên container>
  • Build một image từ container:
$ docker build -t <tên container> .
  • Tạo một container chạy ngầm:
$ docker run -d <tên image>
  • Tải một image trên docker hub:
$ docker pull <tên image>
  • Start một container:
$ docker start <tên container>

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

Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co – Quán quân StartupViet 2019

Đôi nét về anh Tung NS – Nguyễn Sơn Tùng – CTO/Founder | Việc Có

Anh Sơn Tùng từng có 5 năm kinh nghiệm giữ vị trí Head of Technology (Giám đốc kỹ thuật) tại Tiki.vn – sàn giao dịch TMĐT lớn nhất hiện nay. Hiện tại đang nắm giữ vị trí Co-founder & CTO của Viec.co – ứng dụng cùng tên để kết nối người lao động làm việc ngắn với các doanh nghiệp một cách nhanh chóng và đơn giản.

Từ vị trí là một Manager của một trong những trang TMĐT hàng đầu Việt Nam cho đến việc trở thành Founder/CTO của một Startup đoạt giải quán quân Startup Việt và gọi vốn thành công 300.000 USD tại Thương vụ bạc tỷ – Shark Tank Việt Nam. Trong cuộc nói chuyện hôm nay, anh Sơn Tùng sẽ chia sẻ 1 số kinh nghiệm quý giá  trong lĩnh vực công nghệ cũng như phác họa chân dung của 1 người làm CTO ở nhiều môi trường khác nhau. Hãy cùng TopDev gặp gỡ anh Sơn Tùng trong chương trình Chuyên Gia Nói ngày hôm nay nhé!

Chào anh, anh có thể chia sẻ với mọi người về công việc hằng ngày của mình cho mọi người được hiểu thêm về công việc của anh được không?

Mỗi ngày việc đầu tiên khi tới công ty, tôi sẽ lập 1 cái danh sách (blue-list) để xem mình dùng thời gian cho việc gì trong ngày cho nó chất lượng. Cụ thể hơn như là tôi sẽ xem công việc tiến độ ra sao, mọi người có cần tôi hỗ trợ gì không, rồi review công việc của mọi người, sau đó tôi sẽ trao đổi với các team về kinh doanh khác, coi có gì cần cập nhật hoặc chuẩn bị cho planning kế tiếp của team không, rồi thỉnh thoảng tôi sẽ “phân thân” vào phụ mọi người ở các phần việc khác nhau.

Ngoài ra thì tôi cũng dành khá nhiều thời gian để đi kết nối gặp các bạn có cùng chí hướng để xây dựng team và thỉnh thoảng cũng đi gặp khách hàng nữa. 

Hiện tại VIEC.CO đã có những chỉ số hoạt động đáng kể nào, anh có thể chia sẻ cho mọi người được biết không?

Sau 1 năm hoạt động thì bên tôi cũng may mắn là có khoản 50.000 cộng tác viên đăng ký đi làm, cũng giúp kết nối việc làm cho khoảng hơn 100 công ty, và nếu tính trên việc làm thực sự đã diễn ra thì bọn tôi có tạo ra được 500.000 giờ làm giữa 2 bên. 

Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co

Đó là một con số rất ấn tượng, vậy theo anh, để đạt được những thành quả đó, với vai trò là một CTO thì điều quan trọng nhất là gì?

Về vai trò này thì tôi có 4 đặc điểm tối quan trọng như sau 

Thứ 1, CTO thì phải giỏi về công nghệ, vì bạn phải chịu trách nhiệm trả lời mọi thứ về công nghệ, nó có triển khai được không và mọi thứ thì bạn phải có 1 độ hiểu sâu nhất định, không chỉ cho những thứ đang diễn ra mà cho mọi thứ sắp tới nữa, đó là điều quan trọng nhất. 

Điều thứ 2 là bạn phải biết cách xây dựng 1 team hoàn chỉnh, bởi nếu làm 1 mình thì bạn sẽ không làm được những việc lớn, việc khó khăn nhất là tìm ra những người cùng chí hướng, từ đó giúp họ, cùng họ phát triển và trụ lại ở lại công ty. Nhìn chung thì chiến lược của công ty có thể thay đổi lên xuống, nhưng tôi có 1 team mạnh thì mình làm gì cũng được.

Đặc điểm thứ 3 là 1 CTO thì phải biết cách để giúp cho team của mình triển khai được sản phẩm hoặc ý tưởng. Dù bạn có bao nhiêu người giỏi cạnh bên nhưng không có triển khai được ý tưởng hay sản phẩm thì cũng chả giải quyết được vấn đề nào hết. Để làm được như vậy, bạn CTO đó phải xây dựng cho được 1 thứ mà tôi hay gọi là engineering, bao gồm có văn hóa, quy trình, những thứ cần thiết để mọi người biết là sẽ làm việc theo cách như thế nào. Có được điều đó, công ty sau này phát triển lớn lên sẽ có những vị trí chuyên trách như là head director hoặc là VP hoặc là engineering riêng, còn thời gian đầu thì CTO phải làm tất tần tật những việc đó.

Cái thứ 4 mà tôi nghĩ rất là cần là CTO thì phải là người hiểu rất là rõ về business và khách hàng, thì khi đó mới nói chuyện cùng ngôn ngữ được với những team khác. Từ đó tôi mới có thể dùng công nghệ của mình để giải quyết vấn đề và chuẩn bị tốt nhất cho sản phẩm của mình, để nó có thể cũng đáp ứng được cái nhu cầu của tất cả mọi người. 

Tôi nghĩ đó là 4 yếu tố cơ bản nhất, còn 1 cái cuối cùng thì tôi nghĩ mọi người chắc cũng biết đó là sẽ phải học, bởi vì mọi thứ sẽ thay đổi rất nhanh. Tôi học để biết mình áp dụng những công nghệ gì, và chuẩn bị cho việc đón đầu những xu hướng công nghệ tiếp theo.

Với tư cách là một CTO, anh có trực tiếp tham gia code hay trực tiếp đảm nhiệm công việc nào? Theo anh thì phần việc nào CTO không nên trực tiếp làm?

Từ lúc triển khai ban đầu, khi chưa thực sự có gì thì CTO phải làm tất cả mọi thứ. Làm từ code như 1 developer, test như 1 QC, làm sản phẩm để có UI prototype, hay DevOps rồi system deploy tất cả mọi thứ. Tôi cũng code VIEC.CO từ những dòng đầu tiên từ cho mobile cho backend mà từ từ rồi sẽ có người khác tiếp sức. Từ đó, công việc, vai trò của tôi, về những chuyện mà tôi sẽ nhúng tay sâu vào sẽ giảm bớt lại.

Tôi tránh dẫm chân lên mọi người ở những chỗ tôi cảm thấy mọi người có thể làm được hoặc có tiềm năng sẽ làm được, cần tạo cho họ cơ hội để họ có thể làm chuyện đó. Nhưng bên cạnh đó, tôi vẫn phải làm chi tiết như là 1 số technical *debt. Khi thấy mọi người đang gặp khó khăn ở đâu thì tôi cũng phải xắn tay áo lên. Hay đôi lúc có một ý tưởng về business muốn thử nghiệm và triển khai nhanh chẳng hạn, thì CTO cũng có vai trò là sẽ nghiên cứu và triển khai. Ví dụ có 1 đợt mọi người muốn thử nghiệm chấm công bằng công nghệ bluetooth, tôi cũng phải tự nghiên cứu để làm 1 cái MVP (Minimum Viable Product) thử. Đó là chi tiết những gì tôi phải làm. 

Được biết trước đây anh từng làm manager tại Tiki, anh có thể chia sẻ về sự khác nhau khi làm ở 1 tổ chức lớn và ở Startup như VIEC.CO không?

Trước đây thì tôi từng đảm nhận vai trò Head of Technology 5 năm ở Tiki. Vị trí này đối với 1 công ty startup nhỏ như tôi khá tương đồng, nhưng cũng tồn tại sự khác biệt. Khi phụ trách về công nghệ ở Tiki cũng có thách thức cũng tương đối lớn. 

Ví dụ đầu tiên là thách thức để tăng trưởng. Lấy ví dụ bạn có thể đóng 1 chiếc ghế trong vòng một ngày hết sức dễ dàng. Nhưng nếu mà bạn phải đóng chiếc ghế trong vòng 1 giờ và càng ngày càng rút ngắn thời gian, chiếc ghế còn phải đẹp hơn nữa thì việc này sẽ đẩy bạn đến giới hạn cuối cùng của bản thân. Bạn sẽ phải học hỏi và rèn luyện thêm hàng ngày.

Thách thức thứ 2 khi làm với 1 công ty lớn như Tiki đó là bạn phải giải quyết nhiều việc cùng 1 lúc. Thứ nhất, bạn phải duy trì cải tiến những hệ thống cũ, sản phẩm của lúc không có người giỏi tham gia (và họ có xu hướng thích làm cái mới) và bên cạnh cái cũ thì mình phải build những cái mới. Sau đó, phải đi làm chuyện khác như là làm sao migrate những cái cũ sang cái mới. Đó là những việc phức tạp, rủi ro mà không phải ai cũng dũng cảm để làm. Tôi sẽ phải làm sao để mọi thứ nó đều chạy ổn định. Nó không giống startup khi bạn build mọi thứ mới ngay từ đầu, đó là công việc tương đối dễ dàng.

Đấy là những thách thức cơ bản nhất khi bạn làm ở cái startup lớn như Tiki, còn khi làm ở startup nhỏ thì có những điểm thú vị. Thứ nhất, bạn có nhiều cơ hội học hỏi hơn. Ở 1 tổ chức lớn, không phải việc gì bạn cũng có thể tham gia vào ngay được, vì bạn sẽ phải cần có độ sâu sắc nhất định trong từng lĩnh vực. Sự sâu sắc đó không phải tự nhiên bạn tích lũy được mà đến từ một quá trình trước đó. Khi tham gia những startup nhỏ, bạn có cơ hội tích lũy dễ dàng hơn, tích tiểu thành đại. 

Trước đây tôi cũng đảm nhận một vài vị trí mà tôi cảm thấy từng trải qua trong quá trình trước đó. Lúc làm ở vị trí product manager, tôi thực sự chưa hiểu rõ về business khi mình làm. Tôi tìm hiểu qua bằng việc đọc sách rồi thử nghiệm. Nhưng sau khi hoàn thành thì kết quả chưa được tốt. Nhưng dựa trên nền tảng đó, khi làm vị trí hiện tại, khi làm business tôi đã có góc nhìn về business tốt hơn. Đó là cách để tôi học hỏi từ từ, tích lũy để tôi làm startup quy mô lớn hơn. Đó là sự khác biệt khi mà làm cho cái lớn và cái nhỏ.

Anh có thể chia sẻ ví dụ cụ thể hơn khi merge từ công nghệ cũ sang công nghệ mới ở Tiki không?

Tôi có một trải nghiệm về chuyện merge rất đáng nhớ. Khi tôi vô Tiki, họ có hệ thống tương đối phức tạp, quy mô năm 2012 có thể hình dung: khoảng 1000 table và 300 extension chạy trên 1 nguồn mở. Khi có sự cố, bug, vấn đề xảy ra, mọi người cảm thấy bức xúc, cần phải giải quyết. 

Có vấn đề có thể giải quyết được bằng cách fix trực tiếp, có những cái rất là vô vọng. Tôi buộc phải build 1 hệ thống mới nhỏ gọn hơn, đáp ứng đúng cái nhu cầu của Tiki. Nhưng với nguồn lực có hạn, tôi không thể tập trung build một thứ được mà tôi phải tách nguồn lực ra để làm nhiều thứ cùng lúc. Sau khi mất thời gian để build được cái hệ thống core platform của Tiki đó rồi, bài toán tiếp theo là làm sao đưa hệ thống này vào sử dụng. Việc đẩy hệ thống rất lớn qua 1 hệ thống mới hoàn toàn khá là thử thách. Không thể 1 ngày là chuyển giao được mà vẫn phải duy trì fix bug ở bên này, build hệ thống mới ở bên kia. Rồi bạn dành thời gian process để merge cái cũ sang cái mới.

Tôi nhớ mình làm 1 cái script chạy 5 tiếng đồng hồ, đẩy toàn bộ dữ liệu qua và gặp rất nhiều lỗi bởi vì cũ mới thì có những cái không tương thích. Đây cũng là ví dụ về công việc tương đối phức tạp, yêu cầu bạn phải vừa phải rất hiểu cái cũ cũng như rất hiểu cái mới. Không phải ai cũng dũng cảm và sẵn sàng để làm chuyện như vậy và CTO chắc chắn sẽ là người nhận trách nhiệm đó về mình. 

Lý do nào anh chọn khởi nghiệp với ý tưởng sàn giao dịch việc làm thời vụ VIEC.CO?

VIEC.CO tôi start cùng với 1 bạn Co-founder nữa. Bọn tôi mỗi người đến với mục đích khác nhau nhưng đều chung muốn giúp những người lao động phổ thông, những người thu nhập chưa cao, nhiều rủi ro ở trong xã hội. Mọi người có thể hình dung, mỗi tháng mọi người nhận lương thì nghe ‘ting ting’ có khi mấy chục triệu là chuyện bình thường. Nhưng với người lao động phổ thông, mỗi kỳ hay mỗi tuần bọn tôi chuyển cho họ vài trăm nghìn thì họ cũng reo lên sung sướng. Hoặc với 1 triệu mình mua sắm được vài thứ, nhưng số tiền đó đưa cho họ có khi cải thiện bữa ăn được vài tuần. Hoặc là giúp đứa bé đóng học phí không bị cô nhắc nữa chẳng hạn. Đó là động lực để cho bọn mình làm hệ thống này và tiếp tục sẽ làm hệ thống này. 

Hiện tại VIEC.CO cũng được offer từ chương trình Shark Tank và đồng thời cũng là quán quân của Startup Việt 2019 thì – Sau những sự kiện như vậy thì chỉ số hoạt động của VIEC.CO thay đổi tích cực như thế nào? 

Rất là may mắn là bọn tôi đã có 1 cái deal với Shark Tank và quán quân Startup Việt, ngay sau đó bọn tôi cũng rất là bất ngờ với những cái kết quả đi kèm. Có thể mô tả như sau: có một vài ngày, số khách hàng tăng bằng cả năm cộng lại vậy. Có những khách hàng mang lại cho bọn tôi cơ hội mới,  không chỉ là khách hàng mới mà còn nhu cầu mới và từ đó đến nay bọn tôi đã làm việc rất chăm chỉ để đáp ứng nhu cầu của những khách hàng đó. 

Ngoài những thành công, anh có thể chia sẻ một kỷ niệm sai lầm đáng nhớ mà anh từng mắc phải? 

Có một sai lầm mà tôi nghĩ sẽ nhớ rất là lâu. Cách đây cũng 8 năm, khi tôi bắt đầu tham gia Tiki, tôi tham gia với 1 cái tâm thế là mình đã làm 1 cái hệ thống rất lớn, 1 website có thứ hạng thứ 18 trên Alexa. Tôi tin là với Tiki cũng nhỏ thôi, mình tin là sẽ làm ok. Nhưng thực ra thời điểm đó tôi chỉ là tay mơ trong lĩnh vực thương mại điện tử thôi. Hệ thống Tiki rất phức tạp, 1 hệ thống e-commerce pha rất là nhiều (mà chuyên ngành gọi là) write xuống chứ không phải là read như các trang web khác. Cho nên khi đó Tiki làm event là hệ thống bị quá tải, kinh nghiệm rút ra là tôi cần phải rất là ‘ở dưới mặt đất’, nghiên cứu rất là kĩ những cái gì tôi sắp làm và có sự chuẩn bị phù hợp thì tôi mới làm. 

Anh có lời khuyên nào dành cho các bạn developer trong công việc nâng cao kỹ năng cũng như career path.

Để trả lời câu hỏi đó thì tôi xin chia sẻ câu chuyện mình đã từng giới thiệu 1 bạn trong team cho Facebook. Bên Facebook hỏi tôi là theo bạn “người nhân viên này nằm trong top bao nhiêu những người bạn đã làm việc cùng?”. Từ chuyện đó, tôi có liên hệ tới 1 số chuyện khác, khi tôi đã làm việc với rất là nhiều bạn rồi, phỏng vấn khá nhiều thì phải để trở thành 1 cái gì đó rất giỏi, làm được những công ty lớn thì các bạn phải định vị mình là ai trong số tất cả những người còn lại. Ở trong lớp, bạn top bao nhiêu, trong 1 nhóm, hội nhóm, bạn định vị mình là như thế nào, bất kể là bạn đang là bao nhiêu, quan trọng là bạn phải định vị mình, đặt mục tiêu cho mình. Trong sự lựa chọn 100 người tại sao người ta tuyển bạn? Đấy là câu hỏi phỏng vấn thường hay hỏi nhất. Khi mà bạn định hình cho mình một mục tiêu rất cao và chăm chỉ đạt được mục tiêu đó, bạn sẽ có cơ hội làm khác biệt, để làm những chuyện lớn hơn trong tương lai.

Anh có thể chia sẻ một vài lý do để các bạn Developer join vào team VIEC.CO?

Thứ nhất, hiện nay bọn tôi đang có nhiều thách thức lớn, như là xây dựng một nền tảng về lao động để mọi người làm việc, và bọn tôi tin là nó sẽ có nhiều tác động đến việc mọi người đi làm trong tương lai. Đó là một bài toán khó và phức tạp, và nó có quy mô tăng trưởng rất là nhanh nên nếu các bạn tham gia thì các bạn có cơ hội để giải quyết các thử thách đó.

Thứ 2, bọn tôi có nhiều kinh nghiệm giải quyết bài toán ở quy mô lớn như là thương mại điện tử như Tiki, từ đó cũng có thể tư vấn để giúp bạn. Với những bạn còn trẻ nhận biết đây là giai đoạn các bạn phát triển, rút ngắn khoảng thời gian để các bạn có thể phát triển nhanh hơn, và nhìn chung là team của tôi khá là ‘geeky’ thể hiện qua cái cách mà tôi tuyển dụng.

Quy trình tuyển dụng ở VIEC.CO không giống như các công ty khác. Bọn tôi không tuyển theo kiểu chỉ đăng cái job lên rồi nhận CV. Bọn tôi code hẳn 1 cái trang là tuyển dụng gọi là VIEC.CO HIRING API, cũng là cách để tôi giao tiếp với developer bằng cái ngôn ngữ của họ để đăng tuyển. Để tìm hiểu thông tin về công việc và công ty thì bạn phải gửi API, bạn sẽ nhìn thấy mô tả công việc, nhìn thấy cái bài test trên đó các bạn tự sắp list bài test và bạn thấy kết quả luôn. Khi các bạn pass bài test, tôi sẽ liên hệ lại các bạn. Đầu tiên thì cũng sẽ gọi điện hỏi thăm mấy cái vấn đề quan trọng, sau đó là phỏng vấn. 

Phỏng vấn là có cả team bọn tôi cũng phỏng vấn chung, và cuối cùng là bọn tôi sẽ quyết định là sẽ làm việc với bạn đó hay không.

Theo quan điểm cá nhân của anh, xu hướng công nghệ nào trong tương lai mà một Developer cần nắm bắt?

Xu hướng công nghệ cũng phụ thuộc vào tùy lĩnh vực. Còn đối với cá nhân tôi, tôi cũng rất là quan tâm tới AI, bởi những trải nghiệm của tôi với AI rất là tuyệt. Tôi nhớ là khi tôi tham gia một event của GOOGLE IO , tôi nghe rất nhiều SUNDAR PICHAI, anh đó chia sẻ về những thứ mà Google Assistant có thể làm được. Tôi thấy cái này thay đổi cuộc sống rất là khủng khiếp, và tôi nhìn lại thì những cái gì đang diễn ra ở hiện tại thì nó vẫn còn cách đích đến 1 đoạn, để làm sao mà tới cái đích đó thì cần rất nhiều công sức của mọi người nữa. Mọi người học nhiều hơn để chuẩn bị dần để đến cái tương lai đó.

Cũng cách mới đây mấy hôm thôi, tôi cũng thấy Elon Musk đăng tuyển dụng về AI. Những bạn nào có thể làm được AI có thể là nộp đơn cho anh ấy. Thậm chí là không cần phải là qua Đại học, thậm chí phổ thông anh ấy cũng nhận luôn. Miễn là mọi người đam mê và thuyết phục được là mình làm được thì đó là cơ hội, nó không quá xa nếu như các bạn bắt đầu sớm và tạo ra nhiều thay đổi trong tương lai.

Theo quan điểm của anh thì bằng cấp trong công việc IT có quan trọng hay không?

Chúng ta hãy hình dung bằng cấp là gì. Bằng cấp là mục tiêu đặt ra trong cái sự nghiệp của mọi người hay là cái mà ai đó bảo bạn là phải lấy? Như mọi người biết, Bill Gates cũng không có bằng đại học, nhưng bạn có thể không biết là ông là người học giỏi nhất cả mấy tiểu bang, và ông ấy cũng đang ở trong trường tốt nhất mà ra.

Câu chuyện bằng cấp nó không quan trọng nếu như bạn có mục tiêu rõ ràng. Nếu mục tiêu của bạn là lấy bằng thì bạn phải lấy bằng vì đó là commitment giữa cá nhân bạn với chuyện đó. Còn nếu mục tiêu của bạn là đi làm cái chuyện khác chẳng hạn, bạn có đủ động lực, bạn có đủ kiến thức, đủ đam mê thì bạn cứ làm. Và cá nhân tôi cũng không có quan trọng là các bạn tham gia VIEC.CO là phải có bằng cấp.

Anh có nhắc đến việc AI thay đổi cuộc sống của con người theo hướng tích cực, không biết trong tương lai anh có dự định áp dụng AI vào VIEC.CO không?

Tôi quan tâm và tôi đang chuẩn bị dần, thì cũng đi học mọi người thôi. Bởi lẽ tôi tin rằng mình đang giải quyết bài toán rất lớn. Làm sao để ai đó biết họ cần bao nhiêu nguồn lực, ai đó biết rằng họ cần những người như thế nào, rồi người họ cần có những đặc tính ra làm sao.

Như tôi tìm hiểu thì 1 startup giống VIEC.CO ở bên Mỹ, họ đang phân tích chỉ số liên quan tới người lao động, có khoảng 40 chỉ số và họ sẽ dùng những chỉ số này để xây dựng dần những hệ thống, giúp người cần lao động biết được ai phù hợp với họ và ngược lại bạn đi làm chỗ nào thì phù hợp với bạn, và đấy cũng là cái VIEC.CO sẽ chuẩn bị và đầu tư cho tương lai.

Đâu là những khó khăn và thuận lợi nào đối với một Founder, Startup có nền tảng là Tech như anh?

Thuận lợi thì mọi người biết rồi, CTO khá rành về công nghệ cho nên nếu tôi làm Startup công nghệ thì tương đối phù hợp, lên MVP và mọi thứ tương đối nhanh và cũng sẵn sàng để scale lên mức độ cao hơn nữa. Còn khó khăn thì từ đúc kết bản thân tôi, ban đầu cũng hơi khô cứng nên business sense của mình chưa có nhạy, chưa thể dễ dàng làm việc với team business. Tôi nên áp dụng những lĩnh vực này, lĩnh vực kia, những cái đó thì phải học thôi, và tôi sẽ phải tự học thêm hoặc là đi gặp gỡ khách hàng nhiều để lấy insight của họ.

Anh có thể chia sẻ với khán giả về một số cách để học Business không ạ?

Đầu tiên là đọc sách, tôi quan tâm đến lĩnh vực nào thì sẽ kiếm sách về lĩnh vực đó, rồi học chăm chỉ đi trong việc gặp khách hàng, tôi sẽ ghi nhận lại những cái insight của họ, về những cái họ cần là gì. Về sau đó là tôi sẽ so sánh lại cái suy nghĩ hồi trước mà gọi gọi là khô cứng để coi là mình suy nghĩ vậy đã đúng chưa rồi điều chỉnh lại.

Anh hãy chia sẻ về Technical Stack mà anh đã chọn và vì sao anh lại chọn Technical Stack đó?

Technical Stack của VIEC.CO hiện nay sử dụng nhiều. Về cơ bản, bọn tôi sử dụng React Native và Reactjs cho frontend của phần mobile app, core platform thì mình sử dụng Laravel cho backend, còn về hạ tầng thì dùng Google Cloud Platform, Database thì có MySQL.

Lý do mà tôi đang dùng Stack, khi bạn muốn làm việc gì, bạn sẽ bắt đầu với thứ mà bạn mạnh nhất và có đồng đội của bạn mạnh nhất nữa. Rồi bạn phải biết được là cái bạn cần làm thì cần dùng cái công nghệ gì cho phù hợp.

Tôi có thể lựa chọn theo trend thí dụ như là Golang, để làm việc đó nhưng mà bài toán của tôi nó có cần những cái như vậy và nó có phù hợp với những cái như vậy hay không, và đây một quyết định hoặc câu hỏi đặt ra thường xuyên của các bạn developer hay là technical leader thường sẽ giải quyết, sao không áp dụng cái này cái kia đi chẳng hạn. Mình thấy chuyện tranh luận đó không có hồi kết, sẽ phụ thuộc vào chuyện là bạn đang vướng cái gì.

Theo tôi để ý ở các engineer công ty khác, tại sao Airbnb phải lập cái usability hoặc là lập ra rất là nhiều công nghệ riêng của họ, như diaflow chẳng hạn. Bản chất là họ đang gặp phải 1 cái giới hạn và họ phải tìm cách phá vỡ giới hạn đó, nếu như PHP đối với mình giải quyết cái bài toán dưới 100 millisecond, dưới 50-100 millisecond ở server side cũng ok, nếu mà tôi không cần tới mức giới hạn đó thì tôi giải quyết được rồi, tùy theo cái scale của bạn đang làm là gì thì bạn phải chọn công nghệ phù hợp và người tech leader phải dành rất nhiều thời gian để lựa chọn cái stack công nghệ phù hợp thay vì là cứ thấy thị trường đang có cái gì mới thì chạy theo, rồi hệ thống của bạn phải đổi cái nọ cái kia, phải viết đi viết lại nhiều lần thì cũng mất thời gian ra, không để làm gì cả.

Các bạn có quyền là thử nghiệm và áp dụng những cái công nghệ mới nhưng là nên là 1 cái test project nào đó, đến khi bạn thực sự am hiểu nó rồi thì mình hãy dùng, đừng là cái người đi thử hết công nghệ này đến công nghệ kia trong business, mà hãy tìm hiểu rất là sâu để mình biết chắc chắn về công nghệ rồi mới áp dụng cái nào phù hợp. 

Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co

Anh hay dùng công nghệ và cách thức gì để quản lý công việc của team? Cũng như là đo đạc hiệu quả làm việc của họ? 

Thời gian đầu thực sự tôi không dùng công cụ gì hết, startup mọi người làm việc rất là chăm chỉ, 200% sức lực, cái đo hiệu quả nhất là những trải nghiệm của khách hàng, rep của tôi ra sao, tôi chia sẻ lại bạn đó để bạn đó biết là à, cái này khách hàng đang chê này.

Và có những lúc mà bọn tôi deploy 1 cái chức năng nào đó hay ho, cụ thể như là làm chức năng mà đặt cho mọi người 1 cái badge để thể hiện bạn có 500 giờ để làm việc cho cái hệ thống này, deploy xong 1 ít phút  sau thì có bạn khoe “ôi tôi làm được việc nè” với mọi người, thì đó là những cách ghi nhận thành quả ghi nhận là bạn làm việc hiệu quả, rất là tốt.

Còn về cách thức bọn tôi tổ chức trong tương lai khi mà nhiều người hơn thì sẽ cần những quy trình. Thực ra bọn mình đã trải nghiệm chuyện đó rồi, ở quy mô lớn hơn thì tôi cũng đang chuẩn bị quy trình đó cho team tiếp theo, do đông hơn rồi mà. Bọn tôi sẽ có những cái gọi là Story Point để cho task mọi người làm, định nghĩa bởi vì là cái task này là effort của nó bao nhiêu kết hợp với lại cái giá trị business, sau mỗi screen rồi tự nhìn nhận tôi deliver được bao nhiêu point, đấy là 1 cách. Tuy nhiên cái ghi nhận lớn nhất cho tôi là từ khách hàng.

Làm thế nào để anh xác định những tính năng hoặc sản phẩm mà team anh cần làm tiếp? Và làm thế nào để quản lý những request đó?

Tôi sắp xếp việc đó từ 2 phía. Phía thứ nhất là Topdown, từ business xuống và tôi có cái process để phân tích những business infinitive, bọn tôi sẽ có mục tiêu đi xuống cụ thể hóa, cần phải làm gì để giải quyết cái mục tiêu đó. Rồi từ từ sẽ chuyển xuống bên dưới thành những cái ‘to do’ của developer như là IPic, Story Point hay Task.

Còn đi từ hướng thứ 2 là phía khách hàng thì team sales và mình phải đi gặp trực tiếp khách hàng, ghi nhận lại những cái nhu cầu của khách hàng, xong rồi tôi sẽ set độ ưu tiên và bỏ chung vào đánh giá lại cái gì sẽ làm.

Công cụ thì mình đang xài Jira.

Anh quan tâm đến những khía cạnh phi kỹ thuật nào khi có ứng viên muốn apply vào team?

Khi hỏi ứng viên thì tôi sẽ thường hay hỏi là thứ nhất bạn tự hào về cái gì trong quá khứ bạn làm nhất, bạn có một project, thậm chí là code hay không phải code, bởi vì khi mà bạn bỏ tâm đến cái mà bạn làm cái gì đó, tạo ra 1 kết quả thì tôi hiểu là bạn rất nghiêm túc với công việc và có thể là bạn sẽ tạo ra những kết quả awesome trong tương lai.

Cái thứ 2, tôi sẽ hỏi là bạn thường đam mê cái gì nhất. Có nhiều bạn sẽ trả lời là đam mê code, cái đó thì tùy bạn. Nếu bạn yêu code như thế, mình cũng sẽ biết thôi, nhưng cũng có nhiều bạn trả lời đam mê những cái khác nữa. Có bạn thì đam mê xem phim, hay là đi du lịch, đam mê làm cái mà đôi lúc mình cũng không biết nó là cái gì nhưng mà mình cũng đánh giá rất cao chuyện đó vì mình biết rằng là khi bạn đam mê thì bạn cũng có cái sự cam kết nhất định của mình với đam mê đó. Và khi 1 người cam kết với mục đích của mình thì các bạn sẽ đẩy cái khả năng của bạn đến 1 mức cao hơn. Như chơi game, đâu phải ai cũng chơi giỏi, cũng phải có những kỹ năng nào đó.
Và ngược lại có những bạn mà tôi cũng cảm thấy là tôi nên kết thúc buổi gặp này sớm vì bạn không thể hiện được bạn là ai cả, bạn không thực sự thích cái gì cả thì tất nhiên là bạn phải quay lại cái chuyện định hướng 1 chút.

Xin phép được cám ơn anh đã đến với buổi phỏng vấn của TopDev, và nếu các bạn cảm thấy câu trả lời của anh Tùng, những kiến thức mà anh mang lại trong buổi phỏng vấn bổ ích với các bạn thì các bạn đừng quên nhấn ‘Like & Subscribe’ ở Channel nhé, hẹn gặp lại các bạn ở các series tiếp theo.

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

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

Phương pháp test React Component

Trang này mặc định bạn đang dùng Jest làm test runner. Nếu dùng một test runner khác, bạn cần thay đổi API cho phù hợp, giải pháp sẽ gần như nhau. Đọc thêm chi tiết cách cài đặt môi trường test ở Môi trường Test.

Một vài cách viết test phổ biến cho component React. Trên trang này, chúng tôi sẽ tập chung vào function component. Tuy nhiên, cách để tiếp cận test không phụ thuộc vào phần hiện thực cụ thể, nó cũng sẽ làm việc tốt với class c

Tuyển react không cần kinh nghiệm


Cài đặt cụ thể {#setup–teardown}

Trên mỗi test, chúng ta thường muốn render React tree của chúng ta thành DOM element và chèn nó vào document. Chỉ như thế chúng ta mới nhận được các sự kiện trên DOM. Khi kết thúc một test, chúng ta muốn “dọn dẹp” và gỡ bỏ cây này khỏi DOM.

Một cách phổ biến để làm nó là sử dụng bộ đôi beforeEach và afterEach, để chúng luôn chạy một cách độc lập và không ảnh hưởng đến test khác:

import { unmountComponentAtNode } from "react-dom";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

Bạn có thể sử dụng một cách khác, nhưng hãy nhớ chúng ta muốn chạy việc dọn dẹp ngay cả khi test fail. Nếu không, test có thể trở nên “bất ổn”, và một test có thể ảnh hưởng đến hoạt động của test khác. Như vậy sẽ rất khó để debug.


act() {#act}

Khi viết UI test, công việc như render, sự kiện từ user, hoặc fetch dữ liệu có thể được xem như một “đơn vị” tương tác với giao diện người dùng. React cung cấp một hàm trợ giúp act() để đảm bảo tất cả mọi cập nhập liên quan đến “đơn vị” đã được thực thi và áp dụng đến DOM trước khi chúng ta xác nhận kết quả:

act(() => {
  // render component
});
// xác nhận kết quả

Nó giúp test chạy giống nhất với những gì user nhận được khi sử dụng ứng dụng. Tất cả những ví dụ bên dưới sử dụng act() để đảm bảo điều này.

Bạn có thể thấy sử dụng act() trực tiếp rất rườm rà. Để tránh rườm rà, bạn có thể sử dụng một thư viện như React Testing Library, các hàm hỗ trợ đã được wrap lại sẵn trong act().

Lưu ý:

Tên act có nguồn gốc từ cách làm Arrange-Act-Assert.


Rendering {#rendering}

Thường thì, chúng ta muốn test xem một component render đúng hay không với các prop nhận được. Xem xét một component đơn giản sẽ render một thông tin dựa vào prop:

// hello.js

import React from "react";

export default function Hello(props) {
  if (props.name) {
    return <h1>Hello, {props.name}!</h1>;
  } else {
    return <span>Hey, stranger</span>;
  }
}

Chúng ta có thể viết test cho component:

// hello.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Hello from "./hello";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("renders with or without a name", () => {
  act(() => {
    render(<Hello />, container);
  });
  expect(container.textContent).toBe("Hey, stranger");

  act(() => {
    render(<Hello name="Jenny" />, container);
  });
  expect(container.textContent).toBe("Hello, Jenny!");

  act(() => {
    render(<Hello name="Margaret" />, container);
  });
  expect(container.textContent).toBe("Hello, Margaret!");
});

Fetch dữ liệu {#data-fetching}

Thay vì gọi APIs thật trong test, chúng ta có thể giả lập các request này bằng dữ liệu giả. Giả lập dữ liệu với dữ liệu “fake” để tránh ảnh hưởng đến test khi backend không sử dụng được, và để nó chạy nhanh hơn. Lưu ý: bạn có thể muốn nó chạy danh sách các test con sử dụng framework “end-to-end” để xem toàn bộ ứng dụng có làm việc với nhau không.

// user.js

import React, { useState, useEffect } from "react";

export default function User(props) {
  const [user, setUser] = useState(null);

  async function fetchUserData(id) {
    const response = await fetch("/" + id);
    setUser(await response.json());
  }

  useEffect(() => {
    fetchUserData(props.id);
  }, [props.id]);

  if (!user) {
    return "loading...";
  }

  return (
    <details>
      <summary>{user.name}</summary>
      <strong>{user.age}</strong> years old
      <br />
      lives in {user.address}
    </details>
  );
}

Bạn có thể viết test cho nó:

// user.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import User from "./user";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("renders user data", async () => {
  const fakeUser = {
    name: "Joni Baez",
    age: "32",
    address: "123, Charming Avenue"
  };

  jest.spyOn(global, "fetch").mockImplementation(() =>
    Promise.resolve({
      json: () => Promise.resolve(fakeUser)
    })
  );

  // sử dụng một phiên bản async để áp dụng resolved promise
  await act(async () => {
    render(<User id="123" />, container);
  });

  expect(container.querySelector("summary").textContent).toBe(fakeUser.name);
  expect(container.querySelector("strong").textContent).toBe(fakeUser.age);
  expect(container.textContent).toContain(fakeUser.address);

  // xóa giả lập để đảm bảo test chạy tách biệt
  global.fetch.mockRestore();
});

Giả lập các module {#mocking-modules}

Một vài module không làm việc tốt trong môi trường test, hoặc không cần thiết cho test đó. Giả lập các module này bằng dummy để dễ dàng test hơn phần code của chúng ta.

Component Contact có nhúng một component third-party GoogleMap:

// map.js

import React from "react";

import { LoadScript, GoogleMap } from "react-google-maps";
export default function Map(props) {
  return (
    <LoadScript id="script-loader" googleMapsApiKey="YOUR_API_KEY">
      <GoogleMap id="example-map" center={props.center} />
    </LoadScript>
  );
}

// contact.js

import React from "react";
import Map from "./map";

function Contact(props) {
  return (
    <div>
      <address>
        Contact {props.name} via{" "}
        <a data-testid="email" href={"mailto:" + props.email}>
          email
        </a>
        or on their <a data-testid="site" href={props.site}>
          website
        </a>.
      </address>
      <Map center={props.center} />
    </div>
  );
}

Nếu không muốn load component GoogleMap trong test của chúng ta, giả lập bằng một dummy component và chạy test:

// contact.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Contact from "./contact";
import MockedMap from "./map";

jest.mock("./map", () => {
  return function DummyMap(props) {
    return (
      <div data-testid="map">
        {props.center.lat}:{props.center.long}
      </div>
    );
  };
});

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should render contact information", () => {
  const center = { lat: 0, long: 0 };
  act(() => {
    render(
      <Contact
        name="Joni Baez"
        email="test@example.com"
        site="http://test.com"
        center={center}
      />,
      container
    );
  });

  expect(
    container.querySelector("[data-testid='email']").getAttribute("href")
  ).toEqual("mailto:test@example.com");

  expect(
    container.querySelector('[data-testid="site"]').getAttribute("href")
  ).toEqual("http://test.com");

  expect(container.querySelector('[data-testid="map"]').textContent).toEqual(
    "0:0"
  );
});

Event {#events}

Chúng tôi khuyến nghị dispatch một event DOM thật trên DOM element, và đặt phần xác nhận kết quả. Xem một component Toggle:

// toggle.js

import React, { useState } from "react";

export default function Toggle(props) {
  const [state, setState] = useState(false);
  return (
    <button
      onClick={() => {
        setState(previousState => !previousState);
        props.onChange(!state);
      }}
      data-testid="toggle"
    >
      {state === true ? "Turn off" : "Turn on"}
    </button>
  );
}

Chúng ta có thể viết test cho nó:

// toggle.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Toggle from "./toggle";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  // container *phải* được chèn vào document để event chạy đúng.
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("changes value when clicked", () => {
  const onChange = jest.fn();
  act(() => {
    render(<Toggle onChange={onChange} />, container);
  });

  // lấy toàn bộ các element, và trigger một vài sự kiện click
  const button = document.querySelector("[data-testid=toggle]");
  expect(button.innerHTML).toBe("Turn on");

  act(() => {
    button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });

  expect(onChange).toHaveBeenCalledTimes(1);
  expect(button.innerHTML).toBe("Turn off");

  act(() => {
    for (let i = 0; i < 5; i++) {
      button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
    }
  });

  expect(onChange).toHaveBeenCalledTimes(6);
  expect(button.innerHTML).toBe("Turn on");
});

Các event DOM và thuộc tính được mô tả trong MDN. Lưu ý bạn phải truyền vào { bubbles: true } trên từng event bạn tạo cho nó để đến React listener vì React tự động truyền các event này đến document.

Lưu ý:

React Testing Library cung cấp một số hàm hỗ trợ cho việc bắn sự kiện.


Timer {#timers}

Code có thể sử dụng hàm liên quan thời gian như setTimeout để lên lịch các công việc sẽ thực hiện trong tương lai. Trong ví dụ, một cửa sổ nhiều lựa chọn đợi cho đến khi có lựa chọn, nếu sau 5 giây sẽ không thể chọn:

// card.js

import React, { useEffect } from "react";

export default function Card(props) {
  useEffect(() => {
    const timeoutID = setTimeout(() => {
      props.onSelect(null);
    }, 5000);
    return () => {
      clearTimeout(timeoutID);
    };
  }, [props.onSelect]);

  return [1, 2, 3, 4].map(choice => (
    <button
      key={choice}
      data-testid={choice}
      onClick={() => props.onSelect(choice)}
    >
      {choice}
    </button>
  ));
}

Chúng ta có thể viết test cho component bằng cách dùng Jest’s timer mocks và test sự khác nhau của state.

// card.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

jest.useFakeTimers();

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should select null after timing out", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });

  // chạy đến lúc 100ms
  act(() => {
    jest.advanceTimersByTime(100);
  });
  expect(onSelect).not.toHaveBeenCalled();

  // và chạy đến lúc 5 giây
  act(() => {
    jest.advanceTimersByTime(5000);
  });
  expect(onSelect).toHaveBeenCalledWith(null);
});

it("should cleanup on being removed", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });

  act(() => {
    jest.advanceTimersByTime(100);
  });
  expect(onSelect).not.toHaveBeenCalled();

  // unmount app
  act(() => {
    render(null, container);
  });

  act(() => {
    jest.advanceTimersByTime(5000);
  });
  expect(onSelect).not.toHaveBeenCalled();
});

it("should accept selections", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });

  act(() => {
    container
      .querySelector("[data-testid='2']")
      .dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });

  expect(onSelect).toHaveBeenCalledWith(2);
});

Bạn có thể giả lập thời gian trong một test. Ở trên, chúng ta bật lên bằng cách gọi jest.useFakeTimers(). Ưu điểm chính của chúng cho ta là test không cần thực sự đợi đến 5 giây để chạy, và bạn cũng không cần thay đổi component để phục vụ việc test.


Snapshot Test {#snapshot-testing}

Framework như Jest cho chúng ta lưu “ảnh” với toMatchSnapshot / toMatchInlineSnapshot. Với chúng, bạn có thể “lưu” một kết quả render và đảm bảo một thay đổi có thể làm thay đổi của kết quả snapshot.

Trong ví dụ, chúng ta render một component và định dạng HTML đã render với thư viện pretty, trước khi lưu nó như một snapshot inline:

// hello.test.js, again

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import pretty from "pretty";

import Hello from "./hello";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should render a greeting", () => {
  act(() => {
    render(<Hello />, container);
  });

  expect(
    pretty(container.innerHTML)
  ).toMatchInlineSnapshot(); /* ... được tự động điền bởi jest ... */

  act(() => {
    render(<Hello name="Jenny" />, container);
  });

  expect(
    pretty(container.innerHTML)
  ).toMatchInlineSnapshot(); /* ... được tự động điền bởi jest ... */

  act(() => {
    render(<Hello name="Margaret" />, container);
  });

  expect(
    pretty(container.innerHTML)
  ).toMatchInlineSnapshot(); /* ... được tự động điền bởi jest ... */
});

Thường sẽ tốt hơn nếu chỉ rõ kết quả muốn nhận được thay vì snapshot. Những kiểu test này bao gồm phần hiện thực chi tiết để chúng dễ dàng bị fail. Chọn giả lập một vài component con có thể giúp giảm kích thước snapshot và giữ chúng dễ độc lúc review code.


Multiple Renderer {#multiple-renderers}

Trong những tình huống hiếm, bạn có thể chạy một test trên một component sử dụng multiple renderer. Lấy ví dụ, bạn có thể chạy snapshot test trên một component với react-test-renderer, bên trong đó nó dùng ReactDOM.rendertrong một child component để render một vài nội dung. Tình huống đó, bạn có thể wrap phần cập nhập với act() ứng với từng renderer

import { act as domAct } from "react-dom/test-utils";
import { act as testAct, create } from "react-test-renderer";
// ...
let root;
domAct(() => {
  testAct(() => {
    root = create(<App />);
  });
});
expect(root).toMatchSnapshot();

 

Còn thiếu gì đó? {#something-missing}

Nếu các tình huống hay gặp không được đề cập ở đây, có thể liên hệ với chúng tôi qua issue tracker cho toàn bộ tài liệu của website

TopDev via Vuilaptrinh

Xem thêm các việc làm it hấp dẫn tại đây

Realtime API trong 5 phút với FeathersJS

Tác giả: Thành Nguyễn

Lời mở đầu

Sau một thời gian sử dụng Firebase và gặp phải một số hạn chế nhất định (xin phép không được đề cập) thì mình quyết định ra đi tìm đường cứu dự án.

Nhu cầu tại thời điểm đó của mình trước hết là phải đáp ứng được realtime data, dễ tiếp cận cho dev, khả năng custom cao để dễ phát triển thêm tính năng và đặc biệt nếu có hỗ trợ TypeScript thì đó sẽ là một điểm cộng.

Sau một thời gian tìm hiểu thì mình quyết định ra khơi với con tàu FeathersJS.

FeathersJS có gì hot?

Feathers key image

FeathersJS là một framework xây dựng theo hướng service-oriented. Các đặc trưng riêng của FeathersJS các bạn có thể đọc thêm tại đây. Dưới đây là một số cảm nhận của mình sau vài tuần vọc về nó:

  • Document tốt, giải thích kỹ và có liên kết rất rõ ràng
  • Open source nên việc custom lại framework là hoàn toàn có thể (nếu bạn đủ khả năng và thời gian)
  • Hỗ trợ thư viện cho cả frontend và backend để giúp việc kết nối dễ dàng hơn (bạn vẫn có thể kết nối bằng tay nếu muốn, có document cho việc này ngay tại trang chủ)
  • Hướng tiếp cận vấn đề theo service khá thú vị, vừa giúp dev tập trung giải quyết về business logic vừa hỗ trợ quá trình scale về sau (tách thành microservice chẳng hạn)
  • CÓ TYPESCRIPT
  • Có Command-Line Interface (CLI) để generate code (lười gõ như mình RẤT THÍCH điểm này)
  • Có nhiều plugins hay và hữu ích. Xem thêm tại đây

Mình tuy là dân frontend nhưng khi làm và phát triển ứng dụng với FeathersJS thấy khá thoải mái, không phải biết quá nhiều về cách tổ chức code, mô hình/kiến trúc, … của backend project vì đã được chuẩn hóa khi tạo project (tất nhiên mình vẫn phải nắm cấu trúc của dự án được generate ra nhưng tính ra vẫn dễ hơn việc bắt đầu từ số 0).

Một số thứ cần biết về FeathersJS

Services

Service Desk - Welcome Icons chart list email manage users chat message atlassian icon illustration

Như mình đã đề cập ở phía trên, FeathersJS xây dựng theo hướng service-oriented. Vậy nên services chính là trái tim của ứng dụng. Đặc điểm của services trong FeathersJS:

  • Là một object hoặc instance của một class
  • Protocol independent – tức không phụ thuộc vào phương thức bạn gọi service (có thể là REST APIwebsocket, gọi internal (dưới dạng Service.method())
  • Các methods của service bao gồm: findgetcreateupdatepatch và remove
  • Chủ yếu xử lý về truy suất và lưu trữ data xuống database

Khi tạo service thì bạn cần có ít nhất một trong những method được nêu. Ngoài các method kể trên thì FeathersJS không hỗ trợ method nào khác. Nếu bạn muốn thì bạn cần chuyển nhu cầu phát sinh thành một service mới hoặc một hook cho service hiện tại.

Lưu ý: Một số bạn có thể thắc mắc điểm khác nhau giữa patch và update. Mình xin giải thích thêm:

  • patch: cập nhật data bằng cách merge giữa data cũ và data mới
  • update: cập nhật data bằng cách replace data cũ bằng data mới
# Ví dụ để các bạn dễ hình dung hơn

User: {
  id: 69,
  first_name: 'Thanh',
  last_name: 'Nguyen',
  age: 17
}

User.patch(69, { age: 18 })
// => User: { id: 69, first_name: 'Thanh', last_name: 'Nguyen', age: 18 }

User.update(69, { age: 18})
// => User: { id: 69, age: 18 }

Để gọi một service khác trong service hiện tại thì bạn có thể dùng app.service(otherServiceName).

Chi tiết cách override các method của service các bạn tham khảo thêm tại đây.

Hooks

Teamwork ui manage task process workflow character work team illustration vector flat kit8

Hook là một phần quan trọng khác trong ứng dụng FeathersJS. Đặc trưng của hook

  • Xử lý dữ liệu trước, sau hoặc lỗi khi lưu trữ xuống database (validate dữ liệu, phân quyền user, ghi log hoặc trigger các tác vụ như gửi email, gửi tin nhắn, gửi notification, …)
  • Có 3 loại hooksbeforeafter và errorHook có thể được gắn vào bất kì service nào hoặc gắn global vào app (chạy cho mọi service, thường là dạng ghi log)

Nếu các bạn đã từng làm hoặc tiếp xúc với Express.js thì chắc hẳn các bạn đã nghe qua khái niệm middleware. Hook chính là một middleware cho app hoặc service.

Nếu các bạn chưa từng nghe về middleware thì các bạn có thể hiểu nó là một bước trong dây chuyền xử lý. Dưới đây là một ví dụ để các bạn dễ hình dung hơn:

# Service của chúng ta sẽ là nấu cơm
# Quá trình nấu cơm như sau:
// đong gạo => vo gạo => nấu => mời cơm/ăn

# Các công việc "đong gạo", "vo gạo", "mời cơm/ăn" sẽ là hook, cụ thể:

- before hooks: đong gạo & vo gạo
- after hooks: mời cơm/ăn
- error hooks: xử lý khi cơm bị nhão hoặc bị cháy

# Nếu khai báo theo code thì chúng ta sẽ có dạng như sau:

let CookRiceService = { ... }

CookRiceService.hooks = {
  before: [measure(), clean()],
  after: [inviteOrEat()],
  error: [hideInTheCornerBecauseOfShame()]
}

Tất cả các hook sẽ chạy theo thứ tự như sau:

# Normal flow

app.beforeHook -> app.method -> service.beforeHook -> service.method -> service.afterHook -> app.afterHook

# Error flow, skip mọi bước sau bước sinh ra lỗi và chạy
service.errorHook -> app.errorHook

Để gọi service trong hooks thì bạn có 2 cách:

  • Gọi service hiện tại hook đang chạy: context.service
  • Gọi service khác: context.app.service(otherServiceName)
  • (context) sẽ được truyền vào khi khai báo hook với một service nhất định

Chi tiết các bạn đọc thêm tại đây

CLI

Cú pháp CLI của FeathersJS

CLI hay còn gọi là command-line interface là công cụ hỗ trợ chúng ta trong quá trình phát triển ứng dụng . FeathersJS CLI chủ yếu hỗ trợ chúng ta generate code để tiết kiệm thời gian gõ hoặc tránh các lỗi typing khi tạo bằng tay (sai tên file, tên biến, nhận thiếu/dư params, …).

Phần này chủ yếu hỗ trợ quá trình phát triển, không có gì để nói nên mình đi vào demo luôn nhé!

Demo

Preview demo

Demo | Github

Do lười không có thời gian nên demo lần này mình làm khá đơn giản, chủ yếu là để các bạn làm quen với FeathersJS. Nếu có dịp thì mình sẽ đi sâu hơn về nó.

Ứng dụng bao gồm các tính năng:

  • Đăng ký
  • Đăng nhập
  • Danh sách user được cập nhật realtime

Công nghệ sử dụng bao gồm:

  • Backend: FeathersJS (duh)
  • Frontend: VueJS qua CDN. Nếu là dự án thật thì các bạn nên chia làm 2 repo để dễ quản lý và phát triển hơn

Các thành phần liên quan tới FeathersJS:

  • Services: authentication(cái này generate sẵn, sử dụng email + password), users(chứa dữ liệu người dùng)
  • Hooks: validate-user (kiểm tra email + password), init-user (khởi tạo một số dữ liệu thêm), send-email-verification(gửi email sau khi user đăng ký, cái này để vậy thôi chứ chưa có thời gian implement, chủ yếu là demo after hook)

Các bước thực hiện:

# Tạo thư mục project
mkdir vct-11-feathersjs

# Generate khung project bằng CLI
feathers generate app

# Tạo hook bằng CLI
feathers generate hook

Đánh giá

Hiện tại mình đang sử dụng FeathersJS (cùng TypeScript) để phát triển một số ứng dụng trong công ty. Tuy có gặp một số khó khăn trong lúc phát triển nhưng nhìn chung so với việc sử dụng Firebase (Spark Plan) thì mình thấy FeathersJS ổn hơn và “tự do” hơn rất nhiều (đặc biệt là việc query dữ liệu và các hooks khi xử lý data). Và nếu thích thì các bạn vẫn có thể sử dụng Firebase ngay trong FeathersJS mà không gặp phải một vấn đề nào cả.

Dưới đây là một số đánh giá chung của mình:

Ưu điểm:

  • Service-oriented
  • Hỗ trợ TypeScript
  • CLI, documents, libraries, plugins hỗ trợ hữu ích. Cho phép customize nhiều thứ mặc dù là một framework
  • Dễ dùng, dễ tiếp cận

Nhược điểm:

  • Còn khá mới
  • Document còn một số chỗ thiếu sót (do mới upgrade từ v3 lên v4)
  • Cần cân nhắc khi xử lý vấn đề, khi nào nên tạo services và khi nào nên tạo hooks
  • Typing cho TypeScript chưa tốt nên sẽ phát sinh một vài vấn đề trong lúc customize service
  • Vẫn là monolithic application tuy nhiên có thể phát triển theo hướng microservice với các plugins hỗ trợ

Hi vọng các bạn sẽ có cái nhìn rõ hơn về FeathersJS và có thể áp dụng nó vào những project trong tương lai. Nếu có bất kì thắc mắc, góp ý nào thì cứ comment phía dưới.

TopDev via Devnow

Định dạng ngày tháng bằng Intl.DateTimeFormat

Trong javascript, hiệp hội quốc tế ECMAScript công bố một object tên là Intl, trong đó có chứa tất cả các phương thức liên quan tới bản địa hóa (ngôn ngữ, ngày tháng, định dạng tiền tệ, vâng vâng). Cùng lướt qua API Intl.DateTimeFormat được cung cấp

Một chuỗi chuẩn ISO cho ngày tháng

const date = new Date().toISOString();

// "2020-02-05T16:30:41.392Z"

Được sử dụng trong thẻ HTML

<time datetime="2020-02-05T16:30:41.392Z">Published on ...</time>
<meta property="article:published_time" content="2020-02-05T16:30:41.392Z">

Các công cụ tìm kiếm sẽ đọc các giá trị này một cách rất dễ dàng. Nhưng nếu là người bình thường chúng ta không thích như vậy, chuyển đổi qua lại giữa các định dạng theo từng khu vực là điều bắt buộc.

<time datetime="2020-02-05T16:30:41.392Z">Pubblicato il 05 Febbraio 2020</time>
<time datetime="2020-02-05T16:30:41.392Z">Published on February 05, 2020</time>

Sử dụng API để hiển thị cho dân US-UK

const options = { month: "long", day: "numeric", year: "numeric" };
const date = new Date(isoString);
const americanDate = new Intl.DateTimeFormat("en-US", options).format(date);

Định dạng ngày tháng bằng Intl.DateTimeFormat

Để xem tài liệu đầy đủ, xem trên MDN

Bạn sẽ thấy thằng Intl.DateTimeFormat sẽ cho kết quả tương tự như toLocaleDateString

const options = { month: "long", day: "numeric", year: "numeric" };
const longDate = new Date().toLocaleDateString(locale, options);

Vậy xài cái nào?, ngắn gọn, Intl.DateTimeFormat là lựa chọn sáng suốt xét trên phương diện tốc độ

Cũng đáng nhắc đến, format() sẽ trả về ngày hiện tại nếu không truyền tham số cho nó.

new Date() hiển thị kết quả (không phải trả về) như thế này “Wed Feb 05 2020 18:46:03 GMT+0100 (Central European Standard Time)” khi gọi trong trình duyệt, cùng constructor đó gọi trong Node.js sẽ hiển thị (không phải trả về) chuỗi ISO “2020-02-05T17:47:03.065Z”

TopDev via Vuilaptrinh

 

Dev Java đã biết đến 20 thư viện này chưa? (P2)

Chào các bạn, mình đã quay trở lại và mang theo 10 thư viện còn thiếu trong bài trước, chúng ta cùng tìm hiểu nó nhé.

11. Thư viện PDF

Cũng giống như Microsoft Excel, thư viện PDF cũng là một định dạng phổ biến. Nếu bạn cần hỗ trợ những chức năng PDF trong ứng dụng như việc xuất dữ liệu dưới dạng PDF, bạn có thể sử dụng iText và các thư viện Apache FOP.

Dev Java đã biết đến 20 thư viện này chưa

Cả hai thư viện này đều cung cấp những chức năng liên quan đến PDF, tuy nhiên iText sẽ phong phú và dùng tốt hơn. Chúng ta hãy thử tìm hiểu thêm về iText qua hình ảnh dưới đây:

12. Date and Time Libraries

Trước Java 8, thư viện Date and Time của JDK có khá nhiều thiếu sót bởi dễ bị lỗi, không thread-safe và immutable. Một số Java Dev đã phải dựa trên JodaTime để thực hiện những yêu cầu về date and time của họ.

Dev Java đã biết đến 20 thư viện này chưa

Sự xuất hiện của JDK 8 đã loại bỏ những nguyên nhân sử dụng Joda bởi bạn đã nắm trong tay tất cả các tính năng của date and time API mới trong JDK. Tuy nhiên, nếu bạn vẫn đang làm việc trên phiên bản Java cũ thì Joda lại chính là một thư viện kiến thức rất giá trị.

13. Collection Libraries

Mặc dù JDK ó một thư viện tập hợp phong phú và đồ sộ, vẫn còn những thư viện khác có thể đưa ra nhiều sự lựa chọn khác như tập hợp Apache Commons, Goldman Sachs, Google và Trove.

Trong những thư viện trên thì Trove được sử dụng phổ biến nhất bởi tốc độ cao nhưng vẫn đảm bảo được tính ổn định và tập hợp sơ khai cho Java.

Dev Java đã biết đến 20 thư viện này chưa

Thư viện nhúng SQL tốt nhất cho các Dev Java

FastUtil là một API tương tự. Nó cho phép mở rộng Java Collections Framework bằng cách cung cấp những bản đồ, tập hợp, danh sách riêng biệt. Không chỉ vậy, tốc độ truy cập và chèn rất nhanh, nó cũng cung cấp arrays 64-bits, các tập hợp, danh sách, thực hiện I/O classes cho các file nhị phân và văn bản.

Xem tin tuyển dụng Java mới nhất trên TopDev

14. Email APIs

Javax.mail và Apache Commons Email đều cung cấp một API để thực hiện việc gửi email từ Java. Để đơn giản hóa, nó được xây dựng trên đầu của JavaMail API.

Dev Java đã biết đến 20 thư viện này chưa

15. Thư viện phân tích cú pháp HTML

Cũng giống như với JSON và XML, HTML cũng là một định dạng phổ biến mà nhiều người trong chúng ta đã từng mất nhiều thời gian để nghiên cứu trước khi sử dụng. May thay, chúng ta đã có JSoup, thứ có thể đơn giản hóa các công việc với HTML trong ứng dụng Java. Bạn không chỉ sử dụng JSoup để phân tích cú pháp của HTML mà còn có thể tạo ra những tài liệu HTML.

Dev Java đã biết đến 20 thư viện này chưa

Thư viện phân tích cú pháp HTML tốt nhất cho Dev Java

JSoup cung cấp một API rất thuận tiện cho việc giải nén cũng như thao tác dữ liệu, nó còn sử dụng những thứ tốt nhất của DOM và phương pháp jquery-like. JSoup thực hiện kỹ thuật WHATWG HTML5 và phân tích cú pháp HTML với DOM tương tự như một trình duyệt hiện đại vẫn làm.

16. Thư viện mã hóa

Gói Apache Commons Codec bao gồm những mã hóa đơn giản đồng thời cũng giải mã cho rất nhiều định dạng khác nhau như Base64 và Hexadecimal.

Hơn nữa, chúng còn sử dụng rộng rãi các bộ mã hóa và giải mã, gói codec cũng có thể duy trì một tập hợp các tiện ích mã hóa âm.

Dev Java đã biết đến 20 thư viện này chưa

17. Embedded SQL Database Library

Tôi thật sự rất yêu thích cơ sở dữ liệu trên bộ nhớ như H2, bạn có thể nhúng nó trong ứng dụng Java của chính mình. Nó thực sự rất tuyệt trong việc thử nghiệm kịch bản SQL của bạn cũng như chạy thử nghiệm đơn vị cần một cơ sở dữ liệu. Tuy nhiên, H2 không chỉ là DB, bạn cũng có thể lựa chọn Apache Derby và HSQL.

Dev Java đã biết đến 20 thư viện này chưa

18. JDBC Troubleshooting Libraries

Có một vài thư viện mở rộng JDBC tốt vẫn còn tồn tại và có thể gỡ rối một cách dễ dàng hơn như P6spy.

Sử dụng JDBC, bạn có thể thực hiện nhiều tác vụ đa dạng khi làm việc với cơ sở dữ liệu như tạo, xóa cơ sở dữ liệu; tạo và thực thi các lệnh SQL hoặc MySQL; tạo, xóa các bản ghi; …

Dev Java đã biết đến 20 thư viện này chưa

19. Serialization Libraries

Dev Java đã biết đến 20 thư viện này chưa

Protocol Buffer được sử dụng để serialize dữ liệu thành dạng byte stream (tương tự Thrift của facebook và Microsoft Bond protocols). Protobuf giải quyết vấn đề của serialize trong Java đó là dữ liệu được serialize chỉ đọc được bởi Java. Dữ liệu được serialize bởi protobuf có thể được deserialize bằng các ngôn ngữ khác nhau.

20. Networking Libraries

Dev Java đã biết đến 20 thư viện này chưa

Ngoài ra vẫn còn những networking libraries hữu ích như Netty và Apache MINA, chúng có thể giúp bạn có thể làm một ứng dụng yêu cầu kết nối mạng cấp thấp. Nếu các bạn muốn tìm hiểu thêm về lập trình mạng trong Java, hãy kiểm tra Java Network Programming – TCP/IP Socket Programming.

TopDev via Codehub

Tham khảo thêm các vị trí tuyển ngành CNTT tại đây

Code PHP làm sao cho sạch (Phần 2)

Xem lại Phần 1 Cách Code PHP làm sao cho sạch tại đây.

Tránh kiểm tra kiểu dữ liệu (phần 1)

PHP là một ngôn ngữ không ràng buộc kiểu dữ liệu, nghĩa hàm có thể nhận bất kỳ kiểu nào. Thỉnh thoảng thì chúng ta bị ảnh hưởng bởi sự tự do này và nó trở thành điều kiện để phải kiểm tra kiểu dữ liệu trong hàm. Có nhiều cách để tránh phải làm việc đó.

Điều đầu tiên cần làm là tạo ra những API nhất quán.

Chưa tốt:

function travelToTexas($vehicle): void
{
    if ($vehicle instanceof Bicycle) {
        $vehicle->pedalTo(new Location('texas'));
    } elseif ($vehicle instanceof Car) {
        $vehicle->driveTo(new Location('texas'));
    }
}

Tốt:

function travelToTexas(Traveler $vehicle): void
{
    $vehicle->travelTo(new Location('texas'));
}

Tránh kiểm tra kiểu dữ liệu (phần 2)

Nếu bạn đang làm việc với các kiểu dữ liệu nguyên thủy như strings, integers, và arrays, và sử dụng PHP 7+ và bạn không thể sử dụng tính đa hình nhưng bạn vẫn cảm thấy cần kiểm tra kiểu dữ liệu, hãy xem type declaration hoặc strict mode. Nó cung cấp cho bạn kiểu static trên PHP standard. Vấn đề thông thường khi kiểm tra kiểu dữ liệu là sẽ khiến code khó đọc nên tóm lại mất nhiều hơn là được.

Hãy giữ PHP nguyên thủy, viết tests cho tốt, và code reviews cẩn thận là được. Nếu không thì chỉ còn cách định nghĩa theo kiểu nghiêm ngặt(strict type declaration) hoặc dùng strict mode.

Chưa tốt:

function combine($val1, $val2): int
{
    if (!is_numeric($val1) || !is_numeric($val2)) {
        throw new \Exception('Must be of type Number');
    }

    return $val1 + $val2;
}

Tốt:

function combine(int $val1, int $val2): int
{
    return $val1 + $val2;
}

Xem tin tuyển lập trình viên PHP đãi ngộ tốt trên TopDev

Xóa dead code

Dead code thì cũng củ chuối giống như duplicate code. Không có lý do gì để giữ chúng. Nếu đoạn code nào đó không được gọi, hãy xóa đi! Sau này cần thì chỉ cần tìm lại phiên bản trước bằng git là được.

Chưa tốt:

function oldRequestModule(string $url): void
{
    // ...
}

function newRequestModule(string $url): void
{
    // ...
}

$request = newRequestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');

Tốt:

function requestModule(string $url): void
{
    // ...
}

$request = requestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');

Đối tượng và kiến trúc dữ liệu

Sử dụng đối tượng đóng gói

Trong PHP bạn có thể khai publicprotected và private cho phương thức và thuộc tính. Hãy sử dụng chúng để kiểm soát được sự thay đổi thuộc tính trong object.

  • Khi bạn muốn nhiều hơn là chỉ nhận được thuộc tính của object, thì ưu điểm là ta không cần phải tìm kiếm và thay đổi quyền mỗi khi truy cập vào object.
  • Giúp tạo validation đơn giản mỗi khi thực hiện set.
  • Đóng gói các thành phần bên trong.
  • Dễ dàng ghi log và xử lý lỗi khi get và set.
  • Kế thừa lớp, bạn có thể ghi đè những phương thức mặc định.
  • Bạn có thể lazy load các thuộc tính của object, giả sử nó được lấy từ máy chủ chẳng hạn.

Thêm vào đó, đây là một phần của nguyên tắc Open/Closed.

Chưa tốt:

class BankAccount
{
    public $balance = 1000;
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->balance -= 100;

Tốt:

class BankAccount
{
    private $balance;

    public function __construct(int $balance = 1000)
    {
      $this->balance = $balance;
    }

    public function withdraw(int $amount): void
    {
        if ($amount > $this->balance) {
            throw new \Exception('Amount greater than available balance.');
        }

        $this->balance -= $amount;
    }

    public function deposit(int $amount): void
    {
        $this->balance += $amount;
    }

    public function getBalance(): int
    {
        return $this->balance;
    }
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->withdraw($shoesPrice);

// Get balance
$balance = $bankAccount->getBalance();

Tạo đối tượng có chứa thuộc tính hoặc phương thức private/protected

    • Phương thức và thuộc tính public khá nguy hiểm, bởi vì vài dòng code phía bên ngoài có thể dễ dàng thay đổi chúng và bạn không thể kiểm soát được nó bị thay đổi những gì. Thay đổi trong một lớp thì nguy hiểm cho tất cả các người dùng của lớp đó.
    • protected cũng nguy hiểm không kém, bởi vì chúng được cấp quyền ở tất cả các lớp con. Điều này có nghĩa là sự khác nhau giữa public và protected chỉ là cơ chế truy cập, nhưng tính đóng gói đảm bảo vẫn giữ nguyên. Sửa đổi trong lớp thì rất nguy hiểm cho các lớp con.
  • private sửa đổi đảm bảo rằng code sửa đổi chỉ nguy hiểm trong lớp đó (bạn sẽ được an toàn khi sửa và không có hiệu ứng Jenga).

Do đó, hãy mặc định sử dụng private và public/protected khi bạn cần cung cấp sự truy cập cho các class bên ngoài.

Đọc thêm tại blog post được viết bởi Fabien Potencier.

Chưa tốt:

class Employee
{
    public $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->name; // Employee name: John Doe

Tốt:

class Employee
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->getName(); // Employee name: John Doe

Tuyển PHP fresher đãi ngộ tốt, ứng tuyển ngay!

Lớp

Ưu tiên thành phần hơn kế thừa

Như đã nói trong Design Patterns nổi tiếng của Gang of Four, bạn nên ưu tiên sử dụng “kiểu thành phần” hơn là “kiểu kế thừa”. Có nhiều lý do để sử dụng kiểu kế thừa và cũng có nhiều nguyên nhân để sử dụng kiểu thành phần. Điểm chính của sự tối đa hóa này là nếu bạn thích theo kiểu kế thừa, hãy thử suy nghĩ “kiểu thành phần” có thể giúp giải quyết vấn đề tốt hơn không. Vì có một vài trường hợp nó sẽ tốt hơn.

Có thể bạn sẽ tự hỏi, “khi nào thì nên dùng kế thừa?” Nó tùy thuộc vào từng vấn đề, khi nào thì kiểu kế thừa tốt hơn kiểu thành phần:

  1. Kiểu kế thừa đó đại diện cho một mối quan hệ “is-a” (Ví dụ: Người->Động vật) chứ không phải mối quan hệ “has-a” (Người dùng->Thông tin người dùng).
  2. Bạn cần sử dụng lại code từ lớp cha (Người có thể di chuyển như động vật).
  3. Bạn muốn khi sửa đổi lớp cha thì tất cả lớp có liên quan sẽ thay đổi dễ dàng. (Thay đổi lượng calo của tất cả động vật khi chúng di chuyển).

Chưa tốt:

class Employee 
{
    private $name;
    private $email;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }

    // ...
}

// Chưa tốt vì Employees "có" thuế. 
// EmployeeTaxData không phải là một loại của Employee

class EmployeeTaxData extends Employee 
{
    private $ssn;
    private $salary;
    
    public function __construct(string $name, string $email, string $ssn, string $salary)
    {
        parent::__construct($name, $email);

        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}

Tốt:

class EmployeeTaxData 
{
    private $ssn;
    private $salary;

    public function __construct(string $ssn, string $salary)
    {
        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}

class Employee 
{
    private $name;
    private $email;
    private $taxData;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }

    public function setTaxData(string $ssn, string $salary)
    {
        $this->taxData = new EmployeeTaxData($ssn, $salary);
    }

    // ...
}

Tránh viết fluent interfaces

Fluent interface là một API hướng đối tượng có mục đích cải thiện tính dễ đọc của source code bằng cách sử dụng Method chaining.

Trong một số ngữ cảnh, thường là khi xây dựng object nơi mà pattern này giảm tính rườm rà của code (ví dụ PHPUnit Mock Builder hoặc Doctrine Query Builder), sẽ gây ra một số thiệt hại như sau:

  1. Phá vỡ Encapsulation
  2. Phá vỡ Decorators
  3. Khó tạo mock hơn trong test suite
  4. Khiến cho khó đọc “sự khác nhau giữa các file” hơn khi commit code

Để biết thêm thông tin chi tiết, vui lòng đọc bài viết được viết bởi Marco Pivetta.

Chưa tốt:

class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';

    public function setMake(string $make): self
    {
        $this->make = $make;

        // NOTE: Returning this for chaining
        return $this;
    }

    public function setModel(string $model): self
    {
        $this->model = $model;

        // NOTE: Returning this for chaining
        return $this;
    }

    public function setColor(string $color): self
    {
        $this->color = $color;

        // NOTE: Returning this for chaining
        return $this;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}

$car = (new Car())
  ->setColor('pink')
  ->setMake('Ford')
  ->setModel('F-150')
  ->dump();

Tốt:

class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';

    public function setMake(string $make): void
    {
        $this->make = $make;
    }

    public function setModel(string $model): void
    {
        $this->model = $model;
    }

    public function setColor(string $color): void
    {
        $this->color = $color;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}

$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();

SOLID

SOLID là từ viết tắt được đưa ra bởi Michael Feathers cho 5 nguyên lý đầu tiên của Robert Martin, 5 nguyên tắc cơ bản của lập trình hướng đối tượng.

  • S: Nguyên lý trách nhiệm duy nhất (SRP)
  • O: Nguyên lý Đóng/Mở (OCP)
  • L: Nguyên lý thay thế Liskov (LSP)
  • I: Nguyên lý phân tách interface (ISP)
  • D: Nguyên lý đảo ngược dependencies (DIP)

Nguyên lý trách nhiệm duy nhất (SRP)

Như đã đề cập trong cuốn Clean Code, “Không nên có nhiều hơn một lý do để thay đổi class”. Viết một class với thật nhiều chức năng thì quá sướng. Vấn đề là class không có khái niệm liên kết và nó có khá nhiều lý do để thay đổi. Nếu quá nhiều chức năng trong một class thì khi thay đổi gì đó mình không biết được hết những ảnh hưởng của nó đến các chức năng khác trong các module liên quan.

Chưa tốt:

class UserSettings
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function changeSettings(array $settings): void
    {
        if ($this->verifyCredentials()) {
            // ...
        }
    }

    private function verifyCredentials(): bool
    {
        // ...
    }
}

Tốt:

class UserAuth 
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }
    
    public function verifyCredentials(): bool
    {
        // ...
    }
}

class UserSettings 
{
    private $user;
    private $auth;

    public function __construct(User $user) 
    {
        $this->user = $user;
        $this->auth = new UserAuth($user);
    }

    public function changeSettings(array $settings): void
    {
        if ($this->auth->verifyCredentials()) {
            // ...
        }
    }
}

Nguyên lý Đóng/Mở (OCP)

Như đã đề cập bởi Bertrand Meyer, “thực thể phần mềm (lớp, modules, hàm, etc…) nên cho phép mở rộng, nhưng không cho phép sửa đổi.” Điều đó có nghĩa là gì? Nguyên lý này đơn giản là nên cho phép người dùng thêm mới mà không được thay đổi code hiện tại.

Chưa tốt:

abstract class Adapter
{
    protected $name;

    public function getName(): string
    {
        return $this->name;
    }
}

class AjaxAdapter extends Adapter
{
    public function __construct()
    {
        parent::__construct();

        $this->name = 'ajaxAdapter';
    }
}

class NodeAdapter extends Adapter
{
    public function __construct()
    {
        parent::__construct();

        $this->name = 'nodeAdapter';
    }
}

class HttpRequester
{
    private $adapter;

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
    }

    public function fetch(string $url): Promise
    {
        $adapterName = $this->adapter->getName();

        if ($adapterName === 'ajaxAdapter') {
            return $this->makeAjaxCall($url);
        } elseif ($adapterName === 'httpNodeAdapter') {
            return $this->makeHttpCall($url);
        }
    }

    private function makeAjaxCall(string $url): Promise
    {
        // request and return promise
    }

    private function makeHttpCall(string $url): Promise
    {
        // request and return promise
    }
}

Tốt:

interface Adapter
{
    public function request(string $url): Promise;
}

class AjaxAdapter implements Adapter
{
    public function request(string $url): Promise
    {
        // request and return promise
    }
}

class NodeAdapter implements Adapter
{
    public function request(string $url): Promise
    {
        // request and return promise
    }
}

class HttpRequester
{
    private $adapter;

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
    }

    public function fetch(string $url): Promise
    {
        return $this->adapter->request($url);
    }
}

Nguyên lý thay thế Liskov (LSP)

Nguyên lý này được định nghĩa như sau “Nếu S là phụ thuộc của T, thì object của T có thể được thay thế bởi object của S (nghĩa là object của S có thể thay thế object của T) mà không làm thay đổi các thuộc tính của chương trình(tính đúng đắn, công việc thực hiện,…)”

Để dễ hiểu hơn, nếu bạn có một class cha và một class con, sau đó class cha và class con có thể được sử dụng hoán đổi cho nhau mà không sai kết quả trả về. Có thể vẫn còn khó hiểu, hãy xem ví dụ cơ bản Square-Rectangle bên dưới.

Trong toán học, hình vuông là hình chữ nhật, nhưng nếu bạn sử dụng quan hệ “is-a” qua kế thừa, bạn sẽ gặp rắc rối.

Chưa tốt:

class Rectangle
{
    protected $width = 0;
    protected $height = 0;

    public function render(int $area): void
    {
        // ...
    }

    public function setWidth(int $width): void
    {
        $this->width = $width;
    }

    public function setHeight(int $height): void
    {
        $this->height = $height;
    }

    public function getArea(): int
    {
        return $this->width * $this->height;
    }
}

class Square extends Rectangle
{
    public function setWidth(int $width): void
    {
        $this->width = $this->height = $width;
    }

    public function setHeight(int $height): void
    {
        $this->width = $this->height = $height;
    }
}

/**
 * @param Rectangle[] $rectangles
 */
function renderLargeRectangles(array $rectangles): void
{
    foreach ($rectangles as $rectangle) {
        $rectangle->setWidth(4);
        $rectangle->setHeight(5);
        $area = $rectangle->getArea(); // Lỗi rồi: Đoạn này sẽ trả về 25, nhưng 20 mới là kết quả đúng.
        $rectangle->render($area);
    }
}

$rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles($rectangles);

Tốt:

abstract class Shape
{
    abstract public function getArea(): int;

    public function render(int $area): void
    {
        // ...
    }
}

class Rectangle extends Shape
{
    private $width;
    private $height;

    public function __construct(int $width, int $height)
    {
        $this->width = $width;
        $this->height = $height;
    }

    public function getArea(): int
    {
        return $this->width * $this->height;
    }
}

class Square extends Shape
{
    private $length;

    public function __construct(int $length)
    {
        $this->length = $length;
    }

    public function getArea(): int
    {
        return pow($this->length, 2);
    }
}

/**
 * @param Rectangle[] $rectangles
 */
function renderLargeRectangles(array $rectangles): void
{
    foreach ($rectangles as $rectangle) {
        $area = $rectangle->getArea(); 
        $rectangle->render($area);
    }
}

$shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
renderLargeRectangles($shapes);

Nguyên lý phân tách interface (ISP)

ISP đề cập rằng “Không nên ép người dùng phải phụ thuộc vào interface mà họ không sử dụng.”

Để hiểu ý nghĩa của nguyên tắc này, hãy nhìn vào những class yêu cầu một số lượng lớn các object cần phải inject vào để sử dụng. Không yêu cầu người dùng phải inject số lượng lớn các tùy chọn là một lợi thế, bởi vì hầu hết chúng không cần thiết. Hãy coi chúng là tùy chọn(có thể không dùng) để giúp cho interface bớt phình to.

Chưa tốt:

interface Employee
{
    public function work(): void;

    public function eat(): void;
}

class Human implements Employee
{
    public function work(): void
    {
        // ....working
    }

    public function eat(): void
    {
        // ...... eating in lunch break
    }
}

class Robot implements Employee
{
    public function work(): void
    {
        //.... working much more
    }

    public function eat(): void
    {
        //.... robot can't eat, but it must implement this method
    }
}

Tốt:

Không phải tất cả worker đều là employee, nhưng employee là một worker.

interface Workable
{
    public function work(): void;
}

interface Feedable
{
    public function eat(): void;
}

interface Employee extends Feedable, Workable
{
}

class Human implements Employee
{
    public function work(): void
    {
        // ....working
    }

    public function eat(): void
    {
        //.... eating in lunch break
    }
}

// robot can only work
class Robot implements Workable
{
    public function work(): void
    {
        // ....working
    }
}

Nguyên lý đảo ngược dependencies (DIP)

Nguyên lý này đề cập 2 vấn đề cơ bản:

  1. Module cấp cao không nên phụ thuộc vào module cấp thấp. Cả hai nên phụ thuộc vào abstract.
  2. Abstract không nên phụ thuộc vào chi tiết, mà phải ngược lại.

Hơi khó hiểu một chút đúng không? Nhưng nếu bạn làm việc với PHP frameworks (ví dụ Symfony), bạn sẽ thấy nguyên tắc này được áp dụng trên Dependency Injection(DI). Một lợi ích lớn của việc này là chúng giảm sự trùng lặp giữa các modules. Trùng lặp thì tất nhiên Chưa tốt vì chúng khiến code khó refactor.

Chưa tốt:

class Employee
{
    public function work(): void
    {
        // ....working
    }
}

class Robot extends Employee
{
    public function work(): void
    {
        //.... working much more
    }
}

class Manager
{
    private $employee;

    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    public function manage(): void
    {
        $this->employee->work();
    }
}

Tốt:

interface Employee
{
    public function work(): void;
}

class Human implements Employee
{
    public function work(): void
    {
        // ....working
    }
}

class Robot implements Employee
{
    public function work(): void
    {
        //.... working much more
    }
}

class Manager
{
    private $employee;

    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    public function manage(): void
    {
        $this->employee->work();
    }
}

Nguyên lý đừng lặp lại chính mình (DRY)

Đọc hiểu về nguyên lý DRY

Tốt nhất nên chống lặp code ngay khi có thể. Vì lặp code không tốt tí nào, khi bạn muốn thay đổi logic bạn cần phải sửa nhiều chỗ.

Hãy tưởng tượng bạn đang vận hành một nhà hàng và bạn theo dõi lượng hàng tồn kho của bạn: cà chua, hành, tỏi, gia vị,… Nếu bạn có nhiều danh sách để quản lý chúng, bạn cần cập nhật tất cả các danh sách đó mỗi khi bạn bán một đĩa thức ăn. Nhưng nếu như bạn chỉ có 1 danh sách, thì chỉ cần cập nhật ở một nơi!

Thỉnh thoảng vẫn có lặp code bởi vì bạn có hai hoặc nhiều hơn những thứ khác nhau, có nhiều điểm chung, nhưng sự khác nhau giữa chúng buộc bạn phải chia ra 2 hàm làm rất nhiều việc. Để xóa bỏ lặp code, cần tạo ra một abstract có thể xử lý sự khác biệt giữa chúng với chỉ 1 hàm/module/lớp.

Tạo ra được abstract tốt rất quan trọng và khó, đó là lý do tại sao bạn nên dựa theo các nguyên lý SOLID được đưa ra tại mục Lớp. Abstract củ chuối có thể sẽ tệ hại hơn là lặp code, hãy cẩn thận! Nếu có thể tạo một abstract tốt, hãy tạo nó! Đừng lặp lại code, nếu không bạn sẽ phải rất cực khổ mỗi khi muốn sửa đổi gì đó.

Chưa tốt:

function showDeveloperList(array $developers): void
{
    foreach ($developers as $developer) {
        $expectedSalary = $developer->calculateExpectedSalary();
        $experience = $developer->getExperience();
        $githubLink = $developer->getGithubLink();
        $data = [
            $expectedSalary,
            $experience,
            $githubLink
        ];

        render($data);
    }
}

function showManagerList(array $managers): void
{
    foreach ($managers as $manager) {
        $expectedSalary = $manager->calculateExpectedSalary();
        $experience = $manager->getExperience();
        $githubLink = $manager->getGithubLink();
        $data = [
            $expectedSalary,
            $experience,
            $githubLink
        ];

        render($data);
    }
}

Tốt:

function showList(array $employees): void
{
    foreach ($employees as $employee) {
        $expectedSalary = $employee->calculateExpectedSalary();
        $experience = $employee->getExperience();
        $githubLink = $employee->getGithubLink();
        $data = [
            $expectedSalary,
            $experience,
            $githubLink
        ];

        render($data);
    }
}

Rất tốt:

Sử dụng một phiên bản gọn hơn

function showList(array $employees): void
{
    foreach ($employees as $employee) {
        render([
            $employee->calculateExpectedSalary(),
            $employee->getExperience(),
            $employee->getGithubLink()
        ]);
    }
}

TopDev via Chungnguyen

Xem thêm việc làm php lương cao tại Topdev.vn

Dev Java đã biết đến 20 thư viện này chưa? (P1)

Một trong những đặc điểm của một dev Java giỏi và có kinh nghiệm là kiến thức sâu rộng về API, bao gồm JDK và các thư viện của bên thứ ba (thư viện ngoài).

Đây là bộ sưu tập của tôi về một số thư viện ngoài hữu ích mà các nhà phát triển Java có thể sử dụng trong ứng dụng của họ để thực hiện nhiều nhiệm vụ hữu ích. Để sử dụng các thư viện này, các nhà phát triển Java nên quen với điều đó, và đây là toàn bộ vấn đề tôi sẽ chia sẻ.

1. Logging LibrariesX

Việc đăng nhập các thư viện là điều cần thiết trong mọi dự án. Chúng là thứ quan trọng nhất đối với các ứng dụng phía máy chủ, vì các logs chỉ được đặt ở nơi bạn có thể thấy những gì đang diễn ra trên ứng dụng của bạn. Mặc dù JDK gửi cùng với thư viện ghi nhật ký riêng của mình, nhưng có các lựa chọn thay thế tốt hơn, ví dụ: Log4j, SLF4j và LogBack.

Thư viện ghi nhật ký Java hàng đầu

Dev Java đã biết đến 20 thư viện này chưa

Top Java logging libraries

Một nhà phát triển Java nên làm quen với những ưu điểm và nhược điểm của thư viện Logging và biết tại sao sử dụng SLF4j thì tốt hơn Log4j đơn giản. Nếu bạn không biết tại sao, tôi khuyên bạn nên đọc bài viết trước của tôi về cùng một chủ đề.

2. JSON Parsing libraries

Trong thế giới dịch vụ web và IoT ngày nay, JSON đã trở thành giao thức truy cập để mang thông tin từ máy khách đến máy chủ. Họ đã thay thế XML và đây là cách được ưu tiên nhất để chuyển thông tin theo cách độc lập với nền tảng.

Thật không may, JDK không có một thư viện JSON. Tuy nhiên, có rất nhiều thư viện ngoài tốt cho phép bạn phân tích cú pháp và tạo các thông điệp JSON, như Jackson và Gson.

Xem tin tuyển dụng Java mới nhất trên TopDev

3. Unit Testing Libraries

Thử nghiệm đơn vị là điều quan trọng nhất để phân biệt một nhà  phát triển trung bình với một nhà phát triển giỏi. Nhưng lý do phổ biến nhất để tránh kiểm thử đơn vị là thiếu kinh nghiệm và kiến thức về các thư viện phổ biến như JUnit, Mockito và PowerMock.

Dev Java đã biết đến 20 thư viện này chưa

Các thư viện kiểm tra đơn vị tốt nhất cho các nhà phát triển Java

Tôi có một mục tiêu vào năm 2018 để cải thiện kiến thức của mình về các bài kiểm tra đơn vị và các thư viện kiểm thử tích hợp, như JUnit 5, Cucumber, Robot framework và một số khác.

4. General Purpose Libraries

Có một số thư viện tốt, có mục đích chung, bên thứ ba có sẵn cho các nhà phát triển Java, như Apache Commons và Google Guava.  Những thư viện này thường xuất hiện trong dự án của mình, bởi vì chúng đơn giản hóa rất nhiều nhiệm vụ.

Như Joshua Bloch đã nói đúng trong Java hiệu quả, không có điểm nào trong việc tái phát minh ra bánh xe. Chúng ta nên sử dụng các thư viện đã thử và thử nghiệm thay vì viết các thói quen riêng của chúng ta mọi lúc và sau đó.

Dev Java đã biết đến 20 thư viện này chưa

Các thư viện phổ biến nhất cho các nhà phát triển Java

5. HTTP Libraries

Một điều tôi không thích về JDK là chúng thiếu tính hỗ trợ cho HTTP. Mặc dù bạn có thể tạo kết nối HTTP bằng cách sử dụng các lớp trong java.netpackage, nó không dễ dàng hoặc liền mạch để sử dụng các thư viện nguồn mở, bên thứ ba như Apache HttpClient và HttpCore.

Mặc dù JDK 9 đang hỗ trợ HTTP 2.0 và hỗ trợ tốt hơn cho HTTP, tôi đề nghị tất cả các nhà phát triển Java làm quen với các thư viện máy khách HTTP phổ biến, bao gồm HttpClient và HttpCore.

Dev Java đã biết đến 20 thư viện này chưa

HTTP libraries tốt nhất cho Java Dev

6. XML Parsing Libraries

Có nhiều thư viện Parsing XML bao gồm Xerces, JAXB, JAXP, Dom4j và Xstream. Xerces2 là thế hệ tiếp theo của các trình phân tích cú pháp XML tuân thủ đầy đủ, hiệu năng cao trong “gia đình” Apache Xerces. Phiên bản mới này của Xerces được giới thiệu là Xerces Native Interface (XNI), một framework hoàn chỉnh để xây dựng các thành phần và cấu hình phân tích cú pháp cực kỳ mô-đun và dễ lập trình.

Dev Java đã biết đến 20 thư viện này chưa

Các thư viện phân tích cú pháp XML tốt nhất cho các nhà phát triển Java

Trình phân tích cú pháp Xerces2 của Apache là việc triển khai tham chiếu của XNI, nhưng các thành phần, cấu hình và trình phân tích cú pháp khác có thể được viết bằng giao diện Native Xerces. Dom4j là một khung công tác XML linh hoạt cho các ứng dụng Java.

7. Excel Reading Libraries

Tin hay không – tất cả các ứng dụng trong thế giới thực phải tương tác với Microsoft Office dưới dạng này hay dạng khác. Nhiều ứng dụng cần cung cấp chức năng để xuất dữ liệu trong Excel và nếu bạn phải làm như vậy từ ứng dụng Java của mình, bạn cần API Apache POI.

Thư viện Microsoft tốt nhất cho các nhà phát triển Java

Đây là một thư viện rất phong phú cho phép bạn đọc và viết các tệp XLS từ một chương trình Java. Bạn có thể thấy nó liên kết đến một ví dụ làm việc về việc đọc một tệp Excel trong một ứng dụng core Java.

8. Thư viện Bytecode

Nếu bạn đang viết một framework hoặc thư viện tạo mã hoặc tương tác với bytecode, thì bạn phải cần một thư viện bytecode.

Chúng cho phép bạn đọc và sửa đổi bytecode được tạo ra bởi một ứng dụng. Một số thư viện bytecode phổ biến trong thế giới Java là javassist và Cglib Nodep.

Dev Java đã biết đến 20 thư viện này chưa

Các thư viện thao tác Bytecode tốt nhất cho các nhà phát triển Java

Javassist (JAVA programming ASSISTant) làm cho thao tác Java bytecode  trở nên đơn giản hơn. Nó là lass library để chỉnh sửa bytecode trong Java. ASM là một thư viện chỉnh sửa bytecode hữu ích khác.

9. Database Connection Pool Libraries

Nếu bạn đang tương tác với database từ một ứng dụng Java nhưng không sử dụng pool libraries để kết nối cơ sở dữ liệu thì bạn đã bỏ lỡ điều gì đó.

Vì việc tạo các kết nối cơ sở dữ liệu trong thời gian thực hiện mất thời gian và yêu cầu xử lý chậm hơn, nên luôn sử dụng các thư viện kết nối DB. Một số loại phổ biến là Commons Pool và DBCP.

Trong một ứng dụng web, máy chủ web thường cung cấp các chức năng này, nhưng trong các ứng dụng core Java, bạn cần phải bao gồm các thư viện kết nối này vào đường dẫn lớp của bạn để sử dụng pool kết nối cơ sở dữ liệu.

Nếu bạn muốn tìm hiểu thêm về JDBC và nhóm kết nối trong một ứng dụng web, tôi khuyên bạn hãy xem qua khóa học JSP, Servlet và JDBC cho người mới bắt đầu ở Udemy.

10. Messaging Libraries

Tương tự như đăng nhập và kết nối CSDL, nhắn tin cũng là một tính năng phổ biến của nhiều ứng dụng Java trong thế giới thực.

Java cung cấp JMS (Java Messaging Service) đó không phải là một phần của JDK. Đối với thành phần này, bạn cần phải có một jms.jar riêng biệt.

Tương tự, nếu bạn đang sử dụng các giao thức nhắn tin của bên thứ ba, như Tibco RV, thì bạn cần sử dụng một JAR của bên thứ ba – tibrv.jar – trong ứng dụng classpath của bạn.

Dev Java đã biết đến 20 thư viện này chưa

Trên đây là một nửa số thư viện mình dự định sẽ chia sẻ với các bạn, số thư viện còn lại mình xin phép sẽ chia sẻ trong bài tiếp theo. Tuy nhiên, trong thời gian chờ đợi phần tiếp theo, các Java Dev hãy dành thời gian tham gia một sự kiện hoàn toàn miễn phí của LINE Vietnam để được update những cập nhật mới nhất về Java cũng như các vấn đề xoay quanh Java 9 nhé.

TopDev via Codehub

Tham khảo việc làm Java các công ty HOT nhất tại đây

Kambria Cam Kết Hỗ Trợ Cộng Đồng Lập Trình Viên AI

Nhờ sự hỗ trợ từ các cộng đồng, chính phủ và các doanh nghiệp lớn, Kambria cam kết hỗ trợ cho các lập trình viên Trí tuệ nhân tạo. Với việc coi cộng đồng là trọng tâm của Kambria, chúng tôi đang ngày càng nỗ lực hơn nữa trong việc thúc đẩy phát triển hệ sinh thái công nghệ ở Việt Nam và toàn cầu.

Kambria là nhà tổ chức chính của các sự kiện lớn về công nghệ AI ở Việt Nam như Vietnam AI Grand Challenge, VietAI Summit năm 2018 và 2019. Tại các sự kiện này, Kambria đã hỗ trợ cho các bạn lập trình viên cơ hội nâng cao kiến thức, kết nối và xây dựng đội nhóm.

Cùng tìm hiểu thêm về các sự kiện này và cuộc thi Kambria Code Challenge sắp tới để tham gia thúc đẩy sự phát triển của AI và Robotics nhé.

Vietnam AI Grand Challenge

Năm ngoái, Kambria đã tổ chức chuỗi sự kiện Hackathon mang tên Vietnam AI Grand Challenge (VAGC). Sự kiện được tổ chức trong khuôn khổ Ngày hội Trí Tuệ Nhân Tạo quốc gia (AI4VN) do Bộ Khoa học & Công nghệ tổ chức, Bộ Kế hoạch & Đầu tư, Đại học Khoa học & Công nghệ Hà Nội và báo điện tử VnExpress tổ chức.

Được tổ chức ở 3 thành phố lớn tại Việt Nam là Thành phố Hồ Chí Minh, Đà Nẵng và Hà Nội, VAGC đã thu hút 90 đội dự thi tham gia thi đấu trong 40 giờ của Hackathon để phát triển ứng dụng trợ lý ảo AI trong các lĩnh vực bán lẻ, giáo dục, chăm sóc y tế. Các giải pháp được áp dụng bao gồm trợ lý ảo chatbot tự động, tư vấn sản phẩm cho khách hàng thông qua nhận diện khuôn mặt, trợ lý ảo giúp học Tiếng Anh qua tin tức mỗi ngày.

12 đội tranh tài trong vòng chung kết đại diện cho các đội xuất sắc nhất đã trình bày các dự án AI nổi bật qua đó cho thấy được tài năng của các lập trình viên trẻ Việt Nam trong lĩnh vực công nghệ mới.

Đề chuẩn bị cho vòng chung kết Grand Finale, 12 đội thắng cuộc đại diện cho các khu vực đã tham gia vào chương trình Vườn ươm Kambria – Kambria AI Incubation Program. Các đội nhận được sự tư vấn trực tiếp của hơn 20 chuyên gia về công nghệ AI, các CEO doanh nghiệp lớn và các đơn vị nghiên cứu về AI lớn trên thế giới. Tổng kết cuộc thi VAGC, Kambria đã trao tổng giải thưởng trị giá đến 40,000 đô la. Dưới đây là video tổng kết lại sự kiện, mời các bạn xem:

VietAI Summit 2019

Vào tháng 11/2019, sau tiếng vang của VAGC, Kambria đã hợp tác với VietAI để tổ chức thành công sự kiện VietAI Summit 2019 với chủ đề: “AI for the Future” (Công nghệ Trí Tuệ Nhân Tạo cho tương lai). Hội nghị đã thu hút hơn 500 khách mời tham dự đồng thời có sự góp mặt của nhiều chuyên gia công nghệ, các nhà nghiên cứu về AI trên thế giới, và các lãnh đạo doanh nghiệp trong lĩnh vực AI trong và ngoài nước.

Các diễn giả trong sự kiện VietAI Summit 2019 đến từ nhiều công ty lớn như Google Brain, Toyota Research Institute, Kambria, NVIDIA, VinAI Research, Vinbrain, Deakin University, and Đại học Quốc gia tp. Hồ Chí Minh. Người tham dự đã tìm hiểu về nghiên cứu và ứng dụng AI ở Việt Nam và toàn thế giới.

Các diễn giả tại sự kiện từ trái sang phải: Tiến sĩ Vũ Duy Thức (CEO & Co-founder của VietAI & Kambria), Tiến sĩ James J. Kuffner (CEO của Toyota Research Institute – Advanced Development), Tiến sĩ Lương Minh Thắng (Senior Research Scientist, Google Brain), Tiến sĩ Truyền Trần (Associate Professor at Deakin University), Mr. Hung Truong (CEO of VinBrain), and Dr. Hien Pham (Head of Biomedical Photonics Lab of VNU)

Chuỗi sự kiện Kambria Code Challenge – Quiz 02

Trong năm nay, Kambria sẽ tiếp tục phát triển cộng đồng lập trình viên thông qua cuộc thi Kambria Code Challenge. Đây là chuỗi các Quiz được tổ chức online, tại đó các coder có cơ hội kiểm tra kiến thức về AI của họ, nâng cao kỹ năng lập trình, nhận thưởng và kết nối với các đối tác công nghệ lớn của Kambria cho cơ hội nghề nghiệp sau này.

Quiz 01 đã được tổ chức, nhưng bạn vẫn có thể tham gia ngay bây giờ bằng cách đăng ký & tham gia dự thi Quiz 02 của chúng tôi.

Thời gian tổ chức:

  • Online Quiz 2: 29/2/2020
  • Online Quiz 3: 28/3/2020
  • Online Quiz 4: Vòng chung kết:  25/4/2020
  • Online Hackathon: 5/2020
  • AI Panel Discussion: 6/2020

Chủ đề của Quiz 02 lần này sẽ là Convolutional Neural Networks. Hãy tìm hiểu kỹ về chủ đề này bằng cách sử dụng các tài nguyên có sẵn do Kambria cung cấp ngay trên website nhé.

Đăng ký tham gia Quiz 02 ngay hôm nay:

⏰Thời gian: Thứ bảy, ngày 29/2/2020, 14h00 – 14h45  (giờ Việt Nam)

Đăng ký ngay tại: http://bit.ly/KambriaQuiz02

Chi tiết, vui lòng truy cập: http://bit.ly/KambriaQuiz02Announcement

Tìm việc IT lương cao, đãi ngộ tốt trên TopDev ngay!

Bài học đầu tiên tôi có được từ large-scale app Electron đầu tay của mình

8 tháng trước, tôi bắt đầu phát triển một ứng dụng bằng framework Electron. Để hoàn thành project, tôi phải xây dựng 3 ứng dụng “con” riêng lẻ để chạy trong các môi trường khác nhau. Dưới đây là những bài học tôi học được trong quá trình đó:

Hoàn cảnh

Trước khi tôi đi vào chi tiết, hãy bắt đầu với một số thông tin cơ bản. Vào đầu năm 2019, tôi bắt đầu nộp đơn xin thực tập để có thể tốt nghiệp. Đến tháng 4, tôi đã gửi hàng tá cái CV và nhận được tổng cộng 0 phản hồi. Bạn có thể tưởng tượng, điều này khiến tôi xém trầm cảm khi nghĩ bản thân không đủ tốt cho bất kỳ công việc gì. Nhưng thay vì cho phép cảm xúc chiến thắng, tôi đã chọn sẽ tự chứng minh cho bản thân thấy rằng tôi là người hiểu biết và được việc. Cuối cùng, tôi hiểu rằng tôi thực sự không biết nhiều như tôi nghĩ.

  Kỹ thuật làm app bản đồ, tìm đường và tính năng bắt Pokemon GO

Tôi bắt đầu tìm các dự án cũ của mình để tìm ra thứ gì đó mà tôi có thể scale nó đủ lớn và đủ khó khăn để làm vực dậy ngọn lửa trong tôi. Cuối cùng, tôi đã chọn Binder, lúc đó nó chỉ đơn giản là một ứng dụng web quản lý các tệp Onedrive, Google Drive và Dropbox cùng một lúc. Mục tiêu là tạo ra một dịch vụ sao lưu có thể sánh vai với các dịch vụ trên, trừ mảng chia sẻ file.

Bước đầu của hành trình

Tôi đã lấy một ứng dụng hoàn chỉnh vận hành tốt và chật nó ra từng khúc, biến chúng thành những đoạn mã rẻ tiền, như của bọn tay mơ

Có lẽ tôi đã có thể hoàn thành dự án này nếu tôi không đặt ra một số mục tiêu “hoang đường” ngay từ đầu. Cột mốc đầu tiên tôi đặt ra cho bản thân là hoàn thành phiên bản alpha hoạt động được vào ngày sinh nhật của tôi, giữa tháng Bảy. Tôi ngay lập tức bắt tay vào làm. Đầu tháng 5 tôi đã clone Binder về và bắt đầu “tháo dỡ” nó. Loại bỏ đi nhiều tính năng “vô dụng” với tôi. Tôi đã biến một ứng dụng ngon lành thành một đống code rẻ tiền.

Tôi quyết định phát triển 4 ứng dụng cá nhân. Đầu tiên, một client native trên PC của người dùng. Thứ hai, một API backend để tạo điều kiện cho các request. Thứ ba, một cái cloud để đảm bảo tính toàn vẹn của tất cả dữ liệu được lưu giữ. Cuối cùng, một trang web “tiếp thị” vì mọi sản phẩm đều cần một trang web hào nhoáng.

Bản gốc của “Binder”

Đến phần chính nào

Mọi thứ trước đây đều có rất ít hoặc không liên quan gì đến Electron, chứ đừng nói đến việc phát triển ứng dụng quy mô lớn. Nhưng tôi cần thêm ngữ cảnh để bạn hiểu được tôi đã đúc kết ra những bài học như thế nào. Dưới đây là 5 bài học chính tôi học được:

#1 Hãy chắc chắn rằng bạn sử dụng các cấu trúc frontend/backend truyền thống vì IPC thật sự ngu ngốc

Đã quá nhiều lần tôi phát điên vì cách thức giao tiếp giữa các inter-process trong Electron. Chắc chắn, IPC có thể không được thiết kế để thực hiện javascript-esque trừu tượng, passing-functions-as-objects và whatnot. Nhưng nó chắc chắn sẽ rất có ích! Thay vì thiết kế một client “mỏng”, với phần lớn code nằm trong main process, tôi đã phải vẽ một ranh giới nghiêm ngặt giữa những gì người dùng phải và không phải đối mặt. Tôi đã tự lập một bảng đánh giá để quyết định cái gì sẽ có trong main process và cái gì sẽ có trong quá trình render, một câu hỏi đơn giản – code này có tác động đến những phần khác trong Electron không? Không quan trọng việc đoạn mã nhỏ như thế nào. Nếu nó có liên quan đến các đoạn code khác, nó sẽ tồn tại trong main process cu. Ngoại lệ duy nhất là endpoint thanh toán Stripe, vì lý do bảo mật, tôi muốn nó càng gần user càng tốt.

  Một vài lỗi mà những lập trình viên mới có thể mắc phải

#2 Rất khó để đảm bảo dữ liệu giữ được tính toàn vẹn

một Backdoor không khác gì front door, tất cả những gì Thay đổi là người sử dụng nó.

Đó là cho đến khi tôi bắt đầu làm Binder bằng Electron, tôi nhận ra rằng việc đảm bảo tất cả dữ liệu bạn nhận được từ một số lượng lớn khách hàng ngẫu nhiên vẫn chính xác và có thể truy cập được khó khăn đến mức nào. Giữ an toàn dữ liệu đã đủ khó, nhưng xác thực dữ liệu đó và đảm bảo dữ liệu đó phù hợp với dữ liệu khác mà không thực sự biết dữ liệu đó là gì, mới là khó khăn thiệt sự?!

Việc xác nhận (validate) nên diễn ra càng sớm càng tốt và nên lặp lại (ở mức độ thấp hơn) dọc theo luồng dữ liệu. Việc duy trì tính nhất quán có thể được thực hiện dễ dàng hơn bằng cách áp dụng mô hình transactional bất cứ khi nào thay đổi dữ liệu. Nhưng sự thật là, có rất nhiều metadata (siêu dữ liệu) đi kèm với dữ liệu thực tế (actual data) và việc quản lý chỉ dễ dàng hơn một chút. Ý tưởng thực hiện một chức năng đọc dữ liệu người dùng và kiểm tra tính toàn vẹn nghe có vẻ hay đấy. Tuy nhiên sau khi cân nhắc rất lâu, cuối cùng tôi ngẫm thấy rằng một backdoor cũng không khác gì front door, tất cả những gì mà thay đổi là NGƯỜI SỬ DỤNG NÓ.

#3 Hãy đối xử với UI của bạn như một đôi giày custom

UI của Blinder

Một cách tuyệt vời để thiết kế một giao diện người dùng (UI) đẹp, hiệu quả, là xem nó như một đôi giày custom. Điều đầu tiên tôi tự hỏi khi thiết kế là: Người dùng sẽ thấy UI của Binder như thế nào? Lưu ý rằng, ở đây tôi dùng chữ “thấy” chứ không phải “sử dụng”. Bởi vì ngoại hình là tất cả. Tôi đã tham gia một số dự án tốt trong quá khứ và tôi có thể nói với bạn rằng, sẽ không một ai cho feedback về ứng dụng của bạn nếu UI không vừa mắt.

Tôi bắt đầu với việc vẽ những bản phác thảo nhỏ trong cuốn sổ tay (khi tôi tự đặt mình là người dùng và tưởng tượng về UI). Những phác thảo đầu tiên của tôi đã làm nổi bật bố cục tổng thể của giao diện và khi tôi vẽ nhiều hơn, tôi đã nhận ra những gì tôi muốn như các chi tiết của mỗi trang page sẽ trông như thế nào. Đối với tôi, làm cho mọi thứ dễ nhìn quan trọng hơn những thông tin nhàm chán.

#4 Không có định nghĩa viết code quá an toàn

Thành thật mà nói, tôi không biết bạn nghĩ như thế nào nhưng suy nghĩ về việc tồn tại lỗi trong API sau khi tôi vừa nhận được một khoản thanh toán từ khách hàng khiến tôi lo lắng. Khi tôi bắt đầu triển khai dịch vụ thanh toán, tôi đã tự nhủ với bản thân rằng không thể để bất kỳ sai lầm nào được tồn tại. Tôi đã tự mình thực hiện các bước bảo mật ở mọi nơi, ngay từ khi API nhận được yêu cầu mua gói dung lượng cho đến khi Stripe thông báo cho các webhook và kích hoạt gói. Sự cẩn thận này chắc chắn sẽ làm chậm quá trình phát triển của tôi, nhưng tôi không cảm thấy hối tiếc. Nhờ vậy tôi sẽ biết chính xác khi nào thanh toán được gửi, những gì chúng được gửi và trạng thái của bất kỳ hành động nào cần được thực hiện sau đó.

Kết bài

Rất vui vì bạn đã đọc toàn bộ bài viết của tôi. Có thể bạn biết hoặc không, nhưng Binder đã hoàn thành. Trong khi viết bài này, tôi đã release bản đầu tiên (beta 4) dựa trên Electron. Tôi không nghĩ mình có thể biến Binder thành một sản phẩm hoàn hảo, tuy nhiên, tôi đã xây dựng nó để hoạt động như một sản phẩm thực tế. Bạn có thể xem qua thử trang web “hào nhoáng” này ngay tại đây 😉.

Đôi lời

Tôi đã up mọi thứ liên quan đến việc sử dụng Electron để xây dựng Binder trên trang GitHub này. Tôi sẽ đăng các bản cập nhật ở đó (đó cũng là cách bạn tải xuống máy local client và cách nó tự cập nhật). Nếu bạn học được bất cứ điều gì từ bài đăng này, hoặc nếu bạn có gì góp ý, xin vui lòng ở dưới. Tôi thực sự đánh giá cao nó và cảm ơn bạn!

Ồ, ngoài ra, đây là một số thống kê tôi đã tính toán trên 4 ứng dụng phụ, hy vọng sẽ có ý với các bạn:

binder-local (Electron client)

binder-web (fancy webpage)

Tôi đã không chạy 2 phần còn lại thông qua bộ đếm là vì lý do bảo mật.

binder-api

JavaScript: 21 files, 4117 lines
Other files: ~ 150 lines

binder-mongo (integrity service)

JavaScript: 16 files, 2374 lines
Other files: ~ 140 lines

Total lines of code: 30,015

Đừng bỏ qua những bài viết hay trên trang TopDev nhé:

Lộ trình học fullstack web PHP dành cho developer

Giải bài toán cộng 2 số bàng javascript 

Xem thêm việc làm IT tại Hồ Chí Minh, Hà Nội, Đà Nẵng trên TopDev

So sánh ASP.NET và PHP? Lập trình website nên học ngôn ngữ nào?

ASP.NET và PHP là 2 công cụ lập trình đã được đem ra so sánh và thảo luận trong một khoảng thời gian dài. Cả 2 ngôn ngữ lập trình này đều được tận dụng để phát triển rất nhiều ứng dụng web mạnh, đơn cử như Facebook và Twitter. Trong bài viết này, chúng ta sẽ cùng tìm hiểu sự khác biệt giữa lập trình ASP.NET và PHP, cũng như so sánh xem đâu là lựa chọn tốt hơn cho bạn.

ASP.NET là gì?

So sánh ASP.NET và PHP? Lập trình website nên học ngôn ngữ nào?

ASP.NET là một khung ứng dụng web được thiết kế và phát triển bởi Microsoft. Nó là ngôn ngữ mã nguồn mở và là một tập hợp con của .NET Framework và là sự kế thừa của ASP cổ điển (Active Server Pages). Với phiên bản 1.0 của .NET Framework, nó được phát hành lần đầu tiên vào tháng 1 năm 2002.

ASP.NET được xây dựng trên CLR (Common Language Runtime) cho phép các lập trình viên thực thi mã của mình bằng bất kỳ ngôn ngữ .NET nào (C #, VB, v.v.). Nó được thiết kế đặc biệt để làm việc với HTTP và cho các nhà phát triển web để tạo các trang web động, ứng dụng web, trang web và dịch vụ web vì nó cung cấp tích hợp tốt HTML, CSS và JavaScript.

.NET Framework được sử dụng để tạo ra nhiều ứng dụng và dịch vụ như Console, Web và Windows, v.v. Nhưng lập trình ASP.NET chỉ được sử dụng để tạo các ứng dụng web và dịch vụ web. Đó là lý do tại sao chúng ta gọi ASP.NET là một tập hợp con của .NET Framework.

>> Tìm hiểu: ASP.NET Core là gì? So sánh .NET core và ASP.NET core

So sánh cơ bản giữa lập trình ASP.NET và PHP

Microsoft ASP.Net PHP
Giấy phép Mã nguồn mở Mã nguồn mở
Kiểu ngôn ngữ Ngôn ngữ biên dịch (Compiled) Ngôn ngữ kịch bản (Scripting)
Ngôn ngữ phát triển VB.NET, C#.NET, F# ,..v.v PHP
Công cụ phát triển
  • Visual Studio
  • Mono
  • NetBeans
  • PhpStorm
  • WordPress
Hệ điều hành Windows
  • Linux
  • Mac
  • Windows(Bị giới hạn)
Chi phí Có phí và miễn phí tùy loại Miễn phí
Syntax Syntax tương tự như Visual Basic Syntax tương tự như C hay C++

Một số tiêu chí phổ biến để so sánh giữa lập trình ASP.NET và PHP

Khả năng mở rộng và bảo trì

Một thực tế đơn giản là cho dù bạn chọn lập trình ASP.NET hay PHP để phát triển web, khả năng mở rộng và bảo trì của ứng dụng web phụ thuộc vào:

  • Trình độ của lập trình viên
  • Thực hiện các tiêu chuẩn mã hóa tốt nhất
  • Sử dụng một khuôn khổ vững chắc

Vì vậy cả 2 ASP.NET và PHP là tương đương với nhau trong khoản này.

Hiệu suất và tốc độ:

Có một quan niệm sai lầm rằng hiệu suất và tốc độ trang web phụ thuộc vào việc chọn ngôn ngữ nào. Trên thực tế, có rất ít sự khác biệt về hiệu suất dù cho trang web chạy bằng ASP.NET hay PHP. Cả ASP.NET và PHP đều được trang bị tốt để chạy các quy trình thường quy và tạo ra kết quả mong muốn – truy cập hệ thống tệp, tìm hình ảnh và hiển thị các trang. Tốc độ chủ yếu phụ thuộc vào các yếu tố như máy chủ cơ sở dữ liệu, máy tính và băng thông của người dùng cuối.

Có lập luận rằng PHP là ngôn ngữ nhanh hơn ở cấp độ cốt lõi. Nhưng tốc độ ngôn ngữ cốt lõi không còn là vấn đề nữa vì phần cứng hiện đại đang phát triển rất nhanh. Mức tăng biên trong hiệu năng không quyết định ứng dụng nhanh hay chậm, mà phụ thuộc vào việc mã được viết như thế nào cũng như cấu ​​trúc của nó.

Khả năng hỗ trợ

PHP là mã nguồn mở và có một nhóm các nhà phát triển lớn hơn nhiều so với ASP.NET. Tuy nhiên, cả hai đều có một cộng đồng rộng lớn với hàng ngàn nhà phát triển đăng bài thường xuyên lên các diễn đàn. Nếu bạn đã từng bị mắc kẹt với một truy vấn trong PHP hoặc ASP.NET, bạn sẽ thấy cả hai cộng đồng đều hữu ích như nhau. Cộng đồng ASP.NET chủ yếu bao gồm các nhà phát triển chuyên dụng, số lượng người đóng góp hỗ trợ có sẵn để đăng tại một thời điểm nhất định có thể thấp hơn một chút so với PHP. Bạn có thể nhận được phản hồi nhanh hơn trên các diễn đàn và cộng đồng PHP.

Chi phí

PHP hoàn toàn miễn phí, trong khi đó đối với ASP.NET thì bạn phải trả phí cho một số tính năng nhất định. Muốn dùng được ASP.NET, bạn sẽ phải mua hệ điều hành Windows. Nếu xài Mac và Linux, bạn có thể sử dụng dự án Mono để sử dụng ASP.NET trên máy của mình. Có nhiều người cho rằng Windows hosting tương đối đắt tiền nhưng hiện tại Microsoft có các dịch vụ miễn phí và máy chủ Windows có giá gần bằng máy chủ Linux.

Thêm một sự cân nhắc chi phí khác chính là môi trường phát triển. IDE phổ biến nhất cho phát triển ASP.NET chính là Visual Studio. Microsoft cung cấp phiên bản miễn phí có tên Visual Studio Express dành cho người mới bắt đầu nhưng không dành cho chuyên gia. PHP có lợi thế là nó miễn phí, chạy trên máy chủ web Linux, có thể được sử dụng với Windows, Mac hoặc Linux và được hỗ trợ bởi một số IDE (cả trả phí và miễn phí).

  ASP.NET MVC5 #3: Thêm mới View
  Phát triển ứng dụng web – Bạn chọn ASP.NET Web Forms hay ASP.NET MVC ?

Xem tin .NET tuyển dụng mới nhất tại TopDev

ASP.NET và PHP nên chọn học lập trình nào?

PHP là một trong những ngôn ngữ lập trình phổ biến nhất trên thế giới, nó được hỗ trợ bởi một cộng đồng lớn lập trình viên PHP. Trong khi đó, ASP.NET cũng có một cộng đồng hỗ trợ nhưng không nhiều bằng PHP. Vì thế nên PHP có lẽ sẽ dễ học và học nhanh hơn so với ASP.NET

Bù lại, ASP.NET framework có bộ thư viện tuyệt vời nhất, đi kèm với rất nhiều tính năng cho phép các developers tạo một trang web dễ dàng chỉ với các thao tác kéo – thả. Với môi trường ASP.NET Framework, lập trình viên có thể viết code dưới rất nhiều ngôn ngữ như C#, VB.NET hay F#.

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

Việc làm ASP.NET lương cao hấp dẫn

Kết luận

PHP phù hợp hơn để tạo website cho các start-up, doanh nghiệp nhỏ. Trong khi đó, ASP.NET phù hợp hơn với các doanh nghiệp, tập đoàn lớn và Microsoft dường như đã khẳng định nó trong dự án Bugnetproject của mình. Đối với người mới học lập trình thì bắt đầu với PHP sẽ dễ dàng hơn là học C# của ASP.NET.

Cả ASP.NET vs PHP đều hoạt động hiệu quả tùy vào trường hợp kinh doanh và nhu cầu, sở thích của đơn vị kinh doanh. Cả ASP.NET vs PHP đều có những ưu và nhược điểm riêng. Chính các kỹ năng của nhà phát triển và yêu cầu trường hợp kinh doanh sẽ quyết định việc sử dụng loại hình công nghệ nào.

Việc làm .NET fresher tại các công ty tốt nhất Việt Nam

TopDev via Dotnettipoftheday

Giới thiệu về bộ tiền xử lý (preprocessor)

Sự thông dịch và tiền xử lý

Khi bạn biên dịch code của mình, bạn mong đợi rằng trình biên dịch sẽ biên dịch code một cách chính xác như bạn đã viết nó.

Trước khi biên dịch, file code sẽ trải qua giai đoạn được gọi là thông dịch. Nhiều điều xảy ra trong giai đoạn thông dịch này để code của bạn sẵn sàng được biên dịch. Một file code được thông dịch thì được gọi là một đơn vị thông dịch.

Đáng chú ý nhất trong các giai đoạn thông dịch là liên quan đến bộ tiền xử lý. Bộ tiền xử lý được coi là một chương trình riêng biệt để thao tác với các đoạn code(văn bản code) trong mỗi file code.

Khi bộ tiền xử lý chạy, nó quét qua file code (từ trên xuống dưới), tìm kiếm các chỉ thị tiền xử lý. Các chỉ thị tiền xử lý (thường được gọi là các chỉ thị) là các hướng dẫn bắt đầu bằng ký hiệu # và kết thúc bằng một dòng mới (KHÔNG phải là dấu chấm phẩy). Các chỉ thị này báo cho bộ tiền xử lý thực hiện các tác vụ với văn bản code cụ thể. Lưu ý rằng bộ tiền xử lý không hiểu cú pháp C ++.

Đầu ra của bộ tiền xử lý trải qua nhiều giai đoạn thông dịch, và sau đó được biên dịch. Lưu ý rằng bộ tiền xử lý không sửa đổi các file code gốc theo bất kỳ cách nào – thay vào đó, tất cả các thay đổi của đoạn code được thực hiện bởi bộ tiền xử lý xảy ra tạm thời trong bộ nhớ mỗi khi file code được biên dịch.

Trong bài học này, chúng tôi sẽ thảo luận về những gì một số chỉ thị tiền xử lý thường làm nhất.

#includes

Bạn đã thấy lệnh #include hoạt động. Khi bạn #include một file nào đó, bộ xử lý trước tiên sẽ thay thế lệnh #include bằng nội dung của file mà bạn include nó. Các nội dung đã include sẽ được xử lý trước (cùng với phần còn lại của file code của bạn), và sau đó nó sẽ được biên dịch.

Hãy xem xét chương trình sau:

1
2
3
4
5
6
7
#include <iostream>
 
int main()
{
    std::cout << "Hello, world!";
    return 0;
}

Khi bộ tiền xử lý chạy trên chương trình này, bộ tiền xử lý sẽ thay thế #include bằng nội dung được xử lý trước của file có tên là iostream.

Vì #include hầu như chỉ được sử dụng để include các file trong header(file .h), chúng ta sẽ thảo luận về #include chi tiết hơn trong bài học tiếp theo (khi chúng tôi thảo luận chi tiết hơn về các file header).

Định nghĩa Macro

Lệnh #define có thể được sử dụng để tạo macro. Trong C ++, macro là một quy tắc định nghĩa cách chuyển văn bản code đầu vào nào đó thành văn bản code đầu ra theo ý mình muốn.

Có hai loại macro cơ bản: macro giống như đối tượng và macro giống như hàm.

Các macro giống như hàm hoạt động như các hàm và phục vụ một mục đích tương tự. Chúng ta sẽ không thảo luận về chúng ở đây, vì việc sử dụng chúng thường được coi là nguy hiểm và hầu hết mọi thứ chúng có thể làm đều có thể được thực hiện bởi một hàm bình thường.

Các macro giống như đối tượng có thể được định nghĩa theo một trong hai cách:

1
2
#define identifier
#define identifier substitution_text

Định nghĩa trên không có định danh khác để thay thế, trong khi định nghĩa dưới cùng thì có. Bởi vì đây là các chỉ thị tiền xử lý (không phải câu lệnh) nên lưu ý rằng không có dòng nào kết thúc bằng dấu chấm phẩy.

Các macro giống như một đối tượng với một văn bản thay thế

Khi bộ tiền xử lý gặp lệnh này, bất kỳ sự xuất hiện nào nữa của định danh macro sẽ được thay thế bằng một đoạn văn bản mà chúng ta đã định nghĩa trước đó. Thông thường thì tên định danh của macro sẽ được ghi bằng các chữ in hoa, sử dụng dấu gạch dưới để thể hiện khoảng trắng.

Hãy xem xét chương trình sau:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
 
#define MY_NAME "Alex"
 
int main()
{
    std::cout << "My name is: " << MY_NAME;
 
    return 0;
}

Bộ tiền xử lý chuyển đổi phần trên thành như sau:

1
2
3
4
5
6
7
8
// The contents of iostream are inserted here
 
int main()
{
    std::cout << "My name is: " << "Alex";
 
    return 0;
}

Mà khi chạy, in đầu ra My name is: Alex

Các macro giống như đối tượng mà không có văn bản thay thế

Các macro giống như đối tượng cũng có thể được định nghĩa mà không cần văn bản thay thế.

Ví dụ:

define USE_YEN

Các macro của biểu mẫu này hoạt động như sau: mọi sự xuất hiện tiếp theo của định danh macro này(USE_YEN) sẽ bị xóa và được thay thế bởi không có gì!

Điều này có vẻ khá vô dụng. Tuy nhiên, đó không phải là những gì lệnh này thường được sử dụng. Chúng ta sẽ thảo luận về việc sử dụng hình thức này ngay sau đây.

Biên soạn code có điều kiện

Các chỉ thị tiền xử lý biên dịch có điều kiện sẽ cho phép bạn chỉ định những điều kiện nào đó thì sẽ thực hiện một cái gì đó hoặc giành được biên dịch hoặc không được biên dịch. Có khá nhiều chỉ thị biên dịch có điều kiện khác nhau, nhưng chúng tôi sẽ giới thiệu ba chỉ thị được sử dụng nhiều nhất ở đây: #ifdef, #ifndef và #endif.

Chỉ thị tiền xử lý #ifdef cho phép bộ tiền xử lý kiểm tra xem một định danh đã được #define trước đó chưa. Nếu đã định nghĩa rồi thì code ở giữa #ifdef và #endif sẽ được biên dịch. Nếu không, code được bỏ qua.

Hãy xem xét chương trình sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
 
#define PRINT_JOE
 
int main()
{
#ifdef PRINT_JOE
    std::cout << "Joe\n"; // if PRINT_JOE is defined, compile this code
#endif
 
#ifdef PRINT_BOB
    std::cout << "Bob\n"; // if PRINT_BOB is defined, compile this code
#endif
 
    return 0;
}

Vì PRINT_JOE đã được #define, dòng cout << “Joe \ n” sẽ được biên dịch. Vì PRINT_BOB chưa được #define, dòng cout << “Bob \ n” sẽ bị bỏ qua.

#ifndef ngược lại với #ifdef, ở chỗ nó cho phép bạn kiểm tra xem một định danh chưa được định nghĩa.

1
2
3
4
5
6
7
8
9
10
#include <iostream>
 
int main()
{
#ifndef PRINT_BOB
    std::cout << "Bob\n";
#endif
 
    return 0;
}

Chương trình này in ra Bob, vì PRINT_BOB chưa bao giờ được định nghĩa.

#if 0

Một cách sử dụng phổ biến của biên dịch có điều kiện liên quan đến việc sử dụng #if 0 để loại trừ một khối code khỏi được biên dịch (như thể nó nằm trong một khối comments):

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
int main()
{
    std::cout << "Joe\n";
 
#if 0 // Don't compile anything starting here
    std::cout << "Bob\n";
    std::cout << "Steve\n";
#endif // until this point
 
    return 0;
}

Đoạn code trên chỉ in ra Joe, bởi vì Bob và và Steve đã ở trong một khối #if 0 mà bộ tiền xử lý sẽ loại trừ nó khỏi quá trình biên dịch.

Điều này cung cấp một cách thuận tiện để comments code có chứa các nhiều dòng.

Các macro giống như đối tượng không ảnh hưởng đến các chỉ thị tiền xử lý khác

Bây giờ bạn có thể tự hỏi:

1
2
3
4
#define PRINT_JOE
 
#ifdef PRINT_JOE
// ...

Vì chúng ta đã định nghĩa PRINT_JOE là không có gì, tại sao bộ tiền xử lý lại không thay thế PRINT_JOE trong #ifdef PRINT_JOE không có gì?

Macro chỉ thay thế văn bản cho code bình thường. Còn với các lệnh tiền xử lý có điều kiện này sẽ xử lý khác. Do đó, PRINT_JOE trong #ifdef PRINT_JOE chỉ có tác dụng thay thế một văn bản nào đó với code bình thường, bạn có thể xem qua ví dụ sau đây để hiểu hơn

Ví dụ:

1
2
3
4
5
#define FOO 9 // Here's a macro substitution
 
#ifdef FOO // This FOO does not get replaced because it’s part of another preprocessor directive
    std::cout << FOO; // This FOO gets replaced with 9 because it's part of the normal code
#endif

Trong thực tế, đầu ra của bộ tiền xử lý hoàn toàn không chứa bất kỳ chỉ thị nào cho trình biên dịch – tất cả chúng đều được xử lý và loại bỏ trước khi biên dịch.

Phạm vi định nghĩa

Các chỉ thị sẽ được xử lý trước khi biên dịch, từ trên xuống dưới trên của từng file.

Hãy xem xét chương trình sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
void foo()
{
#define MY_NAME "Alex"
}
 
int main()
{
    std::cout << "My name is: " << MY_NAME;
 
    return 0;
}

Mặc dù có vẻ như #define MY_NAME “Alex” Được định nghĩa bên trong hàm foo, bộ tiền xử lý đã được thông báo, vì nó không hiểu các khái niệm C ++ như các hàm. Do đó, chương trình này hoạt động bình thường và nó không phụ thuộc vào vị trí định nghĩa của #define MY_NAME “Alex”, Khi đó, thì chúng ta có thể định nghĩa nó trước hoặc ngay sau hàm foo cũng được nhưng để dễ đọc, bạn thường muốn #define định danh bên ngoài các hàm.

Khi bộ tiền xử lý kết thúc, tất cả các định danh được định nghĩa từ file đó sẽ bị loại bỏ. Điều này có nghĩa là các chỉ thị chỉ có giá trị từ điểm định nghĩa đến cuối file mà chúng được định nghĩa. Các chỉ thị được định nghĩa trong một file code không có tác động đến các file code khác trong cùng một dự án.

Hãy xem xét ví dụ sau:

function.cpp:

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
 
void doSomething()
{
#ifdef PRINT
    std::cout << "Printing!";
#endif
#ifndef PRINT
    std::cout << "Not printing!";
#endif
}

main.cpp:

1
2
3
4
5
6
7
8
9
10
void doSomething(); // forward declaration for function doSomething()
 
#define PRINT
 
int main()
{
    doSomething();
 
    return 0;
}

Chương trình trên sẽ in:

1
Not printing!

Mặc dù PRINT đã được định nghĩa trong main.cpp, nhưng điều đó không có bất kỳ tác động nào đối với bất kỳ code nào trong file function.cpp (PRINT chỉ được #define từ điểm định nghĩa đến cuối main.cpp).

Nguồn gốc bài viết từ CafeDev

Giải bài toán cộng 2 số bằng javascript

Một đề bài cũng không mới, thông qua đó chúng ta sẽ biết thêm tí về cách sử dụng object sao cho hiệu quả.

Đề bài: viết một hàm, nhận 2 tham số đầu vào, tham số thứ nhất là một mảng số, tham số thứ 2 là một số bất kỳ. Yêu cầu trả về một mảng gồm 2 phần tử trong mảng ban đầu và 2 phần tử này cộng lại bằng tham số thứ 2.

/**
 * @param {number[]} nums
 * @param {number} total
 * @return {number[]}
 */
const twoSum = (arr, total) => {
  // Solution here
};

Ví dụ sử dụng hàm này bằng các input như bên dưới

input: nums = [1, 2, 3], total = 4
output: [1, 3]

input: nums = [3, 9, 12, 20], total = 21
output: [9, 12]

Chúng ta coi như mảng nums truyền vào luôn là mảng số, ko cần kiểm trả kiểu giá trị của phần tử trong mảng, total luôn là một con số mà 2 phần tử trong có thể cộng lại bằng. Tất là không có trường hợp ko tìm thấy cặp phần tử nào thỏa yêu cầu

Tuyển lập trình javascript lương cao

Phương pháp: không bỏ sót

Lấy phần tử đầu tiên của nums, duyệt qua toàn bộ các phần tử còn lại, xem có thằng nào cộng lại bằng total không. Cứ làm điều tương tự với từng phần tử một trong mảng.

/**
 * @param {number[]} nums
 * @param {number} total
 * @return {number[]}
 */
const twoSum = (nums, total) => {
  for (let i = 0; i < nums.length - 1; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] + nums[j] === total) {
        return [nums[i], nums[j]];
      }
    }
  }
};

console.log(twoSum([1, 2, 3], 4)); // [1, 3]
console.log(twoSum([3, 9, 12, 20], 21)); // [9, 12]

Có 2 chỗ đáng quan tâm nếu chọn cách này

  1. Tại sao vòng lặp phải kết thúc ở i < nums.length - 1 ?
  2. Tại sao vòng lặp bên trong nữa bắt đầu ở vị trí j = i +1 ?

Cũng như cái tên của nó Vét cạn, chúng ta không bỏ sót trường hợp nào cả, ngay cả những trường hợp có thể bỏ qua. Độ khó của 2 vòng lặp lồng nhau như vậy là n mũ n2, nói cách khác, thời gian chạy của vòng lặp này tỉ lệ với bình phương số lượng các phần tử

Giả dụ bạn có 100000 phần tử, thì số lần chạy của vòng lặp là 4999950000

Nếu thích thì mấy bạn copy đoạn code này chạy thử

const len = 100000;
const bigArr = new Array(len).fill(1);
bigArr[len - 2] = 9;
bigArr[len - 1] = 10;
const total = 19;

const twoSum = (nums, total) => {
  let iterations = 0;
  const startTime = new Date();
  for (let i = 0; i < nums.length - 1; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      iterations++;
      if (nums[i] + nums[j] === total) {
        console.log(
          `Iterations: ${iterations}`,
          `Time: ${new Date() - startTime}ms`
        );
        return [nums[i], nums[j]];
      }
    }
  }
};

twoSum(bigArr, total);

5 triệu lần chạy này sẽ tốn khoảng 20 giây trên máy mình

Phương pháp bảng băm: sử dụng Object của javascript

Chúng ta có thể làm tốt hơn cách trên. Thay vì đặt vòng lặp lồng nhau, chạy qua các phần tử của nums một lượt, đánh dấu các phần tử đã kiểm tra, bỏ vào làm key cho một object, kiểm tra phần tử trong nums có tồn tại trong object hay chưa

const twoSum = (nums, total) => {
    // object chứa những giá trị đã duyệt qua
  const previousValues = {};

  for (let i = 0; i < nums.length; i++) {
    // giá trị cần tìm
    const complement = total - nums[i];
    
    // giá trị cần tìm có nằm ở lần duyệt trước đó
    if (previousValues[complement]) {
      return [complement, nums[i]];
    }
    // lưu lại giá trị này vào object đã duyệt
    previousValues[nums[i]] = true;
  }
};

console.log(twoSum([1, 2, 3], 4)); // [1, 3]
console.log(twoSum([3, 9, 12, 20], 21)); // [9, 12]

Chúng ta chỉ còn một vòng lặp, vòng lặp thứ 2 bị thay thế bởi previousValues[complement]

Đo lại tốc độ với cách làm này

const len = 100000;
const bigArr = new Array(len).fill(1);
bigArr[len - 2] = 9;
bigArr[len - 1] = 10;
const total = 19;

const twoSum = (nums, total) => {
  let iterations = 0;
  const startTime = new Date();

  const previousValues = {};
  for (let i = 0; i < nums.length; i++) {
    iterations++;
    const complement = total - nums[i];
    if (previousValues[complement]) {
      console.log(
        `Iterations: ${iterations}`,
        `Time: ${new Date() - startTime}ms`
      );
      return [complement, nums[i]];
    }
    previousValues[nums[i]] = true;
  }
};

twoSum(bigArr, total);
// Iterations: 100000 Time: 4ms

Không có chi là miễn phí, chúng ta tiết kiệm được thời gian chạy, nhưng phải bỏ ra một vùng nhớ để lưu object previousValues, nếu lưu khoảng 1 triệu phần tử, dung lượng này cũng không nhỏ, khoản đâu đó 10MB RAM

TopDev via Vuilaptrinh

Xem thêm các việc làm IT không yêu cầu kinh nghiệm tại đây

Code PHP làm sao cho sạch (Phần 1)

Giới thiệu

Đây là những nguyên lý kỹ thuật phần mềm, được trích từ cuốn sách Clean Code của tác giả Robert C. Martin (thường gọi là Uncle Bob) rất thích hợp cho ngôn ngữ PHP. Tài liệu này không phải là sách hướng dẫn về phong cách viết code, mà là hướng dẫn cách làm thế nào để viết phần mềm dễ đọc, dễ sử dụng lại, và dễ cải tiến trong PHP.

Bạn không cần phải tuân theo tất cả các nguyên tắc trong tài liệu này. Đây chỉ đơn giản là những hướng dẫn, nhưng dù sao nó cũng là đúc kết từ nhiều năm kinh nghiệm của tác giả.

Repository này lấy cảm hứng từ clean-code-javascript

Lưu ý: Dù nhiều lập trình viên còn sử dụng PHP 5, nhưng nhiều ví dụ trong đây chỉ chạy được trên PHP 7.1+.

Biến

Sử dụng tên biến có ý nghĩa và dễ hiểu

Chưa tốt:

$ymdstr = $moment->format('y-m-d');

Tốt:

$currentDate = $moment->format('y-m-d');

Sử dụng cùng từ vựng cho cùng ột loại biến

Chưa tốt:

getUserInfo();
getUserData();
getUserRecord();
getUserProfile();

Tốt:

getUser();

Đặt tên sao cho dễ tìm kiếm (phần 1)

Thường thì chúng ta sẽ đọc code nhiều hơn viết code. Nên điều quan trọng là code chúng ta viết ra phải dễ đọc và dễ tìm kiếm. Nếu không đặt tên biến có ý nghĩa và làm chương trình dễ hiểu, chúng ta sẽ gây khó cho những lập trình viên khác. Do đó mỗi khi đặt tên biến, hàm thì hãy đặt có ý nghĩa.

Chưa tốt:

// Oh man, 448 là cái giề vậy?
$result = $serializer->serialize($data, 448);

Tốt:

$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

Đặt tên sao cho dễ tìm kiếm (phần 2)

Chưa tốt:

// Lại nữa, 4 nghĩa là cái giề đây?
if ($user->access & 4) {
    // ...
}

Tốt:

class User
{
    const ACCESS_READ = 1;
    const ACCESS_CREATE = 2;
    const ACCESS_UPDATE = 4;
    const ACCESS_DELETE = 8;
}

if ($user->access & User::ACCESS_UPDATE) {
    // do edit ...
}

Đặt tên biến dễ hiểu

Tức là đặt tên biến sao cho đọc vô là hiểu nó là gì và nó dùng để làm gì. Không cần phải suy nghĩ, suy diễn.

Chưa tốt:

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches[1], $matches[2]);

Không tệ lắm:

Tốt hơn một chút, nhưng vẫn còn phụ thuộc nhiều vào regex.

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

[, $city, $zipCode] = $matches;
saveCityZipCode($city, $zipCode);

Tốt:

Đã giảm phụ thuộc vào regex bằng “naming subpatterns”.

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches['city'], $matches['zipCode']);

Xem tin tuyển lập trình viên PHP đãi ngộ tốt trên TopDev

Tránh lồng (nesting) quá nhiều và nên return sớm (phần 1)

Quá nhiều if else lồng nhau sẽ khiến code tăng độ phức tạp, khó debug. Giảm sự phức tạp bằng cách giảm số if else lồng nhau xuống ít nhất có thể. Return sớm chính là một cách giảm số lần lồng nhau.

Chưa tốt:

function isShopOpen($day): bool
{
    if ($day) {
        if (is_string($day)) {
            $day = strtolower($day);
            if ($day === 'friday') {
                return true;
            } elseif ($day === 'saturday') {
                return true;
            } elseif ($day === 'sunday') {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } else {
        return false;
    }
}

Tốt:

function isShopOpen(string $day): bool
{
    if (empty($day)) {
        return false;
    }

    $openingDays = [
        'friday', 'saturday', 'sunday'
    ];

    return in_array(strtolower($day), $openingDays, true);
}

Tránh lồng (nesting) quá nhiều và nên return sớm (phần 2)

Chưa tốt:

function fibonacci(int $n)
{
    if ($n < 50) {
        if ($n !== 0) {
            if ($n !== 1) {
                return fibonacci($n - 1) + fibonacci($n - 2);
            } else {
                return 1;
            }
        } else {
            return 0;
        }
    } else {
        return 'Not supported';
    }
}

Tốt:

function fibonacci(int $n): int
{
    if ($n === 0 || $n === 1) {
        return $n;
    }

    if ($n > 50) {
        throw new \Exception('Not supported');
    }

    return fibonacci($n - 1) + fibonacci($n - 2);
}

Tránh hack não người đọc

Đừng khiến người đọc code phải khó khăn để hiểu ý nghĩa của biến. Tên biến càng rõ ràng càng tốt.

Chưa tốt:

$l = ['Austin', 'New York', 'San Francisco'];

for ($i = 0; $i < count($l); $i++) {
    $li = $l[$i];
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    // Đợi đã, `$li` là cái gì?
    dispatch($li);
}

Tốt:

$locations = ['Austin', 'New York', 'San Francisco'];

foreach ($locations as $location) {
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    dispatch($location);
}

Tìm việc làm PHP Hà Nội tại các doanh nghiệp hàng đầu trên TopDev

Đừng thêm những nội dung không cần thiết

Nếu tên của class/object đã rõ ràng, không nên lặp lại chúng trong tên biến.

Chưa tốt:

class Car
{
    public $carMake;
    public $carModel;
    public $carColor;

    //...
}

Tốt:

class Car
{
    public $make;
    public $model;
    public $color;

    //...
}

Sử dụng đối số mặc định thay vì phải kiểm tra bằng biểu thức điều kiện

Chưa tốt:

Chưa tốt vì $breweryName có thể bị NULL.

function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void
{
    // ...
}

Không tệ lắm:

Cái này tốt hơn cái trước, nhưng nó nên quản lý được giá trị của biến thì tốt hơn.

function createMicrobrewery($name = null): void
{
    $breweryName = $name ?: 'Hipster Brew Co.';
    // ...
}

Tốt:

Bạn có thể sử dụng type hinting và chắc chắn $breweryName sẽ không bị NULL.

function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void
{
    // ...
}

So sánh

Sử dụng identical comparison

Chưa tốt:

Sử dụng simple comparison (so sánh đơn giản)

$a = '42';
$b = 42;
Sử dụng simple comparison thì nó sẽ tự chuyển kiểu string qua kiểu int

if ($a != $b) {
   //Biểu thức này sẽ trả về `false`
}

Phép so sánh $a != $b trả về false nhưng trong thực tế thì nó phải là trueChuỗi ’42’ thì phải khác số 42 chứ đúng không.

Tốt:

Sử dụng identical comparison (so sánh giống hệt nhau) để so sánh cả kiểu dữ liệu và giá trị

if ($a !== $b) {
    //Biểu thức này trả về `true`
}

Hàm

Đối số của hàm (ít hơn hoặc bằng 2 là lý tưởng)

Giới hạn số lượng đối số (parameters) của hàm vô cùng quan trọng bởi vì nó giúp dễ test hơn. Có nhiều hơn 3 đối số dẫn đến một tổ hợp rất nhiều trường hợp khác nhau cần phải test.

Lý tưởng nhất là khi hàm không có đối số nào. Một hoặc hai đối số là ok, còn ba thì nên hạn chế. Bất cứ khi nào hàm có nhiều hơn 3 đối số thì cần phải xem xét tìm cách giảm bớt lại. Bởi vì nếu hàm có nhiều hơn hai đối số thì nó phải xử lý rất nhiều.

Chưa tốt:

function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void
{
    // ...
}

Tốt:

class MenuConfig
{
    public $title;
    public $body;
    public $buttonText;
    public $cancellable = false;
}

$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancellable = true;

function createMenu(MenuConfig $config): void
{
    // ...
}

Ứng tuyển việc làm PHP fresher đãi ngộ tốt trên TopDev

Hàm chỉ thực hiện một chức năng

Đây là nguyên tắc quan trọng nhất trong phát triển phần mềm. Khi hàm thực hiện nhiều hơn một chức năng, chúng khó biên dịch, kiểm tra và biết được nguyên nhân lỗi. Khi bạn tạo hàm chỉ với một chức năng, sẽ dễ dàng refactor hơn và code sẽ dễ đọc hơn. Nếu làm được điều này thì bạn sẽ tốt hơn nhiều lập trình viên khác.

Chưa tốt:

function emailClients(array $clients): void
{
    foreach ($clients as $client) {
        $clientRecord = $db->find($client);
        if ($clientRecord->isActive()) {
            email($client);
        }
    }
}

Tốt:

function emailClients(array $clients): void
{
    $activeClients = activeClients($clients);
    array_walk($activeClients, 'email');
}

function activeClients(array $clients): array
{
    return array_filter($clients, 'isClientActive');
}

function isClientActive(int $client): bool
{
    $clientRecord = $db->find($client);

    return $clientRecord->isActive();
}

Tên hàm nên thể hiện chức năng của hàm

Chưa tốt:

class Email
{
    //...

    public function handle(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// Hàm này dùng làm gì? Có phải nó xử lý mail? Nó có đang ghi gì vào file không?
$message->handle();

Tốt:

class Email 
{
    //...

    public function send(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// Rõ ràng và minh bạch, hàm này gửi mail
$message->send();

Hàm chỉ nên có độ trừu tượng một cấp

Khi bạn có độ trừu tượng nhiều hơn một cấp thì hàm thường phải làm quá nhiều việc. Hãy chia tách hàm ra thành nhiều phần để dễ sử dụng lại và dễ test.

Chưa tốt:

function parseBetterJSAlternative(string $code): void
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            // ...
        }
    }

    $ast = [];
    foreach ($tokens as $token) {
        // lex...
    }

    foreach ($ast as $node) {
        // parse...
    }
}

Cũng chưa tốt:

Chúng ta đã thực hiện tách ra vài hàm, nhưng hàm parseBetterJSAlternative() vẫn còn khá phức tạp và khó test.

function tokenize(string $code): array
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            $tokens[] = /* ... */;
        }
    }

    return $tokens;
}

function lexer(array $tokens): array
{
    $ast = [];
    foreach ($tokens as $token) {
        $ast[] = /* ... */;
    }

    return $ast;
}

function parseBetterJSAlternative(string $code): void
{
    $tokens = tokenize($code);
    $ast = lexer($tokens);
    foreach ($ast as $node) {
        // parse...
    }
}

Tốt:

Giải pháp tốt nhất là chuyển các phần thành các dependencies của hàm parseBetterJSAlternative()

class Tokenizer
{
    public function tokenize(string $code): array
    {
        $regexes = [
            // ...
        ];

        $statements = explode(' ', $code);
        $tokens = [];
        foreach ($regexes as $regex) {
            foreach ($statements as $statement) {
                $tokens[] = /* ... */;
            }
        }

        return $tokens;
    }
}

class Lexer
{
    public function lexify(array $tokens): array
    {
        $ast = [];
        foreach ($tokens as $token) {
            $ast[] = /* ... */;
        }

        return $ast;
    }
}

class BetterJSAlternative
{
    private $tokenizer;
    private $lexer;

    public function __construct(Tokenizer $tokenizer, Lexer $lexer)
    {
        $this->tokenizer = $tokenizer;
        $this->lexer = $lexer;
    }

    public function parse(string $code): void
    {
        $tokens = $this->tokenizer->tokenize($code);
        $ast = $this->lexer->lexify($tokens);
        foreach ($ast as $node) {
            // parse...
        }
    }
}

Đừng sử dụng cờ như là một đối số của hàm

Cờ dùng để nói rằng hàm này thực hiện nhiều hơn một công việc. Nhưng hàm thì chỉ nên xử lý một công việc mà thôi. Do đó hãy chia tách hàm của bạn nếu như chúng có nhiều luồng code phân biệt bằng boolean(true/false).

Chưa tốt:

function createFile(string $name, bool $temp = false): void
{
    if ($temp) {
        touch('./temp/'.$name);
    } else {
        touch($name);
    }
}

Tốt:

function createFile(string $name): void
{
    touch($name);
}

function createTempFile(string $name): void
{
    touch('./temp/'.$name);
}

Tránh tác dụng phụ

Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.

Vậy nếu bây giờ bạn cần hàm thực hiện các tác dụng phụ đó thì sao. Giống như ví dụ trước, bạn cần ghi vào file. Điều bạn cần làm là tập trung những việc này lại một chỗ. Đừng viết vài hàm và vài lớp chỉ để ghi vào vài file cụ thể. Hãy viết một service để làm điều đó. Một và chỉ một service.

Hãy tránh những sai lầm phổ biến như: chia sẻ trạng thái giữa các object mà không tuân theo cấu trúc nào, sử dụng kiểu dữ liệu có thể thay đổi/bị thay đổi dễ dàng, không tổng hợp các tác dụng phụ có thể xảy ra khi viết hàm.

Chưa tốt:

// Biến glabal được tham chiếu bởi hàm bên dưới.
// Nếu ta tạo một function khác sử dụng chính biến name, ví dụ bên dưới cho thấy nó biến thành array và đã bị phá vỡ.
$name = 'Ryan McDermott';

function splitIntoFirstAndLastName(): void
{
    global $name;

    $name = explode(' ', $name);
}

splitIntoFirstAndLastName();

var_dump($name); // ['Ryan', 'McDermott'];

Tốt:

function splitIntoFirstAndLastName(string $name): array
{
    return explode(' ', $name);
}

$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);

var_dump($name); // 'Ryan McDermott';
var_dump($newName); // ['Ryan', 'McDermott'];

Tuyển dụng PHP Đà Nẵng lương cao, ứng tuyển ngay!

Đừng viết hàm global

Dùng nhiều hàm global là bad practice với nhiều ngôn ngữ bởi vì có thể gây xung đột với thư viện khác và người sử dụng API của bạn không hề hay biết gì cho đến khi nhận được thông báo lỗi.

Hãy xem xét ví dụ sau: bạn sẽ làm gì nếu muốn trả về một mảng.

Bạn có thể viết hàm global như config(), nhưng nó có thể xung đột với thư viện khác thực hiện cùng chức năng.

Chưa tốt:

function config(): array
{
    return  [
        'foo' => 'bar',
    ]
}

Tốt:

class Configuration
{
    private $configuration = [];

    public function __construct(array $configuration)
    {
        $this->configuration = $configuration;
    }

    public function get(string $key): ?string
    {
        return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
    }
}

Tạo instance của lớp Configuration

$configuration = new Configuration([
    'foo' => 'bar',
]);

Và bây giờ sử dụng instance Configuration trong ứng dụng của bạn.

Đừng sử dụng Singleton pattern

Singleton là một anti-pattern. Trích đoạn từ Brian Button:

    1. Chúng thường được sử dụng như global instance, vì sao lại Chưa tốt? Bởi vì bạn ẩn dependencies của ứng dụng bên trong code của bạn, thay vì thông qua interfaces
    2. Chúng vi phạm single responsibility principle: bởi vì thực tế là chúng điều khiển những gì chúng tạo ra và vòng đời của nó
  1. Chúng đã tạo ra kiểu code coupling. Đây là một sự giả mạo và được giấu bằng cách tạo ra nhiều trường hợp test khó khăn hơn.
  2. Chúng giữ trạng thái suốt vòng đời của ứng dụng. Bạn nên kết thúc sớm testing khi lỗi. Nhưng Singleton thì lại duy trì trạng thái nên nó Chưa tốt.

Đây là một ý kiến khác của Misko Hevery về gốc rễ của vấn đề.

Chưa tốt:

class DBConnection
{
    private static $instance;

    private function __construct(string $dsn)
    {
        // ...
    }

    public static function getInstance(): DBConnection
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    // ...
}

$singleton = DBConnection::getInstance();

Tốt:

class DBConnection
{
    public function __construct(string $dsn)
    {
        // ...
    }

     // ...
}

Tạo instance của lớp DBConnection và cấu hình chúng với DSN.

$connection = new DBConnection($dsn);

Và bây giờ sử dụng instance DBConnection cho ứng dụng của bạn.

Đóng gói điều kiện

Chưa tốt:

if ($article->state === 'published') {
    // ...
}

Tốt:

if ($article->isPublished()) {
    // ...
}

Tránh điều kiện phủ định

Chưa tốt:

function isDOMNodeNotPresent(\DOMNode $node): bool
{
    // ...
}

if (!isDOMNodeNotPresent($node))
{
    // ...
}

Tốt:

function isDOMNodePresent(\DOMNode $node): bool
{
    // ...
}

if (isDOMNodePresent($node)) {
    // ...
}

Tránh dùng điều kiện

Điều này có vẻ không khả quan. Hầu hết mọi người sẽ thắc mắc, “làm sao có thể làm gì đó mà không có if?” Bạn có thể dùng tính đa hình để hoàn thành việc đó trong khá nhiều trường hợp. Câu hỏi thứ hai là, “ồ ngon nhưng tại sao phải làm thế?” Bởi vì khái niệm clean code mà ta đã học trước đây: một hàm chỉ nên thực hiện một chức năng. Khi bạn có một lớp hoặc hàm chứa if, tức là bạn đang muốn nó thực hiện nhiều việc. Luôn nhớ, chỉ một mà thôi.

Chưa tốt:

class Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        switch ($this->type) {
            case '777':
                return $this->getMaxAltitude() - $this->getPassengerCount();
            case 'Air Force One':
                return $this->getMaxAltitude();
            case 'Cessna':
                return $this->getMaxAltitude() - $this->getFuelExpenditure();
        }
    }
}

Tốt:

interface Airplane
{
    // ...

    public function getCruisingAltitude(): int;
}

class Boeing777 implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getPassengerCount();
    }
}

class AirForceOne implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude();
    }
}

class Cessna implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getFuelExpenditure();
    }
}

Còn tiếp….

TopDev via Chungnguyen

Xem ngay những tin đăng tuyển dụng IT mới nhất trên TopDev

Tìm Hiểu Chủ Đề Các Vòng Thi Kambria Code Challenge Và Thử Sức Vòng Thi Cũ Trong Hôm Nay!

Kambria Code Challenge là chuỗi các bài thi và hackathon trực tuyến liên quan đến lĩnh vực Trí tuệ nhân tạo (AI) được thiết kế để thu hút sự tham gia của các lập trình viên khắp nơi trên thế giới với giải thưởng lớn và cơ hội kết nối nghề nghiệp dành cho các lập trình viên AI.

Kambria Code Challenge sẽ có 4 Quiz, trong đó Quiz 04 có giải thưởng lớn nhất. Để tham gia Quiz 04 này, bạn phải tham dự ít nhất 2 Quiz bất kỳ trước đó.

Mỗi Quiz sẽ xoay quanh từng chủ đề về AI, bao gồm:

🔹 Quiz 1: Multilayer Neural Network

🔹 Quiz 2: Convolutional Neural Network

🔹 Quiz 3: Recurrent Neural Network

🔹 Quiz 4: AI Projects

Quiz 02 của Kambria Code Challenge sẽ diễn ra vào thứ 7 tuần này. Bạn xem hướng dẫn đăng ký ở cuối bài viết hoặc click vào link sau để đăng ký tham gia nhé.

Thử Giải Đề Thi Quiz 01, Chuẩn Bị Cho Quiz 02!

Để có sự chuẩn bị tốt nhất cho Quiz 02, các bạn có thể luyện tập giải các đề thi về AI đã diễn ra trong Quiz 01 của Kambria. Các vòng thi Quiz có những dạng đề bài tương tự nhau, nên việc luyện tập các vòng thi trước sẽ giúp bạn làm quen và nâng cao điểm số khi tham gia dự thi.

Bạn có thể luyện tập Quiz 01 ngay tại đường link dưới đây. Ngoài ra, bạn cũng có thể xem lại bài giải của Quiz 01 sau khi hoàn thành xong.

Link Practice Quiz 01: http://bit.ly/KambriaQuiz01 

Kambria cũng chuẩn bị các tài liệu khác để giúp bạn ôn tập kiến thức. Bạn có thể xem tại đây: https://app.kambria.io/library

—–

Thông tin Quiz 02

⏰Thời gian: 14:00 – 14:45 UTC + 7, Thứ Bảy, ngày 29 tháng 2 năm 2020

📌Đăng ký tại: http://bit.ly/KambriaQuiz02

📢Để biết thêm chi tiết, vui lòng truy cập: http://bit.ly/KambriaQuiz02Announcement

Các thành phần trong hệ sinh thái .NET

Thật sự thì có rất nhiều bạn trong đó có mình đã và đang làm việc với .NET rất lâu năm và nhất nhiều. Tuy nhiên, khi được hỏi và giải thích cho một ai đó rõ về hệ sinh thái của .NET cũng như những khác biệt giữa .NET Framework, .NET Core và .NET Standard như thế nào thì thật sự mọi người thường bị lúng túng và ú ớ trả lời.

Trong bài viết này chúng ta sẽ cùng nhau làm sáng tỏ những mù mờ về những khái niệm này. Qua đó cũng giúp chúng ta có được một kiến thức nền tảng tốt về những gì chúng ta đang sử dụng để có thể phát triển dự án tốt hơn, chất lượng hơn.

Hệ sinh thái .NET

.NET là nền tảng mã nguồn mở do Microsoft phát triển. Dùng để hỗ trợ cho các developers xây dựng các ứng dụng khác nhau và hỗ trợ ứng dụng chạy đa nền tảng (cross-platform).

Các vị trí tuyển dụng .NET hấp dẫn online

.NET là một hệ sinh thái do đó nó hỗ trợ developers nhiều ngôn ngữ lập trình (C#, VB, F#,… ), nhiều trình soạn thảo (VS Code, Visual Studio) và hàng loạt các thư viện để xây dựng các ứng dụng như: web, mobile app, desktop app, gaming và IoT.

>> Xem thêm: TERAAPP.NET – Ứng dụng viết app mobile

Các thành phần kiến trúc chính trong .NET

  • .NET Implementations
  • .NET Runtimes
  • .NET Standard
  • .NET Toolings and Common Infrastructure
This image has an empty alt attribute; its file name is ecosystem_grid_v2.png

Trong bài này, chúng ta chỉ tập trung tìm hiểu về .NET Ecosystem. Thế nên, để hiểu rõ hơn code bên trong các ứng dụng .NET hoạt động thế nào chúng ta xem hình minh họa sau đây:

Hình 3

Trong Hình 3, phần Compile time là quá trình build (lúc bạn code), còn Runtime là quá trình thực thi chương trình (lúc bạn chạy chương trình).

Sau khi bạn viết code xong, bạn có thể dùng tool Visual Studio để build hoặc dùng lệnh ‘dotnet build‘, lúc này source code của bạn sẽ được biên dịch sang một ngôn ngữ trung gian được gọi là MSIL (Microsoft Intermediate Language). Quá trình này gọi là Compile time.

Khi ứng dụng được chạy, một thành phần trong .NET có tên là CLR (Common Language Runtime) sẽ tiến hành dịch mã MSIL thành mã máy (Native Code) để máy tính có thể hiểu và thực thi. Quá trình này gọi là JIT (Just-In-Time) hay Runtime.

Một số ứng dụng .NET thường chạy lần đầu lâu hơn những lần sau đó rất nhiều là vì lần tiên được chạy CLR tiến hành dịch mã MSIL thành mã máy (quá trình JIT), ở những lần chạy tiếp theo, không cần phải JIT nữa vì nó đã được biên dịch trước đó, nên thời gian thực thi nhanh hơn nhiều.

Xem thêm: Bách khoa toàn thư .NET

Vậy tại sao lại cần có ngôn ngữ trung gian?

Ngôn ngữ trung gian (MSIL) trong .NET khá gần với mã máy (Native code) nhưng không chứa thông tin cụ thể về CPU. Việc giúp cho đoạn code trung gian của bạn có thể hoạt động trên nhiều loại CPU (32bit, 64bit), cũng như nhiều loại kiến trúc khác (ARM, Intel,…)

Trên thực tế có một vài ngôn ngữ như JavaScript, Python,… không dùng đến ngôn ngữ trung gian. Lúc này source code sẽ được dịch thẳng ra mã máy tại thời điểm Runtime. Ưu điểm của việc này là quá trình build được đơn giản hóa và nhanh hơn. Tuy nhiên, hiệu năng sẽ bị hạn chế.

Ngoài ra thời gian biên dịch (compile time), thời gian chạy (runtime), trong .NET còn có các công cụ như:

  • Tự động quản lý bộ nhớ: khi làm việc với các ngôn ngữ bậc cao như C#, VB.NET bạn không cần giải phóng bộ nhớ bằng cách gọi free() như khi làm việc với C/C++. CLR bao gồm một công cụ dọn rác gọi là Grabage Collector (GC) sẽ tự động giải phóng những phần bộ nhớ không được sử dụng.
  • Strong Typings: CLR quản lý thông tin về các kiểu dữ liệu mà sử dụng. Điều này giúp cho bạn có thể phần biệt được các định dạng thông tin của từng biến khác nhau.

Bài viết liên quan:

Vậy .NET còn các thư viện (Libraries) và công cụ (Toolings) nào nữa không?

Khi bạn làm việc với .NET, source code của bạn sẽ tương tác với rất nhiều class khác nhau. Trong đó có các class do thư viện .NET hỗ trợ như: System.String, System.WebSystem.DataSystem.IO,… Các class thư viện này trong .NET gọi là Base Class Library (BCL).

Các công cụ (Toolings) của .NET bao gồm compiler và Visual Studio. Trong .NET hệ thống build dùng MSBuild của Microsoft. Còn đối với .NET Core chúng ta có thể dùng công cụ dòng lệnh ‘dotnet cli‘.

Tổng hợp các thư viện và công cụ .NET:

Thư Viện (Libraries)

  1. ASP.NET Core: Framework để xây dựng ứng dụng web và API mạnh mẽ, linh hoạt.
  2. Entity Framework Core: ORM (Object-Relational Mapper) để làm việc với cơ sở dữ liệu một cách hiệu quả và dễ dàng.
  3. Xamarin.Forms: Thư viện để phát triển ứng dụng di động đa nền tảng với một mã nguồn duy nhất.
  4. ML.NET: Thư viện học máy cho .NET, hỗ trợ tích hợp các mô hình học máy vào ứng dụng.
  5. SignalR: Thư viện để xây dựng các ứng dụng web thời gian thực.
  6. gRPC for .NET: Thư viện cho gRPC, một framework RPC hiệu suất cao, đa ngôn ngữ.
  7. Newtonsoft.Json: Thư viện phổ biến để xử lý JSON trong .NET.

Công Cụ (Toolings)

  1. Visual Studio: IDE toàn diện, hỗ trợ phát triển, debug và triển khai ứng dụng .NET.
  2. Visual Studio Code: Trình soạn thảo mã nguồn nhẹ, mã nguồn mở, với nhiều tiện ích mở rộng cho lập trình .NET.
  3. .NET CLI (Command Line Interface): Công cụ dòng lệnh để tạo, xây dựng, chạy và xuất bản các ứng dụng .NET.
  4. NuGet: Hệ thống quản lý gói cho .NET, cung cấp các thư viện và công cụ cần thiết.
  5. MSBuild: Công cụ xây dựng dự án và quản lý quy trình xây dựng.
  6. Roslyn: Trình biên dịch mã nguồn mở cho C# và VB.NET, hỗ trợ phân tích mã và cung cấp các công cụ phát triển.

Đọc thêm:

TopDev via Thangphampt

Xem thêm các vị trí .NET job mới nhất tại đây

JavaScript Tips – Những điều có thể hay trong JS (P1)

Những điều có thể bạn đã biết hoặc chưa, những kinh nghiệm của mình chia sẽ khi lập trình javascript, mong nó có thể giúp ích cho các bạn đang lập trình ngôn ngữ này 

Tránh lỗi khi dùng những default function của array

Các default array function của javascript như map, forEach, filter,… sẽ bị lỗi nếu như trường hợp array của bạn null hoặc undefined (vd: nhận dữ liệu từ server), nó khá nguy hiểm nếu bạn nào quên, khiến ứng dụng mình stop ngay lập tức. Các bạn nên check trước khi gọi các function đó. Bạn có thể lồng nó vào scope của if hoặc gọn hơn thì viết như này.

var newArr = array && array.map(function(item){
    //DO SOMETHING
});

Tìm việc làm Javascript lương cao online

Sử dụng Ternary Operator cho gọn gàng

Ternary Operator, mấy bạn Mễ gọi thế :D, nó là ? : mà các bạn hay sài thôi. Các bạn có thể sử dụng thay cho if else, nhưng nhiều trường hợp nó tiện hơn rất nhiều.Ví dụ:

//jquery css
$element.css('color', per$.isFirstTab ? 'red' : 'green');

//set value for a variable
var color = per$.isFirstTab ? 'red' : 'green';

//Call a function
removeItem(per$.isFirstTab ? item1 : item2); //input values
module[per$.isFirstTab ? 'remove' : 'add'](item); //methods

Hàm map của javascript và jquery

Hai cái này thì nó khác nhau, bạn nào hay sài thì sẽ thấy, function map native của javascript nó sẽ return các giá trị undefined cho những item không thỏa điều kiện, còn jquery thì giúp chúng ta điều đó.

var array = [2, 3, 4, 5, 6, 7, 8];
var map1 = array.map(function(item) {
    if (item > 4)
        return item;
});
var map2 = $.map(array, function(item) {
    if (item > 4)
        return item;
})
console.log(map1); //[undefined, undefined, undefined, 5, 6, 7, 8]
console.log(map2); //[5, 6, 7, 8]

Nên nếu viết bằng hàm map của javascript thì bạn có thể viết lại bằng cách dùng hàm filter của native như thế này:

var array = [2, 3, 4, 5, 6, 7, 8];
var map1 = array.map(function(item) {
    if (item > 4)
        return item;
}).filter(function(item) {
    return item;
});

console.log(map1); //[5, 6, 7, 8]

Iterator trong Javascript

Nếu bạn xử lý nhiều trong javascript thì bạn hay dùng những hàm thông dụng như forEach, map, filter, reduce,… kiểu lập trình như vậy được gọi là Declarative Proramming). Mình sẽ viết lại các hàm mà javascript cung cấp đã được liệt kê ở trên để làm ví dụ.

Hàm forEach

var array = [2, 3, 4, 5, 6, 7, 8];
Array.prototype.foreach = function(iterator) {
    for (var i = 0, len = this.length; i < len; i++) {
        iterator && iterator(this[i]);
    }
}

array.foreach(function(item) {
    if (item > 4)
        console.log(item)
});
//5,6,7,8

Imgur

Hàm map

var array = [2, 3, 4, 5, 6, 7, 8];
Array.prototype.mapping = function(iterator) {
    var list = [];
    for (var i = 0, len = this.length; i < len; i++) {
        if (iterator)
            list.push(iterator(this[i]));
    }
    return list;
}

var newArr = array.mapping(function(item) {
    return item * item;
});

console.log(newArr);//[4, 9, 16, 25, 36, 49, 64]

Hàm remove

Trong javascript không support chúng ta hàm remove 1 item theo 1 điều kiện nào đó nên chúng ta có thể viết lại cho tiện sử dụng, dù chúng ta có thể sử dụng forEach hoặc nhiều cách khác để làm nhưng làm như thể sẽ thấy nó rõ ràng dễ maintain hơn.

var array = [2, 3, 4, 5, 6, 7, 8];
Array.prototype.remove = function(iterator) {
    for (var i = this.length; i >= 0; i--) {
        if (iterator && iterator(this[i]))
            this.splice(i, 1);
    }
}

array.remove(function(item) {
    return item > 4;
});

console.log(array);//2,3,4

Kết thúc ep1, nhớ tiếp những gì hay hay mình sẽ chia sẽ tiếp ở series này phần sau.

TopDev via Jinhduong

Bạn có thể tham khảo các vị trí ngành IT hấp dẫn khác tại đây

.NET – Strong Named Assembly là gì?

Tìm hiểu về các khái niệm liên quan cũng như cách tạo một strong named assembly trong .NET.

DLL Hell

Đây là một rắc rối được nhắc đến từ lâu của Windows trong việc quản lý các thư viện dùng chung của hệ thống. Các tập tin thư viện thường được đặt trong một thư mục chung (như System32) để sử dụng cho toàn hệ thống. Đối mặt với những phần mềm được cài đặt và gỡ bỏ thường xuyên khỏi hệ thống, không ít trường hợp các ứng dụng khác nhau sử dụng những thư viện trùng tên tập tin (hoặc cùng một thư viện nhưng khác phiên bản) và chép đè nhau trong thư mục chung đó.
Kết quả là một vài ứng dụng sẽ tham chiếu đến sai thư viện và khiến nó hoạt động sai hoặc thậm chí không thể hoạt động được. Và các thư viện nằm trong hệ thống đôi khi trở thành “rác” khi việc xóa bỏ chúng tương đối “nhạy cảm” và có thể gây ảnh hướng đến các ứng dụng khác.

DLL Hell

Weak Named và Strong Named assembly

Vậy Strong Named Assembly trong .NET là gì?

Để không gặp phải vấn đề “DLL Hell”, CLR hỗ trợ hai loại assembly là weak name và strong name. Điểm khác biệt duy nhất của hai loại này nằm ở việc strong named assembly được định danh với một cặp khóa public/private.

.NET tuyển dụng lương cao up to 1500USD

Tương ứng với hai loại này, có hai cách để bạn sử dụng chúng:

  • Private: với weak named assembly, bạn chỉ có thể sử dụng nó trong cùng thư mục với chương trình.
  • Public (hay Shared): assembly được đặt vào một thư mục chung (gọi là GAC – Global Assembly Cache) trong hệ thống và các ứng dụng có thể sử dụng nó. với strong named assembly, bạn có thể sử dụng nó dưới dạng Private lẫn Public.

Dĩ nhiên chỉ dựa vào tên tập tin để định danh cho các assembly thì không đủ. Chính vì vậy một strong named assembly được định danh dựa vào 4 thuộc tính:

  • Tên tập tin (không bao gồm phần mở rộng).
  • Số hiệu phiên bản (version).
  • Văn hóa (culture).
  • Một khóa public.

Bởi vì một khóa public khá lớn nên nó sẽ được hash để tạo ra một chuỗi giá trị gọi là “public key token”, dựa vào đây bạn sẽ xác định được một assembly có phải là strong name hay không. Bạn có thể coi thuộc tính Assembly.FullName để thấy được 4 giá trị định danh này. Ví dụ với một weak named assembly:

typeof(Foo).Assembly.FullName

// Output: ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Với một strong named assembly, bạn sẽ thấy được giá trị của PublicKeyToken:

typeof(Console).Assembly.FullName

// Output: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Tạo và cài đặt strong named assembly vào GAC

Trong Visual Studio, bạn có thể tạo một strong named assembly bằng cách mở cửa sổ Properties của project. Trong thẻ Signing, bạn đánh dấu chọn Sign the assembly. Trong mục Choose a strong name key file bên dưới, bạn có thể tạo hoặc chọn một khóa public có sẵn.

Visualstudio - Signing assembly

Ngoài ra bạn có thể tạo thông qua cửa sổ dòng lệnh với Visual Studio Command Prompt.

Sau đó bạn có thể cài đặt assembly vào GAC bằng cách dùng công cụ GACUtil.exe.

gacutil /i MyLibrary.dll
Microsoft (R) .NET Global Assembly Cache Utility. Version 4.0.30319.0
Copyright (c) Microsoft Corporation. All rights reserved.

Assembly successfully added to the cache

Để gỡ assembly ra khỏi GAC, bạn cần sử dụng tên đầy đủ của assembly với tùy chọn /u:

gacutil /u “ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b14988ec222fb4a1”
Uninstalled: ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b14988ec222fb4a1, processorArchitecture=MSIL
Number of assemblies uninstalled = 1
Number of failures = 0

TopDev via Yinyangit

Các bài viết liên quan:

  1. Hệ sinh thái .NET – Anh Em Nhà .NET
  2. Bách khoa toàn thư .Net – Các khái niệm cơ bản cần biết
  3. teraapp TERAAPP.NET – Tạo ứng dụng di động bằng .NET
  4. Fix lỗi .NET runtime optimization service

Lọc các vị trí việc làm cho ngành it hấp dẫn tại Topdev