Trong số 35 triệu project nguồn mở trên GitHub, có rất nhiều gói phần mềm phức tạp dành cho doanh nghiệp trên toàn cầu. Số khác thì nhẹ hơn, là các thư viện code phục vụ cho 1 mục đích mà các dev không thể sống thiếu. Và những cái còn lại, chỉ để cho vui thôi.
Code joke với người ngoài sẽ nghe sẽ hơi kiểu “chỉ dân lập trình mới hiểu”, “những câu đùa của nerd”. Nó sẽ cần một đoạn chú thích nhỏ, nhưng đừng lo – đó là lý do chúng ta ở đây. Dưới đây là top những project “độc nhất vô nhị” trên GitHub.
TrumpScript là một ngôn ngữ lập trình ảo tạo ra bởi 4 sinh viên Đại học một cuộc thi Hackathon trong 36 tiếng. Họ làm ra nó vì nhận thấy “không có ngôn ngữ lập trình nào ở hiện tại có quá nhiều tiêu chuẩn và có thể đáp ứng được những đòi hỏi của Trump như những gì ông ta mong đợi ở đất nước mình.”
Tạo nên nhờ 1,000 dòng code, TrumpScript dường như hoạt động được ổn định như một ngôn ngữ lập trình thông thường. Nó có một số feature như sau:
TrumpScript chỉ cho phép lập trình viên làm việc với những số lớn hơn 1 triệu, bởi vì “the small stuff is inconsequential”. (những thứ nhỏ nhặt là không xứng đáng) Những số dưới 1 triệu sẽ xuất ra một tin báo lỗi dựa trên câu nói của Donald Trump: “I’m really rich, Part of the beauty of me is I’m very rich.” (Tôi khá giàu, và một trong những nét đẹp trong tôi đó là sự giàu có)
TrumpScript không cho phép dùng phân số hoặc số thập phân – chỉ được dùng nguyên số, bởi “America never does anything halfway”. (Người Mỹ không bao giờ làm gì nửa vời cả)
Nếu user muốn chạy TrumpScript trên máy tính Microsoft, nó sẽ xuất tin báo lỗi: “Windows? ‘The big problem this country has is being PC.’”
“Tất cả mọi chương trình đều phải kết thúc là ‘America is great’.”
Nghe mô rả thật sự thú vị, nhưng TrumpScript sẽ khá khó sử dụng vì sẽ bị báo lỗi thường xuyên, do “code không đúng tiêu chuẩn của Trump”. Như lời của những người tạo ra nó nó bởi vì “Trump không thích nói nhiều về những sự thất bại của mình.”
is-thirteen: Phần mềm check xem một số có bằng 13 hay không
Trong các ngôn ngữ lập trình thông thường, câu lệnh check một số có bằng 13 hay không sẽ trông như thế này:
if (someNumber == 13)
// true
else
//false
Logo của is-thirteen
Trông thật đơn giản phải không? Nhưng phần mềm này thì không. is-thirteen là một project gồm hàng trăm dòng code, 92 contributor, và một chính sách viết code dài ngoằn, tất cả phục vụ cho một phần mềm check giá trị của một số có bằng 13 hay không.
Project có vẻ làm ra cho vui như một phần mềm cung cấp một tính năng “vô duyên” không cần thiết. Trước đây cũng có một project check xem số có lớn hơn 0 không… Các phần mềm nhỏ, một tính năng như vậy đã gây tranh cãi nhiều từ hồi tháng Ba khi một cái bị xoá – một bước đi làm chao đảo thế giới lập trình toàn cầu.
Phần highlight của project is-thirteen là phần của GitHub repository nơi mọi người có thể đăng câu hỏi, comment, và các request thêm các feature. Dưới đây là một số ví dụ:
Nếu bạn cần một phần mềm như is-thirteen, hãy đảm bảo rằng bạn đã đọc phần README trên đầu trang: “LÀM ƠN ĐỌC KĨ SOURCE CODE vì chúng tôi đi rất nhanh và hơi phá hoại.”
ComcastifyJS (bởi The Onion)
Ảnh một bé koala chỉ load một nửa (ClickHole)
Có bao giờ bạn tự hỏi, tại sao có những trang web load ảnh… quá nhanh không? Ồ không sao, bởi vì đã có project của chúng tôi!
Hoá ra, thương hiệu độc quyền hài hước này của The Onion không chỉ là một chuyện nhảm trong số nhiều project tuyệt vời và “real” khác của mình. ComcastifyJS, là một thư viện JavaScript “giúp” làm cho ảnh trên web page load chậm hơn so với thông thường.
File README của project có ghi “Với tình trạng hiện tại của Internet, đôi khi bạn chỉ muốn trải nghiệm thử một trang web load chậm. Nó trao cho người dùng cơ hội trải nghiệm cảm giác tĩnh lặng và mong chờ thông qua thao tác load ảnh chậm với ComcastifyJS!”
Các developer còn tạo cả một page demo, ví dụ cho việc load ảnh “cực chậm”. Còn có người comment đã đề xuất add thêm “hệ thống đòi phí mà thành viên premium có thể giảm tốc độ tải ảnh HƠN so với người dùng free!!!” Người khác thì gợi ý biến nó trở thành một tiện ích Chrome.
Có vẻ như, thư viện này được viết dựa trên story trên ClickHole tên là “Các bé Koala này không muốn load thêm để ủng hộ Net Neutrality.” Các dev khác từ The Onion cũng đã upload lên GitHub fartscroll.js, một plugin có tiếng xì hơi khi bạn kéo web page, và Betty Cropper, một tool cắt ảnh tên lạ.
Bản thay thế lorem ipsum
Khi tạo các webpage, designer và developer thường dùng placeholder text gọi là “lorem ipsum” để preview text trông như thế nào ở một vùng nhất định trên page. Nó thay cho đoạn text “text here text here text here” phiên bản tiếng Latin. Có rất nhiều web cho dev có thể tạo và copy một số lorem ipsum. Nó trông như thế này:
ipsum dolor sit amet, consectetur adipiscing elit. Quisque consequat eleifend justo vitae facilisis. Praesent ut felis in velit feugiat accumsan.
Với nhiều dev thì một vài text đơn giản tiếng Latin là chưa đủ. Trong awesome-ipsum repository trên GitHub, có một list hàng tá các thay thế cho placeholder text. Dưới đây là một số cái tôi thích, kèm cả ví dụ của text mà họ đã tạo:
Pasta Ipsum: “Feature nhiều loại pasta, bao gồm option được add các từ ngẫu nhiên.”
Pasta ipsum dolor sit amet farfalloni marziani mafalde shit ricciutelle pappardelle rat fart lasagne spaghettini orzo. Lasagne lasagnette conchiglie frakking sumbitch cellentani fagioloni maltagliati conchiglie farfalloni. Creste di galli strozzapreti penne zita asshole mafaldine pastina asshole foglie d’ulivo.
Hipster Ipsum: “Điền text thủ công cho site hoặc project.”
Brooklyn photo booth blue bottle tumblr, franzen 8-bit crucifix meh godard four dollar toast neutra cray asymmetrical. Pug DIY brunch trust fund XOXO, lo-fi sartorial kickstarter heirloom kitsch plaid bespoke pork belly tacos viral.
Corporate Ipsum: “Được design để đáp ứng nhu cầu.”
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition.
It’s a Bieber world live it or die I make good grilled cheese and I like girls don’t be so cold, we could be fire. And all the haters I swear they look so small from up here swag swag swag, on you, chillin by the fire.
Các library Mock JavaScript
Trong thế giới lập trình, các dev luôn cố gắng làm cho mọi thứ dễ dàng hơn. Thông thường họ sẽ viết library, là các extension của ngôn ngữ lập trình để xử lý các task đơn giản, phổ biến. Các library cho phép lập trình viên dễ thực hiện task mà không cần phải viết code. Khi các thư viện xuất hiện ở khắp nơi, như nhiều cái trong JavaScript, nó giúp người mới dùng ngôn ngữ, nhưng có thể họ sẽ không biết mình đang làm gì. Do đó, họ thường phải dùng thư viện vận hành mà JavaScript tự xử lý được.
Từ đó, một số dev tạo ra các thư viện mock dùng để mua vui lẫn cho những phản hồi có tính xây dựng trên các thư viện thực:
vapor.js là một thư viện JavaScript không chứa dòng code nào. Trên project page, nó được gán cho là “Thư viện JavaScript nhanh nhất và nhỏ nhất trên thế giới.”
semicolon.js là một vapor.js phiên bản đáng tin hơn và an toàn hơn, và nó chứa các dấu chấm phẩy.
Tương tư vậy, vanilla.js là một JavaScript compiler gộp JavaScript vào JavaScript. Thế thôi, không làm gì khác. Nhưng GitHub page của nó cũng đủ thuyết phục để làm cho người ta tin vào nó.
Phần mềm được xem là tốt khi khi nó có kiến trúc tốt. Kiến trúc phần mềm tương tự như móng nhà, móng yếu nhà sẽ không vững. Để viết được phần mềm tốt bạn phải học rất nhiều, điều đầu tiên bạn cần biết là SOLID.
SOLID ra đời như thế nào?
Lập trình hướng đối tượng (object oriented programming – OOP) là một trong những mô hình lập trình được sử dụng nhiều nhất. Các tính chất đặc biệt khiến việc hướng đối tượng trở nên hiệu quả đó là:
Tính trừu tượng (abstraction): Tạo ra các lớp trừu tượng mô hình hoá các đối tượng trong thế giới thực.
Tính đóng gói (Encapsulation): Các thực thể của lớp trừu tượng có các giá trị thuộc tính riêng biệt.
Tính kế thừa (Inheritance): Các đối tượng có thể dễ dàng kế thừa và mở rộng lẫn nhau.
Tính đa hình (Polymorphism): Có thể thực hiện một hành động đơn theo nhiều cách thức khác nhau tuỳ theo loại đối tượng cụ thể đang được gọi.
Các tính chất đặc biệt này của OOP giúp chúng ta xây dựng được các chương trình giải quyết được nhiều vấn đề cụ thể khác nhau trong thế giới thực. Hầu hết lập trình viên đều đã biết các tính chất này của OOP, nhưng cách thức để phối hợp các tính chất này với nhau để tăng hiệu quả của ứng dụng thì không phải ai cũng nắm được. Một trong những chỉ dẫn để giúp chúng ta sử dụng được OOP hiệu quả hơn đó là nguyên tắc SOLID.
SOLID là gì?
SOLID là viết tắt của 5 chữ cái đầu trong 5 nguyên tắc thiết kế hướng đối tượng. Giúp cho lập trình viên viết ra những đoạn code dễ đọc, dễ hiểu, dễ maintain. Nó được đưa ra bởi Robert C. Martin và Michael Feathers. 5 nguyên tắc đó bao gồm:
Single responsibility priciple (SRP)
Open/Closed principle (OCP)
Liskov substitution principe (LSP)
Interface segregation principle (ISP)
Dependency inversion principle (DIP)
Single responsibility priciple
Nội dung:
Mỗi lớp chỉ nên chịu trách nhiệm về một nhiệm vụ cụ thể nào đó mà thôi.
Nguyên lý đầu tiên ứng với chữ S trong SOLID, có ý nghĩa là một class chỉ nên giữ một trách nhiệm duy nhất. Một class có quá nhiều chức năng sẽ trở nên cồng kềnh và trở nên khó đọc, khó maintain. Mà đối với ngành IT việc requirement thay đổi, cần thêm sửa chức năng là rất bình thường, nên việc code trong sáng, dễ đọc dễ hiểu là rất cần thiết.
Ví dụ: Hình dung rằng nhân viên của một công ty phần mềm cần phải làm 1 trong 3 việc sau đây: lập trình phần mềm (developer), kiểm tra phần mềm (tester), bán phần mềm (salesman). Mỗi nhân viên sẽ có một chức vụ và dựa vào chức vụ sẽ làm công việc tương ứng. Khi đó bạn có nên thiết kế lớp “Employee” với thuộc tính “position” và 3 phương thức developSoftware(), testSoftware() và saleSoftware() không?
class Employee
{
string position;
function developSoftware(){};
function testSoftware(){};
function saleSoftware(){};
}
Câu trả lời là KHÔNG. Thử hình dung nếu có thêm một chức vụ nữa là quản lí nhân sự, ta sẽ phải sửa lại lớp “Employee”, thêm phương thức mới vào sao? Nếu có thêm 10 chức vụ nữa thì sao? Khi đó các đối tượng được tạo ra sẽ dư thừa rất nhiều phương thức: Developer thì đâu cần dùng hàm testSoftware() và saleSoftware() đúng không nào, lỡ may dùng lầm phương thức cũng sẽ gây hậu quả khôn lường.
Áp dụng nguyên tắc Single Responsibility: mỗi lớp 1 trách nhiệm. Ta sẽ tạo 1 lớp trừu tượng là “Employee” có phương thức là working(), từ đây bạn kế thừa ra 3 lớp cụ thể là Developer, Tester và Salesman. Ở mỗi lớp này bạn sẽ implement phương thức working() cụ thể tuy theo nhiệm vụ của từng người. Khi đó chúng ta sẽ bị tình trạng dùng nhầm phương thức nữa.
Open/Closed principle
Nội dung:
Không được sửa đổi một Class có sẵn, nhưng có thể mở rộng bằng kế thừa.
Nguyên lý thứ 2 ứng với chữ O trong SOLID.
Theo nguyên lý này, mỗi khi ta muốn thêm chức năng cho chương trình, chúng ta nên viết class mới mở rộng class cũ (bằng cách kế thừa hoặc sở hữu class cũ) chứ không nên sửa đổi class cũ. Việc này dẫn đến tình trạng phát sinh nhiều class, nhưng chúng ta sẽ không cần phải test lại các class cũ nữa, mà chỉ tập trung vào test các class mới, nơi chứa các chức năng mới.
Thông thường việc mở rộng thêm chức năng thì phải viết thêm code, vậy để thiết kế ra một module có thể dễ dàng mở rộng nhưng lại hạn chế sửa đổi code ta cần làm gì. Cách giải quyết là tách những phần dễ thay đổi ra khỏi phần khó thay đổi mà vẫn đảm bảo không ảnh hưởng đến phần còn lại.
Ví dụ:
Đặt vấn đề: Ta cần 1 lớp đảm nhận việc kết nối đến CSDL. Thiết kế ban đầu chỉ có SQL Server và MySQL. Thiết kế ban đầu có dạng như sau:
class ConnectionManager
{
public function doConnection(Object $connection)
{
if($connection instanceof SqlServer) {
//connect with SqlServer
} elseif($connection instanceof MySql) {
//connect with MySql
}
}
}
Sau đó yêu cầu đặt ra phải kết nối thêm đến Oracle và một vài hệ CSDL khác.
Để thêm chức năng ta phải thêm vào code những khối esleif khác, việc này làm code cồng kềnh và khó quản lý hơn.
Giải pháp:
Áp dụng Abstract thiết kế lại các lớp SqlServer, MySql, Oracle…
Các lớp này đều có chung nhiệm vụ tạo kết nối đến csdl tương ứng có thể gọi chung là Connection.
Cách thức kết nối đến csdl thay đổi tùy thuộc vào từng loại kết nối nhưng có thể gọi chung là doConect.
Vậy ta có lớp cơ sở Connection có phương thức doConnect, các lớp cụ thể là SqlServer, MySql, Oracle… kế thừa từ Connection và overwrite lại phương thức doConnect phù hợp với lớp đó.
Thiết kế sau khi làm lại có dạng như sau:
abstract class Connection()
{
public abstract function doConnect();
}
class SqlServer extends Connection
{
public function doConnect()
{
//connect with SqlServer
}
}
class MySql extends Connection
{
public function doConnect()
{
//connect with MySql
}
}
class ConnectionManager
{
public function doConnection(Connection $connection)
{
//something
//.................
//connection
$connection->doConnect();
}
}
Với thiết kế này khi cần kết nối đến 1 loại csdl mới chỉ cần thêm 1 lớp mới kế thừa Connection mà không cần sửa đổi code của lớp ConnectionManager, điều này thỏa mãn 2 điều kiện của nguyên lý OCP.
Liskov substitution principle
Nội dung:
Các đối tượng (instance) kiểu class con có thể thay thế các đối tượng kiểu class cha mà không gây ra lỗi.
Nguyên tắc thứ 3, ứng với chữ L trong SOLID.
Minh hoạ một trường hợp vi phạm nguyên tắc Liskov substitution. Nếu thiết kế lớp như thế này, thì lớp CleanerStaff sẽ dùng được hàm checkAttendance(), mà điều này là không đúng, nên đây sẽ là một kiểu thiết kế sai nguyên tắc.
Quay trở lại ví dụ lớp Emloyee trong phần 1, ta giả sử có công ty sẽ điểm danh vào mỗi buổi sáng, và chỉ có các nhân viên thuộc biên chế chính thức mới được phép điểm danh. Ta bổ sung phương thức checkAttendance() vào lớp Employee.
Hình dung có một trường hợp sau: công ty thuê một nhân viên lao công để làm vệ sinh văn phòng, mặc dù là một người làm việc cho công ty nhưng do không được cấp số ID nên không được xem là một nhân viên bình thường, mà chỉ là một nhân viên thời vụ, do đó sẽ không được điểm danh.
Nguyên tắc này nói rằng: Nếu chúng ta tạo ra một lớp CleanerStaff kế thừa từ lớp Employee, và implement hàm working() cho lớp này, thì mọi thứ đều ổn, tuy nhiên lớp mới này cũng lại có hàm checkAttendance() để điểm danh, mà như thế là sai quy định dẫn đến chương trình bị lỗi. Như vậy, thiết kế lớp CleanerStaff kế thừa từ lớp Employee là không được phép.
Có nhiều cách để giải quyết tình huống này ví dụ như tách hàm checkAttendance() ra một interface riêng và chỉ cho các lớp Developer, Tester và Salesman implements interface này.
Interface segregation principle
Nội dung:
Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với nhiều mục đích cụ thể.
Nguyên lý này rất dễ hiểu. Hãy tưởng tượng chúng ta có 1 interface lớn, khoảng 100 methods. Việc implements sẽ rất vất vả vì các class impliment interface này sẽ bắt buộc phải phải thực thi toàn bộ các method của interface. Ngoài ra còn có thể dư thừa vì 1 class không cần dùng hết 100 method. Khi tách interface ra thành nhiều interface nhỏ, gồm các method liên quan tới nhau, việc implement và quản lý sẽ dễ hơn.
Chúng ta có 2 class Dog và Snake implement interface Animal. Nhưng thật vô lý, Dog thì làm sao có thể fly(), cũng như Snake không thể nào run() được? Thay vào đó, chúng ta nên tách thành 3 interface như thế này:
1.Các module cấp cao không nên phụ thuộc vào các modules cấp thấp. Cả 2 nên phụ thuộc vào abstraction.
2.Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại (Các class giao tiếp với nhau thông qua interface (abstraction), không phải thông qua implementation.)
Có thể hiểu nguyên lí này như sau: những thành phần trong 1 chương trình chỉ nên phụ thuộc vào những cái trừu tượng (abstraction). Những thành phần trừu tượng không nên phụ thuộc vào các thành phần mang tính cụ thể mà nên ngược lại.
Những cái trừu tượng (abstraction) là những cái ít thay đổi và biến động, nó tập hợp những đặc tính chung nhất của những cái cụ thể. Những cái cụ thể dù khác nhau thế nào đi nữa đều tuân theo các quy tắc chung mà cái trừu tượng đã định ra. Việc phụ thuộc vào cái trừu tượng sẽ giúp chương trình linh động và thích ứng tốt với các sự thay đổi diễn ra liên tục.
Ví dụ:
Lấy ví dụ về ổ cứng của máy tính, bạn có thể dùng loại ổ cứng thể rắn SSD đời mới để chạy cho nhanh, tuy nhiên cũng có thể dùng ổ đĩa quay HDD thông thường. Nhà sản xuất Mainboard không thể nào biết bạn sẽ dùng ổ SSD hay loại HDD đĩa quay thông thường. Tuy nhiên họ sẽ luôn đảm bảo rằng bạn có thể dùng bất cứ thứ gì bạn muốn, miễn là ổ đĩa cứng đó phải có chuẩn giao tiếp SATA để có thể gắn được vào bo mạch chủ. Ở đây chuẩn giao tiếp SATA chính là interface, còn SSD hay HDD đĩa quay là implementation cụ thể.
Trong khi lập trình cũng vậy, khi áp dụng nguyên lý này, ở những lớp trừu tượng cấp cao, ta thường sử dụng interface nhiều hơn thay vì một kiểu kế thừa cụ thể. Ví dụ, để kết nối tới Database, ta thường thiết kế lớp trừu tượng DataAccess có các phương thức phương thức chung như save(), get(), … Sau đó tùy vào việc sử dụng loại DBMS nào (vd: MySql, MongoDB, …) mà ta kế thừa và implement những phương thức này. Tính chất đa hình của OOP được vận dụng rất nhiều trong nguyên lý này.
Tổng kết
SOLID là 5 nguyên tắc cơ bản trong việc thiết kế phần mềm. Nó giúp chúng ta tổ chức sắp xếp các function, method, class một cách chính xác hơn. Làm sao để kết nối các thành phần, module với nhau.
Rõ ràng, dễ hiểu
Teamwork là điều không thể tránh trong lập trình. Áp dụng SOLID vào công việc bạn sẽ tạo ra các hàm tốt, dễ hiểu hơn. Giúp cho bạn và đồng nghiệp đọc hiểu code của nhau tốt hơn.
Dễ thay đổi
SOLID giúp tạo ra các module, class rõ ràng, mạch lạc, mang tính độc lập cao. Do vậy khi có sự yêu cầu thay đổi, mở rộng từ khách hàng, ta cũng không tốn quá nhiều công sức để thực hiện việc thay đổi.
Tái sử dụng
SOLID khiến các lập trình viên suy nghĩ nhiều hơn về cách viết phần mềm, do vậy code viết ra sẽ mạch lạc, dễ hiểu, dễ sử dụng.
LẬP TRÌNH LÀ NGÀNH CHƯA BAO GIỜ HẾT HOT VÀ NHU CẦU TUYỂN DỤNG IT CHƯA BAO GIỜ HẠ NHIỆT!
Ngành công nghệ lập trình đang tăng trưởng nhanh và là một trong những xu thế đầy tiềm năng trong lựa chọn nghề nghiệp của các bạn trẻ, đồng thời là ngành có nhu cầu tuyển dụng rất cao trong những năm gần đây. Có rất nhiều nhà tuyển dụng IT luôn phải trong tâm thế “chạy đua” cạnh tranh trong tìm kiếm ứng viên.
Để tuyển được lập trình viên phù hợp, các nhà tuyển dụng cần chú ý tiếp cận ứng viên thông qua các mô tả công việc sát với nhu cầu của dự án, sản phẩm mà lập trình viên sẽ tham gia phụ trách và đánh giá đúng thị trường tuyển dụng IT để có những quyết định tuyển dụng phù hợp nhất. Từng vị trí sẽ có những yêu cầu và trách nhiệm ra sao? Công việc cụ thể của một lập trình viên là gì?Hãy cùng TopDev khám phá mô tả công việc các vị trí dưới đây:
Tập trung vào mảng phát triển xây dựng giao diện và trải nghiệm cho người dùng, là người phụ trách phát triển hiển thị và trải nghiệm người dùng cho ứng dụng web
Chịu trách nhiệm chính cho Server của các ứng dụng chạy trên Web, hiểu đơn giản hơn là những hoạt động mà không thể nhìn thấy được ở trình duyệt. Chi tiết mô tả công việc tại đây
Xây dựng các ứng dụng cấp doanh nghiệp, quản lí quá trình phát triển ứng dụng bằng Java/ Java EE, đồng thời tham gia vào toàn bộ quá trình phát triển sản phẩm, từ lên ý tưởng, phân tích, thiết kế đến kiểm tra, vận hành thực tế theo kế hoạch
Vị trí này đảm nhiệm việc cài đặt, nâng cấp và giám sát phần mềm và phần cứng. Họ cũng có thể tham gia vào việc sao lưu và khôi phục dữ liệu. Bạn có thể tham khảo chi tiết các yêu cầu công việc của một lập trình system tại đây
Có nhiệm vụ tiến hành theo dõi và kiểm tra hệ thống liên tục và xuyên suốt trong quá trình sử dụng để đảm bảo rằng việc bảo trì và khắc phục sự cố luôn được xử lý kịp thời và nhanh chóng. Xem thêm các mô tả công việc hằng ngày cần có tại đây
Back-end Developer chịu trách nhiệm chính cho Server của các ứng dụng chạy trên Web, hiểu đơn giản hơn là những hoạt động mà không thể nhìn thấy được ở trình duyệt. Back-end Developer yêu cầu có kĩ năng lập trình phát triển ứng dụng hoặc cải tiến các ứng dụng có sẵn để trực tiếp với các kỹ sư đảm bảo sự thống nhất toàn hệ thống cũng nhưng cải thiện trải nghiệm của người dùng. Ngoài ra Back-end Developer còn có trách nhiệm phải tìm cách tối ưu chức năng, đảm bảo về tốc độ xử lý và hiệu suất của toàn bộ trang web.
YÊU CẦU
Kinh nghiệm phát triển ứng dụng/services về mặt Back-End.
Thành thạo lập trình với một trong các ngôn ngữ lập trình back-end
Nắm rõ toàn bộ quá trình phát triển web (thiết kế, phát triển và thực thi)
Có hiểu biết cơ bản về cơ sở dữ liệu và hệ thống: MySQL, MongoDB hay PostgreSQL
Có kiến thức về lập trình hướng đối tượng.
Khả năng làm việc tốt trong môi trường tốc độ cao.
MÔ TẢ CÔNG VIỆC
Tham gia vào toàn bộ vòng đời của ứng dụng, tập trung và coding và debug các dự án website và hệ thống
Viết API kết nối giữa các hệ thống, và phục vụ trao đổi dữ liệu với mobile & front-end
Xây dựng code có thể sử dụng lại và các thư viện để thuận tiện cho việc sử dụng trong tương lai
Thu thập và xử lí các yêu cầu thiết kế và kĩ thuật
Tham gia vào quá trình phân tích và thiết kế hệ thống;
Nghiên cứu và áp dụng các công nghệ mới để tối ưu hóa hiệu quả phát triển sản phẩm.
Đảm bảo sản phẩm làm ra cần phải chạy đúng nghiệp vụ và tốc độ xử lý cũng phải tối ưu cho lượng người dùng lớn
Lập trình viên Front-end là người tập trung phát triển phía Client Side, nói một cách đơn giản dễ hiểu là tập trung vào mảng phát triển xây dựng giao diện và trải nghiệm cho người dùng, là người phụ trách phát triển hiển thị và trải nghiệm người dùng cho ứng dụng web. Front-end Developer chính là người quyết định cái nhìn đầu tiên của người dùng về trang web, đồng thời mang lại một trang web dễ dàng thao tác và sử dụng.
Lập trình viên không đơn thuần chỉ là một coder giỏi mà còn phải có tư duy như một Designer, một Business Analyst (BA) có thể phát triển sản phẩm đẹp, tiện dụng mang lại trải nghiệm tốt nhất cho người dùng.
Bởi vì trong sự cạnh tranh khốc liệt thì một sản phẩm đẹp hơn sẽ chiếm được tình cảm và sự ủng hộ từ phía người dùng, người ta không thể sử dụng một thiết bị rất đẹp về mọi thứ như iPhone, nhưng khi mở ứng dụng của bạn lên lại thấy xấu, thiết kế cẩu thả, mắc phải các lỗi cơ bản về hiển thị. Có hàng tá sản phẩm giống bạn nhưng lại rất tiện dụng, trong khi đó sản phẩm của bạn lại rối rắm, phức tạp, thì rõ ràng không ai muốn bỏ thời gian, công sức để tìm hiểu. Nói một cách khác, một sản phẩm xấu, hoặc khó sử dụng sẽ làm cho người dùng cảm thấy nó thiếu chuyên nghiệp và không tôn trọng họ. Sản phẩm đẹp sẽ giúp bạn nâng cao tính cạnh tranh, dễ quảng bá và truyền thông đến với người dùng.
Một frontend dev phải có tư duy về làm sản phẩm có performance tốt
Một sản phẩm có performance tệ, người sử dụng sẽ nghĩ sản phẩm này ảnh hưởng đến thiết bị ví dụ như điện thoại của họ, ảnh hưởng đến thói quen sử dụng hằng ngày và như vậy họ sẽ đưa sản phẩm của bạn vào blacklist.
Một frontend dev phải có tư duy nắm bắt các nền tảng công nghệ
Phương pháp phát triển hiện đại đang thống trị hiện nay như Angular, Reactjs, Vuejs, Webpack, Gulp
Những nền tảng công nghệ không đơn thuần chỉ là cung cấp phương pháp để phát triển ứng dụng theo một cách thức nhất định nào đó, mà còn mang trong mình triết lý và phương pháp luận để phát triển sản phẩm hiệu quả: nhanh, ổn định, ít bug, chi phí thấp, dễ dàng để mở rộng, bảo trì. Nền tảng công nghệ mới còn mang trong mình sức mạnh từ việc kế thừa ưu điểm những nền tảng trước đó, và tận dụng được các thế mạnh từ version mới của ngôn ngữ như Js, trình duyệt v.v…
Ngoài ra,một frontend dev phải có kiến thức về bảo mật, hiểu đúng bản chất của ngôn ngữ như Javascript, hiểu cơ chế hoạt động của trình duyệt. Để tránh ứng dụng gặp phải các lỗi bảo mật, các lỗi về leak memory cơ bản ảnh hưởng đến uy tín và thương hiệu của sản phẩm.
Một bảng mô tả công việc mẫu cho vị trí Frontend Developer
YÊU CẦU
Thành thạo HTML, CSS, Boostrap và ngôn ngữ lập trình JavaScript
Nắm rõ toàn bộ quá trình phát triển web (thiết kế, phát triển và thực thi)
1995 là một trong những năm điên rồ nhất lịch sử máy tính. Phiên bản Java đầu tiên xuất hiện, và rồi lòi ra thêm cậu em JavaScript. Hai cái tên “na ná” nhau làm mọi người lầm tưởng cả hai là “anh em song sinh dính liền” vừa mới tách ra vậy, nhưng thực tế cả hai chả giống gì nhau cả.
Một cái theo kiểu compiled và stactical, cái kia thì interpreted và dynamical. Và đây chỉ là khởi đầu cho sự khác biệt “trời-vực” giữa hai ngôn ngữ này. Sau này, sự xuất hiện của Node.js sẽ càng khiến người ta điên đầu.
Có lẽ, những bạn lập trình viên “già” vẫn còn nhớ ngày xưa, thời đỉnh điểm mà Java còn làm mưa làm gió trước khi dần nhường sân khấu cho những đàn em khác.
Ngày ấy, mọi người cứ tưởng Java sẽ bất bại và thống trị cả thế giới máy tính cơ, nhưng đự đoán này chỉ đúng một nửa. Ngày nay, Java vẫn thống trị, nhưng chủ yếu trên Android, môi trường doanh nghiệp và thế giới embed (như blu-ray chẳng hạn).
Dù đã có nhiều thành công như vậy, Java chưa bao giờ có sức ảnh hưởng quá lớn trên môi trường desktop hay trình duyệt. Người ta cứ hay ca tụng sức mạnh của applets và những công cụ dựa trên Java khác, nhưng lại hay kết hợp loạn xạ cả lên. Và rồi server dần chuyển thành “điểm G” của Java.
Lúc này, nhiều người cho rằng JavaScript là một “người song sinh đần độn” của Java dần nhận ra sức mạnh của nó. Hiển nhiên, JavaScript trước đó vẫn “gắng gượng” được vài năm trong môi trường HTML, và Web cũng đang dần thống trị thế giới. Nhưng bỗng nhiên, AJAX xuất hiện, và “người song sinh đần độn” dần dần có thêm sức mạnh mới.
Rồi lại lòi ra thêm Node.js, với tốc độ làm biết bao developers chao đảo. JavaScript không những thao tác trên server nhanh hơn người ta dự đoán, mà còn nhanh hơn cả Java và vô số công cụ khác mới đáng sợ. Menu nhẹ, nhanh, request data vô tận tiếp tục khiến Node.js nổi tiếng đến tận ngày nay, và các trang Web ngày càng mọc ra nhiều hơn bao giờ hết.
20 năm trôi qua, có ai lại ngờ được: Cả hai người anh em này ngày nay lại “kẻ tám lạng người nửa cân” đến vậy, ai cũng lăm le thống trị thế giới lập trình. Một bên cán cân, ta có một nền móng bám sâu, mạnh với cấu trúc và nguyên lý vững vàng. Bên kia lại có sự đơn giản và linh hoạt không kém phần tinh tế. Thế giới compiler “hoài niệm” của Java sẽ giữ được ngôi vương, hay bị tốc độ và sự linh hoạt của JavaScript với Node.js lật đổ?
Cùng so sánh Java và Nodejs trong bài viết hôm nay nhé.
Thế mạnh của Java: Nền tảng vững chắc
Tôi bắt đầu nghe thấy tiếng vài người cười nhạo nhễ rồi đấy, có khi còn cười đến trụy tim cũng không chừng. Hiển nhiên, Java vẫn có glich và bug (chương trình nào lại không có chứ), nhưng công bằng mà nói, nền móng của nó vững như đá tảng. Còn với Node.js, muốn vững thế này chắc cũng phải… vài năm nữa.
Thực ra, chắc cũng phải vài chục năm nữa thì phía JavaScript mới cho ra được mấy cái regression tests như Sun/Oracle làm được trên Java Virtual Machine. Khi bạn khởi động một JVM, bạn có ngay một “người” quản trị kỳ cựu 20 năm kinh nghiệm quyết tâm thống trị server doanh nghiệp.
Còn khi bạn khởi động JavaScript, bạn cũng có ngay một “liên minh tạp nham”, “lâu lâu” mới muốn hợp tác một lần, nhưng “lâu lâu” cũng muốn dùng chuẩn JavaScript để ngầm đấu đá với nhau.
Thế mạnh của Node: Chỗ nào cũng góp mặt
Nhờ ơn Node.js, JavaScript mới bám vúi được một chút ở server và trên trình duyệt. Nhưng ở đời có chuyện gì chắc chắn đâu, cả ngành công nghiệp máy tính cũng vậy.
Ta thà viết hết bằng JavaScript cho cả hai bên client/server, còn hơn là mải mê viết trước bằng Java rồi lại ngậm ngùi viết lại bằng JavaScript; bạn chắc hẳn phải viết lại nếu quyết định chuyển business logic đã viết cho server bằng Java sang browser đấy.
Cũng có khi ông xếp còn sẽ nài nỉ bạn chuyển logic đã built cho browser về ngược lại server cũng nên. Dù có chuyển theo chiều nào đi nữa, Node.js và JavaScript vẫn giúp ta migrate code dễ hơn nhiều.
Lập trình Java có Eclipse, NetBeans và IntelliJ. Đến 3 công cụ hàng đầu có tích hợp mạnh mẽ debuggers, decompilers, và servers. Cái nào cũng đã qua nhiều năm phát triển, với lượng người dùng trung thành, và hệ sinh thái đầy plugins.
Trong khi đó, đa số lập trình viên Node.js phải ngồi cặm cuội gõ từng từ vào command line và code vào text editor. Nhiều người lựa chọn Eclipse hoặc Visual Studio, cả hai đều có hỗ trợ Node.js. Hiển nhiên, sự chú ý gần đây vào Node.js đồng nghĩa với nhiều công cụ hơn nữa trong tương lai.
Chẳng hạn như WebStorm từ JetBrains, xâu chuỗi nhiều công cụ command-line build lại với nhau, là một công cụ khá chất lượng. Nhưng muốn hoàn thiện như Eclipse. Còn lâu!
Tất nhiên, nếu bạn đang tìm kiếm một IDE giúp đơn thuần “cắt xén và tung hứng”, những công cụ mới có hỗ trợ Node.js đã khá đủ rồi. Nhưng nếu bạn đòi hỏi IDE vừa edit vừa vận hành trên soure code đang chạy như “vừa ăn vừa nói”, thì Java vẫn tốt hơn nhiều.
Thế mạnh của Node: dùng cùng một ngôn ngữ để đơn giản hóa build process
Các build tools phức tạp như Ant và Maven đã phần nào cách mạnh hóa cách lập trình trên Java. Nhưng vẫn còn một vấn đề. Bạn phải viết specification bằng XML, một kiểu data format không được thiết kế để hỗ trợ việc lập trình logic.
Hiển nhiên, biểu thị branching bằng nested tags không khó. Nhưng việc chuyển đổi liên tục giữa Java và XML cũng không kém phần khó chịu.
Java tràn ngập đủ loại công cụ mạnh mẽ giúp ta quản lý được hàng đống thiết bị. Thậm chí những công cụ đào sâu vào JVM và chi tiết hóa profiling giúp xác định bottlenecks và failures cũng không thiếu.
Stack doanh nghiệp Java quản lý một trong những hệ thống servers phức tạp nhất hành tinh, và những công ty sử dụng những server này đã từng có những yêu cầu hàng đầu… từ xa. Tất cả những công cụ dùng để theo dõi và debug hầu như đã trưởng thành và sẵn sàng làm việc hết công suất.
Thế mạnh của Node: Database queries
Queries cho các database mới hơn, như CouchDB, được viết bằng JavaScript. Trộn lẫn giữa CouchDB và Node.js không cần phải đổi công cụ, nên cũng chả cần nhớ cả khác biệt cú pháp luôn.
Trong khi đó, Nhiều lập trình viên Java sử dụng SQL. Ngay cả khi sử dụng Java DB (database viết bằng Java cho lập trình viên Java) họ cũng viết queries bằng SQL (SQL là gì)
Nhiều bạn thấy chỉ việc call Java methods thôi phải không? Nhưng sai rồi!
Bạn phải viết database code bằng SQL, rồi để Derby phân tích từ SQL. SQL là một ngôn ngữ hay, nhưng quá khác biệt và và có khi team phải cần đến hai người khác nhau để một người chuyên cho SQL, và người kia chuyên về Java.
Thế mạnh của Java: Thư viện
Hiện Java có một tập hợp thư viện khổng lồ, và những thư viện này là giải pháp rút ngắn mạnh mẽ. Những công cụ text indexing như Lucene, và computer vision toolkits như OpenCV là hai ví dụ open source projects có thể làm nền tảng vững chắc cho các project nghiêm túc.
Còn có rất nhiều thư viện được viết bằng JavaScript, trong đó có không ít thư viện hết sức tuyệt vời, nhưng khó có thể so với Java code base, cả về chất lượng lẫn số lượng.
Thế mạnh của Node: JSON
Khi Database đã là câu trả lời cho nhiều vấn đề, Java lại nỗ lực hết sức để chuyển kết quả về phía Java objects. Giới lập trình chắc sẽ tranh cãi hàng tiếng đồng hồ về POJO mappings, Hibernate…
Tinh chỉnh những công cụ này sẽ tốn hàng tiếng đồng hồ, thậm chí nhiều ngày liền. Dần dần, đoạn mã java này sẽ lấy tất cả các đối tượng sau khi chuyển đổi chúng.
Nhiều dịch vụ và database Web hiện định hoán data bằng JSON, một phần tự nhiên sẵn có của JavaScript. Format JSON hiện thông dụng và hữu ích, đến nỗi nhiều lập trình viên Java đã chuyển sang dạng JSON luôn, và như vậy việc các thư viện Java chuyển sang JSON là không thể tránh khỏi.
Nhưng cần gì đến thư viện, JSON đã có sẵn trong JavaScript rồi còn gì, cứ lấy và dùng thôi (JSON là gì)
Thế mạnh của Java: Kết cấu vững chắc
Nhìn chung, nhiều bộ phần mềm phức tạp cho các công trình khoa học được viết bằng Java, vì Java có nền tảng thuật toán siêu vững chắc. Sun dành nhiều thời gian để “rặng” ra chi tiết của utility classes. Còn có BigIntegers, elaborate IO routines, và code Date phức tạp có bổ sung cả lịch Gregorian và lịch Julian.
JavaScript, phù hợp với các công việc đơn giản hơn, và vẫn còn nhiều vướng mắc. Như ta thấy, trong JavaScript, một function không có kết quả được trả về đến ba kiểu: undefined, NaN, và null. Cái nào mới đúng? Mỗi kết quả đều có vai trò riêng — và cả ba đều có chung một mục đích là làm developer phát điên mới thôi. Những vấn đề “nho nhỏ” như thế này tuy không gây quá nhiều khó khăn cho những công việc đơn giản, nhưng khi động đến lượng lớn thuật toán phức tạp thì lại khác.
Điểm mạnh của Node: Tốc độ
Mọi người mải mê đắm say trong tốc độ của Node.js. Dữ liệu vào, kết quả ra với tốc độ ánh sáng. Node.js không không quá chú trọng vào setting thread độc lập với locking. Cũng không có overhead làm mọi thứ chạy chậm chạp hẳn đi. Bạn cứ việc dùng code đơn giản, và Node.js sẽ nhanh chóng hoàn thành mọi thứ.
Nhưng “nhân bất thập toàn”. Node.js yêu cầu code của bạn phải đơn giản và ít bug. Nếu bị deadlock, có khi cả server cũng lock up luôn. Lập trình viên bù đầu bứt tóc viết nets an toàn tránh lỗi bao nhiêu, Node.js lại quăng đi hết bấy nhiêu.
Điểm mạnh của Java: Threads
Web servercủa Java đều là đa nhiệm. Tuy việc tạo nhiều threads sẽ tốn bộ nhớ và thời gian, nhưng kết quả đem lại hoàn toàn xứng đáng. Nếu một thread deadlocks, những thread khác sẽ theo sau. Nếu một thread cần tính toán lâu hơn, các thread khác (thường) sẽ không chen đẩy.
Khi một Node.js request chạy quá chậm chạm, cái gì cũng chậm hết. Vì Node.js chỉ có một thread duy nhất, và chỉ ra event khi nó sẵn sàng. Trông thì nhanh đấy, nhưng thực tế không khác gì bưu điện ngày tết mà chỉ có một ô phục vụ cả.
Bây giờ, khi người ta đã hướng đến những hệ thống có thể cùng lúc làm nhiều thứ. Tại sao phải quay lại thời máy tính đơn nhiệm thập niên 60 làm gì?
Thế mạnh của Node: Tuổi trẻ
Lời dạy “tham thì thâm” của các cụ có vẻ càng ngày càng đúng: Java có thể đuổi theo Node, nhưng có quá nhiều code cũ còn tồn tại. Chắc hẳn rồi, Java có IO routines mới, những vẫn còn IO routines cũ tồn tại. Quá nhiều applet và util class có thể gây nhiều khó khăn.
Thế mạnh của cả hai: Cross-compiling với nhau
Nên dùng Java hay Node.js cho server, có cãi đến tết công-gô cũng chưa quyết được. Thay vì tranh cãi, tại sao không dùng luôn cả hai. Java cũng có thể cross-compiled vào JavaScript được. Google rất hay áp dụng biện pháp này cho Google Web Toolkit, mà nhiều website quan trọng nhất có Java code chạy trong đó — Java đã được dịch sang JavaScript.
Tương tự, ngược lại ta có nhiều JavaScript engines như Rhino chạy JavaScript trong ứng dụng Java ở những điểm link được. Nếu “tham vọng”, bạn có thể link vào Google’s V8 engine.
Tuyệt vời chưa, tất cả code đều link vừa khít với nhau mà không cần phải “kén cá chọn canh”.
Cách đây độ hơn một năm, mình có viết một bài giải thích về thẻ [ SerializeField ] trong Unity C# hoạt động như thế nào. Trong bài đó, mình có nói rằng các trường dữ liệu public có thể là những mối nguy tiềm ẩn trong chương trình của bạn. Với các lập trình viên có kinh nghiệm, việc này khá là hiển nhiên, như kiểu trái đất quay quanh mặt trời vậy.
Tuy nhiên, với các bạn newbie hoặc các bạn tự học lập trình tại nhà, nguy cơ đến từ các biến public có vẻ chẳng đáng lo ngại lắm. Trong bài viết này mình sẽ giải thích kỹ hơn quan điểm của mình, và bạn sẽ thấy vì sao nhiều lập trình viên khác cũng có chung quan điểm đó.
“Em thấy hai cách chả khác quái gì nhau cả.”
Nhìn qua thì việc dùng một biến public và một biến private + [SerializeField] có vẻ không khác gì nhau. Cả hai cách đều cho phép mọi người trong project của bạn chỉnh sửa trực tiếp giá trị của biến trong Unity Inspector. Nghĩa là dù bạn làm như thế này:
public class Character : MonoBehaviour
{
[SerializeField]
private int healthPoint;
}
… hay như thế này:
public class Character : MonoBehaviour
{
public int healthPoint; // Đừng làm theo, mình khuyên thật
}
… thì ai cũng có thể mở script Character ra và sửa giá trị HP của nhân vật đó, ví dụ như từ 5 lên 10. Sự khác biệt có vẻ không rõ ràng lắm?
Giờ thì hãy nhìn theo hướng của lập trình viên. Tưởng tượng một ngày thằng Dũng ngồi cạnh bạn sẽ dùng đám code trên cho tính năng mà nó đang xử lý. Nếu bạn dùng biến public, thì thằng Dũng có thể truy cập tới biến đó từ bất cứ đâu trong chương trình, và đoạn code dưới đây hoàn toàn hợp lệ:
public class Foo: MonoBehaviour
{
void Update()
{
Character c;
Debug.Log(c.healthPoint);
}
}
Nhưng nếu bạn chọn cách dùng biến private, Dũng không thể dùng biến đó được nữa. Sự khác biệt là đây.
Hàng công cộng tức là công chúng xài sao cũng được
Sự khác biệt này có vẻ không đáng kể – Dũng có thể dùng biến HP đó hoặc không, chấm hết. Vậy tại sao lại phải làm to chuyện lên làm gì? Tại sao bạn cần giấu các biến dữ liệu của bạn mà không phải phô ra?
Hãy xét trường hợp bạn dùng biến public. Thằng Dũng chỉ cần lấy giá trị HP hiện tại để làm thanh máu của nhân vật, ví dụ thế. Ừ thì không phải cái gì to tát lắm, nó chỉ đọc thôi mà, chẳng ảnh hưởng gì tới bạn. Nhưng đến khi Cường nhảy vào project và nó phải lập trình một vật phẩm giúp x2 máu của nhân vật. Vì biến HP của bạn là public, nên thằng Cường chỉ cần nhặt nó lên và xài như bình thường, tự nhiên như ruồi vậy.
Character c;
c.healthPoint = c.healthPoint * 2;
Cách làm này sẽ dẫn tới những vấn đề đau đầu rất nhanh. Với trường hợp đoạn code ở trên, chẳng có giá trị giới hạn nào cho máu của nhân vật cả, nó có thể tăng, tăng nữa, tăng mãi. Rõ ràng là không ổn chút nào. Bạn cần phải thêm một giá trị nữa vào class Character của bạn để giới hạn giá trị lớn nhất của giá trị HP. Lấy ví dụ, nhân vật đang có máu là 80/100 và nó nhặt được vật phẩm của Cường. Nếu không có giới hạn trên, máu của nhân vật sẽ là 160/100 (wtf?) thay vì giá trị đúng là 100/100.
public class Character : MonoBehaviour
{
public int healthPoint;
// Bạn thêm giá trị max HP vào để giới hạn giá trị max của HP
public int maxHealthPoint;
}
// Code của Cường
Character c;
c.healthPoint = c.healthPoint * 2;
if (c.healthPoint > c.maxHealthPoint)
c.healthPoint = c.maxHealthPoint;
Để đạt được điều này, Cường phải sửa phần code của nó sau khi bạn đã thêm giá trị max HP vào code của bạn – thật sự là phiền. Tại sao không phải đơn giản là nó chỉ việc tập trung vào vật phẩm hồi máu của nó, còn bạn xử lý những chi tiết lặt vặt ở trên. Dù gì bạn cũng là người chịu trách nhiệm cho class Character cơ mà?
Trường hợp của Cường là một ví dụ cho việc sử dụng biến public làm cho code của bạn mong manh dễ vỡ thế nào. Các yêu cầu phát triển của mọi phần mềm, bao gồm cả game, không bao giờ là bất biến trong suốt quá trình phát triển. Thậm chí, các yêu cầu này thay đổi nhanh như người yêu cũ trở mặt vậy. Mặc dù các biến public có thể được truy cập dễ dàng từ mọi nơi, nhưng nó đồng nghĩa với việc code của bạn khó có thể thích nghi với sự thay đổi từ yêu cầu. Giờ chúng ta sẽ thử thay đổi phương án tiếp cận, bằng cách đổi sang sử dụng biến private.
Đẹp khoe xấu che
public class Character : MonoBehaviour
{
[SerializeField]
private int maxHealthPoint;
private int healthPoint; // Notice that I removed [SerializeField] on purpose
public int HealthPoint { get { return healthPoint; } }
public int GetHealthPoint() { return healthPoint; }
public void Damage(float value)
{
healthPoint -= value;
if (healthPoint < 0)
healthPoint = 0;
}
public void Heal(float value)
{
healthPoint += value;
if (healthPoint > maxHealthPoint)
healthPoint = maxHealthPoint;
}
}
Chúng ta có hai sự thay đổi chính với hai giá trị về máu của nhân vật. Thứ nhất, cả hai giá trị bây giờ đều là các biến private, tức là nó chỉ có thể được đọc hoặc ghi trong phạm vi class Character của bạn. Nói cách khác, bạn là người toàn quyết quyết định rằng hai giá trị này sẽ được xử lý ra sao. Thứ hai, bây giờ nếu mọi người mở script Character của bạn ra trong Unity, họ chỉ có thể thay đổi giá trị max HP. Giá trị HP hiện tại hoàn toàn không thể bị chỉnh sửa tuỳ tiện được. Sự thay đổi này là hợp lý, bởi nếu một ai đó sửa giá trị HP hiện tại của nhân vật trong Play mode, có thể sẽ có lỗi logic xảy ra.
Vấn đề của Dũng, người cần đọc giá trị máu của nhân vật, được giải quyết bằng một hàm getter hoặc một property giúp đọc giá trị của biến healthPoint. Nếu bạn đến từ background về C++ như mình thì bạn sẽ thấy hàm getter khá quen thuộc. Thực ra trong công việc hàng ngày, mình thích cách sử dụng hàm getter hơn là property, bởi mục đích của nó rõ ràng hơn. Thêm nữa là thao tác tìm kiếm các chỗ sử dụng hàm getter dễ hơn nhiều so với property, bởi bạn không cần phải lọc ra các đoạn mà property dùng để sửa giá trị của biến. Đây hoàn toàn là vấn đề sở thích cá nhân thôi, bạn cứ xài cách nào khiến bạn cảm thấy thoải mái là được.
Với việc để các biến trong class của bạn là private, nếu Dũng hoặc Cường trực tiếp truy cập tới các biến này trong code của chúng nó, chúng nó sẽ bị quăng vào mặt một đống lỗi biên dịch. Với mình thì đây là một điều tốt, bởi ý đồ của mình là không nơi nào khác trong code base, ngoại trừ class Character, có thể trực tiếp truy cập tới những biến này. Nếu Dũng hoặc Cường muốn đọc hoặc ghi các giá trị này, chúng nó phải xài những hàm do mình cung cấp và chơi theo luật của mình đề ra. Ví dụ, Cường chỉ cần tập trung vào việc hồi máu của vật phẩm với hàm Heal mà không cần phải lo đến việc máu nhân vật vượt ngưỡng max, bởi mình đã xử lý đoạn đó rồi. Mình không cần phải bảo nó xử lý những trường hợp đó, nó cũng bớt việc phải làm. Một người khoẻ, hai người vui.
Khi các biến được giới hạn trong phạm vi của class, bạn có thể kiểm soát toàn bộ mọi việc liên quan đến các biến này như theo dõi, maintain hoặc update. Lấy ví dụ, nếu bạn nhìn hàm Heal và Damage ở trên, bạn có thể thấy mình hoàn toàn chưa hề xử lý trường hợp tham số đầu vào có giá trị âm. Nếu Cường vô tình (hoặc cố tình) truyền vào một tham số âm, chắc chắn sẽ có biến xảy ra, khi mà vật phẩm được cho là sẽ giúp hồi máu nhân vật thực tế lại làm cho nhân vật ngoắc ngoải đến mức muỗi đốt cái là chết. Vậy đây là một bug. Bạn chỉ cần thêm các đoạn code xử lý cho trường hợp tham số âm, và đời lại phơi phới. Cường và Dũng thậm chí còn chẳng phải sửa gì trong code của chúng nó.
Đặt lại vấn đề, nếu như bạn dùng biến public. Dũng sẽ phải xử lý trường hợp máu của nhân vật tụt xuống giá trị âm. Cường còn thảm hơn. Nó phải xử lý mọi thứ như giá trị âm hay giá trị max một mình. Điều này làm code của nó trông như một mớ rác vậy. Rồi một ngày nó nhận ra là phía game design yêu cầu làm thêm phần trang bị, trong đó có một trang bị giúp tăng max HP của nhân vật. Và nó phải tìm đến từng chỗ một để cập nhật các thay đổi. Sau đó nó rage quit, tìm đến bạn để phang cả cái bàn phím vào đầu bạn, rồi phi ra khỏi cửa và không bao giờ quay lại.
Thế là bạn phải tiếp quản phần việc của Cường. Bạn nhận thấy với những tính năng mới được thêm vào, bạn có thể sẽ ổn hơn nếu bạn chuyển hết phần xử lý máu vào một script khác, gọi là Health Component. Chuyển xong, bạn nhận được 666 lỗi biên dịch, bởi những biến public này được dùng ở khắp mọi nơi xung quanh project. Kể cả khi bạn dọn sạch những lỗi biên dịch này, bạn vẫn cần phải miệt mài test đi test lại các tính năng để đảm bảo rằng chúng vẫn hoạt động đúng như trước khi bạn refactor.
Kết
Phàm trên đời cái gì xảy ra cũng có nguyên nhân của nó. Giấu dữ liệu của bạn vào trong phạm vi class (hay nói văn hoa hơn theo chuẩn OOP là đóng gói – encapsulation) cũng không ngoại lệ. Với tất cả những bất lợi đã trình bày ở trên, mình tin rằng việc sử dụng một biến public chỉ để bạn có thể thay đổi giá trị của nó trong Unity Inspector là không đáng. [SerializeField] đảm nhiệm việc đó ngon lành, và bạn vẫn có thể tận dụng mọi lợi thế của tính đóng gói.
Mình thừa nhận là khi mới viết những dòng code đầu tiên, làm mọi thứ public có vẻ dễ dàng hơn nhiều so với việc viết thêm các đoạn accessor như getter hay setter. Nhưng hãy nhớ rằng, một phút lười nhác bây giờ có thể tốn của bạn hàng giờ làm bù trong tương lai.
Vậy nên đừng dùng các biến public. Nếu phải dùng, bạn tốt nhất là nên có nhiều lý do thật tốt để sử dụng chúng.
Người viết: Giang – Bài viết gốc được đăng tải tại vinaludens
.NET đã phát triển vượt trội kể từ những ngày đầu xuất hiện vào năm 2000 và đến thời điểm này nó đang dần vươn lên để đối đầu với Java. Bài viết này là một list các thuật ngữ căn bản trong .NET để giúp cho những ai chưa quen với ngôn ngữ này có thể phần nào hiểu được khi làm việc với nó.
.NET Framework là gì?
NET Framework là tên của tập lệnh nguyên bản .Net runtime và các thư viện chỉ chạy trên Windows. Nó bắt đầu từ version 1.0, rồi lên dần 1.1, 2.0, 3.0, 3.5, 4.0, 4.5 trước khi bùng nổ hệ sinh thái. Từ đó mà các phiên bản ra mắt thường xuyên hơn và giống các bản patch hơn. Phiên bản hiện tại bài này viết là bản 4.7.1.
Là phiên bản cộng đồng nhằm mang .NET đến những nền tảng ngoài Windows. Mono được phát triển chủ yếu nhằm xây dựng những ứng dụng với giao diện người dùng và được sử dụng rất rộng rãi: Unity Game, Xamarin…
Xamarin là gì?
Xamarin framework cùng tên với công ty có nguồn gốc base từ source của Mono. Giờ đây nó thuộc sở hữu của Microsoft, Framework này là các ứng dụng runtime/ library được thiết kế để chạy lần lượt trên iOS và Android.
Cho đến năm 2013, Microsoft định hướng đi đa nền tảng và phát triển .NET core. .NET core hiện được sử dụng trong các ứng dụng Universal Windows platform và ASP.NET Core.
.NET Standard là gì?
.Net Standard mở rộng thêm một tập các APIs có sẵn bao gồm rất nhiều các tính năng còn thiếu. Nó đã hỗ trợ đến 32.000 API. Việc này giúp cho việc nâng cấp code có sẵn từ phiên bản .NET cũ mà không cần phải thay đổi nhiều code. .NET Standard được đưa vào vào một giải pháp cho sự tương thích là hỗ trợ hoàn toàn thư viện đã có trong .NET Framework.
UWP
Universal Windows Platform là một nỗ lực từ Microsoft để thống nhất từ điện thoại, tablet, và desktop vào một code base. Đây là môi trường sandbox mà nó chứa các ứng dụng được thiết kế để phân phối trên Windows Store.
Roslyn là gì?
Nó là tên của một C# compiler environment mới nhất của Microsoft. Đây là opensource và có sẵn các library prebuilt để bất cứ app nào cũng có thể access vào C# compilation và các phương thức analysis mạnh mẽ của Visual Studio sử dụng.
.NET Native là gì?
Đây là một AOT (ahead of time) compiler cho UWP, nó sẽ precompile .NET IL (một loại ngôn ngữ trung cấp, là một dạng compile của các assembly được .Net runtime sử dụng) thành các code căn bản (either x86, x64, or ARM) được thiết kế để chạy trên Windows.
ASP.NET
Đây là một framework Microsoft để build các app server side được thiết kế để tương tác qua các HTTP call. Nó chỉ dành cho Windows thôi, nhưng đứa em mới trẻ trung của nó – ASP .NET Core thì được thiết kế để chạy được đa platform.
.NET Runtime
Đây là một engine phụ trách host các app viết bằng .NET. Nó là một chương trình C++ thiết kế để xác định bộ nhớ được quản lý, thực hiện thu thập “rác” (garbage collection:thực hiện quá trình tự động khôi phục lại bộ nhớ không được sử dụng tại runtime một cách tự động. Nói cách khác, đó là một cách để phá hủy các đối tượng không sử dụng nữa.), và load các .NET assembly vào và ra khỏi address space của nó và sau đó compile chúng theo ý muốn bởi operating system host.
Các library .NET Framework
Đây là các library cung cấp các tính năng căn bản như một platform. Chúng cung cấp những thứ như string operations, file I/O, threading, v.v. Ban đầu nó được nhóm với các phiên bản đã ra mắt của .NET Framework, nhưng ngày nay chúng đã tách ra.
Assembly trong C# là gì
Khi một mã lệnh đơn lẻ được người lập trình viết xong , nó sẽ được biên dịch bởi trình biên dịch. sau khi biên dịch mã lệnh được chuyển đổi thành tập tin dạng EXE/DLL. Kết quả này được hiểu là “đơn vị chương trình được quản lý – Managed Module”. Trên thực tế Managed Module được hiểu là ASSEMBLY, nó bao gồm ngôn ngữ trung gian (Intermediate Language (IL) ) và thông tin về dữ liệu (siêu dữ liệu – Metadata).
Câu hỏi phỏng vấn Javascript dưới đây sẽ giúp bạn vượt qua được những câu hỏi của nhà tuyển dụng.
Giới thiệu
Theo một cuộc điều tra cách mạng ẩn danh mà mình tìm hiểu được thì trong cuộc sống có 2 thứ khó hiểu nhất. Đối với nam giới thì đó là phụ nữ, còn đối với lập trình viên thì đó hẳn là javascript rồi.
Javascript là ngôn ngữ không thể thiếu trong thời đại hiện nay. Dù bạn có học ngôn ngữ gì đi chăng nữa thì bạn vẫn cần phải biết về javascript.
Hôm nay mình xin chia sẻ một chút kiến thức về javascript, cụ thể là các câu hỏi phỏng vấn JavaScript mà nhà tuyển dụng thường sẽ hỏi bạn.
Bài viết này mình viết với mục đích dành cho các bạn Intern, Fresher trong quá trình đi phỏng vấn có thể tham khảo vì đây là những câu hỏi liên quan đến javascript mà rất dễ bị “sờ gáy”.
Câu hỏi phỏng vấn JavaScript dành cho Intern/ Fresher
#1. Javascript là gì?
Trả lời: Javascript là một ngôn ngữ lập trình kịch bản dựa vào đối tượng phát triển có sẵn hoặc tự định nghĩa ra, javascript được sử dụng rộng rãi trong các ứng dụng Website. Nó là một ngôn ngữ thông dịch.
#2. Các kiểu dữ liệu trong Javascript?
Trả lời: Có 6 kiểu đó là String, Number, Object, Undefined, Boolean, Array, Null
Ví dụ:
// String
var a = 'viblo';
// Number
var b = 3;
// Object
var c = { a: 1, b: '2' }
// Undefined
var d;
// Boolean
var e = 10 < 11;
// Array
var f = [1, 2, 3, 'z'];
#3. Hàm delete có chức năng gì?
Trả lời: Hàm delete loại bỏ một thuộc tính khỏi object; nếu không tồn tại tham chiếu tới thuộc tính, nó sẽ tự động giải phóng.
Ví dụ:
var user = { name: 'Vu', age: 18 };
delete student.age;
console.log(student)
// => { name: 'Vu' }
#4. Phân biệt var, let và const?
Trả lời: const dùng để khai báo một hằng số – là một giá trị không thay đổi được trong suốt quá trình chạy. let tạo ra một biến chỉ có thể truy cập được trong block bao quanh nó var – tạo ra một biến có phạm vi truy cập xuyên suốt function chứa nó.
Ví dụ:
// let
if (true) {
let text = 'in if statement'
}
console.log(text) // undefinded
//const
const a = 'vu';
a = 'nguyen'; // Lỗi Identifier 'a' has already been declared
// var
var a = 'viblo';
if (true) {
var a = 'viblo.asia';
console.log(a); // 'viblo.asia'
}
console.log(a); // 'viblo.asia'
#5. Strict mode trong javascript là gì?
Trả lời: Strict theo nghĩa tiếng Việt là “nghiêm khắc”. Strict Mode là một quy mẫu nghiêm khắc trong Javascript. Nếu như việc viết code bình thường là Normal mode, thì Strict Mode sẽ có thêm các quy định khác so với Normal mode.
Ví dụ:
"use strict";
function foo(){
var bar = 'viblo';
return bar;
}
// Uncaught ReferenceError: bar is not defined
bar = 'asia';
#6. this trong javascript là gì?
Trả lời: Từ khóa this dùng để chỉ đối tượng từ nơi nó được gọi.
Ví dụ:
var data = {
name: "vu",
age: 18,
getName: function(){
return this.name; // this = data;
}
};
console.log(data.getName()) // 'vu'
#7. Khác nhau giữa undefined và null trong javascript?
Trả lời: Khi tạo ra một biến mà không gán giá trị thì nó sẽ là undefined. Còn null là một object
Ví dụ:
var a;
console.log(typeof a) // undefined
console.log(typeof null) // object
#8. == và === khác nhau như thế nào?
Trả lời: Toán tử == kiểm tra tính bằng nhau, còn === kiểm tra cả tính bằng nhau và cả kiểu dữ liệu
Ví dụ:
var number1 = 1;
var number2 = '1';
console.log(number1 == number2) // true
console.log(number1 === number2) // false
#9. Thay đổi style/class của element?
Trả lời: Sử dụng thuộc tính document trong javascript. Có thể sử dụng với id, class hay bất cứ element nào.
Trả lời: Khi chạy code Math.max() > Math.min(), giá trị trả về là False, nghe có vẻ không hợp lý. Tuy nhiên, nếu không có tham số nào được truyền vào, Math.min() trả về Infinity và Math.max() trả về -Infinity. Vậy nên Math.max() < Math.min().
Ví dụ:
var infinity = 5
var value1 = Math.min(1)
var value2 = Math.min(1, infinity)
var value3 = Math.min(1, -infinity)
console.log(value1) // 1
console.log(value2) // 1
console.log(value3) // -5
#11. Closure trong javascript là gì?
Trả lời: Closure là một hàm bên trong, truy cập đến các giá trị bên ngoài phạm vi của nó. Closure có thể truy cập vào các biến trong phạm vi của riêng nó (Variables in their own scope), trong hàm (Variables in the function’s scope), và biến toàn cục (Global variables).
Ví dụ:
const arr = [1, 2, 3, 4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(i);
}, 10);
}
// 4 4 4 4
Lý do là bởi vì hàm setTimeout sẽ tạo ra 1 function (closure) có thể truy cập phạm vi bên ngoài nó, vòng loop sẽ chứa index i. Sau 10ms, hàm được thực thi và nó sẽ log ra giá trị của i, là giá trị cuối cùng của vòng lặp (4). Xem thêm JavaScript Closures là gì
#12. Hosting trong javascript là gì?
Trả lời: Hoisting là hành động mặc định của Javascript, nó sẽ chuyển phần khai báo lên phía trên top Trong Javascript, một biến (variable) có thể được khai báo sau khi được sử dụng.
Ví dụ:
a = 'https://viblo.asia';
console.log("My website: ", a); // My website: https://viblo.asia
var domain;
console.log("My website: ", a); // // My website: https://viblo.asia
#13. Phân biệt giữa Function Declaration và Function Expression
Trả lời: Function declaration sẽ sử dụng từ khóa function rồi đến tên hàm. Còn Function expression sẽ được bắt đầu với từ khóa var, const, hoặc let.
Ví dụ:
// Function Declaration:
function a(x,y,z) {
// code here
}
// Function Expression
const a = function(x,y,z) {
// code here
}
#14. Hàm Array.splice() và hàm Array.slice() khác nhau như thế nào ?
Trả lời: Hàm Array.splice() sẽ thay thế một hoặc một số phần tử của mảng bằng một hoặc một số phần tử khác. Trong khi hàm Array.slice() sẽ trích xuất một số phần tử của mảng, vị trí bắt đầu và kết thúc việc trích xuất sẽ được xác định bởi tham số truyền vào hàm. Lưu ý hàm sẽ trích xuất không bao gồm phần tử end truyền vào.
Ví dụ:
// Array.splice()
var language = ["php", "css", "html", "js"];
language.splice(1, 1, "python", "c#", "ios");
console.log(language) // ['php', 'python', 'c#', 'ios', 'html', 'js'];
// Array.slice()
var language = ["html", "js", "php", "c#", "python", "androi", "ios"];
var slice = language.slice(1, 4);
console.log(slice); // ['js(1)', 'php(2)', 'c#(3)']
#15. Spread Operator trong javascript ?
Trả lời: Spread operator là một biểu thức mở rộng giúp gộp các phần tử vào trong một cách viết ngắn gọn hơn. Cách viết này được thể hiện bằng dấu ...
Ví dụ:
var topLane = ['zed', 'akali'];
var all = ['yasuo', 'rengar', ...topLane, 'đan trường'];
console.log(all) // ['yasuo', 'rengar', 'zed', 'akali', 'đan trường']
#16. Anonymous function là gì ?
Trả lời: Là một hàm ẩn danh, không có tên gọi, thường được sử dụng khi xử lý các công việc có quy mô nhỏ, vì thế không cần thiết phải khởi tạo tên định danh cho hàm này. Giúp nó có tốc độ xử lý nhanh hơn hàm truyền thống phải có tên định danh.
Ví dụ:
var anonymous = function(a, b) {
return a + b;
};
console.log(anonymous(5, 10)); // 15
//Anonymous Function không có đối số
var anonymous = function() {
return "Hello World";
};
console.log(anonymous()); // Hello World
(function(){
console.log('viblo');
}).call(this) // viblo
#17. Tại sao 0.1 + 0.2 không bằng 0.3 ?
Trả lời: Vấn đề này liên quan đến việc Javascript lưu trữ dữ liệu float ở dạng nhị phân chính xác tới từng con số sau dấu phẩy. Hơn nữa máy tính không thể biểu diễn chính xác số thập phân, nên gây ra sai số kiểu này. Giải pháp ở đây có thể sử dụng hàm toFixed() để đạt được kết quả đúng.
console.log(0.1 + 0.2) //0.30000000000000004
// Sử dụng toFixed()
var number = 0.1 + 0.2;
console.log(number.toFixed(2)) // 0.3
#18. Sự khác nhau giữa window.onload và onDocumentReady ?
Trả lời: Sự kiện window.onload có ý nghĩa rằng khi trình duyệt đã load xong mọi thứ (image, js, css) thì những đoạn code nằm bên trong đó mới được chạy. Với onDocumentReady, mọi thứ bên trong hàm này sẽ được load ngay khi DOM được load và trước khi toàn bộ nội dung bên ngoài được load.
#19. Kết quả của 1 + 2 + ‘3’ ?
Trả lời: 33. 1 và 2 là kiểu integer, khi cộng lại sẽ được 3, sau đó sẽ nối với string ‘3’ để được kết quả là 33.
#20. Promise trong javascript là gì ?
Trả lời: Promise là một cơ chế trong JavaScript giúp bạn thực thi các tác vụ bất đồng bộ mà không rơi vào callback hell hay pyramid of doom, là tình trạng các hàm callback lồng vào nhau ở quá nhiều tầng. Có 3 trạng thái: pending, fulfilled, reject.
Hi vọng sau khi đọc xong bài này các bạn có thể nhớ thêm, học thêm một chút gì đó về javascript. Mong có thể giúp ích cho các bạn.
Việc hiển thị hình ảnh trên nhiều độ phân giải màn hình là một vấn đề đau đầu với bất kì một ai dù là trên web hay ứng dụng di động.
Hôm nay bài này sẽ giới thiệu cho các bạn một thủ thuật. Đó là việc dùng hình ảnh dạng vector, mà cụ thể là SVG để tiết kiệm công sức, tăng tốc độ load trang, giảm dung lượng website.
SVG là gì?
SVG là viết tắt của Scalable Vector Graphics. SVG là một ngôn ngữ dạng XML, dùng để miêu tả hình ảnh đồ họa vector 2 chiều, tĩnh và hoạt hình. Bạn cũng có thể hiểu là nó một định dạng hình ảnh (tương tự như ảnh bitmap JPG, PNG…). Chỉ khác là chúng sử dụng cấu trúc XML để hiển thị hình ảnh dưới dạng vector còn ảnh bitmap sử dụng cấu trúc ma trận pixel. Tập tin có đuôi .svg được mặc định hiểu là tập tin SVG.
Tại sao nên dùng SVG?
Chất lượng hình ảnh tốt: Vì là hình ảnh dạng vector nên chúng ta có thể hiển thị, co giãn (scale) thoải mái mà không làm giảm chất lượng hình ảnh.
Tiết kiệm dung lượng: Vì là hình ảnh dạng vector nên dung lượng một file hình ảnh SVG rất nhỏ so với một file hình ảnh thông thường.
Animation: Tất cả mọi element và thuộc tính của chúng trong file SVG đều có thể animate được. Nên chúng ta hoàn toàn có thể sử dụng một file SVG duy nhất và dùng CSS hoặc Javascript để làm animation cho từng thành phần của hình ảnh đó. Nó giúp giảm bớt lượng request và làm cho trang web của bạn load nhanh cực kỳ dù cho có animation rất nhiều.
Độ tương thích tốt: Sau nhiều năm không tương thích trình duyệt, SVGs cuối cùng đã đến. Chúng được hỗ trợ trong tất cả các trình duyệt hiện đại.
Bảng so sánh:
Loại định dạng
Bảng màu
Công dụng
JPG
Nén mất dữ liệu
Hàng triệu màu sắc
Ảnh tĩnh, nhiếp ảnh
PNG-8
Nén không mất dữ liệu
Tối đa 256 màu
Tương tự như định dạng GIF, xử lý transparency tốt hơn nhưng không có hình động, tuyệt vời khi sử dụng cho biểu tượng
PNG-24
Nén không mất dữ liệu
Không giới hạn màu sắc
Tương tự như định dạng PNG-8, xử lý hình ảnh tĩnh và transperency
GIF
Nén không mất dữ liệu
Tối đa 256 màu
Hình động đơn giản, đồ họa với màu trơn, đồ họa không có gradient
SVG
Vector/ Nén không mất dữ liệu
Không giới hạn màu sắc
Đồ họa/Logo cho web, màn hình Retina/độ phân giải dpi cao
Một ưu điểm của SVG là tất cả mọi element và attribute của các element đó đều có thể animate.
Hãy xem hình bên dưới, đây là 2 hình ảnh giống nhau, bên trái là hình ảnh với định dạng thông thường, tức là hình ảnh sử dụng cấu trúc từng pixel, hình bên phải là một hình ảnh vector. Và khi chúng ta phóng to hình ra, đây là kết quả:
Những thành phần cơ bản của SVG
<svg> là thẻ bao ngoài, định nghĩa đó là phần tử SVG.
<line> tạo đường thẳng đơn.
<rect> Tạo hình chữ nhật và hình vuông.
<polygon> Tạo hình đa giác, với ba hoặc nhiều cạnh.
<path> Tạo bất kỳ hình nào mà bạn thích bằng cách định nghĩa những điểm và đường thẳng giữa chúng.
<defs> Định nghĩa những tài nguyên có thể sử dụng lại. Không có gì nằm trong phần <defs> được hiển thị.
<g> Gom nhiều hình dạng thành một nhóm. Đặt các nhóm trong phần <defs>để cho phép chúng được sử dụng lại.
<use> Lấy những tài nguyên được định nghĩa bên trong phần <defs> và làm cho chúng hiển thị trong SVG.
SVG Tools
Một số tool hỗ trợ cho bạn vẽ các hình ảnh SVG như là:
Trình duyệt sẽ disable mọi đoạn script, link hay các tính năng tương tác khác được nhúng vào file SVG. Bạn có thể thao tác SVG bằng cách sử dụng CSS giống với các loại ảnh bình thường như là filter, transform,… Kết hợp CSS với SVG thường cho kết quả tốt hơn vì ảnh SVG có thể thu nhỏ được vô hạn.
Chèn ảnh SVG vào code CSS
Một SVG có thể được viết trực tiếp trong code CSS bằng thuộc tính background. Nó phù hợp cho những icon nhỏ, tái sử dụng được và tránh việc thêm những request HTTP.
.element {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600"><circle cx="400" cy="300" r="50" stroke-width="5" stroke="#f00" fill="#ff0" /></svg>') center center no-repeat;
}
ViewBox ở đây định nghĩa tọa độ không gian. Trong ví dụ trên, sẽ có 1 vòng tròn màu vàng viền đỏ, và có diện tích là 800×600 bắt đầu từ vị trí 0, 0.
Responsive với ảnh SVG
Khác với ảnh thông thường, ảnh SVG phải xác định thuộc tính width và height cho ảnh, bởi nếu ko set thì nó sẽ coi như là max-width bằng 0 và sẽ không thể nhìn thấy hình ảnh.
Ảnh SVG có thể được đặt trực tiếp vào code HTML, khi đó nó sẽ trở thành 1 phần của cây DOM và có thể thao tác với CSS và Javascript:
<p>SVG is inlined directly into the HTML:</p>
<svg id="invader" xmlns="http://www.w3.org/2000/svg" viewBox="35.4 35.4 195.8 141.8">
<path d="M70.9 35.4v17.8h17.7V35.4H70.9zm17.7 17.8v17.7H70.9v17.7H53.2V53.2H35.4V124h17.8v17.7h17.7v17.7h17.7v-17.7h88.6v17.7h17.7v-17.7h17.7V124h17.7V53.2h-17.7v35.4h-17.7V70.9h-17.7V53.2h-17.8v17.7H106.3V53.2H88.6zm88.6 0h17.7V35.4h-17.7v17.8zm17.7 106.2v17.8h17.7v-17.8h-17.7zm-124 0H53.2v17.8h17.7v-17.8zm17.7-70.8h17.7v17.7H88.6V88.6zm70.8 0h17.8v17.7h-17.8V88.6z"/>
<path d="M319 35.4v17.8h17.6V35.4H319zm17.6 17.8v17.7H319v17.7h-17.7v17.7h-17.7V159.4h17.7V124h17.7v35.4h17.7v-17.7H425.2v17.7h17.7V124h17.7v35.4h17.7V106.3h-17.7V88.6H443V70.9h-17.7V53.2h-17.7v17.7h-53.2V53.2h-17.7zm88.6 0h17.7V35.4h-17.7v17.8zm0 106.2h-35.5v17.8h35.5v-17.8zm-88.6 0v17.8h35.5v-17.8h-35.5zm0-70.8h17.7v17.7h-17.7V88.6zm70.9 0h17.7v17.7h-17.7V88.6z"/>
</svg>
<p>The SVG is now part of the DOM.</p>
Bạn có thể định nghĩa chiều rộng và chiều cao cho ảnh SVG ở trong CSS như thế này:
See the Pen
HTML-Inlined SVG by SitePoint (@SitePoint)
on CodePen.
Các phần tử SVG như là paths, circle, hay rectangles có thể chỉnh sửa được style như CSS:
/* CSS styling for all SVG circles */
circle {
stroke-width: 20;
stroke: #f00;
fill: #ff0;
}
Khi chỉnh sửa như vậy thì nó sẽ bị ghi đè lên bất kì thuộc tính nào được xác định trong SVG vì CSS được ưu tiên cao hơn. SVG CSS có 1 số lợi ích:
attribute-based styling có thể được loại bỏ khỏi SVG để làm giảm dung lượng trang.
CSS có thể được sử dụng lại cho các ảnh SVG khác ở các trang khác nhau.
Có thể sử dụng các hiệu ứng của CSS lên SVG như là: :hover, animation, transition…
SVG Sprites là gì?
Thuật ngữ Sprites thực ra là kỹ thuật đưa tất cả các hình ảnh trang trí như các icon hay button đặt vào một file hình duy nhất. Sau đó dùng thuộc tính background-position của CSS để hiện ra đúng vị trí cần thiết. Lợi ích của kỹ thuật này là thay vì server bạn phải nhận rất nhiều request cho những tấm ảnh nhỏ…khiến cho web bạn load chậm đi. Lúc này bạn chỉ cần bỏ tất cả ảnh vào 1 tấm duy nhất, server chỉ nhận dc một request nhẹ nhàng, chưa kể tấm ảnh này dung lượng sẽ nhỏ hơn nhiều tấm ảnh kia cộng lại.
SVG cũng có sprites giống như ảnh thông thường. Một file SVG có thể chứa số lượng ảnh bất kì. Ví dụ file .svg này chứa folder icon được tạo bởi IcoMoon. Mỗi một icon lại được chứa trong 1 thẻ <symbol> và có 1 ID riêng biệt.
Tất nhiên không thể dùng SVG trong 100% mọi trường hợp. Nhược điểm của SVG là giới hạn về độ chi tiết và màu sắc, tất nhiên chúng ta có thể sử dụng SVG để vẽ một hình ảnh phức tạp, hoặc thực như ảnh chụp, nhưng nếu làm vậy thì performance sẽ rất tệ.
Nhưng với xu hướng hiện nay, phong cách thiết kế phẳng đang là mốt, những website với giao diện đơn giản, sử dụng hình ảnh cũng đơn giản, ít chi tiết thì SVG hoàn toàn có thể phát huy được thế mạnh của mình.
Lập trình là một công việc có tốc độ phát triển và đổi mới rất nhanh. Để không bị tụt lại phía sau, cập nhật kiến thức và phát triển bản thân, dưới đây là 100tips cho lập trình viên mà có thể bạn sẽ cần.
Là 1 lập trình viên, bạn phải biết cách search câu trả lời cho các câu hỏi của mình. Khi bạn biết cách search Google hiệu quả, bạn sẽ tiết kiệm được rất nhiều thời gian của mình và có thể code tốt hơn đó.
2. Giữ lời hứa và làm đúng deadline
Sẽ tốt hơn nếu team bạn biết task của mình và hoàn thành sản phẩm trong 3 tuần cho khách hàng hay công ty của bạn. Giữ đúng lời hứa và hoàn thành kịp deadline, bạn sẽ xây dựng được niềm tin cho mình.
3. Đối xử tốt với designer, họ là bạn không phải là kẻ thù
Designer cung cấp giải pháp, “gãi đúng chỗ ngứa” cho người dùng. Học hỏi từ họ và làm việc đoàn kết sẽ cho ra sản phẩm hiệu quả nhất.
4. Tìm một “sư phụ” cho mình
Tìm một ai đó bạn có thể học hỏi được cũng như bàn luận về các ý tưởng với họ. Coding Coach là một nơi tuyệt vời nếu bạn đang cần tìm một sư phụ cho mình đấy.
5. Hãy trở thành “sư phụ” của người khác
Sau khi tìm được sư phụ cho mình, học hỏi đủ và ở một đẳng cấp cao hơn, bạn có thể thử trở thành người chỉ dẫn cho người mới giống như bạn ngày trước. Giúp họ học hỏi và đưa ra những ý tưởng tuyệt vời nhất.
6. Viết comment có tâm
Khi viết comment, bạn nên giải thích tại sao, chứ không phải là bắt bẻ người ta đang làm cái gì.
7. Đặt tên biến và hàm thích hợp
Bạn nên đặt tên biến và hàm chính xác với mục đích của chúng.
8. Đi nghỉ mát
Chúng ta cần thời gian để xả stress. Nếu bạn muốn thì hay đi du lịch. Não của bạn và đồng nghiệp sẽ rất biết ơn nếu bạn đi chơi đấy!
9. Xoá code không hữu dụng
Không lý do gì gánh thêm bug về cho mình nhé.
10. Học cách đọc code
Đọc code mặc dù là một kỹ năng bị đánh giá thấp, nhưng lại vô cùng hữu ích cho bạn.
Bạn cần thời gian để nghỉ ngơi sau 1 ngày dài làm việc. Tắt thông báo về công việc và xoá mọi ứng dụng không cần thiết ra khỏi điện thoại của bạn nhé.
12. Chỉ lên lịch nhưng cuộc gặp mặt cần thiết
Nếu chỉ cần giải quyết qua mail hay nhắn tin, thì bạn nên làm vậy và hạn chế gặp nhau. Còn nếu bắt buộc phải gặp, thì hãy sắp xếp thời gian, càng nhanh càng tốt.
13. Lập trình cặp
Lập trình cặp cho phép bạn tham gia với cả 2 vai là thầy và trò.
14. Viết email tốt
Học cách thu hút audience trong email bằng cách cô đọng và rõ ràng. Không ai muốn đọc một cái mail dài 4 trang cả đâu.
15. Tham gia vào các cộng đồng
Gặp gỡ nhiều người cùng ngành và đam mê sẽ giúp bạn phát triển hơn.
16. Dọn dẹp branch
Dọn branch của bạn như dọn nhà trước khi có khách tới. Nếu bạn không cần cái gì, bỏ luôn chứ đừng quăng nó ở đâu đó.
17. Đừng giữ cửa
Ai cũng có thế mạnh riêng cả. Nếu người ngoại đạo muốn vào ngành, hãy ủng hộ họ. Đừng nói rằng họ không đủ giỏi để vào ngành của chúng ta.
18. Luôn học hỏi
Bạn đã chọn cái nghề mà luôn phải học rồi. Vậy nên làm quen với điều đó đi.
19. Đừng từ bỏ
Sẽ không bao giờ dễ dàng cho bạn. Nhưng chúng ta đều có xuất phát điểm như nhau mà, phải không? Bạn có thể làm được. Cố lên nhé!
20. Nhận task khó
Task dễ sẽ không bao giờ giúp bạn phát triển được.
21. Hiểu rõ yêu cầu trước khi bắt tay vào làm
Bạn nên hiểu các tiêu chí đã được chấp nhận trước khi bắt tay vào code. Bạn sẽ đỡ đau đớn và mất thời gian hơn rất nhiều sau này đó.
22. Có một bộ công cụ cho mình
Bạn hãy chuẩn bị cho mình một bộ công cụ đầy đủ. Hiểu rõ công cụ nào hỗ trợ cái gì và nó sẽ giúp ích cho bạn khi vào project rất nhiều đó.
23. Học cách yêu những lời chỉ trích mang tính xây dựng
Hãy hỏi những người đồng nghiệp và bạn bè đáng tin cậy của bạn để cho nhận xét. Cách này sẽ giúp bạn phát triển nhanh hơn trên con đường lập trình viên.
24. Luôn mở mang đầu óc
Công nghệ luôn thay đổi nhanh chóng. Đừng phản đối những công nghệ mới mà hãy học nó rồi đưa ý kiến sau.
25. Cập nhật tin tức
Luôn cập nhật những tin tức công nghệ mới nhất bằng cách theo dõi các cộng đồng, blog, podcast, tin công nghệ,…
26. Tập trung giải quyết vấn đề
Với kỹ năng giải quyết vấn đề tốt, bạn có thể chinh phục mọi vấn đề. Tập trung vào những thứ cần thiết để giải quyết vấn đề.
27. Khiêm tốn
Không cần biết bạn làm chức gì và ở công ty nào, hãy luôn khiêm tốn nhé!
28. Học thuyết trình tốt
Học cách thu hút người nghe và có một bài thuyết trình cực hay nhé!
29. Kiểm tra tất cả những giải pháp trước khi nhảy vô làm
Đừng làm liền giải pháp đầu tiên. Hãy thử mọi phương án trước khi bắt đầu code.
30. Tìm kiếm ngách riêng cho mình
Có nhiều lĩnh vực trong ngành CNTT. Hãy tìm lĩnh vực nào bạn thích nhất và trở thành một chuyên gia về lĩnh vực đó.
31. Có thói quen lập trình tốt
Cố gắng có những thói quen nhất quán và lành mạnh cho việc code. Ví dụ như bỏ qua những thứ làm bạn phân tâm, làm task nhanh chóng, đi họp đều đặn, bắt đầu với task ưu tiên nhất,… Có thể bạn sẽ mất thời gian để làm quen, nhưng về sau này thì sẽ rất có ích cho bạn đấy.
Khám phá các công cụ debugger trình duyệt. Học debug I/O với IDE của bạn. Bằng cách học những phương pháp hiệu quả để debug và tìm lỗi, bạn có thể giải quyết những con bug khó nhất.
33. Luyện kỹ năng của bạn thường xuyên
Việc bạn biết một kỹ năng nào đó rồi không có nghĩa là bạn không luyện tập nó. Các kỹ năng có thể mờ đi theo năm tháng nếu bạn không liên tục cải thiện nó, đặc biệt là trong cách ngành phát triển rất nhanh như thế này thì việc luyện tập là vô cùng quan trọng. Hãy bỏ ngay cái suy nghĩ “Mình luôn làm cách này”, mà hãy thử nghĩ là: “Liệu có cách tốt hơn không?”.
Nói đơn giản là, bạn có 6 múi rồi không có nghĩa là bạn được ăn thoải mái không tập tành gì mà vẫn giữ được 6 múi.
34. Hiểu lý do tại sao
Sẽ đến lúc bạn cần phải nói ra quan điểm của mình. Và sẽ rất quan trọng cho bạn để hiểu được lý do đằng sau vấn đề mà bạn đang tìm hiểu. Tại sao giải pháp A thì tốt hơn B? Chỉ khi nào bạn giải thích được, thì ý kiến và lời nói của bạn mới có trọng lượng.
35. Hiểu được giá trị của mình
Bạn cần nhận lại được những gì tương xứng với thứ mình bỏ ra. Luôn nắm được mức lương và thu nhập trong ngành của bạn ở khu vực bạn đang đi làm. Nếu nó thấp hơn mức trung bình, thì đã đến lúc nói chuyện với sếp và thoả thuận một mức lương tốt hơn rồi đấy.
36. Đừng sợ khi hỏi xin sự giúp đỡ
Nếu bạn đang gặp rắc rối với 1 vấn đề nào đó và đã tốn quá nhiều thời gian tìm kiểu giải pháp, đã đến lúc cầu cứu rồi đấy. Chúng ta cũng là con người với nhau mà. Con người thì cần phải hỗ trợ giúp đỡ lẫn nhau. Chẳng việc gì phải xấu hổ khi tìm đồng nghiệp để giúp mình đâu.
37. Học cách học
Người ta học theo nhiều cách khác nhau. Một vài người học qua video hướng dẫn, người khác thì đọc sách… Hãy tự tìm ra cách học tốt nhất cho bản thân và học thật siêng năng nhé các bạn.
38. Hãy tử tế với mọi người
Khi bạn nhận xét về đồng nghiệp, hãy nhẹ lời thôi. Bạn có thể nêu lên quan điểm của mình về cách làm việc của đồng nghiệp, chứ đừng chì chiết hay dùng lời lẽ quá nặng để nói với họ.
39. Biết nghỉ giải lao
Bạn không thể dành 8 tiếng đồng hồ liên tục để code được. Bạn sẽ mất sức nhanh chóng và để lại rất nhiều lỗi trong phần mềm của mình đấy. Vậy nên hãy hẹn giờ đồng hồ để nhắc bạn tạm dừng công việc và nghỉ ngơi. Đi bộ, uống cafe, tán gẫu với đồng nghiệp,… Rời mắt khỏi màn hình một chút sẽ giúp bạn lấy lại sức và chất xám, từ đó nâng cao chất lượng công việc của bạn đấy.
40. Theo dõi tiến trình
Sẽ không hay nếu bạn code một thời gian dài mà không thấy mình phát triển thêm hay đạt được gì. Bạn nên theo dõi những gì mình đạt được và tiến trình phát triển của bản thân. Dán một tờ note với list những gì bạn làm được trên máy tính của mình. Mỗi lần bạn làm được gì, hay viết lên đó, dù là việc nhỏ nhất. Những gì bạn làm được sẽ đem đến cho bạn một giá trị lớn hơn về sau.
41. Đừng dựa vào framework và thư viện
Học sâu về ngôn ngữ tốt hơn là học cách dùng framework và thư viện. Bạn không thật sự cần học về các thư viện hay framework, nhưng nếu bạn hiểu cách mà chúng hoạt động, thì sẽ giúp bạn viết code đẹp hơn và rõ hơn đấy.
42. Học cách yêu việc review code
Nhờ ai đó đọc và phân tích code cho bạn nghe có vẻ kinh khủng, nhưng có thể giúp bạn nhận được nhiều lời nhận xét giá trị để làm lập trình tốt hơn. Bạn cũng nên tập review code cho người khác nữa nhé.
43. Học những lĩnh vực khác
Bạn nên tìm hiểu thêm cơ bản về một số lĩnh vực khác có liên quan đến lập trình như thiết kế, marketing, lập trình frontend hay backend,… Điều này sẽ giúp bạn trở thành một lập trình viên toàn diện hơn.
44. Đừng chọn công nghệ dễ cho mình, hãy chọn công nghệ đúng cho mình
Mỗi project sẽ có những yêu cầu khác nhau, và chúng ta cần chọn những công cụ phù hợp cho từng công việc phải làm. Mặc dù chọn công cụ bạn đã từng làm sẽ thoải mái hơn, nhưng nếu chúng không phù hợp với yêu cầu của project, đã đến lúc bạn cần tìm cái khác để thay thế rồi.
45. Chịu trách nhiệm cho mọi lỗi sai
Đã là người thì chắc chắn phải mắc sai lầm, và bạn sẽ còn mắc nhiều lỗi sai hơn nữa trong suốt sự nghiệp của mình. Chính vì vậy, biết và nhận trách nhiệm khi mắc lỗi sai vô cùng quan trọng. Bạn sẽ xây dựng được lòng tin với team của mình cũng như sếp của mình.
46. Tự review code của chính mình
Trước khi bạn mở pull request, hãy xem lại code của mình trước. Nếu như đó là code của đồng nghiệp, thì bạn sẽ comment gì? Điều quan trọng là đoán trước được vấn đề hay lỗi sai trước khi yêu cầu review code.
47. Học từ thất bại của bản thân
Thất bại là mẹ thành công. Chúng ta sẽ gặp nhiều thất bại trên đường đời của mình. Học từ thất bại, sẽ giúp bạn biết hướng đi đúng hơn trong tương lai.
48. Biết điểm yếu của mình
Hãy hiểu chính mình. Điểm yếu của bạn là gì? Có thể bạn sẽ luôn quên cập nhật test trước khi push, hay bạn rất tệ trong việc trả lời email,… Biết điểm yếu của bạn sẽ giúp bạn loại bỏ được chúng.
49. Luôn tò mò
Đây là ngành luôn không ngừng tiến hoá, vậy nên tò mò sẽ rất quan trọng. Nếu bạn không hiểu thứ gì, một yêu cầu của project hay một dòng code, hãy nói ra. Không ai chỉ trích bạn vì hỏi để làm rõ và bạn sẽ code tốt hơn nhiều.
50. Đừng cố học mọi thứ
Có một thế giới kiến thức khổng lồ và không thể nào học hết một cách dễ dàng được. Hãy chọn lọc và học chuyên sâu, và đừng quan tâm những thứ còn lại. Bạn có thể có kiến thức về công việc hay các lĩnh vực khác, nhưng không thể có tất cả được.
51. Chia tay
Chỉ vì bạn viết một vài dòng code không có nghĩa là bạn phải yêu thương nó. Đúng là không ai muốn quẳng công trình của mình đi, nhưng code có vòng đời của nó. Vậy nên bạn không cần phải khư khư yêu thương code cũ đâu.
52. Có team hỗ trợ
Team giỏi luôn hỗ trợ nhau. Điều này giúp bạn thấy an toàn hơn nhiều khi thử cái mới.
53. Tìm cảm hứng từ cộng đồng
Hãy tìm một ai đó trong ngành mà bạn ngưỡng mộ. Điều đó sẽ tạo cảm hứng cho bạn để tiếp tục project và thử nhiều thứ mới.
54. Hãy làm công việc của bạn trở nên giá trị
Bất kể là bạn làm vị trí nào hay công việc là gì, những gì bạn làm đều có cái giá của nó. Vì thế hãy cho nó giá trị xứng đáng nhé.
55. Bỏ qua những thứ làm phiền bạn
Tắt thông báo Slack, tin nhắn, email và các trang mạng xã hội sẽ giúp bạn tập trung tối đa cho công việc. Bạn sẽ không phải mất 30 phút trả lời tin nhắn và không làm được việc trong 30 phút đó đúng không.
56. Luôn hỗ trợ mọi người
Cố gắng hỗ trợ team của bạn trong mọi tình huống, kể cả là một buổi thuyết trình quan trọng hay đơn giản giúp họ gỡ rối tơ lòng.
57. Hay trao niềm tin đúng nơi đúng lúc
Nếu ai đó làm tốt công việc, hãy khen họ. Dùng sự tích cực để xây dựng lòng tin tới các thành viên trong team, cũng như hỗ trợ họ trong công việc. Một ngày nào đó họ cũng sẽ giúp lại bạn.
58. Kiểm tra code
Công đoạn kiểm tra rất quan trọng. Kiểm tra đơn vị, kiểm tra hồi quy, kiểm tra tích hợp, kiểm tra đầu cuối,… Kiểm tra code của bạn sẽ giúp sản phẩm của bạn ổn định hơn.
59. Lên kế hoạch tiếp cận
Khi bạn nhận được một yêu cầu về tính năng mới, hoặc là có bug, đầu tiên phải lên kế hoạch chuẩn bị. Bạn cần những gì để giải quyết vấn đề hay làm tính năng mới? Dành một vài phút để lên kế hoạch sẽ tiết kiệm hàng giờ đồng hồ hối hận của bạn sau này.
60. Học viết mã giả
Code giả là một kỹ năng tuyệt vời vì nó cho phép bạn vượt qua những vấn đề phức tạp mà không mất thời gian viết một dòng code nào. Viết lên giấy, chạy thử một vài cách test khác nhau và xem bạn gặp những lỗi gì.
61. Ghi nhớ thành tựu của mình
Nếu bạn được thưởng khi đi làm, ghi nó lại. Nếu bạn phát triển được một tính năng quan trọng, ghi nó lại. Bạn sẽ có một danh sách những thứ mình đã đạt được, dùng nó làm động lực vượt qua những thử thách khó khăn hơn.
62. Học nền tảng lập trình
Hãy học những thuật toán sắp xếp và tìm kiếm và cấu trúc dữ liệu cơ bản. Đây là những điều bạn chưa biết về các ngôn ngữ và có thể giúp bạn giải quyết các vấn đề gặp phải.
63. Chọn công nghệ nào lâu dài và dễ maintain
Mặc dù thử nghiệm các công nghệ mới khá vui, nhưng hãy chọn những công nghệ nào dễ maintain trong ứng dụng doanh nghiệp. Team của bạn sẽ thấy biết ơn cả đời đó.
64. Học về Design Patterns
Design Patterns là những công cụ hữu ích cho việc thiết kế code của bạn. Có thể bạn không cần chúng ở nhiều project, nhưng hiểu căn bản về chúng sẽ giúp bạn làm nhiều ứng dụng lớn hơn.
Thay vì viết code phức tạp để thể hiện trình độ, hãy hướng đến sự dễ đọc và đơn giản. Việc này sẽ giúp thành viên team bạn dễ dàng đóng góp hơn.
66. Trả nợ kỹ thuật (Technical debt)
Technical debt có thể tác động lớn đến hiệu suất. Vậy nên nếu bạn có thể tái cấu trúc thì hãy làm đi nhé.
67. Nâng cấp thường xuyên
Thay vì nâng cấp lớn mỗi tháng, hãy nâng cấp thường xuyên hơn nhưng với các thay đổi nhỏ thôi. Bạn sẽ ít gặp bug hay phá vỡ các thay đổi trong sản phẩm của mình hơn.
68. Cam kết sớm và thường xuyên hơn
Cam kết sớm và thường xuyên là cách tốt nhất để đảm bảo công việc vẫn ổn, đồng thời cũng giảm stress khi bạn vô tình hoàn lại các thay đổi quan trọng.
69. Học cách khi nào thì hỏi trợ giúp
Không chỉ đừng ngại khi hỏi trợ giúp, mà bạn còn cần biết khi nào thì nên hỏi. Cố gắng giải quyết vấn đề trước khi hỏi ai đó. Nhưng khi bạn mắc kẹt với một vấn đề đơn giản trong 1 tiếng rồi, chi phí cao hơn lợi ích, thì hãy cầu cứu ngay nhé.
70. Hỏi những câu hỏi hiệu quả
Khi hỏi, bạn hãy cố gắng chi tiết nhất có thể.
71. Nhận feedback khi chưa xong việc
Bạn không cần phải hoàn thành hết công việc rồi mới nhận feedback. Nếu bạn chưa chắc chắn về hướng đi của mình, hãy hỏi một người đồng nghiệp đáng tin cậy để review mọi giải pháp của bạn.
72. Đọc documentation
Documentation là nguồn sự thật thuần tuý nhất về công nghệ. Vậy nên học cách đọc documentation có thể giúp bạn nhanh chóng trở thành một chuyện gia.
73. Thử mọi thứ
Không gì có thể ngăn cản bạn thử một giải pháp nào đó. Sau cùng thì, bạn có mất gì đâu nào?
74. Phát biểu ý kiến trong các cuộc họp
Ý tưởng và quan điểm của bạn đều có giá trị. Vì thế tham gia vào các cuộc họp sẽ giúp bạn phát triển mối quan hệ với team cũng như với sếp.
75. Phối hợp team chéo
Nếu bạn có cơ hội làm việc với các team khác trong công ty, đừng ngại thử.
76. Có những dự án vì đam mê
Khi bạn làm việc 40 tiếng 1 tuần, bạn cần 1 project mà bạn đam mê để làm. Những project đó sẽ giúp bạn tìm lại tình yêu với lập trình và thử những công nghệ mới mà bạn không có cơ hội để làm trên công ty.
77. Xác định mục tiêu nghề nghiệp của bạn
Nắm rõ con đường sự nghiệp lập trình của bạn rất quan trọng. Không có mục tiêu, cũng giống như bạn bắn tên mà không có bia để ngắm vậy.
78. Tham gia vào các cuộc thảo luận
Comment trên blog, tham gia trên twitter, trong các cộng đồng,… Bạn sẽ học được nhiều thứ hơn khi chủ động tranh luận.
79. Thực hiện những task ưu tiên
Học cách lên thứ tự ưu tiên cho task sẽ giúp bạn xử lý hiệu quả hơn. Luôn có một to-do list cho các công việc hàng ngày cũng như những công việc dài hạn và sắp xếp chúng khoa học.
80. Đừng nhìn sơ qua những cái chi tiết
Những thứ chi tiết có thể tạo nên khác biệt lớn cho project của bạn.
81. Tin tưởng đồng đội
Thành viên trong team của bạn được trả lương nhờ những kỹ năng của họ. Hãy dùng chúng và tin tưởng đồng nghiệp để hoàn thành công việc.
82. Học cách phân công nhiệm vụ
Nếu bạn ở vị trí leader, hãy học cách chia công việc cho team thật hiệu quả. Bạn sẽ tiết kiệm được thời gian và không phải thất vọng, vì bạn không thể làm hết được mọi thứ.
83. Đừng so sánh mình với người khác
Người duy nhất bạn nên so sánh đó chính là bạn của ngày hôm qua.
84. Luôn có đồng minh bên cạnh
Học lập trình là một hành trình rất lâu và khó khăn. Hãy tìm kiếm người có thể giúp bạn hoàn thành hành trình của bạn nhé.
85. Đừng bắt đầu làm những thứ có quy mô lớn
Bắt đầu làm một project quy mô lớn có thể giúp bạn trở thành người giỏi trong mắt mọi người. Tuy nhiên, bạn chỉ nên giữ tư tưởng làm những thứ to lớn trong đầu, chứ đừng bắt đầu làm liền. Vì nếu vậy bạn sẽ khiến cho team khó chịu khi phải làm những thứ phức tạp không cần thiết.
86. Cân nhắc về hiệu suất làm việc
Nếu bạn muốn dùng công nghệ xịn sò, bạn nên cân nhắc hiệu suất làm việc của mình. Liệu bạn có đang làm việc với hiệu suất thấp? Nếu vậy thì bạn nên cân nhắc việc sử dụng công nghệ mới.
87. Đừng phân biệt
Đừng phân biệt công nghệ hay ý tưởng mới. Hãy luôn sẵn sàng đón nhận và học hỏi những kỹ năng mới. Và bạn cũng đừng phân biệt người khác. Ai cũng xứng đáng được đối xử tốt.
88. Mô-đun hoá code của bạn
Bạn có thể viết toàn bộ code trong một file lớn, nhưng sẽ không thể maintain tốt. Mô-đun hoá sẽ giúp code của bạn chạy ngon và dễ test hơn.
89. Đừng tin vào copy và paste
Nếu bạn định copy paste một giải pháp nào đó trên StackOverflow, bạn nên hiểu rõ nó là gì. Hãy có trách nhiệm trong việc chọn lựa code để làm việc.
90. Tạo ra một môi trường đầy cảm hứng
Bạn sẽ có nhiều động lực làm việc hơn nếu có không gian làm việc tốt.
91. Luôn nhớ về xuất phát điểm của mình
Chúng ta đều bắt đầu giống như nhau. Mặc cho những kỹ năng hay vị trí bạn làm là gì, đừng bao giờ quên mình đến từ đâu.
92. Luôn lạc quan
Nếu mọi thứ trở nên xấu đi, hãy cố gắng và lạc quan lên. Ngày mai là một ngày mới. Lạc quan sẽ giúp team bạn có sức mạnh vượt qua tất cả.
93. Liên tục xem lại quy trình làm việc của bạn
Không phải mọi thứ đang hoạt động tốt ở hiện tại thì tương lai cũng sẽ vậy. Luôn đánh giá lại workflow và điều chỉnh khi cần thiết.
94. Học cách làm việc ở nhà
Nếu bạn có thể làm việc ở nhà, hãy làm thật hiệu quả. Tìm một nơi ngoài văn phòng và tránh bị làm phiền. boneskull có viết một bài khá hay về làm việc tại nhà. Bạn có thể đọc ở đây.
95. Mã truy cập
Khả năng truy cập không cần phải suy nghĩ quá nhiều, và cũng không nên quá khó. Mọi người cần dễ dàng sử dụng sản phẩm của bạn.
96. Tôn trọng cam kết
Nếu bạn cam kết với ai sẽ làm đúng deadline thì nên giữ lời hứa. Còn trong trường hợp có thể trễ deadline thì hãy nói ra ngay.
97. Hãy chủ động
Nếu bạn đang rảnh thì hãy giúp team bạn. Họ sẽ rất biết ơn vì bạn đã chủ động như vậy đấy.
98. Làm Portfolio đẹp
Một Portfolio đẹp sẽ làm bạn khác biết với người khác. Thể hiện mọi kỹ năng lập trình và thiết kế trong Portfolio của mình nhé.
99. Ghi nhớ lý do bạn yêu lập trình
Bạn vào ngành này vì thấy nó thú vị. Nếu bạn có thất vọng hay bực bội, hãy nghỉ ngơi một chút. Cho bạn không gian để tìm lại đam mê lập trình trong mình.
100. Chia sẻ kiến thức
Nếu bạn học được thứ gì đó hữu ích, hãy chia sẻ cho mọi người. Hãy chia sẻ trong một buổi meetup hay hội thảo. Chia sẻ cho đồng nghiệp hay nhân viên của bạn trong mỗi buổi ăn trưa. Chia sẻ kiến giúp bạn củng cố lại kiến thức của mình trong khi đem đến nhiều giá trị hơn cho người khác.
Kết
Trên đây là 100 tips cho lập trình viên để phát triển bản thân. Hy vọng các bạn sẽ cần tới. Cảm ơn các bạn đã theo dõi bài viết!
Full Stack Overflow Developer, thực tế phần lớn mọi người sau khi nghe cụm từ này đều sẽ cười. Họ coi đó như một sự chế nhạo hay là một điều gì không tốt cho lắm.
Mình từng đọc được một đoạn bài viết như thế này trên một trang tạp chí công nghệ lớn:
“Một Full Stack Overflow Developer làm việc gần như 100% bằng cách sao chép và dán các đoạn code copy từ trang Stack Overflow. Thay vì nghiên cứu về vấn đề trước, họ lại đến đó ngay để đặt câu hỏi và ngồi đợi, hy vọng mọi người sẽ vào và cho họ kết quả.”
Vậy những gì ở trên là đúng hay sai? Và tại sao đúng, tại sao sai? Hôm nay mình sẽ chia sẻ những hiểu biết và chính kinh nghiệm của bản thân về vấn đề này.
Stack Overflow là cách gọi quen thuộc của trang web https://stackoverflow.com. Đây là nơi mà các lập trình viên trên khắp thế giới vào đặt câu hỏi về những vấn đề khó khăn, bugs mà họ chưa giải quyết được.
Những người khác có hiểu biết về vấn đề đó sẽ vào bình luận giúp đỡ. Rõ ràng là một cộng đồng lớn như vậy, Stack Overflow thực sự là một nguồn tài nguyên quý báu và đáng kinh ngạc.
Nếu có một developer nào nói với bạn rằng anh ta chưa từng vào stackoverflow bao giờ mà vẫn giỏi thì mình khuyên các bạn nên dành 99% tỏ ra nghi ngờ và 1% thì hãy tin, nhưng là tin rằng anh ta vào những trang forum tương tự khác để học hỏi.
Trở lại vấn đề, nếu Stack Overflow tốt là thế thì tại sao nhiều người lại cho rằng,việc tìm hiểu và học hỏi ở trên này là không nên? Là không tốt?
Mình sẽ không hoàn toàn phủ nhận câu nói đó. Với mình, điều trên sẽ đúng đối với những ai Không chịu đào sâu vấn đề, lúc nào cũng đi tìm code trên Stack Overflow. Copy và paste mà không hề suy nghĩ, chỉ mong code chạy được cái là dừng lại ở đó luôn. Và sai đối với những người lên đó học hỏi, tìm ra cách giải quyết vấn đề cũng như đào sâu vào nó, tại sao làm như thế này lại giải quyết được vấn đề…
Cho phép mình gọi ngắn gọn 2 nhóm người này một là “lạm dụng stackoverflow” theo nghĩa tiêu cực. Và hai là “tận dụng stackoverflow” theo nghĩa tích cực.
Nhóm người lạm dụng Stack Overflow một cách không tốt.
Đối với những người này, khi họ sao chép và dán một đoạn mã và thấy chúng chạy được, điều tuyệt vời đối với họ là họ có thể thấy được kết quả ngay lập tức mà không cần phải hiểu tại sao vấn đề lại được giải quyết như vậy.
Trước mắt, họ sẽ cảm thấy tốt hơn nhiều so với việc thất vọng nếu không làm được gì đó. Nhưng sẽ có những vấn đề như sau:
Khi họ không đào sâu hơn vào vấn đề, có thể chỗ code hay thư viện mà họ copy & paste đã cũ và lỗi thời, có thể chứa các lỗ hổng bảo mật… Rõ ràng là rất nguy hiểm nếu không tìm hiểu những cách giải quyết khác tốt hơn hay là cập nhật các bản sửa lỗi mới nhất của thư viện.
Trường hợp gỡ lỗi, nâng cấp hoặc bảo trì ứng dụng trong tương lai, với việc không hiểu biết về đoạn code mà họ copy & paste vào, thực sự sẽ trở thành một vấn đề khó khăn lớn. Không những ảnh hưởng tới chính họ, mà còn cả chất lượng sản phẩm, cả những người khác cùng trong team làm việc.
Về lâu dài, việc đó sẽ còn khiến giảm giá trị nghề nghiệp của bản thân họ. Họ khá giống những con robot, cứ đi đặt mọi thứ vào nhau và hy vọng có được kết quả. Những người khác sẽ không thể coi họ là chuyên gia được.
“Sinh ra là một bản thể, đừng chết như một bản sao.”
(Một câu nói và cũng là tên của một cuốn sách cực kỳ hay.)
Nhóm người biết tận dụng tốt Stack Overflow.
Với những người biết tận dụng, họ vẫn lên trang này để tìm kiếm ý tưởng giải quyết vấn đề mà họ chưa biết. Họ mong muốn hiểu vấn đề, hiểu ý tưởng giải quyết chứ không đơn thuần chỉ là copy và paste.
Khi tìm hiểu vấn đề, họ sẽ học được rất nhiều tips của những người giỏi hơn. Tư duy giải quyết vấn đề của họ cũng sẽ được mở rộng hơn trong cả quá trình tìm hiểu.
Về sau, khi gặp lại vấn đề này hoặc những vấn đề tương tự, việc giải quyết đã quá đơn giản. Họ sẽ tiết kiệm được rất nhiều thời gian.
Tốt hơn nữa, nếu gặp phải vấn đề mới, thì họ cũng sẽ có thói quen chủ động suy nghĩ cách giải quyết trước khi tìm kiếm ý tưởng trên mạng, vì trong họ đã hình thành tư duy này. Điều này thực sự là một tinh thần rất tốt mà tất cả mọi người cần học hỏi dù ở bất kỳ lĩnh vực nào.
Việc nghiên cứu, tận dụng tài nguyên học tập đúng cách cũng giống như bạn học kiến thức từ một người thầy vậy.
Stack overflow là nguồn tài nguyên kiến thức cực kỳ hữu ích. Hầu như những vấn đề mà chúng ta gặp phải đã có những người đi trước. Họ gặp rồi và đã có những ý tưởng giải quyết vấn đề trên đó. Chỉ cần tìm kiếm chính xác từ khóa, chịu đào sâu tư duy khám phá vấn đề, bạn sẽ càng thành công trên con đường học tập và làm việc của chính bạn.
Kết luận
Dĩ nhiên, còn rất nhiều lợi ích nữa đối với người tận dụng và nhiều vấn đề khác nữa với người lạm dụng, những điều đó chỉ có bản thân của chính mỗi chúng ta thực tế trải qua mới có thể hiểu hết được.
Hy vọng một chút chia sẻ của bản thân mình sẽ giúp được các bạn phần nào đó trong việc tìm hiểu, định hướng học tập, để biết cách tận dụng kiến thức một cách hợp lý và đúng đắn.
Cảm ơn các bạn đã dành chút thời gian xem bài viết của mình!
Trong bài viết này, Topdev sẽ giới thiệu với các bạn một thuật toán thần thánh: thuật toán quy hoạch động. Nếu bạn tham gia các cuộc thi code, bạn nhất định phải biết thuật toán này.
Gần một nửa các bài thi trong các cuộc thi code cần đến quy hoạch động. Tất nhiên, có những cách khác để giải bài toán đó. Nhưng vì các cuộc thi code đều có giới hạn về thời gian, cũng như bộ nhớ của chương trình, nên một thuật toán hiệu quả là cực kỳ cần thiết. Và trong những trường hợp như vậy, quy hoạch động là một trong những thuật toán xuất hiện nhiều nhất.
Quy hoạch động là gì?
Quy hoạch động (dynamic programming) là một phương pháp được sử dụng trong toán học và khoa học máy tính để giải quyết các vấn đề phức tạp bằng cách chia chúng thành các bài toán con đơn giản hơn. Bằng cách giải mỗi bài toán con chỉ một lần và lưu trữ kết quả, nó tránh được các phép tính dư thừa, dẫn đến giải pháp hiệu quả hơn cho nhiều bài toán.
Quy hoạch động (DP) hoạt động như thế nào?
Quy hoạch động (DP) hoạt động như thế nào?
Nguyên lý hoạt động của Dynamic Programming
Chia bài toán thành các bài toán con nhỏ hơn: Bài toán ban đầu được chia thành các bài toán con mà mỗi bài toán con là một phần của bài toán lớn hơn. Các bài toán con này thường có tính chất lặp lại.
Lưu trữ kết quả của các bài toán con: Để tránh việc tính toán lặp lại các bài toán con nhiều lần, kết quả của chúng được lưu trữ trong một bảng (thường là mảng hoặc ma trận).
Sử dụng lại kết quả đã lưu: Khi cần kết quả của một bài toán con nào đó, chương trình sẽ kiểm tra bảng lưu trữ để xem kết quả đã được tính toán trước đó chưa. Nếu đã có, nó sẽ sử dụng lại kết quả đó thay vì tính toán lại từ đầu.
Các bước thực hiện
Xác định cấu trúc của bài toán con tối ưu: Hiểu rõ cách mà bài toán lớn được chia thành các bài toán con và cách mà kết quả của các bài toán con này kết hợp lại để giải quyết bài toán lớn.
Định nghĩa hàm hồi quy: Xác định công thức hoặc hàm hồi quy để giải quyết bài toán dựa trên các bài toán con. Đây là bước quan trọng để hiểu cách các bài toán con liên kết với nhau.
Tính giá trị của bài toán con theo thứ tự từ nhỏ đến lớn: Bắt đầu từ những bài toán con nhỏ nhất và sử dụng công thức hồi quy để tính giá trị của các bài toán con lớn hơn dựa trên các bài toán con nhỏ hơn đã được tính toán trước đó.
Lưu trữ kết quả của các bài toán con: Kết quả của mỗi bài toán con được lưu trữ trong bảng để sử dụng lại sau này.
Giải quyết bài toán lớn nhất: Sau khi đã tính toán và lưu trữ kết quả của tất cả các bài toán con, sử dụng chúng để giải quyết bài toán ban đầu.
Khi nào thì dùng thuật toán quy hoạch động
Khi nào thì chúng ta cần đến quy hoạch động? Đó là một câu hỏi rất khó trả lời. Không có một công thức nào cho các bài toán như vậy.
Tuy nhiên, có một số tính chất của bài toán mà bạn có thể nghĩ đến quy hoạch động. Dưới đây là hai tính chất nổi bật nhất trong số chúng:
Bài toán có các bài toán con gối nhau.
Bài toán có cấu trúc con tối ưu.
Thường thì một bài toán có đủ cả hai tính chất này, chúng ta có thể dùng quy hoạch động được. Một câu hỏi rất thú vị là không dùng quy hoạch động có được không? Câu trả lời là có, nhưng nếu bạn đi thi code, bạn trượt là cái chắc. Để hiểu rõ hơn, chúng ta sẽ tìm hiểu từng tính chất một trong những phần dưới đây
Bài toán con gối nhau
Tương tự như thuật toán chia để trị, quy hoạch động cũng chia bài toán lớn thành các bài toán con nhỏ hơn. Quy hoạch động được sử dụng khi các bài toán con này được gọi đi gọi lại. Phương pháp quy hoạch động sẽ lưu kết quả của bài toán con này, và khi được gọi, nó sẽ không cần phải tính lại, do đó làm giảm thời gian tính toán.
Quy hoạch động sẽ không thể áp dụng được (hoặc nói đúng hơn là áp dụng cũng không có tác dụng gì) khi các bài toán con không gối nhau. Ví dụ với thuật toán tìm kiếm nhị phân, quy hoạch động cũng không thể tối ưu được gì cả, bởi vì mỗi khi chia nhỏ bài toán lớn thành các bài toán con, mỗi bài toán cũng chỉ cần giải một lần mà không bao giờ được gọi lại.
Một ví dụ rất điển hình của bài toán con gối nhau là bài toán tính số Fibonacci. Bài toán quá nổi tiếng rồi, chúng ta có thể tính toán số Fibonacci theo đúng công thức như sau:
def fib(n):
if n <= 1:
return n
return fib(n -1) + fib(n - 2)
Nếu tính toán như trên, chúng ta rất nhiều bài toán con sẽ được tính đi tính lại, điển hình là các số fib(0) và fib(1).
Và quy hoạch động chính là một trong số những phương pháp có thể giúp chúng ta tối ưu hóa quá trình tính toán này. Mỗi bài toán con (số fib) sẽ được lưu lại trước khi tính những bài toán con lớn hơn. Nhờ đó, mà việc tính toán giảm đi đáng kể, mỗi bài toán con chỉ cần tính đúng một lần.
Một ví dụ quy hoạch động với bài toán này như sau:
def fib(n):
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
Qua ví dụ trên, bạn đã thấy được sức mạnh vượt trội của quy hoạch động chưa? Đó cũng chính là lý do mà nó rất được ưa chuộng trong các cuộc thi lập trình, khi mà thời gian và bộ nhớ đều là hữu hạn (và thường khá nhỏ).
Cấu trúc con tối ưu là một tính chất là lời giải của bài toán lớn sẽ là tập hợp lời giải từ các bài toán nhỏ hơn.
Mình lấy một ví dụ cho dễ hiểu:
Trong bài toán tìm đường đi ngắn nhất trong đồ thị, nếu một node x nằm trên đường đi ngắn nhất giữa hai node u, v thì đường đi ngắn nhất từ u đến v sẽ là tổng hợp của đường đi ngắn nhất từ u đến x và đường đi ngắn nhất từ x đến v. Môt số thuật toán tìm đường trên đồ thị (nổi tiếng nhất có lẽ là Dijkstra) đều dựa trên tính chất này, và nó cũng áp dụng quy hoạch động.
Tính chất cấu trúc con tối ưu rất quan trọng. Nó cho phép chúng ta giải bài toán lớn dựa vào các bài toán con đã giải được. Nếu không có tính chất này, chúng ta không thể áp dụng quy hoạch động được.
Không phải bài toán nào cũng có tính chất cấu trúc con tối ưu này. Ví dụ với đồ thị sau:
Đường đi dài nhất từ q -> t sẽ là q -> r -> t hoặc q -> s -> t. Nhưng không giống như bài toán tìm đường đi ngắn nhất, đường đi dài nhất không phải là tổ hợp của những đường đi thành phần, do đó, bài toán này không có cấu trúc con tối ưu.
Ví dụ, đường q -> r -> t không phải là tổ hợp của đường đi dài nhất từ q -> r và đường đi dài nhất từ r -> t. Bởi vì, đường đi dài nhất q -> rphải là q -> s -> t -> r và đường đi dài nhất từ r -> t phải là r -> q -> s -> t.
Ưu và nhược điểm của Quy hoạch động
Ưu điểm của Dynamic Programming (DP)
Dynamic Programming có nhiều ưu điểm, bao gồm:
Giảm thời gian tính toán: Quy hoạch động lưu trữ kết quả của các bài toán con đã được tính toán trước đó, giúp giảm thiểu đáng kể số lượng phép tính cần thực hiện. Điều này đặc biệt hiệu quả trong các bài toán có tính chất lặp lại, như dãy Fibonacci hoặc bài toán tối ưu hóa.
Giải quyết được các bài toán phức tạp: DP cho phép giải quyết nhiều bài toán tối ưu hóa và lập lịch phức tạp mà các phương pháp khác không thể làm được hiệu quả, chẳng hạn như bài toán balo, chuỗi con chung dài nhất (LCS), và nhiều bài toán đồ thị.
Tránh được các tính toán lặp lại không cần thiết: Bằng cách lưu trữ kết quả của các bài toán con, DP tránh được việc tính toán lặp lại những phần đã giải quyết, giúp tiết kiệm tài nguyên và tăng hiệu suất.
Độ chính xác cao: Kỹ thuật DP đảm bảo tìm ra giải pháp tối ưu cho bài toán bằng cách xây dựng các giải pháp từ các bài toán con tối ưu.
Nhược điểm của Quy hoạch động
Tốn bộ nhớ: DP yêu cầu lưu trữ kết quả của tất cả các bài toán con, điều này có thể tốn rất nhiều bộ nhớ, đặc biệt đối với các bài toán có không gian trạng thái lớn. Điều này có thể gây ra vấn đề khi áp dụng DP cho các bài toán có quy mô lớn.
Phức tạp trong việc triển khai: Hiểu và triển khai một giải pháp DP đòi hỏi người lập trình phải hiểu rõ cấu trúc của bài toán và cách chia nhỏ nó thành các bài toán con. Điều này có thể phức tạp và tốn nhiều thời gian để thiết kế và debug.
Không phải lúc nào cũng khả thi: Không phải bài toán nào cũng có thể áp dụng được DP. Các bài toán cần có tính chất con tối ưu (optimal substructure) và tính chất lặp lại của bài toán con (overlapping subproblems). Nếu bài toán không có các tính chất này, DP sẽ không hiệu quả.
Tiêu tốn thời gian cho việc lưu trữ và truy xuất: Mặc dù giảm thời gian tính toán, việc lưu trữ và truy xuất kết quả từ bộ nhớ cũng tiêu tốn thời gian, đặc biệt khi kích thước của bảng lưu trữ lớn.
Một số bài toán quy hoạch động
Trong phần này, chúng ta sẽ làm quen với quy hoạch động thông qua một số ví dụ cụ thể. Chúng ta sẽ xem xét cách quy hoạch động được áp dụng vào các bài toán cụ thể như thế nào, đồng thời qua đó, chúng ta sẽ hiểu hơn về các tính chất ở phần trước.
Ví dụ 1: Bài toán kinh điển với đồng xu
Đây là một ví dụ rất kinh điển khi học về quy hoạch động. Có thể có nhiều cách phát biểu khác nhau nhưng về cơ bản, nội dung của nó sẽ tương tự như sau.
Giả sử chúng ta có n đồng xu nặng lần lượt là W1, W2, ..., Wn, và bài toán đặt ra là tìm số lượng đồng xu nhỏ nhất để tổng khối lượng của chúng là một giá trị S. Tất nhiên, số lượng đồng xu là không giới hạn.
Giả sử chúng ta có n đồng xu nặng lần lượt là W1, W2, ..., Wn, và bài toán đặt ra là tìm số lượng đồng xu nhỏ nhất để tổng khối lượng của chúng là một giá trị S. Tất nhiên, số lượng đồng xu là không giới hạn.
Với bài toán này, chúng ta cần xây dựng và giải các bài toán con gối nhau. Với ví dụ của chúng ta, mỗi bài toán con dp(P) với P <= S là bài toán tìm số đồng xu nhỏ nhất để khối lượng của chúng là P. và dp(P) = k chính là số lượng đồng xu nhỏ nhất đó.
Chúng ta sẽ áp dụng phương pháp quy hoạch động bằng cách bắt đầu từ bài toán con dp(0) sau đó tiếp tục với các bài toán con lớn hơn. Lời giải của các bài toán con sẽ được xây dựng lần lượt cho đến chúng ta xây dựng đến bài toán dp(S) và đó chính là kết quả của bài toán lớn. Một điều cần lưu ý với kỹ thuật này là bài toán con tiếp theo sẽ không thể giải được nếu chúng ta chưa giải bài toán con trước đó.
Cuối cùng là phần khó nhất của mọi bài toán quy hoạch động, đó là trả lời câu hỏi: cấu trúc con tối ưu của bài toán này ở đâu. Hay nói một cách khác, làm thế nào để từ những bài toán nhỏ hơn có thể tổ hợp ra lời giải cho bài toán lớn. Với vị dụ kinh điển này, mọi thứ sẽ tương đối đơn giản, nhưng với những bài toán phức tạp hơn, chúng ta cần suy nghĩ và tính toán nhiều hơn.
Quay trở lại với bài toán của chúng ta. Giả sử P là tổng khối lượng của các đồng xu nặng lần lượt là V1, V2, ..., Vj. Để có được khối lượng P, chúng ta cần thêm vài đúng 1 đồng xu nặng U vào khối lượng Q sao cho Q + U = P. Tất nhiên, bài toán con dp(Q) chúng ta đã có lời giải nên chúng ta sẽ biết được cần bao nhiêu đồng xu cho dp(P). Và vì có nhiều đồng xu U(nhiều nhưng hữu hạn) nên chúng ta có thể cần đến nhiều bài toán con trước đó, và dp(p) là giá trị nhỏ nhất sau khi tổng hợp những bài toán con đó.
Ví dụ với n = 3, S = 11, W = [1, 3, 5].
Bắt đầu với bài toán con 0 ta có dp(0) = 0
Với bài toán con 1, có 1 đồng xu (nặng 1) có thể thêm vào từ 0 đồng xu nào cả. Vậy dp(1) = dp(0) + 1 = 1.
Với bài toán con 2, cũng chỉ có 1 đồng xu (nặng 1) có thể thêm vào từ 1 đồng xu. Vậy dp(2) = dp(1) + 1 = 2.
Với bài toán con 3, chúng ta có thể thêm 1 đồng xu 3 vào 0 đồng xu hoặc thêm 1 đồng xu 1 vào 2 đồng xu. Rõ ràng là cách đầu tiên cho kết quả nhỏ hơn. Vậy dp(3) = min(dp(2) + 1, dp(0) + 1) = min(3, 1) = 1
…
Cứ tiếp tục như vậy cho đến bài toán S chính là đáp án chúng ta cần tìm.
Về mặt cài đặt, quy hoạch động thường lưu kết quả vào một mảng. Trong ví dụ của chúng ta, mảng dp[0..S] sẽ lưu kết quả cho từng bài toán con. Nói cách khác, dp[P] = k nghĩa là cần ít nhất k đồng xu để có khối lượng là PToàn bộ mảng này sẽ được tính bằng vòng lặp. Đoạn code sau mô tả toàn bộ quá trình này.
n, S = map(int, input().split())
w = list(map(int, input().split()))
dp = [0] * (S + 1)
dp[0] = 0
for P in range(1, S + 1):
dp[P] = min(dp[P - x] for x in w if x <= P) + 1
print(dp)
print(dp[S])
# Nếu đầu vào như sau: n = 3, S = 11, w = [1, 3, 5]
# Thì bảng lời giải cho các bài toán con sẽ lần lượt như sau:
# P = 0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10|11
# ------+--+--+--+--+--+--+--+--+--+--+--
# k = 0 |1 |2 |1 |2 |1 |2 |3 |2 |3 |2 |3
Ví dụ 2: Xâu con chung dài nhất (LCS)
Thêm một ví dụ nữa cho dễ, cũng là một bài toán rất nổi tiếng.
Cho hai xâu ký tự. Tìm độ dài xâu con chung nhỏ nhất giữa chúng. Ví dụ với 2 xâu “quetzalcoatl” và “tezcatlipoca” thì xâu con chung dài nhất sẽ là “ezaloa” với độ dài 6.
Với bài toán này, chúng ta sẽ lần lượt giải các bài toán con như sau:
Lấy i ký tự đầu tiên từ xâu thứ nhất và j ký tự đầu tiên từ xâu thứ hai và tìm độ dài xâu chung dài nhất giữa 2 xâu con được lấy ra đó. Dễ dàng thấy được rằng, lời giải của mỗi bài toán con sẽ phụ thuộc vào i và j, dp(i, j). Và bài toán lớn sẽ được giải bằng cách lần lượt giải các bài toán con lần lượt từ dp(0, 0) và tăng dần độ dài xâu được lấy ra cho đến khi chúng ta lấy ra toàn bộ xâu của đề bài.
Chúng ta hãy bắt đầu lần lượt các bài toán con. Đương nhiên, nếu một trong hai xâu là rỗng thì xâu con chung của chúng cũng rỗng. Vậy dp(0, j) = dp(i, 0) = 0. Nếu cả i và j đều dương, chúng ta cần suy xét một vài trường hợp.
Nếu ký tự cuối cùng của xâu thứ nhất không có mặt trong xâu con chung dài nhất, nó có thể bị bỏ qua mà không ảnh hưởng gì đến kết quả. Công thức ở đây sẽ là dp(i, j) = dp(i - 1, j).
Tương tự như trường hợp trên, ký tự cuối cùng của xâu thứ hai không ảnh hưởng đến kết quả thì dp(i, j) = dp(i, j - 1).
Trường hợp cuối cùng, nếu hai ký tự cuối cùng của hai xâu x1, x2 đều có mặt trong xâu con chung dài nhất. Dĩ nhiên là hai ký tự này phải là một thì điều này mới xảy ra, tức là x1 == x2. Trong trường hợp này, khi xoá đi bất cứ một ký tự nào trong hai ký tự đó đều khiến xâu con chung dài nhất ngắn đi 1 ký tự. Vậy rõ ràng là dp(i, j) = dp(i - 1, j - 1) + 1.
Trong cả ba trường hợp trên, chúng ta phải chọn ra trường hợp nào cho kết quả là xâu con chung dài nhất (với bài toán này thì chỉ cần đưa ra độ dài đó là đủ).
Về mặt cài đặt, dp sẽ được lưu trong mảng hai chiều. Kết quả của mảng này sẽ được tính toán thông qua vòng lặp hai lớp. Lưu ý rằng, chúng ta cần thực hiện vòng lặp sao cho chúng ta sẽ giải lần lượt từng bài toán con một, theo thứ tự từ nhỏ đến lớn. Bởi vì mỗi bài toán con dp(i, j) đều phụ thuộc vào các bài toán con trước đó dp(i - 1, j), dp(i, j - 1), dp(i - 1, j - 1).
n1, n2 = map(int, input().split())
s1, s2 = input().split()
t = [[0] * (len(s2) + 1) for _ in range(len(s1) + 1)]
for i, x1 in enumerate(s1, 1):
for j, x2 in enumerate(s2, 1):
if x1 == x2:
t[i][j] = t[i - 1][j - 1] + 1
else:
t[i][j] = max(t[i][j - 1], t[i - 1][j])
print(t[-1][-1])
# Kết quả khi giải các bài toán con như bảng sau:
#
# S| t e z c a t l i p o c a
# T ji| 0 1 2 3 4 5 6 7 8 9 10 11 12
# ----+--------------------------------------
# 0 | 0 0 0 0 0 0 0 0 0 0 0 0 0
# q 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0
# u 2 | 0 0 0 0 0 0 0 0 0 0 0 0 0
# e 3 | 0 0 1 1 1 1 1 1 1 1 1 1 1
# t 4 | 0 1 1 1 1 1 2 2 2 2 2 2 2
# z 5 | 0 1 1 2 2 2 2 2 2 2 2 2 2
# a 6 | 0 1 1 2 2 3 3 3 3 3 3 3 3
# l 7 | 0 1 1 2 2 3 3 4 4 4 4 4 4
# c 8 | 0 1 1 2 3 3 3 4 4 4 4 5 5
# o 9 | 0 1 1 2 3 3 3 4 4 4 5 5 5
# a 10| 0 1 1 2 3 4 4 4 4 4 5 5 6
# t 11| 0 1 1 2 3 4 5 5 5 5 5 5 6
# l 12| 0 1 1 2 3 4 5 6 6 6 6 6 6
Quy hoạch động vs Memoization
Có một kỹ thuật khác gọi là “memoization” cũng có cách tiếp cận tương tự với quy hoạch động. Cả quy hoạch động và memoization đều dùng để tối ưu các vòng lặp mà có tính toán tượng tự nhau, trong đó kết quả của phép tính lớn hơn sẽ cần được tính toán dựa vào kết quả của phép tính nhỏ hơn. Memoization thường được sử dụng trong các phép tính đệ quy khi mà một tính toán bị lặp đi lặp lại nhiều lần. Nó sẽ lưu một bảng các giá trị tính được, mỗi khi có tính toán cần thực hiện, chúng ta sẽ tra bảng đó trước. Nếu bảng đã có kết quả rồi, chúng ta chỉ cần lấy ra là xong, nếu chưa, chúng ta sẽ tính toán như thường và tiếp tục lưu vào bảng.
Memoization không phải là một thuật toán theo đúng nghĩa, nó là một kỹ thuật được sử dụng trong lập trình thì đúng hơn. Để hiểu rõ hơn về kỹ thuật này, mình xin lấy ví dụ ngay với bài toán Fibonacci. Chúng ta sẽ sử dụng memoization như sau:
Sự khác biệt chủ yếu là quy hoạch động sẽ thực hiện việc tính toán theo một thứ tự định trước, trong khi memoization duyệt theo chiều sâu. Quy hoạch động không bao giờ tính toán một bài toán con hai lần, tương đối giống với các phép tính đệ quy với memoization. Tuy nhiên memoization thì không bao giờ tính toán những phép tính thừa trong khi quy hoạch động sẽ cần tất cả mọi bài toán con. Đây là một phương pháp khá hay, nó chỉ tính toán những gì cần thiết và lưu kết quả này lại để sau này dùng lại khi nào được gọi mà không cần tính toán nữa.
Dưới đây là một số ưu, nhược điểm của memoization khi so sánh với quy hoạch động:
Ưu điểm
Dễ code hơn
Không yêu cầu thứ tự thực hiện tính toán
Chỉ tính toán những gì cần thiết
Nhược điểm
Chỉ có một kiểu duyệt duy nhất
Thường chậm hơn quy hoạch động.
Các dạng toán quy hoạch động
Phần lớn các bài toán quy hoạch động có thể chia làm hai loại: bài toán cần quy hoạch động để tối ưu và bài toán quy tổ hợp. Trong những phần dưới đây, chúng ta sẽ xem xét từng loại bài toán này.
Bài toán tối ưu
Bài toán tối ưu yêu cầu chúng ta phải tìm đáp án tốt nhất từ mục tiêu của bài toán. Cả hai ví dụ mình đưa ra ở trên đều thuộc loại bài toán này (một bài tìm số đồng xu ít nhất, một bài tìm xâu con dài nhất). Mối liên hệ của các bài toán con thuộc dạng này có công thức chúng là dp[s] = min(F1(dp[i], dp[j], ..., dp[k]), F2(dp[u], dp[v], ..., dp[w]), ..., Fl(dp[q], dp[p], ..., dp[z])), trong đó dp mảng lưu kết quả của các bài toán con đó.
Mỗi bài toán được giải dựa trên bài toán đã được giải trước đó. Đây chính là tính chất cấu trúc con tối ưu của mỗi bài toán. Với bài toán đồng xu, mỗi bài toán mới đều được giải bằng cách thêm đúng 1 đồng xu vào kết quả từ trước đó. Kết quả cuối cùng là kết quả tốt nhất thu được từ nhiều cách thêm đồng xu với khối lượng khác nhau.
Trước khi tính toán, mảng chứa kết quả có thể được điền đầy một giá trị trung tính nào đó. Giá trị trung tính có nghĩa là giá trị đó sẽ không bao giờ là đáp án cho bất kỳ bài toán con nào. Ví dụ khi cần tìm ra số đồng xu nhỏ nhất, chúng ta có thể điền mảng này bằng số dương lớn nhất, mọi tính toán tiếp theo sẽ cho ra một kết quả nhỏ hơn nhiều. Nếu không ra kết quả nào khác, chúng ta có thể coi như là không có một đáp án nào cho bài toán con đó.
Bài toán tổ hợp
Bài toán tổ hợp thường yêu cầu chúng ta tìm ra số cách khác nhau để thực hiện một việc gì đó. Nhiều bài thi code thường có kết quả rất lớn và họ yêu cầu chúng ta đưa đáp án dạng modulo của 10000007. Trong dạng bài toán này, công thức khi xây dựng các bài toán con sẽ là R[s] = F1(R[i], R[j], ..., R[k]) + F2(R[u], R[v], ..., R[w]) + ... + Fl(R[q], R[p], ..., R[z]). Sự khác biệt cơ bản của dạng bài toán này với dạng bài toán tối ưu là ở chỗ chúng ta cần tính tổng thay vì tìm số lớn nhất hoặc nhỏ nhất.
Trong mọi bài toán quy hoạch động, tính chất cấu trúc con tối ưu luôn là quan trọng nhất và cũng là tính chất khó đảm bảo nhất. Nếu cấu trúc con không được tối ưu, chúng ta sẽ tính toán theo một phương thức sai lầm và đương nhiên, kết quả thu được cũng không chính xác.
Với phần lớn các bài toán quy hoạch động, việc chia các bài toán con gối nhau khá dễ dàng trong khi đảm bảo cấu trúc con tối ưu thì khó hơn nhiều.
Mình sẽ đưa ra hai ví dụ tương tự nhau cho các bạn hiểu rõ hơn về những khó khăn để đảm bảo tính chất này.
Vẫn với bài toán đồng xu, chúng ta sẽ thay đổi một chút để có bài toán tổ hợp như sau:
Tìm số cách khác nhau để chọn ra các đồng xu sao cho tổng khối lượng của chúng là S.
Các bài toán con sẽ tương tự như trước: dp(P) = k là số cách khác nhau để chọn ra các đồng xu có tổng khối lượng là P. Công thức đệ quy trong trường hợp này sẽ biến đổi theo bài toán như sau:
# Công thức đệ quy cho bài toán quy hoạch động
# {dp[0] = 1;
# {dp[P] = sum(dp[P-Wi]); (for Wi <= P)
#
# Với đầu vào như sau: n = 3, S = 11, W = [1, 3, 5]
# Mảng kết quả quy hoạch động sẽ là
# P = 0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10|11
# ------+--+--+--+--+--+--+--+--+--+--+--
# k = 1 |1 |1 |2 |3 |5 |8 |12|19|30|47|74
Bài toán tổ hợp cũng có thể có một giá trị trung tính. Bởi vì bài toán tổ hợp thường tính tổng, giá trị trung tính sẽ là 0. Bài toán tổ hợp yêu cầu tìm số cách khác nhau để làm gì đó, do đó giá trị 0 sẽ không ảnh hưởng gì đến đáp án. Một điểm đặc biệt quan trọng trong bài toán tổ hợp này là mỗi cách chúng ta chỉ tính đúng một lần. Nói thì dễ nhưng nhiều khi trong thực hành chúng ta hay gặp sai sót ở chỗ cực kỳ quan trọng này.
Tiếp tục thay đổi thêm một chút, chúng ta sẽ có bài toán tổ hợp như sau:
Tìm số cách khác nhau để chọn ra các đồng xu sao cho tổng khối lượng của chúng là S. Với điều kiện, các cách lấy đồng xu là hoán vị của nhau không được coi là khác nhau.
Bài toán này khó hơn bài toán trước một chút. Nếu chúng vẫn chia các bài toán con như cũ thì không thể có được cấu trúc con tối ưu. Ví dụ, với các đồng xu 1, 3, 5 thì (1, 3) và (3, 1) đều cho kết quả là 4 nhưng chỉ được coi là 1 cách.
Với bài toán này, chúng ta sẽ chia bài toán lớn thành các bài toán con theo một cách tương đối khác. Chúng ta thấy rằng, kết quả (số cách chọn đồng xu) sẽ là tổng hợp của hai kết quả:
Số cách lấy đồng xu từ n - 1 đồng xu đầu tiên, tức là chúng ta coi như không có đồng xu nặng nhất
Số cách lấy đồng xu có chứa đồng xu nặng nhất.
Kết quả sẽ là tổng của hai kết quả trên. Các bạn thấy đó, với cách xây dựng bài toán con như thế này, chúng ta đã xây dựng các bài toán con gối nhau mà vẫn đảm bảo cấu trúc con tối ưu (kết quả bằng tổng của các bài toán con).
Nhân tiện, với cách chia bài toán như vậy, chúng ta có thể thu được lời giải bằng cách đệ quy đơn giản như sau:
n, S = map(int, input().split())
w = list(map(int, input().split()))
def count(arr, x):
# Có 1 cách (lấy ra 0 đồng xu) cho tổng khối lượng bằng 0
if x == 0:
return 1
# Không thể lấy được các đồng xu cho khối lượng âm
if x < 0:
return 0
# Không thể lấy nếu không có đồng xu nào
if not arr and x >= 1:
return 0
# Kết quả là tổ hợp các bài toán con
return count(arr[:-1], x) + count(arr, x - arr[-1])
print(count(w, S))
Tuy nhiên, như mình đã nói ở phần trước, nếu bạn đang thi code, cách làm này sẽ không mang lại bất cứ hy vọng đạt giải nào, do nó cực kỳ mất thời gian và bộ nhớ. Tuy nhiên, chúng ta có thể áp dụng quy hoạch động cho bài toán này rất dễ dàng sau khi có được cấu trúc con tối ưu với các bài toán con gối nhau:
n, S = map(int, input().split())
w = list(map(int, input().split()))
dp = [[0 for _ in range(n)] for _ in range(S + 1)]
for i in range(n):
dp[0][i] = 1
for i in range(1, S + 1):
for j in range(n):
x = dp[i - w[j]][j] if i - w[j] >= 0 else 0
y = dp[i][j - 1] if j >= 1 else 0
dp[i][j] = x + y
print(dp[-][n - 1])
# Kết quả tính toán với n = 3, w = [1, 3, 5] như sau:
# S = 0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10|11
# ------+--+--+--+--+--+--+--+--+--+--+--
# k = 1 |1 |1 |2 |2 |3 |4 |4 |5 |6 |7 |8
Các bạn thấy đó, xây dựng các bài toán con gối nhau sao cho cấu trúc con vẫn tối ưu nhiều khi không đơn giản chút nào. Và mỗi bài toán quy hoạch động lại có những biến hóa khác nhau mà không theo một khuôn mẫu khô cứng nào. Ngay cả khi bạn có thể giải được rất nhiều bài toán quy hoạch động rồi, không gì có thể đảm bảo bạn có thể giải được các bài khác nữa. Đó cũng là một lý do khiến cho dạng bài này luôn “hot” trong các cuộc thi.
Tất cả những ví dụ mình đã trình bày ở trên đều sử dụng quy hoạch động kiểu “ngược”. Ngược ở đây không phải là chúng ta duyệt các bài toán con từ lớn ngược về nhỏ. Mà quy trình sẽ như thế này: Duyệt qua tất cả các bài toán con (từ nhỏ đến lớn), với mỗi bài toán đó, chúng ta tính toán kết quả dựa vào bài toán con trước đó. Tất nhiên, bài toán con phía trước đã được giải theo quy trình duyệt, và với mỗi bài toán, chúng ta phải “nhìn ngược lại” bài toán trước đó, nên cách làm này gọi là quy hoạch động kiểu “ngược”.
Phương pháp quy hoạch động ngược này được sử dụng rộng rãi, vì nó khá tương ứng với suy nghĩ tự nhiên của chúng ta. Chúng ta đọc đề bài, suy nghĩ cách giải cho nó. Cách giải đó yêu cầu phải giải những bài toán nhỏ hơn, như kiểu làm toán ngày phải chứng minh các bổ đề vậy. Chúng ta tiếp tục suy nghĩ cho những bài toán con này, rồi tổng hợp để tìm ra lời giải cho bài toán lớn. Quá trình cứ tiếp tục như vậy, và quy hoạch động kiểu “ngược” này đang được xây dựng đúng như vậy.
Ngoài ra, về mặt lập trình, kiểu quy hoạch động này có mối quan hệ tương đối gần gũi với đệ quy. Một bài toán lớn được giải dựa vào các bài toán con tương tự nhau (và tương tự bài toán lớn) thì việc áp dụng đệ quy có thể là một phương pháp dễ dàng để code. Vì vậy, nhiều trường hợp, có thể coi quy hoạch động là một cách để tối ưu phương pháp đệ quy để giải một bài toán.
Ngoài kiểu quy hoạch động ngược này, có một kiểu quy hoạch động “xuôi”. Tuy không phổ biến, kiểu quy hoạch động xuôi cũng khá khó áp dụng, nhưng quy hoạch động “xuôi” mang đến cho chúng ta nhiều tiện lợi. Kiểu xuôi này cũng cần duyệt qua các bài toán con từ nhỏ đến lớn, nhưng với mỗi bài toán con, chúng ta tính toán kết quả và từ đó tìm cách thực hiện một số phép tính để giải bài toán lớn hơn. Nghĩa là, với mỗi bài toán con, chúng ta sẽ nhìn về phía trước để xem phải giải bài toán tiếp theo như thế này từ bài toán hiện tại.
Phương pháp này khó áp dụng hơn phương pháp ngược kia, và cũng không phải bài toán nào cũng áp dụng được. Với mỗi bài toán, việc xác định bước tiếp theo tương đối khó khăn, thậm chí việc kiểm tra tính đúng sai của phương pháp cũng không hề dễ dàng.
Như chúng ta đã thấy ở những phần trước, thông thường, mỗi bài toán cần phải giải bằng cách tổng hợp kết quả từ một vài bài toán con trước đó. Vì vậy, cách quy hoạch động xuôi này chỉ sử dụng một bài toán con để tính toán trước bài toán tiếp theo sẽ chỉ cho ra một phần của kết quả chứ không phải kết quả cuối cùng. Vì vậy, để thực hiện quy hoạch động xuôi, việc điền sẵn một mảng các giá trị trung tính là điều bắt buộc (sau đó chúng ta sẽ cộng dồn kết quả vào mỗi khi giải được một bài toán con mới).
Mình lấy vị với bài toán xâu con chung dài nhất. Với bài toán này, chúng ta có thể chọn giá trị trung tính là một số âm. Chúng ta sẽ tìm cách quy hoạch động xuôi như sau:
dp(0,0) = 0 là bài toán với hai xâu rỗng
Với mỗi bài toán dp(i, j) chúng ta sẽ tìm cách tính toán kết quả cho các bài toán lớn hơn. Lúc này, có 3 hướng phát triển tiếp:
Lấy thêm một ký tự từ xâu thứ nhất => Kết quả không thay đổi.
Lấy thêm một ký tự từ xâu thứ hai => Kết quả cũng không thay đổi.
Nếu ký tự tiếp theo của cả hai xâu giống nhau => Lấy tự từ này và độ dài xâu con chung tăng lên 1.
Dưới đây là code cho bài toán này:
n1, n2 = map(int, input().split())
s1, s2 = input().split()
s1 += '\0x00'
s2 += '\0x00'
# Điền sẵn giá trị trung tính
dp = [[-1] * (n1 + 2) for _ in range(n2 + 2)]
dp[0][0] = 0
for i in range(n1 + 1):
for j in range(n2 + 1):
tres = dp[i][j]
# Phát triển theo hướng thứ nhất
if dp[i + 1][j] < tres:
dp[i + 1][j] = tres
# Phát triển theo hướng thứ hai
if dp[i][j + 1] < tres:
dp[i][j + 1] = tres
# Phát triển theo hướng thứ ba
if s1[i] == s2[j] and dp[i + 1][j + 1] < tres + 1:
dp[i + 1][j + 1] = tres + 1
print(dp[n1][n2])
Kết luận
Hy vọng qua bài viết này, mình đã trình bày được phần nào về thuật toán quy hoạch động. Về cơ bản, với mọi bài toán quy hoạch động, chúng ta có thể xây dựng các bài toán con gối nhau với cấu trúc con tối ưu là 90% công việc đã hoàn thành.
Tuy nhiên, cũng cần hiểu rằng, mặc dù thuật toán quy hoạch động là một thuật toán thần thánh, nó có thể giải được rất nhiều bài toán, nhưng nó không phải là chìa khóa vạn năng. Có một điều rất hiển nhiên: phương pháp tốt nhất để giải quyết mọi bài toán trong tin học là biết sử dụng và phối hợp uyển chuyển nhiều thuật toán, chúng ta không nên phát cuồng một thuật toán và cũng không nên coi thường bất cứ một thuật toán nào.
Không chỉ là “thùng rác” để các bạn lập trình viên “xả” những cảm xúc và ức chế của mình, Lập trình viên confession còn là một cầu nối giúp các bạn coder giải đáp những thắc mắc hóc búa xoay quanh công việc và học tập. Dưới đây là những thắc mắc về nghề lập trình kinh điển thường gặp của cộng đồng.
Mình là du học sinh vừa tốt nghiệp hè vừa rồi, vừa về nước đi làm mấy tháng lương 8 củ/tháng (mình đã có công ty ở nước ngoài tuyển cơ mà hè sang năm mới xuất ngoại đi làm). Có thể nói là mình về VN làm lấy kn vì ngồi không chả được gì. Qua việc tiếp xúc thì mình thấy mấy devs mình tiếp xúc họ rất thông thạo 1 ngôn ngữ lập trình nhưng họ thiếu những cái cơ bản như: thuật toán, cơ sở dữ liệu (chuẩn hóa,…). Mình không công nhận mình giỏi giang gì nhưng trong quá trình học mình vẫn tập trung các môn cơ sở. Ngày trước mình có phỏng vấn qua các cty bên nước ngoài, trc khi pv mình có chuẩn bị kĩ kiến thức về iava (công ty tuyển java), và ngạc nhiên là 80 – 90% họ hỏi thuật toán, chuẩn hóa csdl,… còn về ngôn ngữ lt chỉ có 10% còn lại là hỏi về kn làm việc. Mình chỉ muốn khuyên các bạn đang còn học về ngành này là hãy thông thạo các kiến thức cơ sở, ngôn ngữ lt nó cũng như ngôn ngữ bt ta nói hàng ngày. Bạn có thể giỏi tiếng Anh nhưng toán chưa chắc đã giỏi, ngôn ngữ chỉ là cái cầu để ta truyền đạt và tiếp thu. Khi bạn đã chắc chắn về kiến thức cơ sở, bạn có thể tự tin chọn ngôn ngữ bạn thích và apply cty bạn muốn.
#C116
⚠️ Bài ngắn lắm, cố gắng đọc hết nhé !
Disclaimer: Tôi cũng từng là du học sinh, cũng làm về java, và cũng từng trúng tuyển vào vị trí lập trình viên hệ thống thông tin tại một tập đoàn lớn có giá trị vốn hóa hơn 30 tỷ euro (xin phép cho tôi nổ con số này vì nó khè được khối người) nên có đôi điều muốn chia sẻ thêm về những điều bạn tác giả #C67 đã viết.
KĨ THUẬT KHÔNG PHẢI TẤT CẢ
Đầu tiên tôi muốn nói thêm về vấn đề tuyển dụng. Trong các công ty, đặc biệt là các công ty lớn, quy trình tuyển dụng thường rất lằng nhằng và phức tạp. Thông thường các bạn sẽ phải trải qua vòng hồ sơ (CV và thư), vòng này được thực hiện [chủ yếu] bởi bộ phận nhân sự và đôi khi có tham khảo ý kiến của bộ phận chuyên môn nơi ứng viên trúng tuyển sẽ làm việc. Qua được vòng này bạn sẽ bắt đầu đến với các bài test và các cuộc phỏng vấn.
Đầu tiên là phỏng vấn với nhân sự: bạn có thể được yêu cầu làm các bài kiểm tra về trình độ logic (giải đố) và ngôn ngữ (nếu vị trí yêu cầu ngoại ngữ không phải tiếng mẹ đẻ của bạn). Tiếp theo đó là một cuộc phỏng vấn khoảng 1 giờ đồng hồ, tất cả xoay quanh những gì bạn đã trình bày trong CV và Thư, chủ yếu là để nhân sự kiểm tra các thông tin về bằng cấp của bạn, vạch nên một hồ sơ khái quát về con người và tính cách của bạn và kiểm chứng các *kĩ năng mềm* và xem bạn có thật sự KHAO KHÁT công việc này hay không. Nếu trước mặt những người này bạn tỏ ra mình thờ ơ hoặc có những biểu hiện bất thường về nhân cách thì hãy coi chừng! Nghe toát mồ hôi nhỉ :’))
Sau đó mới đến các bài test và phỏng vấn về chuyên môn. Ở đây có thể có nhiều hơn 1 bài test và 1 cuộc phỏng vấn, tùy theo vị trí mà bạn ứng tuyển. Những người mà bạn sẽ gặp có thể là Lead dev, PM, Architect, thậm chí là CTO nếu bạn may mắn (hay là không may nhỉ ;D). Vì thế nên các câu hỏi đưa ra có độ khó không giống nhau và trải khá rộng trên lĩnh vực mà bạn ứng tuyển, từ những vấn đề lý thuyết cổ điển như thuật toán, độ phức tạp của thuật toán, bài toán NP-complete… cho đến các xu hướng mới [nổi] trong lập trình như functional programming, reactive programming… Kinh nghiệm của tôi cho thấy các công ty càng nhỏ thì càng có xu hướng hỏi những câu “hẹp”, tập trung vào loại kĩ thuật và công cụ mà họ đang tìm kiếm, còn ở các công ty lớn thì chủ yếu xem cách bạn giải quyết vấn đề và khả năng học kĩ năng mới để đối phó với vấn đề mới. Qua được vòng này mà nhận được những cái gật gù từ người hỏi thì 80% là bạn sẽ có được vị trí mong muốn, còn lại phụ thuộc vào kết quả test và do ..ăn ở :’))
LÝ THUYẾT HAY THỰC TẾ ? CẢ HAI!
Ở đây xin trở lại với chia sẻ #C67. Bạn OP nói rất đúng về mức độ quan trọng của các kiến thức lý thuyết đối với một kĩ sư máy tính vì nó là một tiêu chuẩn để phân biệt một kĩ sư và một lập trình viên thông thường (mặc dù tôi có chút thắc mắc, vì sao một ứng viên lập trình viên Java, là fresher, lại bị hỏi về tối ưu hóa csdl ? Vị trí bạn ứng tuyển xem ra không đơn giản). Tuy nhiên đừng vì thế mà xem nhẹ việc thành thạo một loại công nghệ hay ngôn ngữ nào đó. Lý thuyết và thực hành, nói theo kiểu Maxism (khốt ta bít), là hai thứ có quan hệ *biện chứng*, hỗ trợ qua lại với nhau. Lí do các công ty không đặt nặng phần thực tiễn khi đánh giá khả năng của bạn OP là vì (theo tôi võ đoán) bạn ứng tuyển vào vị trí lập trình viên Java nói chung, không có mission cụ thể, ngoài ra bạn không có kinh nghiệm làm việc trước đó nên họ chuyển sang đánh giá tiềm năng của bạn trước khi nhận bạn vào và tiếp tục đào tạo bạn trong quá trình làm việc.
Tôi đọc các cfs trên page này hàng ngày và có tham gia một số group lập trình trên facebook, câu hỏi (hay lời ta thán) thường gặp nhất của các bạn sinh viên là “đi học để làm gì khi mà không cần bằng vẫn có thể đi làm lương chục củ”, “tại sao ở trường chỉ toàn dạy các môn đại cương, các môn lý thuyết mà không _dạy code_”, “em đang tự học làm web, chỉ cần tốt nghiệp nữa thôi là đi làm được rồi mà sao khó [tốt nghiệp] quá”… Tôi nghĩ rằng các bạn đang nhầm lẫn về môi trường học và định hướng nghề nghiệp của mình sau này. Các chương trình đại học ở Việt Nam theo tôi biết (trừ ĐH FPT) đều là hướng nghiên cứu chứ không phải hướng chuyên nghiệp. Điều này có nghĩa là các bạn chủ yếu học lý thuyết, thiếu hoàn toàn mảng doanh nghiệp và quan trọng nhất là không biến các bạn thành các “lập trình viên web”, “lập trình viên javascript”… Bạn muốn phát triển theo hướng nào thì phải TỰ HỌC, tự trau dồi, tự tìm hiểu bằng cách sử dụng một phần những kiến thức ở trường, đó là điều bắt buộc khi bạn học về khoa học máy tính trong các chương trình hàn lâm. Còn nếu chỉ đơn giản muốn thành thục một framework, một ngôn ngữ nào đó để làm nghề thì hãy tìm đến các chương trình dạy nghề hay các khóa đào tạo ngắn hạn rồi sau đó đi làm luôn và ngay, việc học của bạn nên được thực hiện ở trường đời chứ không phải ở trường đại học.
VÀ CHUYỆN CỦA TÔI
Khi còn là sinh viên tôi cũng giống như một số bạn, trễ nải các môn lý thuyết cổ điển và các môn nghiên cứu máy tính ở bậc thấp (Unix API, OS…), và tôi đặt biệt thù ghét việc lập trình ở đây vì ngôn ngữ chính họ dạy tôi là C, sau này lên năm cuối còn “được” dạy thêm Common Lisp nữa (đừng thắc mắc về ngôn ngữ đồ đá này, nó còn nhiều tuổi hơn cả tôi với bạn mà chả thấy ai dùng). Khi đó tôi chỉ có một ước mơ duy nhất là cố gắng qua môn, tốt nghiệp để trở thành “java developer”, còn cụ thể là làm gì thì tôi mù tịt :* . Mọi chuyện chỉ thay đổi khi tôi quyết định thi OCPJP (chủ yếu là để khè HR thôi chứ sau này đi làm mới thấy các tiền bối expert có khi còn chả biết nó là cái giống gì 😀). Những kiến thức sơ sài về Data Structure đã giúp tôi qua phần Collections, những đêm ôn thi nước đến chân mới nhảy bộ môn OS thì giúp tôi qua rất nhiều phần, từ I/O, File I/O đến Threads và Concurrency. Đó là lúc tôi hiểu mình mới chỉ chạm đến bề mặt của ngôn ngữ Java, còn phần nền tảng của nó thì đã bị tôi bỏ rơi đâu đó trong suốt mấy năm sinh viên ăn chơi. Bạn thấy đấy, tất cả mọi thứ, kể cả những thứ chúng ta thù ghét nhất, đều tồn tại vì một lí do nào đó và sẽ đến lúc chúng trở nên có ích, chỉ là chúng ta có nắm bắt được hay không mà thôi.
Tôi vẫn nhớ bài test tuyển dụng đầu tiên tôi làm khi mới ra trường là phản biện một UML diagram, chỉ ra các điểm mà tôi cảm thấy bất hợp lí và đề xuất các thay đổi cho nó, sau đó generate code bằng java và viết một thuật toán *đơn giản* và *tối ưu* (giải thích lí do) để đọc/thêm/bớt dữ liệu từ cấu trúc dữ liệu đề xuất trước đó.
Chỉ bằng một bài test này thôi, họ đã đồng thời đánh giá được khả năng của tôi về OOP, Algorithm, Data structure và một chút về ngôn ngữ. Sau bài test thì tôi phải qua một lần phỏng vấn nữa, chủ yếu là về những hiểu biết của tôi về Agile, git workflow và CI/CD rồi đi về. Kết quả là tôi tạch các bạn ạ). Phải 7 – 8 lần như thế tôi mới có một lần tỏa sáng mà phần nhiều là nhờ kinh nghiệm từ các lần tạch trước đó.
Vì thế nên các bạn sinh viên hãy yên tâm, sinh viên mới ra trường ở bất kì đâu trên thế giới này cũng đều phần nào ngơ ngác như các bạn cả thôi và các nhà tuyển dụng RẤT HIỂU điều này. Tuy nhiên cần xác định rõ mong muốn cũng như khả năng của bản thân để có thể ứng tuyển một cách hợp lí. Hãy ghi nhớ, mỗi cuộc phỏng vấn là một cuộc thi và bạn thật sự cần phải ÔN THI một cách nghiêm túc.
Điểm khác biệt duy nhất ở đây chỉ là bạn có quyền và được khuyến khích học tủ. Tủ ở đây chính là thông báo tuyển dụng, nơi bạn sẽ tìm thấy tất cả các thông tin cần thiết về công ty, về vị trí tuyển dụng, về các yêu cầu (bắt buộc cũng như khuyến khích) đối với ứng viên cho vị trí đó. Hãy tìm hiểu tất cả các thông tin liên quan, mở rộng hết cỡ những vấn đề có thể được nhắc đến theo cả chiều dọc lẫn chiều ngang. Và cuối cùng, tuyệt đối không nói dối về khả năng của bản thân, vì các chuyên gia HR cũng như chuyên môn rất giỏi trong khoản bóc phốt này. Đừng dại dột!!!
Chuyên mục bốc phét đêm khuya đến đây đã dài, xin được dừng lại. Tôi hi vọng những tâm sự trên đây của tôi có thể giúp được một số các bạn trẻ đang và sắp bước vào những trận chiến đầu đời để giành lấy một công việc. Chúc tất cả các bạn may mắn và nếu có tin vui thì xin chia sẻ với tôi cũng như các bạn khác, vì một cộng đồng IT lương khởi điểm $2000
#C112
Thấy có bạn nữ lên hỏi kiểu nên theo hướng nào thì phù hợp với con gái. Và mình thấy mấy anh chị đi trước thường khuyên là nên làm tester hay đại loại là những thứ không liên quan đến code. Và mình xin kể câu chuyện của mình cho các bạn nữ còn đang đi học biết.
Mình là nữ, mình tốt nghiệp được khoảng gần 1 năm, hiện tại đang làm dev.
Hồi đó mình đỗ cả 2 trường đại học, nếu không đi theo con đường này thì năm sau có thể mình sẽ trở thành một bác sĩ. Và đương nhiên cả nhà chẳng ai muốn mình đi theo con đường trở thành một lập trình viên (mình thì thích dùng từ dev hơn). Mình đã đấu tranh rất nhiều để được học cái ngành này. Một đứa con gái trong một cái lớp có tới vài chục thậm chí cả trăm đứa con trai, dĩ nhiên mấy bạn nam rất giỏi, nhiều lúc mình cũng bị chùn chí.
Nhưng nghĩ tới cảnh ra trường không có việc làm hoặc làm một công việc không liên quan đến những thứ mình học ba mẹ sẽ thất vọng, hàng xóm dưới quê sẽ nói ra nói vào, với lại mình đã chọn con đường này mình phải có trách nhiệm với nó vì không có cơ hội để chọn lại, mình lại tiếp tục cố gắng. Lúc sắp tốt nghiệp mình đi thực tập với mức lương đủ sống. Hiện tại mức lương của mình đã gấp vài lần con số đó. Mình thấy hạnh phúc vô cùng khi được code và có thể kiếm ra tiền để lo cho gia đình bằng chính ước mơ từ hồi đi học của mình.
Mình muốn gởi tới mấy bạn nữ lời nhắn là hãy cố gắng lên, nếu đã lựa chọn con đường này thì phải đi cho tới cùng. Đừng có tư tưởng trở thành một tester nếu không thích nó. Con gái học lập trình đâu phải để làm tester, con gái cũng sẽ trở thành dev như bao đứa con trai khác, chỉ cần có ước mơ. Cố lên!
Chúng ta hãy cùng khám phá một số khái niệm cơ bản sẽ giúp chúng ta tạo ra một AI cờ vua đơn giản:
move-generation (Khởi tạo các bước di chuyển)
board evaluation (Khả năng tiên đoán nước cờ của đối thủ)
minimax (Thuật toán tính toán nước đi)
alpha beta pruning. (Tinh chỉnh phương thức Alpha-beta)
Ở mỗi bước, chúng tôi sẽ cải tiến thuật toán bằng một trong những kỹ thuật lập trình game cờ vua đã qua thử nghiệm. Tôi sẽ chứng minh làm thế nào để tác động đến lối chơi của thuật toán.
Bạn có thể xem thuật toán AI hoàn chỉnh trên GitHub.
Bước 1: Hiển thị các bước di chuyển và bàn cờ
Chúng tôi sẽ sử dụng thư viện chess.js để phát triển các bước di chuyển và chessboard.js để hiển thị bàn cờ. Thư viện tạo ra các bước di chuyển cơ bản tuân theo tất cả quy luật của cờ vua. Dựa vào điều này, chúng ta có thể tính toán tất cả các bước đi hợp lý cho một ván chơi nhất định.
Hình dung chức năng tạo các nước đi. Dựa vào vị trí bắt đầu để tính toán ra tất cả các nước đi có thể từ vị trí đó.
Sử dụng các thư viện này sẽ giúp chúng ta có thể dồn toàn tâm toàn ý vào công việc thú vị nhất: tạo ra thuật toán tìm nước cờ tốt nhất.
Chúng ta sẽ bắt đầu bằng cách tạo ra một chức năng chỉ trả về một nước đi ngẫu nhiên từ tất cả hướng có thể:
var calculateBestMove =function(game) {
//generate all the moves for a given position
var newGameMoves = game.ugly_moves();
return newGameMoves[Math.floor(Math.random() * newGameMoves.length)];
};
Mặc dù thuật toán này không tạo nên đối thủ cứng tay, tuy nhiên đó là một khởi đầu tốt, chúng ta thực sự đã có thể chơi với nó:
Bây giờ chúng ta hãy cố gắng hiểu được bên nào mạnh hơn ở vị trí nào đó. Cách đơn giản nhất để đạt được điều này là tính toán sức mạnh tương đối của các quân cờ trên bàn bằng cách sử dụng bảng sau:
Với chức năng dự đoán, chúng ta có thể tạo ra một thuật toán chọn nước cờ đạt hiểu quả cao nhất:
var calculateBestMove = function (game) {
var newGameMoves = game.ugly_moves();
var bestMove = null;
//use any negative large number
var bestValue = -9999;
for (var i = 0; i < newGameMoves.length; i++) {
var newGameMove = newGameMoves[i];
game.ugly_move(newGameMove);
//take the negative as AI plays as black
var boardValue = -evaluateBoard(game.board())
game.undo();
if (boardValue > bestValue) {
bestValue = boardValue;
bestMove = newGameMove
}
}
return bestMove;
Cải tiến duy nhất đạt được là thuật toán của chúng ta sẽ nắm bắt được một phần phương thức di chuyển nếu có thể.
Cờ đen chơi với sự hỗ trợ của chức năng dự đoán đơn giản.
Bước 3: Tìm kiếm nước đi hiểu quả nhất bằng Minimax
Tiếp theo, chúng ta sẽ tạo ra các hướng đi có thể xảy ra từ đó thuật toán có thể chọn bước di chuyển tốt nhất. Điều này được thực hiện bằng cách sử dụng thuật toán Minimax.
Trong thuật toán này, hướng đi của tất cả các nước cờ có thể được tính toán kỹ trong từng tình huống nhất định, và vị trí được dự đoán cuối cùng là hiệu quả nhất.
Sau đó, chúng ta sẽ trả lại giá trị nhỏ nhất hoặc lớn nhất của child cho parent node, tùy thuộc vào việc đó là cờ trắng hoặc đen để di chuyển. (Đó là, chúng tôi cố gắng để giảm thiểu hậu quả hoặc tối đa hóa hiệu quả ở mỗi tình huống.)
Hình dung thuật toán minimax cho trí thông minh nhân tạo. Nước đi tốt nhất cho cờ trắng là b2-c3, bởi vì chúng tôi có thể đảm bảo rằng tại vị trí mà tính toán là -50
var minimax = function (depth, game, isMaximisingPlayer) {
if (depth === 0) {
return -evaluateBoard(game.board());
}
var newGameMoves = game.ugly_moves();
if (isMaximisingPlayer) {
var bestMove = -9999;
for (var i = 0; i < newGameMoves.length; i++) {
game.ugly_move(newGameMoves[i]);
bestMove = Math.max(bestMove, minimax(depth - 1, game, !isMaximisingPlayer));
game.undo();
}
return bestMove;
} else {
var bestMove = 9999;
for (var i = 0; i < newGameMoves.length; i++) {
game.ugly_move(newGameMoves[i]);
bestMove = Math.min(bestMove, minimax(depth - 1, game, !isMaximisingPlayer));
game.undo();
}
return bestMove;
}
Với minimax, thuật toán của chúng tôi bắt đầu hiểu một số chiến thuật cơ bản của cờ vua:
Minimax tính toán được trước 2 nước đi.
Hiệu quả của thuật toán minimax chủ yếu dựa vào khám phá ra nước cờ tiếp theo đó mà chúng ta có thể đạt được. Đây là điều chúng tôi sẽ cải thiện trong bước tiếp theo.
Việc tinh chỉnh Alpha-beta là một phương pháp tối ưu hóa thuật toán minimax cho phép chúng ta bỏ qua một số hướng trong tất cả hướng đi có thể. Điều này giúp chúng tôi dự đoán hướng bằng minimax hiệu quả, trong khi sử dụng cùng một thuật toán.
Việc giảm thiểu alpha-beta dựa trên tình huống mà chúng ta có thể ngừng đưa ra hướng đi nếu chúng ta thấy hướng đi đó dẫn đến một kết quả tồi tệ hơn là bước di chuyển đã tìm ra từ trước.
Việc điều chỉnh alpha-beta không ảnh hưởng đến kết quả của thuật toán minimax, nó chỉ làm cho thuật toán nhanh hơn.
Thuật toán alpha-beta cũng hiệu quả hơn nếu chúng ta tìm ra những hướng đi dẫn tới các nước cờ tốt đầu tiên.
Các vị trí chúng ta không cần phải ngợi ý nếu việc tinh chỉnh alpha-beta được sử dụng và hướng đi tuân theo thứ tự được mô tả.
Với alpha-beta, chúng tôi đạt được sự cải thiện đáng kể cho thuật toán minimax, như thấy trong ví dụ sau:
Theo link này để thử phiên bản cải tiến alpha-beta của AI cờ vua.
Bước 5: Cải thiện chức năng dự đoán
Chức năng dự đoán ban đầu khá là đơn giản vì chúng chỉ đếm các nước đi được tìm thấy trên bàn cờ. Để cải thiện điều này, chúng tôi thêm vào dự đoán một yếu tố có tính đến vị trí của các quân cờ. Ví dụ, một con mã nằm ở giữa bàn cờ là tốt hơn (vì nó có nhiều lựa chọn hơn và vì vậy hoạt động mạnh hơn) so với một con mã trên mép của bàn cờ.
Chúng ta sẽ sử dụng các ô mà từng quân cờ có thể đi được dựa trên nguồn chess-programming-wiki nhờ vậy mà chất lượng AI được cải thiện.
Các ô minh họa để dễ hình dung. Chúng tôi có thể tính được điểm tăng hay giảm của mỗi nước đi, tùy thuộc vào vị trí của mỗi quân cờ.
Với những cải tiến sau đây, chúng tôi bắt đầu có được một thuật toán chơi cờ với những nước đi “hợp lý”, ít nhất là từ quan điểm của một kỳ thủ bình thường:
Cải thiện dự đoán và tinh chỉnh alpha-beta với khả năng tính toán trước được 3 nước đi. Có thể thử trên https://jsfiddle.net/q76uzxwe/1/
Kết luận
Sức mạnh của ngay cả một thuật toán chơi cờ vua đơn giản cũng có là không tạo ra những sai lầm ngu ngốc. Tuy nhiên, nó vẫn còn thiếu về phần lên chiến lược.
Với các phương pháp tôi giới thiệu ở đây, chúng tôi đã có thể lập trình một thuật toán chơi cờ vua có thể chơi cơ bản. Về phần “AI” (các nước đi ngẫu nhiên bị loại bỏ) của thuật toán cuối cùng chỉ có 200 dòng code, điều này khá đơn giản để thực hiện. Bạn có thể kiểm tra phiên bản hoàn chỉnh trên GitHub.
Một số cải tiến khác chúng tôi có thể thực hiện cho thuật toán như:
move-ordering (sắp xếp các nước đi)
faster move generation (tính toán các nước đi nhanh hơn)
và end-game specific evaluation. (dự đoán khi nào kết thúc ván cờ)
Nếu bạn muốn tìm hiểu thêm, hãy thử xem qua chess programming wiki. Đó là một nguồn thông tin hữu ích để bạn có thể khám phá vượt qua khái niệm cơ bản mà tôi giới thiệu ở bài này.
Cảm ơn bạn đã đọc bài viết này!
Đừng bỏ lỡ những bài viết hay về Machine Learning:
Làm sao để nâng cao khả năng code? Dĩ nhiên là phải code rồi, còn cách nào khác nữa. Nhưng code thì cũng có nhiều kiểu code. Có kiểu giúp bạn lên trình, cũng có kiểu giúp trình bạn lên. Dù là kiểu nào thì muốn code giỏi bạn đều phải code cả.
Tuy nhiên, lên trình nhiều hay lên trình ít lại phụ thuộc vào nhiều khía cạnh. Giả sử như bạn được code với một anh senior, code của anh ấy đẹp như đêm trăng rằm, hoặc bạn code một dự án thực tế hay ho thì trình của bạn sẽ lên rất nhanh. Còn nếu bạn cứ code đi code lại mấy bài CRUD trong trường đại học với mấy thằng bạn, hoặc làm vài task lặt vặt trong công ty thì trình sẽ khó lên hơn.
Nhưng đâu phải ai cũng có cơ hội code với senior, làm dự án thực tế đúng không? Đặc biệt là các bạn sinh viên như mình, hoặc các bạn mới ra trường. Nhưng không sao, hôm nay mình sẽ giúp bạn biết cách làm sao để nâng cao khả năng code của bạn, dễ thôi.
Ơn trời chúng ta có internet, vô số các dự án thực tế hay ho được các developer chia sẻ đang chờ bạn.
Nếu bạn biết đến github thì chắc rằng bạn đã hiểu open source là gì. Open source đơn giản là các dự án tuyệt vời được chia sẻ lên internet, bạn có thể dễ dàng clone source code, tham gia đóng góp vào các dự án này.
Vậy open source giúp bạn lên trình code như thế nào?
Thứ nhất, bạn có thể học hỏi từ những developer giỏi trên khắp thế giới. Một đại thi hào thường đọc rất nhiều sách hay, tương tự, một developer giỏi thường đọc rất nhiều source code từ những đồng nghiệp tuyệt vời. Thông qua việc tiếp xúc, đọc hiểu những dự án open source chất lượng, bạn sẽ học được cách tổ chức code, cách viết code sạch đẹp, cách các developer khác xử lý vấn đề…
Sau khi đọc code, bạn có thể bắt đầu chọn cho mình những module nhỏ mà bạn hứng thú để bắt đầu code. Bạn có thể tự nghĩ ra các tính năng mới, hoặc cũng thể giải quyết các issues có sẵn. Dù cách nào đi chăng nữa thì bạn cũng sẽ học được vô số điều mới mẻ.
Nếu bạn có thể hoàn thành một vài tính năng mới hoặc giải quyết được các issues, đừng ngần ngại commit code của bạn, rất có thể nó sẽ được chấp nhận và tích hợp vào dự án đấy.
Ngoài lợi ích giúp bạn nâng cao khả năng code, việc đóng góp vào các dự án open source đôi khi còn là thước đo khả năng của bạn trong mắt các nhà tuyển dụng. Rất nhiều công ty, nhà tuyển dụng xem profile github là một trong những khía cạnh để đánh giá ứng viên đấy.
Pet project
Rất nhiều bạn bè của mình ở trường đại học nói với mình rằng họ đã quá chán nản với những project CRUD trong các đồ án môn học, họ muốn làm gì đó thực tế hơn. Tại sao không bắt đầu một pet project nhỉ?
Pet project là những dự án mà bạn nghĩ ra ý tưởng và làm trong thời gian rảnh. Giá trị của những project này nằm ở chỗ bạn có thể làm bất cứ thứ gì bạn thích, bất cứ công nghệ nào bạn muốn học, đặc biệt là nó giúp bạn làm ra được sản phẩm, điều mà bạn khó có thể đạt được khi đi làm.
Nhiều developer thường có ý nghĩ rằng pet project phải là thứ gì đó cao siêu, làm mấy cái nhỏ nhoi thì chẳng bõ công. Tuy nhiên, mục đích của pet project đơn giản là để bạn giải trí, rèn luyện khả năng code, khả năng làm sản phẩm chứ không phải để bán kiếm tiền hay startup. Vì vậy bạn có thể làm bất cứ thứ gì, miễn là bạn cảm thấy nó hữu ích, bạn yêu thích làm ra nó.
Những ý tưởng nhỏ nhặt có thể đến từ bất cứ đâu. Giả sử như crush của bạn thích được nhắn tin chào buổi sáng nhưng bạn lại hay ngủ đến tận trưa thì làm sao? Viết ngay một con bot tự động nhắn tin cho crush. Nhỡ crush nhắn tin lại mà con bot ngu quá không biết trả lời thì làm sao. Học ngay AI và train trình nhắn tin cho con bot. Thế là bạn có ngay một pet project.
Việc bắt đầu một pet project giúp bạn trải nghiệm được toàn bộ quy trình tạo ra một phần mềm ở giai đoạn development. Nếu như ở công ty, công việc của bạn là hoàn thành những task nhỏ trong một dự án lớn, mọi thứ đã được setup sẵn khiến bạn khó có thể nhìn thấy toàn bộ quá trình thì với pet project, bạn sẽ được làm mọi thứ từ việc setup môi trường, coding, testing, deploy…
Hy vọng mình thuyết phục được bạn rằng bạn nên bắt đầu một pet project. Nhưng chưa hết, bạn còn có thể đưa các pet project của bạn vào CV khi đi xin việc, các công ty sẽ đánh giá bạn cao hơn rất nhiều, đặc biệt nếu bạn là một người chưa có kinh nghiệm thì pet project lại càng quan trọng hơn. Bởi nó chứng minh cho phía công ty biết rằng bạn thật sự biết code, biết làm ra sản phẩm. Ngoài ra, biết đâu pet project của bạn lại được mọi người ưa chuộng thì sao? Rất nhiều dự án startup khởi đầu chỉ là một pet project, như Facebook chẳng hạn, mình tin rằng lúc code Facebook gã Mark Zuckerberg cũng chỉ xem nó như một pet project thôi.
Đọc sách về coding
Lần cuối cùng bạn đọc một cuốn sách về coding là khi nào? Mình tin rằng một số lượng kha khá developer không trả lời được câu hỏi này. Dù thế nào đi chăng nữa thì sách vẫn là một công cụ tuyệt vời giúp bạn nâng cao khả năng code, vì vậy đừng bỏ qua chúng.
Ai cũng có thể viết code, nhưng không phải ai cũng có thể viết code chất lượng. Ngoài code, bạn nên dành thời gian đọc sách, bởi sách là nơi các bậc lão thành truyền đạt kinh nghiệm lại cho hậu thế. Những kiến thức như: viết code sao cho dễ đọc, dễ hiểu, design kiến trúc sao cho dễ mở rộng, dễ bảo trì…, những kiến thức dạng này bạn khó có thể tìm thấy ở các video, các khóa học trên internet.
Hãy đứng trên vai những người khổng lồ. Bạn phải chấp nhận một điều rằng bạn không phải developer giỏi nhất trên thế giới. Vì vậy, hãy học tập từ những người giỏi nhất, những người có kinh nghiệm nhất, làm theo những chỉ dẫn của họ trong sách sẽ giúp bạn tiết kiệm được rất nhiều thời gian và công sức.
Code đã khó, đọc sách về code còn khó hơn rất nhiều, tuy nhiên, hãy tập cho bản thân thói quen này. Nếu bạn làm được, bạn sẽ nằm trong top những lập trình viên giỏi nhất.
Kết
Nâng cao khả năng code là cả một quá trình dài. Vì vậy, theo mình, bạn không nên quá quan trọng việc bạn có code giỏi hay không, trình độ của bạn đến đâu. Cái bạn cần làm là tận hưởng những gì code đem đến cho bạn, học cách tận hưởng 3 điều mình nêu ở trên, rồi sẽ đến lúc bạn không ngờ rằng bản thân code khiếp đến thế.
Dù là một lập trình viên có kinh nghiệm nhưng bạn đã phân biệt được .NET core vs ASP.NET core? Một trong những điểm mạnh của hệ sinh thái .NET là sự hỗ trợ rất tốt của các công cụ như Visual Studio. Tuy nhiên đây cũng là điểm yếu vì nó ngăn cản rất nhiều lập trình viên tiếp xúc với những lý thuyết căn bản của Framework. Trong bài viết này chúng ta sẽ tìm hiểu xem .NET là gì, và các nền tảng .NET Framework, .NET core, và Mono khác nhau như thế nào.
TẠI SAO CẦN NẮM RÕ LÝ THUYẾT CĂN BẢN?
Mình đã từng gặp rất nhiều những người đã làm việc với .NET lâu năm nhưng vẫn mù mờ về những khái niệm, định nghĩa của Framework. Điều này rất nguy hiểm vì nó gây ra việc hiểu sai về nền tảng, dẫn đến việc sản sinh ra những phần mềm chất lượng không cao. Do đó hiểu rõ về framework của mình là trách nhiệm của bất cứ lập trình viên nào?
KHI NÓI VỀ .NET, NGƯỜI TA THƯỜNG HÀM Ý 3 THÀNH PHẦN:
– Runtime (môi trường hoạt động)
– Libraries (thư viện)
– Toolings (công cụ phát triển).
Chúng ta sẽ từ từ tìm hiểu chức năng từng thành phần ở phần sau của bài viết.
Trước hết, Chúng ta cần hiểu được làm thế nào .NET có thể chạy được đoạn code C# (hay VB, F#) mà bạn viết ra. Để hiểu được điều này, chúng ta cần nắm rõ quy trình biên soạn code trong .NET
Trong hình trên, phần ‘Compile time’ là quá trình ‘Build’, và ‘Runtime’ là quá trình chạy (tức là tính từ khi bạn khởi động ứng dụng của mình).
Về cơ bản, khi bạn thực hiện lệnh build (trong Visual Studio, hay ‘dotnet build’ bằng dòng lệnh) thì source code của bạn được chuyển hóa thành một dạng ngôn ngữ trung gian có tên là MSIL (Microsoft Intermediate Language). Khi ứng dụng được khởi chạy, thành phần Runtime-hay tên gọi riêng biệt trong .NET là CLR(common language runtime) sẽ tiến hành dịch mã MSIL thành mã máy(Native code) để cho máy tính có thể thực thi. Quá trình này gọi là JIT (just-in-time) compilation. Cách biên soạn và vận hành của .NET khá giống với Java.
Để hiểu rõ hơn về JIT, chúng ta hãy thử làm một thí nghiệm. Copy đoạn code sau vào file Program.cs trong project của bạn:
using System;
using System.Diagnostics;
namespace ConsoleApplication
{
public class Program
{
public static void DoSomeCalculation()
{
string input = "0";
for (int i = 0; i < 10; i++)
{
var converted = Convert.ToInt32(input);
converted++;
input = converted.ToString();
}
}
public static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
//first time
sw.Start();
DoSomeCalculation();
sw.Stop();
Console.WriteLine($"First calculation: {sw.ElapsedTicks} ticks");
//second time
sw.Restart();
DoSomeCalculation();
sw.Stop();
Console.WriteLine($"Second calculation: {sw.ElapsedTicks} ticks");
}
}
}
Chúng ta sẽ tiến hành gọi cùng 1 hàm 2 lần và đo xem thời gian chạy của 2 lần gọi là bao nhiêu. (10000 ticks = 1 ms). Tiến hành chạy ‘dotnet run’ và xem kết quả.
Bạn có thể thấy rằng lần chạy đầu tiên lâu hơn lần thứ 2 rất nhiều. Tại sao vậy? Đó là do khi DoSomeCalculation() được gọi lần đầu, CLR tiến hành biên soạn (JIT) hàm này thành ngôn ngữ máy. Ở những lần chạy kế tiếp, DoSomeCalculation() không cần ‘JIT’ lại (do đã được ‘JIT’ xong), nên thời gian thực thi nhanh hơn nhiều. (Điều này cũng lý giải tại sao trong một số phần mềm, thời gian thực hiện một chức năng nào đó lần đầu tiên thường chậm hơn so với các lần kế tiếp).
Tại sao lại cần có ngôn ngữ trung gian? Ngôn ngữ trung gian trong .NET khá gần với mã máy 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 (64bit, 32bit), cũng như nhiều loại kiến trúc khác nhau (ARM, Intel…)
Trên thực tế một vài ngôn ngữ (Javascript, Python…) không sử dụng đến ngôn ngữ trung gian: Source sẽ được dịch thẳng ra mã máy tại tại ‘Runtime’. Điểm lợi của việc này là quá trình build được đơn giản hóa, tuy nhiên hiệu năng sẽ bị hạn chế.
Ngoài việc biên dịch, môi trường hoạt động (Runtime) còn có những công dụng như:
– Tự động quản lý bộ nhớ. Khi làm việc với những ngôn ngữ bậc cao như C# hay Java, 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 (Garbage 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à bạn 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 (class, structure…)
Thế còn hệ thống thư viện (Libraries) và công cụ (Toolings) thì sao?
Khi bạn làm việc với .NET, code của bạn sẽ tương tác với rất nhiều các class khác nhau. Ví dụ: Class được sử dụng nhiều nhất trong .NET là System.String. Tất cả những class này được định nghĩa trong hệ thống thư viện cơ bản của .NET mà người ta hay gọi tắt là BCL (Base class libraries).
Mã nguồn của BCL, trái với mọi người hay nghĩ, là mã nguồn mở. Bạn có thể truy cập mã nguồn này tại sourceof.net .
Các công cụ (toolings) của .NET bao gồm compiler và Visual Studio .NET sử dụng hệ thống build của Microsoft gọi là MSBuild. Đối với nền tảng .NET core mới thì chúng ta còn có thêm công cụ dòng lệnh (dotnet cli).
Phân biệt .NET Framework, .NET Core, và Mono
Tại sao cần phải phân biệt chúng? Vì bạn cần phải hiểu rõ mình đang làm gì. VD: Nếu bạn có ý định chạy một Web server trên Linux thì tuyệt đối không nên sử dụng Mono.
Đối với những người mới làm quen với .NET hay kể cả một số người đã làm việc với .NET lâu năm, những cái tên như .NET Framework, Mono hay gần đây nhất là .NET core vẫn hay gây ra những hiểu nhầm. Tuy nhiên những khái niệm căn bản về .NET nói trên giúp chúng ta phân biệt khá dễ dàng. Về cơ bản, .NET Framework, .NET core và Mono là ba phiên bản .NET khác nhau (có nghĩa là mỗi phiên bản có Runtime, Libraries và Toolings riêng).
Vậy tại sao lại có đến 3 phiên bản khác nhau?
– .NET Framework được Microsoft đưa ra chính thức từ năm 2002. .NET Framework chỉ hoạt động trên Windows. Những nền tảng ứng dụng như WPF, Winforms, ASP.NET(1-4) hoạt động dựa trên .NET Framework.
– Mono là phiên bản cộng đồng nhằm mang .NET đến những nền tảng ngoài Windows. Mono được phát triển chủ yếu nhằm xây dựng những ứng dụng với giao diện người dùng và được sử dụng rất rộng rãi: Unity Game, Xamarin…
– Cho đến năm 2013, Microsoft định hướng đi đa nền tảng và phát triển .NET core. .NET core hiện được sử dụng trong các ứng dụng Universal Windows platform và ASP.NET Core.
VẬY TÔI NÊN SỬ DỤNG .NET FRAMEWORK, .NET CORE, HAY MONO?
Điều đó tùy thuộc vào ứng dụng mà bạn có ý định phát triển. Đối với các ứng dụng Windows desktop, .NET Framework sẽ là sự lựa chọn của bạn. Nếu bạn phát triển game dựa trên Unity, hay những ứng dụng di động với Xamarin, bạn sẽ sử dụng Mono. Đối với các Web server, bạn có thể sử dụng cả .NET Framework và .NET Core.
Tuyệt đối không nên dùng Mono để vận hành web server. Bộ máy dọn rác của Mono không được thiết kế để hoạt động với webserver và sẽ gây ra quá tải nhanh chóng.
Vậy nên lựa chọn .NET Framework hay .NET Core cho các web server? .NET Core chạy được đa nền tảng và có hiệu năng cao hơn. Nhược điểm duy nhất của nó là số lượng thư viện hỗ trợ vẫn còn hạn chế. .NET Framework có hệ sinh thái lớn hơn với nhiều các thư viện hỗ trợ hơn.
Series này sẽ sử dụng .NET Core. Chúng ta nên hướng đến tương lai đúng không?
Postman là gì? Nó được các developer sử dụng để làm gì? Có thể đây là lần đầu tiên bạn nghe đến Postman, nhưng đừng lo, tất cả thông tin chi tiết nhất sẽ được TopDev chia sẻ trong bài viết dưới đây!
Postman là gì?
Đầu tiên ta cần nắm khái niệm Postman là gì?
Postman là một nền tảng API để xây dựng và sử dụng API.
Postman – API platform
Postman hiện là một trong những công cụ phổ biến nhất được sử dụng trong thử nghiệm các API.
Như ta đã biết, API chịu trách nhiệm kết nối các ứng dụng với nhau, có Postman sẽ giúp cho thao tác với API này trở nên dễ dàng hơn. Thông thường, Postman sẽ được dùng cho API kiểu REST. Với Postman, ta có thể gọi Rest API mà không cần viết dòng code nào.
Postman hỗ trợ tất cả các phương thức HTTP (GET, POST, PUT, PATCH, DELETE, …). Bên cạnh đó, nó còn cho phép lưu lại lịch sử các lần request, rất tiện cho việc sử dụng lại khi cần.
Các tính năng chính của Postman
Postman là một công cụ mạnh mẽ giúp các nhà phát triển và tester làm việc với API một cách hiệu quả. Dưới đây là một số tính năng nổi bật của Postman:
Postman Visualizer
Postman’s Visualizer cho phép bạn tăng cường việc trình bày dữ liệu phản hồi API bằng cách sử dụng HTML, CSS và JavaScript. Bạn có thể tạo các hình ảnh tùy chỉnh như biểu đồ, bảng hoặc các phần tử khác để làm cho dữ liệu phản hồi dễ đọc và hiểu hơn.
Cách sử dụng Postman Visualizer:
Tạo một yêu cầu mới hoặc mở một yêu cầu hiện có trong Postman.
Gửi yêu cầu để nhận phản hồi từ API.
Trong bảng phản hồi, nhấp vào nút “Visualize” ở góc trên bên phải.
Mở trình soạn thảo Visualizer, nơi bạn có thể viết mã HTML, CSS và JavaScript để tùy chỉnh hình ảnh phản hồi.
Sử dụng các biến dữ liệu sẵn có (ví dụ: responseBody, responseHeaders, v.v.) để truy cập dữ liệu phản hồi trong mã của bạn.
Viết mã HTML, CSS và JavaScript để tạo hình ảnh mong muốn.
Xem trước hình ảnh bằng cách nhấp vào nút “Preview”.
Lưu các thay đổi và đóng trình soạn thảo Visualizer.
Thư viện tích hợp sẵn (Built-in Libraries)
Postman cung cấp nhiều thư viện bên ngoài mà bạn có thể sử dụng trong các tab Pre-request Script và Test Script để thêm các chức năng bổ sung mà JavaScript không có sẵn.
Một số thư viện thông dụng trong Postman:
Moment.js: Một thư viện JavaScript phổ biến để phân tích, thao tác và định dạng ngày giờ.
Lodash: Thư viện tiện ích cung cấp nhiều hàm hỗ trợ xử lý mảng, đối tượng và chuỗi.
Faker.js: Thư viện để tạo dữ liệu giả lập thực tế như tên, địa chỉ, số điện thoại, email, v.v.
Kiểm soát luồng công việc (Workflow Control)
Postman cho phép bạn kiểm soát thứ tự thực hiện các yêu cầu trong một collection bằng phương thức postman.setNextRequest. Bằng cách sử dụng logic điều kiện trong tab Pre-request Script hoặc Tests, bạn có thể thiết lập yêu cầu tiếp theo được thực hiện dựa trên các điều kiện cụ thể.
Cách sử dụng postman.setNextRequest:
Mở Collection Postman và điều hướng đến yêu cầu mong muốn.
Trong tab Pre-request Script hoặc Tests, viết logic điều kiện bằng JavaScript.
Sử dụng phương thức postman.setNextRequest để chỉ định yêu cầu tiếp theo được thực hiện.
Lưu các thay đổi.
Hỗ trợ GraphQL tích hợp sẵn (Built-in GraphQL Support)
Postman cung cấp hỗ trợ mạnh mẽ cho GraphQL, giúp các nhà phát triển làm việc với các API GraphQL dễ dàng hơn.
Các tính năng và chức năng chính cho GraphQL trong Postman:
GraphQL Variables: Cho phép bạn định nghĩa và sử dụng các biến GraphQL trong các yêu cầu.
GraphQL Request Body: Gửi các truy vấn và mutation GraphQL dưới dạng yêu cầu POST trong phần thân yêu cầu.
GraphQL Autocompletion: Hỗ trợ tự động hoàn thành các trường, kiểu và tham số khi bạn gõ.
User-Defined GraphQL Schemas: Cho phép nhập và sử dụng các schema GraphQL của riêng bạn.
GraphQL Query History: Giữ lịch sử các truy vấn GraphQL, giúp dễ dàng truy cập và tái sử dụng.
Collections
Postman cung cấp tính năng “Collections” để giúp bạn tổ chức các yêu cầu API của mình. Với collections, bạn có thể nhóm các yêu cầu liên quan lại với nhau, giúp quản lý và thực hiện nhiều yêu cầu như một đơn vị đồng nhất.
Các tính năng và lợi ích của Collections trong Postman:
Organization: Tổ chức các yêu cầu API theo cách phân cấp.
Execution: Dễ dàng thực hiện tất cả các yêu cầu trong một collection chỉ với một lần nhấp.
Variables and Environments: Hỗ trợ sử dụng các biến và thiết lập các môi trường khác nhau.
Sharing and Collaboration: Chia sẻ các collections với các thành viên trong nhóm để cộng tác.
Documentation: Tạo tài liệu cho các collections để người khác hiểu và sử dụng API.
Kiểm thử và xác nhận (Tests and Assertions)
Postman bao gồm một khung kiểm thử mạnh mẽ, cho phép bạn viết các bài kiểm tra bằng JavaScript để xác thực các khía cạnh khác nhau của phản hồi API.
Các xác nhận phổ biến trong Postman tests:
Status code assertion: Xác minh mã trạng thái phản hồi khớp với giá trị mong đợi.
Response body assertion: Kiểm tra dữ liệu trong phần thân phản hồi.
Header assertion: Xác thực sự hiện diện và giá trị của các tiêu đề cụ thể trong phản hồi.
Response time assertion: Đảm bảo thời gian phản hồi nằm trong phạm vi chấp nhận được.
Ví dụ về kiểm thử đơn giản:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response body contains key 'username'", function () {
pm.response.to.have.jsonBody('username');
});
pm.test("Content-Type header is present and set to JSON", function () {
pm.response.to.have.header("Content-Type", "application/json");
});
pm.test("Response time is within acceptable range", function () {
pm.expect(pm.response.responseTime).to.be.below(500);
});
Postman là một công cụ không thể thiếu cho các nhà phát triển và tester làm việc với API. Với các tính năng mạnh mẽ và linh hoạt như Visualizer, thư viện tích hợp sẵn, kiểm soát luồng công việc, hỗ trợ GraphQL, collections và kiểm thử, Postman giúp đơn giản hóa và tối ưu hóa quy trình phát triển và kiểm thử API.
Thông tin Account: dùng để Login, logout và sync data.
Settings tùy chỉnh: themes, shortcut, format…
Import data từ ngoài vào
Collections: lưu trữ thông tin của các API theo folder hoặc theo thời gian.
API content: hiển thị nội dung chi tiết API và các phần hỗ trợ giúp thực hiện test API. Đây là phần mà tester phải làm việc nhiều nhất.
Trong phần này gồm có 3 thành phần chính:
Environments: Chứa các thông tin môi trường. Ví dụ: mình làm 1 dự án nhưng có 3 môi trường khác nhau: dev, staging và product. Có phần này, mình có thể nhanh chóng đổi sang môi trường cần test mà không phải mất công đổi URL của từng request. (Cái này sẽ được nói rõ hơn ở những bài sau)
Request: Phần chứa các thông tin chính của API.
Response: Chứa các thông tin trả về sau khi Send Request.
Các thành phần khác của Postman
New – tạo request, collection hoặc enviroment mới.
Import – import collection hoặc environment. Có các tuỳ chọn để import từ file, folder, link hoặc paste từ text thuần.
Runner – Kiểm tra tự động hóa có thể được thực hiện thông qua Runner cả collection.
Open New – Mở một tab mới, cửa sổ Postman hoặc cửa sổ Runner.
My Workspace – Tạo khu vực làm việc riêng hoặc cho một nhóm.
Invite – Cộng tác với nhiều thành viên bằng việc mời các thành viên.
History – Các request đã thực hiện mà bạn đã thực hiện sẽ được hiển thị trong History. Giúp bạn có thể lần theo các hành động bạn đã làm.
Collections – Tổ chức bộ thử nghiệm của bạn bằng cách tạo collection. Mỗi collection có thể có các thư mục con và nhiều yêu cầu. Request hoặc thư mục cũng có thể được trùng lặp.
Tab Request – Hiển thị tiêu đề của requet mà bạn đang làm việc. Mặc định “Untitled Request” sẽ được hiển thị cho các request không có tiêu đề.
HTTP Request – Click vào đây sẽ hiển thị danh sách thả xuống với các request khác nhau như GET, POST, COPY, DELETE, v.v. Trong thử nghiệm, các yêu cầu được sử dụng phổ biến nhất là GET và POST.
Request URL – Còn được gọi là điểm cuối (endpoint), đây là nơi bạn sẽ xác định liên kết đến nơi API sẽ giao tiếp.
Save – Nếu có thay đổi đối với request, nhấp vào Save là bắt buộc để những thay đổi mới sẽ không bị mất hoặc bị ghi đè.
Params – Đây là nơi bạn sẽ viết các tham số cần thiết cho một request, ví dụ như các cặp key – value.
Authorization – Để truy cập API, cần được cấp quyền. Nó có thể ở dạng tên người dùng và mật khẩu, bearer token, v.v.
Headers – Bạn có thể thiết lập các header như nội dung kiểu JSON tùy theo cách tổ chức của bạn.
Body – Đây là nơi chúng ta có thể tùy chỉnh chi tiết trong request thường được sử dụng trong request POST.
Pre-request Script – Đây là các tập lệnh sẽ được thực thi trước request. Thông thường, script tiền request (pre-request) cho cài đặt môi trường được sử dụng để đảm bảo các kiểm tra sẽ được chạy trong môi trường chính xác.
Tests – Đây là các script được thực thi khi request. Điều quan trọng là phải có các thử nghiệm như thiết lập các điểm checkpoint để kiểm tra trạng thái là ok, dữ liêu nhận được có như mong đợi không và các thử nghiệm khác.
API Testing với Postman
API Testing là một phần quan trọng trong quá trình phát triển phần mềm, giúp đảm bảo rằng các API hoạt động đúng và hiệu quả. Dưới đây là quy trình cơ bản để thực hiện API Testing với Postman:
Gửi yêu cầu API cơ bản
Mở Postman và tạo một yêu cầu mới bằng cách nhấp vào New > Request.
Chọn phương thức HTTP (GET, POST, PUT, DELETE, v.v.) và nhập URL của API endpoint.
Nhấp vào Send để gửi yêu cầu và xem phản hồi từ máy chủ.
Tạo Collection và thêm yêu cầu
Tạo một Collection mới bằng cách nhấp vào New > Collection.
Đặt tên cho Collection và lưu lại.
Thêm các yêu cầu API vào Collection bằng cách nhấp chuột phải vào Collection và chọn Add Request.
Thiết lập biến môi trường
Tạo môi trường mới bằng cách nhấp vào biểu tượng Manage Environments và chọn Add.
Đặt tên môi trường và thêm các biến môi trường cần thiết (ví dụ: base URL, token xác thực, v.v.).
Sử dụng các biến môi trường trong các yêu cầu API bằng cú pháp {{variable_name}}.
Viết kịch bản kiểm thử
Mở yêu cầu API và chuyển đến tab Tests.
Viết các kịch bản kiểm thử bằng JavaScript để xác thực phản hồi từ API. Ví dụ:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response time is less than 500ms", function () {
pm.expect(pm.response.responseTime).to.be.below(500);
});
Chạy Collection và kiểm tra kết quả
Chạy toàn bộ Collection bằng cách nhấp vào nút Runner trong Postman.
Chọn Collection và môi trường cần chạy, sau đó nhấp vào Start Test.
Xem kết quả kiểm thử và các báo cáo chi tiết.
Tích hợp với CI/CD
Sử dụng Postman CLI (Newman) để chạy các Collection trong các pipeline CI/CD.
Cài đặt Newman bằng lệnh NPM:
npm install -g newman
Chạy Collection bằng Newman:
newman run path_to_your_collection.json -e path_to_your_environment.json
Tóm lại, Postman là một công cụ API client không thể thiếu cho các nhà phát triển và tester, giúp họ làm việc với API một cách hiệu quả và dễ dàng. Với nhiều tính năng mạnh mẽ và khả năng tích hợp với các công cụ khác, Postman không chỉ giúp tiết kiệm thời gian và công sức mà còn đảm bảo chất lượng và tính nhất quán của các API trong suốt quá trình phát triển phần mềm.
[Chú ý:bài viết sử dụng 1 vài từ ngữ thuộc *văn nói* nơi gầm bàn công sở. Sẽ không phù hợp với một số anh chị.]
Các dự án mà thiếu PM cứng, đủ tầm để dọn ngang dẹp dọc thì kiểu gì cũng đi vào thảm cảnh như bài dưới đây.
Hiện tại có rất nhiều anh chị em lập team, mở công ty, đa phần là code outsourcing (web và mobile app). Đa phần là các công ty có quy mô vừa và nhỏ, từ 30 đến 50 người là đã hoành tráng rồi.
Tuy nhiên không phải công ty nào cũng có một quy trình quản lý dự án chuẩn để đảm bảo chất lượng. Mình xin chia sẻ 1 số kinh nghiệm như bên dưới, hy vọng có thể giúp ích được cho anh chị em.
Đầu tiên là thằng Sales nó sẽ gặp khách hàng, dụ dỗ chào mời, đưa giá làm trong 5 tháng. (project cỡ trung bình).
Xong thằng BA/PM sẽ cùng thằng Sales lấy requirements từ khách hàng. Song song HR sẽ chạy đi tuyển dev về để làm.
Khách hàng ok, ký hợp đồng và thanh toán 30%, sales hốt trước 5% của cái 30% đó, tháng này ấm rồi.
Thằng team lead / project lead sẽ cùng với thằng senior dev ngồi break down requirement, tính task, xong thằng team lead căng thời gian, với requirements này, giả tỷ như không thay đổi thêm bớt gì, thì làm 8 tháng xong.
Thằng PM gật gù, ok deadline 4 tháng, kèm lời hứa làm ngon anh share bonus dự án cho.
Thằng team lead sắp xếp nhân sự, chia task, scrum/agile các kiểu, hùng hồn: dự án này anh em mình 3 tháng xong, Thằng senior dev vừa training cho 3 đứa mới vô vừa gánh team 3 thằng dev mới vô tuần làm 5 ngày, ngày 8 tiếng, chưa kể OT buổi tối + thứ 7, CN làm ở nhà.
Sau 1 tháng 1 thằng out. HR tuyển thêm 1 thằng nữa + PM hốt 1 thằng dev từ team khác qua phụ.
1 tháng sau thì phát hiện hiểu sai một số yêu cầu từ khách hàng + khách hàng mới thấy 1 trang web nào đó có tính năng hay quá, yêu cầu làm giống vậy.
Tiếp 1 tháng nữa, về cơ bản dự án đã fail 1 cách hoàn hảo. Thằng team leader nhảy vô code phụ 2 thằng junior, do thằng senior dev đang nằm viện vì bệnh lao.
PM quyết định kéo thêm 2 thằng dev khác từ 1 dự án khác vào. 2 thằng này tốn tầm 2 tuần để ngồi đọc documents và hiểu cái mớ bùi nhùi do 2 thằng junior dev mới vào code.
Qua tháng thứ 6, thằng sales hiện đã sang làm cho công ty khác. Thằng PM vẫn liên lạc với khách hàng để kỳ kèo thời gian. Về mặt development team thì thằng senior dev đã ra viên, OT được hơn 1 tuần rồi, đám còn lại vẫn đang code + debug, thằng team lead thi thoảng xin nghỉ nửa buổi đi phỏng vấn công ty nào đó.
Qua tháng thứ 7, khách hàng đã có demo để xem.
Qua tháng thứ 8, khách hàng thanh toán 40% chi phí project để nhận 1 cục source code “có thể chạy được nếu click vào các nút theo đúng thứ tự đã chỉ dẫn” kèm theo lời tuyên bố không bao giờ quay lại.
Qua tháng thứ 9, 2 thằng junior dev bị đuổi do kỹ năng không đạt yêu cầu, năng suất làm việc kém.
Cuối quý thằng PM được bonus dự án như bình thường. Thằng team lead đã tự ra mở công ty riêng. Thằng senior dev qua Lazada làm với mức lương gấp đôi. HR lại tiếp tục tuyển thêm dev để lấp vào chỗ trống.
Sau vài tháng công ty code outsourcing chuẩn bị phá sản.
Đến 90% người dùng sử dụng ứng dụng do hiệu suất kém, 86% người dùng xoá ứng dụng khi gặp phải vấn đề thiết kế hay chức năng. Ngược lại, có đến 86% người dùng đánh giá cao những ứng dụng có trải nghiệm đặc biệt. Từ những con số này, không khó để hiểu được lý do tại sao các công ty tập trung vào User Experience như Apple, Google, Adobe, Dropbox và Amazon vẫn luôn dẫn đầu trong lĩnh vực của họ.
UX – hay còn gọi là trải nghiệm người dùng (User experience) – là cách mà người dùng cảm nhận về một sản phẩm cụ thể, cách họ sử dụng sản phẩm đó.
Người làm về UX gọi là UX Designer. Họ là những người nghiên cứu và đánh giá về thói quen, cách mà một người khách hàng sử dụng và cảm nhận về 1 hệ thống (Sử dụng hệ thống thông qua UI). Sử dụng và cảm nhận ở đây đơn giản là những vấn đề như tính dễ sử dụng, sự tiện ích, tính hiệu quả khi sản phẩm hoạt động.
Tại Việt Nam, thói quen sử dụng UX của các nhà phát triển web hiện vẫn còn gặp rất nhiều lỗi như: quá “tham” khi đưa nhiều chức năng, nhiều tiểu tiết vào giao diện game hoặc trang web, như kết nối 3G hay dung lượng file cài đặt, hay bỏ sót các yếu tố quan trọng ảnh hưởng đến trải nghiệm người dùng, hoặc làm việc đơn lẻ thay vì tập hợp thành nhóm…
Chuyển động bằng hình ảnh sẽ nhanh hơn dạng text 60,000 lần
Não bộ tiếp nhận thông tin 90% từ hình ảnh
Não và mắt của chúng ta được “lập trình” để chú ý những chi tiết chuyển động và nhiều màu sắc
Những điều trên là lý do vì sao hình ảnh càng động thì hiệu ứng của UX cũng sẽ càng được cải thiện. Từ đó
+ Người dùng có thể thư giãn nếu App bạn đang load
+ Sự chuyển động sẽ giúp người dùng tập trung hơn vào đối tượng cần chú ý.
+ Sự kết nối mạch lạc khiến người dùng thích thú.
+ Làm nổi bật lỗi khi thông báo hoặc tác vụ hoàn thành.
◾️ Song, người dùng luôn bận rộn đồng nghĩa với việc bạn chỉ có một chút ít thời gian để thu hút họ? Vậy nên, nếu chúng ta có thể tận dụng được khoảnh khắc đó, chúng ta có thể có thêm những khách hàng tiềm năng với giá trị to lớn.
Để hiểu rõ hơn cách cải thiện UX của các marketers, businessman, developers.. trong bức tranh UX muôn hình vạn trạng, bạn có sẵn sàng đi sâu hơn về UX/UI được xem là một trong những ngành nghề “hot” nhất hiện nay cùng chúng tôi?
Buổi Meetup đặc sắc mang tên “ (UX) – Multiple Screen, Surprise and Delights Users by Animation” sẽ được diễn ra vào ngày 12/07/2019 với sự chia sẻ đầy kinh nghiệm từ các chuyên gia trong lĩnh vực mobile:
Anh HOÀNG NGUYỄN – Senior Product Design | Interactive Labs & Design Coach | GEEK Up
Anh PHAN VIẾT TRUNG – Mobile Lead | Dwarves Foundation
Điều gì đáng quan tâm và cải thiện? hay nói cách khác bạn đang mang đến giá trị gì cho người dùng? Thay vì mơ hồ tìm kiếm trên Internet thì hãy giữ ngay cho mình một slot tham dự cùng bạn bè và “mổ xẻ” những điều còn là ẩn số trong lĩnh vực UX/UI nhé!
NHẬP NGAY CODE EARLYBIRD@UX1207 GIẢM 50.000Đ/VÉ BẠN NHÉ! HẠN CHÓT ĐẾN 17:00 NGÀY MAI!
=== THÔNG TIN CHUNG Thời gian: 17:30 ngày 12/07/2019 Địa điểm: Saigon Innovation Hub – 273 Điện Biên Phủ, O7, Q3, HCM === THÔNG TIN LIÊN HỆ Event team: event@applancer.net | 028 6681 3236 Ms. Thoa | thoa.nguyen@applancer.net | 038 5098 969