Lập trình "an tâm" cùng anh GitHub

đăng 02:34, 5 thg 9, 2016 bởi Mẫn Lê Viết   [ đã cập nhật 07:11, 9 thg 9, 2017 ]

Lập trình "an tâm" cùng anh GitHub


Nếu bạn là dân lập trình, có lẽ bạn đã từng (hoặc sẽ) xoá một đoạn code vì nghĩ rằng đoạn code đó không cần dùng nữa, nhưng sau đó lại phát hiện là vẫn cần dùng đến nó. Khi đó, nếu bạn vẫn nhớ đoạn code đó thì chẳng có vấn đề gì, nhưng thường thì bạn sẽ khó mà nhớ rõ ràng từng dòng code được. Lúc này, nếu bạn có dùng một trình quản lý phiên bản mã nguồn thì mọi việc không dở khóc dở cười như vậy nữa. Một trình quản lý phiên bản mã nguồn (code version control) sẽ cho phép bạn lưu trữ lại các phiên bản khác nhau theo thời gian của các tập tin mã nguồn và cho phép bạn có thể quay lại một phiên bản cũ của tập tin đó khi cần thiết. GitHub là nền tảng hỗ trợ quản lý phiên bản như vậy.

Quản lý phiên bản không chỉ được dùng cho lập trình. Bạn có thể gặp nó ở bộ ứng dụng văn phòng MS Office với tính năng Auto Recover, tính năng quản lý phiên bản của Google Drive, hay ứng dụng Time Machine của hệ điều hành macOS. Do đó, bạn có thể sử dụng GitHub để quản lý phiên bản cho bất cứ thứ gì trong máy tính của bạn.

Bài viết này sẽ giới thiệu với các bạn nền tảng GitHub và tập trung vào quy trình làm việc với GitHub thông qua ví dụ cụ thể để bạn có thể nhanh chóng nắm bắt và sử dụng được GitHub cho công việc, học tập của bạn. Bài viết gồm những nội dung sau:

Nội dung

  1. 1 Giới thiệu GitHub
    1. 1.1 Cách quản lý phiên bản của GitHub
    2. 1.2 Tạo tài khoản
    3. 1.3 Trang của bạn trên GitHub
    4. 1.4 GitHub Desktop và GitHub Extension for Visual Studio
  2. 2 Quy trình làm việc với GitHub
  3. 3 Ngày thứ nhất
    1. 3.1 Tạo một kho (repository)
      1. 3.1.1 Tạo kho sử dụng trang web GitHub
      2. 3.1.2 Giao diện trang web của một kho
      3. 3.1.3 Tạo kho sử dụng GitHub Desktop
      4. 3.1.4 Giao diện của GitHub Desktop
    2. 3.2 Đoạn mã đầu tiên cho ứng dụng Contacts
      1. 3.2.1 Tạo project trong Visual Studio
      2. 3.2.2 Mã nguồn lớp Contact
      3. 3.2.3 Mã nguồn lớp Program
      4. 3.2.4 Giao diện tab Changes trên GitHub Desktop
    3. 3.3 Lưu trạng thái và đẩy kho lên web GitHub
      1. 3.3.1 Thực hiện lưu trạng thái
      2. 3.3.2 Đẩy kho lên web GitHub
    4. 3.4 Kết luận của ngày thứ nhất
  4. 4 Ngày thứ hai
    1. 4.1 Mời người khác tham gia cùng dự án
    2. 4.2 Chấp nhận lời mời và sao chép (clone) kho về máy để xem mã nguồn
    3. 4.3 Trao đổi, bình luận trên các lần lưu trạng thái (commit)
    4. 4.4 Quản lý dự án với tab Projects
    5. 4.5 Quản lý các vấn đề (issue)
      1. 4.5.1 Tạo vấn đề (issue) ngay trong tab Issues
      2. 4.5.2 Tạo vấn đề (issue) trong tab Projects
    6. 4.6 Đồng bộ (sync) lại kho trên máy tính
    7. 4.7 Phân nhánh trong GitHub
      1. 4.7.1 Nhánh là gì ?
      2. 4.7.2 Phân nhánh
      3. 4.7.3 Gộp nhánh
      4. 4.7.4 Tạo nhánh (branch) giải quyết vấn đề
    8. 4.8 Tách (fork) kho
    9. 4.9 Sao chép (clone) mã nguồn về để làm việc
    10. 4.10 Đọc và ghi dữ liệu danh bạ xuống tập tin
    11. 4.11 Sử dụng thuộc tính thay cho các hàm get/set
    12. 4.12 Lưu trạng thái và yêu cầu gộp (pull request)
    13. 4.13 Lưu trạng thái và yêu cầu gộp (pull request)
    14. 4.14 Quản lý và thảo luận trên các yêu cầu gộp
      1. 4.14.1 So sánh mã nguồn ngay trên GitHub
      2. 4.14.2 Xem mã nguồn được thay đổi trên máy tính
      3. 4.14.3 Xét duyệt các thay đổi (Review changes)
    15. 4.15 Chỉnh sửa, lưu trạng thái (commit) và yêu cầu gộp (pull request) mới
    16. 4.16 Thực hiện gộp (Merge pull request)
    17. 4.17 Đóng một vấn đề
    18. 4.18 Kết luận ngày thứ 2
  5. 5 Ngày thứ i của dự án
  6. 6 HẾT

Giới thiệu GitHub

Như đã nói ở trên, GitHub là nền tảng lưu trữ mã nguồn miễn phí (đối với các dự án mở). Anh ấy được xây dựng dựa trên hệ thống quản lý mã nguồn phân tán Git, nên anh ấy thừa hưởng được tất cả các tính năng nổi bật của công nghệ quản lý mã nguồn này. Dựa trên đó, anh ấy cung cấp một quy trình làm việc đơn giản và hỗ trợ lập trình cộng tác một cách hiệu quả. Hiện nay, GitHub là dịch vụ lưu trữ mã nguồn được sử dụng phổ biến nhất, thu hút không chỉ những lập trình viên, mà còn cả các nhóm, tổ chức, công ty nổi tiếng sử dụng để lưu trữ, quản lý mã nguồn. Do đó, GitHub cũng là nơi giúp bạn tiếp cận vô vàn kinh nghiệm lập trình.

Dự án mở là dự án cho phép mọi lập trình viên khác có thể xem, sửa đổi, sử dụng mã nguồn trong dự án đó.
Git là hệ thống quản lý mã nguồn phân tán. Nó cung cấp những lợi ích sau:
  • Có thể lưu lại một cách có tổ chức các phiên bản khác nhau của mã nguồn phần mềm
  • Có thể khôi phục lại từ một phiên bản bất kỳ trong quá khứ
  • Có thể so sánh giữa các phiên bản
  • Xem ai đã sửa cái gì để gây ra lỗi
  • Ai phát hiện lỗi và khi nào
  • Khôi phục lại tập tin bị mất
  • và bạn không mất nhiều công sức để làm tất cả việc trên

Cách quản lý phiên bản của GitHub

GitHub sử dụng kỹ thuật quản lý phiên bản của Git. Do đó, nó coi dữ liệu của nó giống như một tập hợp các ảnh (snapshot) của một hệ thống tập tin nhỏ. Mỗi lần bạn thực hiện lưu trạng thái (commit), GitHub sẽ chụp một bức ảnh (snapshot) ghi lại nội dung của tất cả các tập tin tại thời điểm đó và tạo ra một tham chiếu tới ảnh đó. Để hiệu quả hơn, nếu như tập tin không có sự thay đổi nào, GitHub không lưu trữ tập tin đó lại một lần nữa mà chỉ tạo một liên kết tới tập tin gốc đã tồn tại trước đó. GitHub thao tác với dữ liệu như hình minh hoạ dưới đây.

Cách thức quản lý này cũng giống như một hệ thống quản lý tập tin thu nhỏ. Do đó, GitHub có thể cung cấp nhiều tính năng, công cụ vô cùng mạnh mẽ.

Tạo tài khoản

Để bạn có thể lưu trữ mã nguồn và sử dụng các dịch vụ của GitHub, trước tiên, bạn phải tạo một tài khoản của GitHub. Để tạo tài khoản, đơn giản, trên trang web chính của GitHub, bạn bấm nút Sign up hoặc Sign up for GitHub.

Tiếp theo, tại bước 1, bạn điền username, emailpassword (Ví dụ xem hình bên dưới). Password của bạn nên có ít nhất một ký tự in thường, một chữ số và gồm ít nhất 7 ký tự. Bấm Create an account để sang bước 2.

Tại bước 2, bạn sẽ lựa chọn loại tài khoản. Hiện tại, GitHub cung cấp hai loại tài khoản là: miễn phí cho việc lưu trữ các dự án mở (chọn Unlimited public repertoires for free) và có phí (7 đô/tháng) cho lưu trữ các dự án đóng (chọn Unlimited private repertoires for $7/month). Bấm Continue để sang bước 3.

Ở bước 3, bạn trả lời một số câu hỏi điều tra của GitHub, rồi bấm Submit.

GitHub sẽ gởi cho bạn một email vào địa chỉ mail của bạn đã cung cấp cho GitHub để xác thực. Trong email đó, bạn bấm đường link Verify email address để xác thực. Lúc này, GitHub sẽ gởi email chúc mừng và cho phép bạn tạo repertoire (kho) đầu tiên trên GitHub.

Sau khi Submit, giao diện của GitHub sẽ như sau:

Bạn bấm Read the guide để vào xem các hướng dẫn sử dụng của GitHub. Chọn Start a project để bắt đầu tạo một repertoire đầu tiên trên GitHub. Tuy nhiên, trước tiên, tôi muốn giới thiệu trang web của bạn trên GitHub. Để mở trang web của bạn, bạn bấm vào nút avatar phía trên bên phải, rồi chọn Your profile như hình dưới đây.

Trang của bạn trên GitHub

Đây chính là trang web mà người dùng khác trên GitHub có thể xem thông tin và các dự án mở của bạn, cũng như là nơi bạn quản lý các dự án của bạn và xem các vấn đề, trao đổi, đóng góp cho các dự án của người dùng khác. Trang web của bạn (ngoài cách ở ngay phía trên) có thể truy cập thông qua địa chỉ http://github.com/<username> (với <username> thay bằng username khi bạn tạo tài khoản, ví dụ: với tài khoản vừa rồi tôi tạo thì địa chỉ sẽ là http://github.com/lvman). Khi truy cập vào lần đầu, trang web sẽ có dạng như sau:

Cột bên trái, sẽ có ảnh avatar, username, thông tin mô tả, ngày tạo tài khoản,... Tất cả những thông tin này có thể được chỉnh sửa bằng cách bấm vào nút Edit profile (phía trên bên phải của trang web trên). Phần chính của giao diện này là 3 tab: Overview, Repositories và Public activity. Tab Overview chứa thông tin tóm tắt về dự án bạn đang làm việc, một lược đồ cho thấy quá trình đóng góp của bạn và những hoạt động gần đây của bạn. Tab Repositories cho bạn quản lý tất cả các dự án bạn đang làm việc. Còn tab Public activity cho bạn xem các đóng góp gần đây của bạn trên các dự án. Dưới đây là một ví dụ tab Repositories của tôi:


GitHub Desktop và GitHub Extension for Visual Studio

Bên cạnh việc dùng web để quản lý các dự án, bạn có thể sử dụng phần mềm GitHub Desktop để đồng bộ với các dự án trên GitHub về máy của mình. Ứng dụng này hỗ trợ đầy đủ quy trình làm việc, quản lý dự án với GitHub. Còn nếu bạn sử dụng Visual Studio 2015 trở lên, bạn có thể tải về GitHub Extension for Visual Studio. Ứng dụng này tích hợp trực tiếp vào giao diện Visual Studio và cũng hỗ trợ đầy đủ quy trình làm việc, quản lý dự án với GitHub ngay trên giao diện của Visual Studio.

Trong bài viết này, tôi chỉ hướng dẫn sử dụng GitHub Desktop.

Một điểm cần lưu ý khi làm việc với GitHub Desktop và GitHub Extension (cũng như Git) đó là khi bạn sao chép (clone) một kho (repository) với GitHub thì kho đó sẽ được sao chép một bản về trên máy của bạn và bản đó tồn tại độc lập với bản ở trên GitHub (xem hình ở dưới). Thao tác này khác với tính năng đồng bộ thông thường mà chúng ta hay thấy. Do đó, mọi thao tác sửa đổi, lưu trạng thái (commit - sẽ nói rõ ở phần sau) sẽ chỉ được thực hiện ở máy của bạn. Chỉ đến khi bạn đồng bộ (publish hoặc sync) với máy chủ GitHub thì những sửa đổi và lần lưu trạng thái đó mới được chuyển lên GitHub.

Đến lúc này, bạn bắt đầu bước vào quy trình làm việc với GitHub.

Quy trình làm việc với GitHub

Quy trình làm việc với GitHub bao gồm các bước như sau:

  1. Tạo một kho (repository) mới hoặc phân tách (fork) một kho đã có
  2. Tạo dự án trong tab Project, chuyển đổi các nhiệm vụ thành các vấn đề (issue)
  3. Quản lý các vấn đề (issue), gán nhãn, gán mốc, gán người phụ trách
  4. Lựa chọn vấn đề (issue) cần giải quyết, kéo nhiệm vụ (task) liên quan sang cột In Progress
  5. Sao chép (clone) về máy tính hoặc đồng bộ (sync) lại kho
  6. (Nếu cần thiết) Tạo nhánh (branch) mới để giải quyết vấn đề
  7. Chỉnh sửa và thực hiện lưu trạng thái (commit)
  8. Tạo một yêu cầu gộp (pull request)
  9. Kiểm duyệt yêu cầu gộp (review changes): chấp nhận gộp hoặc không chấp nhận gộp, đóng yêu cầu gộp
  10. Trộn (merge) yêu cầu gộp vào nhánh chính
  11. Xoá nhánh (branch) nếu muốn
  12. Đóng vấn đề (issue) liên quan, kéo nhiệm vụ (task) liên quan sang cột Done
  13. Quay lại bước 4

Để minh hoạ cho quy trình làm việc với GitHub trên, chúng ta sẽ cùng đi qua quá trình làm dự án Sổ danh bạ (Contatcts). Đây là ứng dụng đơn giản, chỉ để minh hoạ. Do đó, chỉ có 2 lớp: lớp Contact và lớp Program. Lớp Contact dùng để mô hình hoá một thông tin liên lạc của một người nào đó. Lớp Pogram thì chứa chương trình chính của ứng dụng. Trong lớp Program, chúng ta sẽ tổ chức một danh sách các đối tượng Contact (List<Contact>). Ứng dụng này chỉ có tính năng đơn giản là nhập dữ liệu danh bạ vào đến khi nào không còn muốn nhập nữa, rồi in ra danh sách tất cả các danh bạ đã nhập.

Để thấy rõ được quy trình, các bước làm việc cộng tác, phần minh hoạ sẽ có sự tham gia của hai lập trình viên: anh A (tài khoản tên lvman) và anh B (tài khoản tên manleviet). Ngoài ra, các thuật ngữ cũng sẽ được giải thích bên trong các bước của quy trình.

Ngày thứ nhất

Trong ngày đầu tiên của dự án, anh A khởi tạo dự án sẽ tiến hành những công việc sau:

  1. Tạo kho contactsapp để chứa mã nguồn cho dự án
  2. Viết những dòng mã nguồn đầu tiên cho dự án
  3. Thực hiện lưu trạng thái (commit)
  4. Đẩy kho lên trang web GitHub (publish)

Tạo một kho (repository)

Kho thường được dùng để tổ chức một dự án (project), nó có thể chứa các thư mục, tập tin, ảnh, video, bảng biểu và dữ liệu - tất cả những gì mà dự án của bạn cần. Khi tạo một kho, bạn nên thêm một tập tin README hoặc một tập tin thông tin giới thiệu về dự án của bạn, hoặc một tập tin chứa thông tin license.

Một kho (repository) có thể lưu trữ nhiều dự án. Điều này tuỳ vào sự quản lý của bạn. Trong lập trình C# với Visual Studio, bạn có thể lưu trữ một solution trong một kho, solution đó có thể chứa nhiều project; hay thậm chí một kho có thể chứa nhiều solution.

Để tạo một kho, bạn có lựa chọn:

  1. Sử dụng trang web GitHub
  2. Sử dụng ứng dụng GitHub Desktop

Tạo kho sử dụng trang web GitHub

Trên trang web GitHub, sau khi đăng nhập, bạn nháy chuột vào nút dấu + (bên cạnh avatar của bạn ở góc trên bên phải) và chọn mục New repository. Giao diện trang web để tạo kho sẽ xuất hiện ra như sau:

Bạn điền tên của kho vào ô Repository name, điền mô tả của dự án vào ô Description, chọn Public cho loại kho và chọn mục Initialize this repository with a README trước khi bấm nút Create repository để tạo. Khi đó, giao diện của trang web sẽ chuyển vào trang nội dung của kho mới được tạo. Bởi vì, chúng ta chọn mục Initialize this repository with a README, nên GitHub sẽ tự động tạo tập tin README.md với nội dung được lấy từ ô Description.


Bạn có thể bấm vào nút Clone or download (màu xanh lá cây) để thực hiện sao chép (clone) dự án về máy sử dụng GitHub Desktop.

Giao diện trang web của một kho

Giao diện trang web của một kho (như hình ngay trên) sẽ bao gồm 7 tab:

  1. Tab Code: trong tab này, bạn có thể truy cập vào tất cả các tập tin được lưu trữ trong kho. Bạn có thể xem nội dung của từng tập tin, các lần lưu trạng thái (commit), so sánh mã nguồn giữa các lần chuyển giao với nhau, xem và chuyển đổi giữa các nhánh (branch), thêm tập tin vào kho, tạo một yêu cầu gộp (pull request) mới và sao chép (clone) về máy sử dụng GitHub Desktop,...
  2. Tab Issue: cho phép bạn quản lý các vấn đề (issue), nhãn (label) và điểm mốc (milestone) của dự án được bạn hoặc đồng nghiệp của bạn tạo ra
  3. Tab Pull request: cho phép bạn quản lý các yêu cầu gộp (pull request) của dự án được bạn hoặc đồng nghiệp của bạn tạo ra
  4. Tab Projects: là công cụ cho phép quản lý dự án sử dụng bảng kanban
  5. Tab Wiki: cho phép bạn tạo mô tả dự án (document) cho dự án của bạn
  6. Tab Pulse và Graphs: cho phép bạn xem các thống kê về tình hình cập nhật và sửa đổi của dự án
  7. Tab Settings: cho phép bạn thiết lập các tuỳ chọn cho dự án

Tạo kho sử dụng GitHub Desktop

Để tạo một kho sử dụng GitHub Desktop, bạn bấm vào nút dấu + (phía trên bên trái của cửa sổ GitHub Desktop), chọn tab Create, nhập tên của kho muốn tạo, chọn đường dẫn để lưu kho (đương nhiên là trong máy của bạn), rồi bấm Create repository như hình dưới đây.

Lúc này, GitHub Desktop sẽ tạo cho bạn một kho tại vị trí bạn đã chọn. Kho này chỉ tồn tại trong máy tính của bạn. Bạn phải bấm nút Publish (ở trên bên phải của GitHub Desktop) để đẩy nó lên GitHub. Tuy nhiên, hiện tại, bạn hãy khoan bấm vội bởi vì kho hiện tại vẫn chưa có gì cả. Chúng ta sẽ lập trình các lớp đầu tiên xong rồi hẳn Publish.

Khi tạo kho bằng cách này, bạn sẽ không có sẵn tập tin README.md, mà phải tự tạo nếu muốn.

Giao diện của GitHub Desktop

Giao diện của GitHub Desktop được chia ra làm 2 phần. Phần ngoài cùng bên trái liệt kê tất cả các kho đang nằm trên máy của bạn. Bạn có thể bấm phải chuột vào tên các kho, rồi bấm Open in Explorer để duyệt qua thư mục chứa nội dung của kho sử dụng Windows Explorer. Phần chính của ứng dụng lại chia thành hai tab: Changes và History. Tab Changes để thể hiện những thay đổi trên các tập tin trong kho. Tab History ghi nhận lịch sử các lần lưu trạng thái (commit), cập nhật (publish/sync). Phía dưới 2 tab là phần thể hiện cho các nhánh làm việc (branch) (nhánh là gì sẽ được nói rõ ở phần sau). Như trong hình, ta có nhánh master, là nhánh gốc của bất kỳ một kho nào. Trên nhánh master, hiện có 2 vòng tròn. Vòng tròn màu xanh thể hiện điểm mốc tạo ra kho và vòng tròn màu trắng đứt nét thể hiện phiên bản làm việc đang ở trên nhánh master. Phần còn lại của giao diện GitHub Desktop được chia ra 2 ngăn, 1 ngăn để thể hiện danh sách các tập tin được thay đổi, 1 ngăn thể hiện nội dung thay đổi.

Như vậy, anh A đã tạo được kho contactsapp trên máy tính sử dụng GitHub Desktop. Tiếp theo, anh A mở Visual Studio để lập trình dự án.

Đoạn mã đầu tiên cho ứng dụng Contacts

Tạo project trong Visual Studio

Sau khi mở ứng dụng Visual Studio, anh A tạo ứng dụng loại Console Application và trong ô Location, anh A chọn đến thư mục đã tạo khi tạo kho bằng GitHub Desktop (xem hình dưới).

Sau khi bấm OK, Visual Studio sẽ tạo cho một dự án mới và tập tin Program.cs được mở ra sẵn.

Mã nguồn lớp Contact

Tiếp theo, anh A tạo một Class mới có tên là Contact với mã nguồn như sau: 

Mã nguồn lớp Contact

Trong đoạn mã nguồn trên, lớp Contact có 4 biến thành phần ho, ten, diachisdt để lưu họ tên, địa chỉ và số điện thoại của một người nào đó; 8 hàm get, set thực hiện chức năng lấy thông tin ra và gán thông tin vào các biến thành phần trên; 1 hàm cấu tử để khởi tạo giá trị cho các biến thành phần.

Mã nguồn lớp Program

Tương tự, anh A tiếp tục viết mã nguồn của lớp Program như dưới đây:

Mã nguồn lớp Program

Như vậy, anh A đã hoàn thành mã nguồn của ngày đầu tiên. Anh A chạy và kiểm tra chương trình có đúng như mong muốn hay không. Mọi việc đều ổn. Anh A quay lại với ứng dụng GitHub Desktop để thực hiện lưu trạng thái (commit) và đẩy kho lên web GitHub (publish).

Giao diện tab Changes trên GitHub Desktop

Lúc này, trong GitHub Desktop, anh A bấm vào tab Changes để xem những thay đổi trong kho contactsapp được GitHub Desktop ghi nhận như thế nào. Ở ngăn nhỏ, bạn sẽ thấy danh sách tất cả các tập tin có sự thay đổi (tạo mới hay sửa nội dung). Còn ở ngăn lớn, là nội dung một tập tin đang được chọn xem (trong hình là tập tin Contact.cs). Các dòng màu xanh lá cây với dấu cộng ở đầu dòng thể hiện là các dòng mới được thêm vào, màu đỏ với dấu trừ ở trước thể hiện dòng bị xoá.

Ở bên dưới ngăn nhỏ, bạn có 2 ô Summary, Description và 1 nút Commit to master được dùng để chấp thuận chuyển giao những thay đổi này.

Lưu trạng thái và đẩy kho lên web GitHub

Thực hiện lưu trạng thái

Đến đây, anh A điền mô tả ngắn cho những thay đổi vào ô Summary, điền mô tả chi tiết của những thay đổi vào ô Description, rồi bấm nút Commit to master.

Những thay đổi đã được lưu lại. Lúc này, nhánh master sẽ có thêm một vòng tròn liền nét màu trắng để thể hiện cho lần lưu trạng thái vừa rồi (Xem hình dưới).

Đẩy kho lên web GitHub

Đến đây, anh A bấm nút Publish để đẩy kho lên web GitHub. Trong khung mới hiện ra, bạn điền mô tả cho dự án vào ô Description và bấm nút Publish contactsapp.

Sau khi chờ đợi một đoạn thời gian để GitHub Desktop sao chép các tập tin trong kho lên web GitHub, nạn có thể truy cập web GitHub để kiểm tra kho của bạn đã được đẩy lên hay chưa (như hình dưới đây).

Kết luận của ngày thứ nhất

Để tạo một kho bạn có hai cách là sử dụng trang web của GitHub hoặc sử dụng ứng dụng GitHub Desktop. Nếu sử dụng trang web GitHub thì sau khi tạo xong, bạn phải sao chép (clone) kho đó về máy để làm việc, rồi đồng bộ (sync) lên web GitHub sau mỗi lần lưu trạng thái (commit). Còn nếu bạn sử dụng ứng dụng GitHub Desktop để tạo thì bạn phải đẩy kho đó lên GitHub (publish) sau lần lưu trạng thái (commit) đầu tiên.

Ngày thứ hai

Trong ngày tiếp theo này, anh B sẽ tham gia vào dự án và cùng làm việc. Những công việc anh A và anh B đã thực hiện trong ngày này như sau:

Ở các mục tiếp theo, mục nào màu xanh là công việc anh A làm, mục nào màu cam là công việc của anh B làm, mục nào màu đen là cả hai cùng làm.

 Anh A

Anh B 

  1. Mời anh B tham gia vào dự án
  1. Chấp nhận lời mời và sao chép (clone) kho về máy để xem mã nguồn của anh A
        1. Trao đổi với nhau trên lần lưu trạng thái gần nhất của dự án và chỉ ra một số việc cần làm tiếp theo
  1. Tạo quản lý dự án sử dụng tab Projects
  2. Tạo các vấn đề (issue) và gán người phụ trách, gán nhãn (label), điển mốc (milestone)
  1. Thảo luận trên các vấn đề (issue) và thao tác trên bảng quản lý dự án trong tab Projects
  1. Đồng bộ (sync) lại kho trên máy tính 
  2. Tạo nhánh (branch) giải quyết vấn đề
  3. Lập trình giải quyết vấn đề
  4. Lưu trạng thái (commit)
  5. Yêu cầu gộp (pull request)
  1. Tách (fork) kho để giải quyết vấn đề
  2. Sao chép (clone) mã nguồn về máy để làm việc
  3. Lập trình giải quyết vấn đề
  4. Lưu trạng thái (commit)
  5. Yêu cầu gộp (pull request)
        1. Thảo luận trên các yêu cầu gộp
        2. So sánh mã nguồn giữa các lần lưu trạng thái
        3. Xét duyệt các thay đổi: không chấp nhận gộp (request changes)
  1. Xét duyệt yêu cầu gộp mới
  2. Chấp nhận gộp (approve)
  3. Thực hiện gộp (Merge pull request)
  4. Xoá nhánh (branch) (nếu muốn)
  5. Đóng vấn đề (issue) liên quan
  6. Kéo nhiệm vụ liên quan sang cột Done
  1. Mở lại kho với nhánh mới do GitHub tự tạo
  2. Sửa lại mã nguồn theo đề nghị
  3. Lưu trạng thái (commit)
  4. Yêu cầu gộp mới (pull request)

Mời người khác tham gia cùng dự án

Để mời người khác (đã có tài khoản của GitHub), trong trang web của kho, bạn chuyển đến tab Settings, chọn Collaborator, gõ tên hoặc địa chỉ email của người bạn muốn mời vào ô Search by username, fullname or email address. Ví dụ hình bên dưới, anh A gõ tên anh B vào và GitHub tự động tìm kiếm và đề xuất ra những tài khoản có cùng tên hoặc tên gần giống như vậy.

Sau khi chọn tài khoản người muốn mời xong bạn bấm nút Add collaborator.

Chấp nhận lời mời và sao chép (clone) kho về máy để xem mã nguồn

Lúc này, anh B sẽ nhận được một thư mời. Anh B vào trang web GitHub và bấm vào nút Notifications (nút hình quả chuông, bên cạnh avatar) để xem thư mời.

Anh B sẽ dùng trình duyệt Firefox để phân biệt với trình duyệt Safari của anh A.

Sau khi bấm Accept để chấp nhận lời mời, giao diện của trang web GitHub sẽ như sau:

Lúc này, anh B có thể bấm Clone or download để sao chép mã nguồn của anh A về máy để ngâm cứu.

Trao đổi, bình luận trên các lần lưu trạng thái (commit)

Lúc này, anh B mở Visual Studio để xem phần mã nguồn của anh A và lên web GitHub để viết các nhận xét trong lần lưu trạng thái gần nhất (có tên là Mã nguồn của ngày đầu tiên - Bấm nút commit trong tab Code). Anh B và anh A trao đổi với nhau tại lần lưu trạng thái đó và chỉ ra những tính năng cần làm (xem ví dụ ở hình bên dưới).

Như vậy, anh A và anh B xác định sẽ cùng nhau phát triển thêm 2 tính năng mới:

  1. Thay các hàm get và set bằng thuộc tính, có kiểm tra độ dài chuỗi ký tự được nhập vào
  2. Đọc và ghi dữ liệu danh bạ xuống tập tin
Để xem các thông báo của các trao đổi này, bạn phải chuyển ra trang chủ của GitHub (www.github.com).

Quản lý dự án với tab Projects

Anh A vào tab Projects (như hình bên dưới) trong kho contactsapp, bấm nút Create a project để bắt đầu tạo một trang quản lý dự án mới theo kiểu kanban.

Lúc này, GitHub sẽ hiển thị form (như hình bên dưới) cho phép bạn điền tên (Name) và mô tả (Description) cho dự án. Rồi bạn bấm Save project để lưu lại.

GitHub sẽ tạo cho bạn một dự án mới (theo ví dụ hình trên thì dự án này có tên là Contacts), trong đó, bạn có thể tạo các cột tương ứng với tình trạng của các nhiệm vụ (task). Mỗi cột sẽ chứa nhiều nhiệm vụ. Bạn có thể kéo thả các nhiệm vụ qua lại giữa các cột để chuyển trạng thái cho các nhiệm vụ.

Ví dụ, với dự án Contacst hiện tại, anh A sẽ tạo 3 cột TODO, In Progress và Done (tức là: cần làm, đang làm và đã xong) và có 2 nhiệm vụ đang cần làm ở cột TODO (như hình bên dưới).

Quản lý các vấn đề (issue)

Sau khi tạo xong danh sách các công việc cần làm, anh A chuyển sang tab Issues (xem hình bên dưới) để tạo các vấn đề (issue). Các vấn đề này được dùng để các lập trình viên thông báo cho nhau biết các lỗi hay các vấn đề cần giải quyết trong các lần lưu trạng thái (commit). Mỗi vấn đề sẽ có một mã số (ghi sau dấu #), có tên và mô tả. Bạn còn có thể gán nhãn, gán điểm mốc, gán người phụ trách giải quyết vấn đề. Bạn có thể trao đổi ý kiến trên các vấn đề và đóng (close) vấn đề lại nếu đã giải quyết xong.


Tạo vấn đề (issue) ngay trong tab Issues

Để tạo một vấn đề mới, chúng ta bấm nút New issue. Trong trang web mới (như hình bên dưới), bạn điền tên của vấn đề, viết mô tả nội dung vấn đề. Bạn cũng có thể gán nhãn (label), thiết lập mốc (milestone) và chỉ định người đảm trách việc xử lý vấn đề (assignees). Sau khi làm xong, bạn bấm Submit new issue để hoàn tất.

Tạo vấn đề (issue) trong tab Projects

Đúng vậy, bạn có thể tạo một vấn đề từ các nhiệm vụ trong tab Projects. Để làm việc đó, bạn bấm nút mũi tên hướng xuống bên phải các nhiệm vụ, rồi chọn Convert to issue (như hình dưới đây).

Sau khi thực hiện xong, GitHub sẽ chuyển nhiệm vụ đó thành một vấn đề và có liên kết để bạn bấm vào xem và sửa đổi nội dung của nó (xem hình bên dưới).

Cũng giống như tạo vấn đề trong tab Issues, bạn bấm vào liên kết đó để viết mô tả (phần chính của giao diện trang web), gán nhãn, gán mốc, gán người thực hiện (ngăn bên phải của trang web) (xem hình bên dưới).

Với anh B, mỗi khi có một vấn đề (issue) mới được tạo ra hay thay đổi, thì GitHub đều báo về cho anh B. Anh B có thể xem, bình luận và có thể thao tác trên dự án Contacts trong tab Projects vừa được tạo (các hình bên dưới).

Trên hình trên, anh B đã kéo nhiệm vụ Thuộc tính sang cột In Progress để bắt đầu giải quyết nhiệm vụ này.

Phân tiếp theo dưới đây sẽ được thể hiện trong hai cột để thể hiện các công việc này cùng đồng thời diễn ra. Cách đọc tốt nhất của phần này là đọc từng cột một. Nếu trang web hiển thị mất cột, bạn vui lòng giảm kích cỡ chữ sẽ thầy trọn cả hai cột.

Đồng bộ (sync) lại kho trên máy tính

Do trên máy tính của anh A đã có mã nguồn cũ, nên anh A chỉ việc mở ứng dụng GitHub Desktop và bấm nút Sync để đồng bộ với mã nguồn mới (nếu có) trên trang web GitHub.

Phân nhánh trong GitHub

Nhánh là gì ?

Khi bạn thực hiện lưu trạng thái (commit), hệ thống sẽ lưu trữ đối tượng trạng thái (commit) mà có chứa một con trỏ tới ảnh (snapshot) của nội dung bạn đã tổ chức và 0 hoặc nhiều con trỏ trỏ tới một hoặc nhiều lần lưu trạng thái (commit) cha trực tiếp của lần lưu trạng thái (commit) đó. Sau hai lần lưu trạng thái, lịch sử của dự án sẽ tương tự như hình sau:

Lần lưu trạng thái đầu tiên không có cha, trạng thái thông thường có một cha, và nhiều cha cho lần lưu trạng thái là kết quả được tích hợp lại từ hai hoặc nhiều nhánh.

Một nhánh trong GitHub đơn thuần là một con trỏ có khả năng di chuyển được, trỏ đến một trong những lần lưu trạng thái này. Tên nhánh mặc định là master. Mỗi lần bạn thực hiện lưu trạng thái, nó sẽ được tự động ghi vào theo hướng tiến lên (move forward).

Trong GitHub Desktop, để chuyển sang lần lưu trạng thái khác, bạn phải chuyển đổi nhánh làm việc hoặc sử dụng tính năng Revert.

Phân nhánh

Mỗi khi bạn thực hiện phân nhánh, hệ thống sẽ tạo ra một con trỏ mới cho phép bạn di chuyển vòng quanh. Ví dụ, hình dưới đây, thể hiện con trỏ testing mới được tạo ra đại diện cho nhánh testing. Con trỏ testing này cùng trỏ tới lần lưu trạng thái hiện tại (mới nhất).

Để biết được bạn đang làm việc trên nhánh nào, GitHub giữ một con trỏ đặc biệt tên là HEAD. Khi mới tạo một nhánh mới thì con trỏ HEAD vẫn sẽ trỏ về nhánh mà trước đó nó đang làm việc.

Nếu bạn thực hiện thêm một lần lưu trạng thái mới trên nhánh testing, con trỏ HEAD sẽ hình dưới đây:

Tiếp đến, nếu như bạn thực hiện chuyển về nhánh master, làm việc, rồi thực hiện một lần lưu trạng thái mới trên nhánh master, bạn sẽ có con trỏ HEAD như sau:

Khi thực hiện chuyển con trỏ HEAD về nhánh master, hệ thống sẽ thực hiện hai việc: 1. chuyển con trỏ về nhánh master, 2. phục hồi lại các tập tin trong kho làm việc của bạn trở lại ảnh (snapshot) mà con trỏ master đang trỏ tới. Điều này có nghĩa là những gì bạn thay đổi từ lúc đó trở đi sẽ tách ra so với phiên bản cũ hơn của kho. Nó "tua lại" các thay đổi cần thiết mà bạn đã thực hiện trên nhánh testing một cách tạm thời để bạn có thể đi theo một hướng khác.

Từ lúc này trở đi, bạn có thể chuyển qua lại giữa các nhánh để làm việc. Những thay đổi trên hai nhánh sẽ bị cô lập với nhau và bạn có thể tích hợp chúng lại với nhau khi cần thiết.

Trong GitHub, để chuyển đổi nhánh làm việc, bạn đơn giản bấm nút được đánh dấu viền đỏ trong hình dưới đây.

Gộp nhánh

Để gộp nhánh, GitHub tạo một ảnh (snapshot) mới - được hợp thành từ hai lần lưu trạng thái được yêu cầu gộp trên hai nhánh và con trỏ nhánh master được tiếp tục tiến về phía trước. Lần lưu trạng thái được gọi là lưu trạng thái tích hợp (merge commit) và nó đặc biệt vì có nhiều hơn một cha.

Trong thực tế, quá trình gộp này đôi khi không diễn ra một cách suôn sẻ.

Tạo nhánh (branch) giải quyết vấn đề

Trong GitHub Desktop, anh A bấm nút Create new branch để tạo một nhánh mới tên là fileio như hình dưới đây.

Sau khi bấm Create new branch, GitHub Desktop sẽ tạo nhánh fileio và thể hiện nó lên khung các nhánh như hình dưới đây.

Vậy là anh A sẽ làm việc trên nhánh fileio. Tiếp theo, anh A mở Visual Studio để lập trình giải quyết vấn đề.

Tách (fork) kho

Sau khi kéo thả nhiệm vụ sẽ giải quyết vào cột In Progress, anh B có thể thực hiện tách (fork) dự án.

Có thể xem việc tách (fork) kho này giống như thực hiện phân nhánh (branch) (Xem phần Phân nhánh trong GitHub ở cột bên cạnh để hiểu rõ hơn). Cho nên, sau khi giải quyết xong vấn đề, cần phải gộp (merge) vào lại nhánh chính. Tức là, với kho contactsapp của anh B (được tách ra từ contactsapp của anh A), sau khi anh B hoàn tất tính năng mới thì phải thực hiện yêu cầu gộp (pull request) để gộp những tính năng mới đó vào kho của anh A. Mô hình các kho sau khi tách (fork) sẽ như sau:

Việc tách (fork) kho giúp cho việc hạn chế các thay đổi từ các lập trình viên khác sẽ ảnh hưởng trực tiếp đến mã nguồn gốc của dự án.

Ở góc trên bên phải, dưới avatar của bạn, GitHub cung cấp 3 chức năng:

  1. Watch: cho phép bạn theo dõi sự phát triển của dự án. Tất cả các vấn đề được tạo, những trao đổi, những lần yêu cầu gộp đều được cảnh báo cho bạn biết.
  2. Star: gắn sao để bạn dễ dàng tìm lại khi cần đến mã nguồn của dự án này.
  3. Fork: cho phép sao chép dự án này ra thành một kho riêng của bạn (như hình minh hoạ bên dưới). 

Trên trang web quản lý kho, anh B bấm vào nút Fork để phân tách kho này và giao diện trang web GitHub sẽ thành như sau:

Nút Watch giờ đã đổi tên thành Unwatch và con số phía sau nút Unwatch và Fork giờ là 1 để báo rằng kho này có 1 người dùng khác đang làm việc cùng. Ở góc bên trái, thể hiện tên kho và cho biết nó được tách (forked) từ kho nào, của ai.

Sao chép (clone) mã nguồn về để làm việc

Tiếp theo, anh B sao chép (clone) mã nguồn về máy để bắt đầu lập trình. Anh B có thể bấm nút Clone or download trên trang web, hoặc bấm nút dấu + trong ứng dụng GitHub Desktop, rồi chọn phần Clone (xem ví dụ hình dưới đây).

Bạn phải chọn tên kho có ký hiệu gần giống với ký hiệu cây đinh ba. Đó là ký hiệu của kho được tách ra từ một kho khác. Phân biệt với ký hiệu quyển sổ là kho do chính bạn tạo ra, ký hiệu này là để chỉ kho được bạn tách (forked) ra từ kho của người khác.

Sau khi bấm Clone contactsapp, GitHub Desktop sẽ kết nối với máy chủ GitHub để sao chép kho về máy. Sau khi kết thúc, GitHub Desktop sẽ có giao diện như sau:

Như các bạn có thể thấy, ta có 2 nhánh master, một của anh A (tên là lvman/master) và một của anh B (tên là master). Nhánh master của anh B được tách ra từ nhánh master của anh A. Đường tròn đứt nét, thể hiện bạn đang làm việc với lần lưu trạng thái nào, đang ở trên nhánh master của anh B.

Tiếp đến, anh B mở Visual Studio lên để lập trình tính năng mới.


Đọc và ghi dữ liệu danh bạ xuống tập tin

Anh A tạo một lớp tiện ích có tên là XuLyFile.cs có mã nguồn như sau:

Mã nguồn lớp XuLyFile.cs

Mã nguồn lớp Program được sửa lại để sử dụng các hàm tiện ích đọc và lưu tập tin trong lớp XuLyFile như sau:

Mã nguồn lớp Program có đọc và ghi file

Anh A hoàn thành mã nguồn cho tính năng đọc và ghi dữ liệu danh bạ xuống tập tin. Anh A quay lại với ứng dụng GitHub Desktop để thực hiện lưu trạng thái và yêu cầu gộp.

Sử dụng thuộc tính thay cho các hàm get/set

Mã nguồn lớp Contact được anh B sửa lại như sau:

Mã nguồn lớp Contact sử dụng thuộc tính

Mã nguồn lớp Program được sửa lại để sử dụng các thuộc tính trong lớp Contact như sau:

Lớp Program sửa lại sử dụng thuộc tính

Anh B đã hoàn thành mã nguồn cho tính năng anh B đảm nhận. Anh B quay lại với ứng dụng GitHub Desktop để thực hiện lưu trạng thái (commit) và đề nghị gộp (pull request).

Lưu trạng thái và yêu cầu gộp (pull request)

GitHub Desktop lúc này ghi nhận 3 tập tin có sự thay đổi (xem hình bên dưới).

Anh A điền thông tin vào ô Summary và Description, rồi bấm Commit to fileio. Sau đó, bấm nút Pull Request (phía trên bên phải cửa sổ) để gởi yêu cầu gộp (pull request).

Như các bạn thấy ở hình trên, từ nhánh fileio bây giờ có một đường đứt nét nối liền vào nhánh master. Điều này thể hiện một yêu cầu gộp đã được thực hiện.

Lưu trạng thái và yêu cầu gộp (pull request)

GitHub Desktop lúc này ghi nhận có hai tập tin có thay đổi là Contact.cs và Program.cs (xem hình bên dưới). Khi xem nội dung từng tập tin, bạn sẽ thấy các dòng màu đỏ và màu xanh tương ứng với các dòng đã bị xoá và những dòng mới được thêm vào.

Anh B điền thông tin vào ô Summary và Description, rồi bấm Commit to master để thực hiện lưu trạng thái. Sau đó, anh B, bấm nút Pull Request để gởi yêu cầu gộp (pull request).

Như các bạn thấy ở hình trên, từ nhánh master của anh B bây giờ có một đường đứt nét nối liền vào nhánh master của anh A. Điều này thể hiện một yêu cầu gộp đã được thực hiện.

Quản lý và thảo luận trên các yêu cầu gộp

Anh A nhận được email thông báo có yêu cầu gộp #5 từ nhánh master của anh B vào nhánh master của anh A. Anh B nhận được email thông báo có yêu cầu gộp #4 từ nhánh fileio vào nhánh master của anh A. Vì cả hai yêu cầu gộp này đều là vào nhánh master của A, cho nên chỉ có thể xem nó trên kho của anh A.

Tất các yêu cầu gộp trên đều được thông báo qua mail hoặc xem tại trang chủ của GitHub (khi đã đăng nhập).

Anh A và B truy cập vào kho của anh A trên GitHub, giao diện trang tab Pull requests như sau:

Anh A vào xem yêu cầu gộp có tên Sử dụng thuộc tính (của anh B), giao diện trang web như sau:

Trong trang web cho từng yêu cầu gộp, chúng ta có thể xem thông tin của lần lưu trạng thái muốn được gộp, cụ thể những thay đổi trên các tập tin, viết bình luận và thực hiện gộp (Merge pull request).

So sánh mã nguồn ngay trên GitHub

GitHub hỗ trợ công cụ cho phép so sánh mã nguồn giữa các lần lưu trạng thái. Để xem phần so sánh này, bạn vào lớp Files changed. Ví dụ dưới đây là trang xem các tập tin có thay đổi theo kiểu tách biệt (Split).

Xem mã nguồn được thay đổi trên máy tính

Nếu muốn xem mã nguồn ngay trên máy, ví dụ anh B muốn xem mã nguồn của anh A. Trên GitHub Desktop, đầu tiên, anh B thực hiện đồng bộ (sync). Lúc này, tất cả các nhánh trong kho anh A cũng được cập nhật về máy anh B và có thể thấy được trên khung thể hiện các nhánh của GitHub Desktop (xem hình dưới đây). Anh B có thể chuyển sang làm việc trên một nhánh nào đó trong kho của anh A.

Ví dụ, với hình trên, anh B đã chuyển sang làm việc trên nhánh fileio của anh A để xem mã nguồn anh A đã viết.

Xét duyệt các thay đổi (Review changes)

Ngay trên trang web cho các yêu cầu gộp, bạn bấm nút Add your review. Lúc đó, giao diện trang web sẽ chuyển sang trang so sánh mã nguồn và nút Review changes (màu xanh lá) sẽ mở ra một hộp thoại. Trên đó, bạn nhập thảo luận vào ô Review summary, chọn một trong ba tuỳ chọn: Comment, Approve hoặc Request changes. Trong đó, Approve có nghĩa là chấp thuận cho gộp và Request changes là yêu cầu chỉnh sửa.

Như vậy, anh A đã không đồng ý cho anh B gộp mã nguồn với lý do là anh B chưa có phần kiểm tra độ dài của các chuỗi được gán vào. Sau khi anh A bấm Submit review một thông báo sẽ gởi đến cho anh B. Đồng thời, anh A cũng bấm nút Close Pull Request để đóng yêu cầu gộp này lại. 

Chỉnh sửa, lưu trạng thái (commit) và yêu cầu gộp (pull request) mới

Anh B xem thông báo và bấm vào liên kết sẽ mở ra trang web như sau:

Như vậy, anh B thấy được yêu cầu mà mình đã bỏ sót. Để bổ sung tính năng bị thiếu, anh B bấm vào đường liên kết open this in GitHub Desktop (bên cạnh nút Merge pull request). GitHub sẽ tự động lấy kho của anh A về, tự động tạo một nhánh mới (ví dụ hình dưới, nhánh này tên là pr/5) để anh B làm việc trên đó.

Anh B mở Visual Studio và bổ sung đoạn mã nguồn kiểm tra độ dài chuỗi ký tự nhập vào. Sau khi xong thì thực hiện lưu trạng thái cho các đoạn mã nguồn mới, rồi thực hiện yêu cầu gộp mới. Lúc này, trong kho của anh A sẽ có thêm một yêu cầu gộp mới tên là Pr 5.

Thực hiện gộp (Merge pull request)

Anh A vào kiểm tra và phê duyệt cho yêu cầu gộp mới.

Sau khi bấm Submit review, giao diện trang web sẽ như sau:

Lúc này, anh A có thể bấm nút Merge pull request để thực hiện gộp.

Sau khi gộp xong, bạn có thể xoá nhánh nếu muốn.

Đóng một vấn đề

Như vậy, một vấn đề đã được hoàn thành. Anh A vào tab Issues, chọn vấn đề đã hoàn thành, bấm Mark as và chọn Closed để đóng vấn đề đó. Tiếp đến, anh A vào tab Projects để kéo nhiệm vụ tương ứng vào cột Done.

Kết luận ngày thứ 2

Anh A và B đã kết thúc một cách xuất sắc công việc của ngày thứ 2. Công việc của ngày này khá rắc rối. Tuy nhiên, nó lại thể hiện được hầu hết một quy trình công việc mỗi ngày để làm việc cộng tác. Quy trình công việc này sẽ được kết luận lại trong mục Ngày thứ i của dự án dưới đây.

Ngày thứ i của dự án

Tại một ngày thứ i nào đó của dự án, cả hai anh A và B ngồi vào máy tính của mình rồi thực hiện:
  1. (Nếu cần thiết) Thiết lập các nhiệm vụ mới và chuyển chúng thành các vấn đề (issue)
  2. (Nếu cần thiết) Quản lý các vấn đề (issue), gán nhãn, gán mốc, gán người phụ trách
  3. Lựa chọn vấn đề (issue) cần giải quyết, kéo nhiệm vụ (task) liên quan sang cột In Progress
  4. Sao chép (clone) về máy tính hoặc đồng bộ (sync) lại kho
  5. (Nếu cần thiết) Tạo nhánh (branch) mới để giải quyết vấn đề
  6. Chỉnh sửa và thực hiện lưu trạng thái (commit)
  7. Tạo một yêu cầu gộp (pull request)
  8. Kiểm duyệt yêu cầu gộp (review changes): chấp nhận gộp hoặc không chấp nhận gộp, đóng yêu cầu gộp
  9. Trộn (merge) yêu cầu gộp vào nhánh chính
  10. Xoá nhánh (branch) nếu muốn
  11. Đóng vấn đề (issue) liên quan, kéo nhiệm vụ (task) liên quan sang cột Done

HẾT

Tài liệu tham khảo
2. Sách Pro Git được viết bởi Scott Chacon và Ben Straub

Một số giả định về nghề Hệ thống Thông tin

đăng 20:03, 18 thg 6, 2016 bởi Mẫn Lê Viết   [ đã cập nhật 20:18, 18 thg 6, 2016 ]

Một số giả định về nghề Hệ thống Thông tin



Bài này được dịch từ phần 5, tài liệu IS 2010 - Curriculum Guidelines for Undergraduate Degree Programs in Information Systems.

Đối với vai trò của các Hệ thống thông tin (viết tắt IS - Information System) trong tương lai và những yêu cầu đối với chương trình đào tạo IS, một vài thành tố vẫn còn quan trọng và là đặc thù của ngành học. Những đặc thù này phát triển quanh bốn miền chính của nghề IS và do đó phải được tích hợp vào bất cứ một chương trình đào tạo IS nào:

  1. Chuyên gia IS có mặt trong nhiều lĩnh vực khác nhau như kinh doanh, y tế, chính phủ và các tổ chức phi lợi nhuận. Do đó, sinh viên phải hiểu:
    • Chuyên gia IS cho phép sự vận hành thành công trong nhiều tổ chức
    • Chuyên gia IS tham gia xuyên suốt các cấp độ, chức năng  tổ chức
    • Chuyên gia IS cần cả hiểu biết chuyên xâu về lĩnh vực họ làm việc và cả hiểu biết về công nghệ thích hợp cho vai trò tổ chức của họ
    • IS trong các tổ chức có ý nghĩa chiến lược ngay càng tăng bởi vì phạm vi cũng như vai trò mà nó tham gia trong các hệ thống tổ chức 
  2. Chuyên gia IS phải có kỹ năng tư duy phân tích và phản biện mạnh mẽ để phát triển trong một môi trường cạnh tranh toàn cầu. Do đó, sinh viên phải:
    • Trở thành người giải quyết vấn đề và người có tư duy phản biện
    • Sử dụng các khái niệm hệ thống để hiểu và định vị vấn đề
    • Có thể ứng dụng những khái niệm, kỹ năng cả cũ và mới
    • Hiểu rằng một hệ thống sẽ bao gồm con người, tiến trình, phần cứng, phần mềm và dữ liệu bên trong một môi trường toàn cầu
  3. Chuyên gia IS phải thể hiện các yếu tố đạo đức và có khả năng giao tiếp và làm việc nhóm tốt. Sinh viên phải hiểu rằng các chuyên gia IS nên có thể:
    • Đánh giá và hành động dựa trên các vấn đề đạo đức trong lĩnh vực IS
    • Thực thi các quy tắc đạo đức chuyên nghiệp
    • Hợp tác với các chuyên gia khác cũng tốt như làm việc độc lập
    • Giao tiếp hiệu quả với các kỹ năng nghe, nói, viết tốt
    • Thể hiện sự kiên định, linh hoạt, ham hiểu biết, sáng tạo, liều lĩnh và khả năng chịu đựng những tính cách này của người khác
  4. Chuyên gia IS phải thiết kế và cài đặt các giải pháp Công nghệ thông tin nhằm nâng cao hiệu quả tổ chức. Do đó, sinh viên phải:
    • Có những kỹ năng trong việc hiểu và mô hình các tiến trình và dữ liệu tổ chức, xác định và cài đặt các giải pháp kỹ thuật và tiến trình, quản lý dự án, và tích hợp hệ thống xuyên suốt các tổ chức.
    • Thành thạo các kỹ thuật để trích rút, chuyển đổi, truyền gởi và lưu trữ dữ liệu, thông tin, bao gồm cả đảm bảo chất lượng dữ liệu
    • Tập trung vào việc ứng dụng IT trong việc trợ giúp các cá nhân, nhóm và tổ chức để đạt được mục đích của họ trong một môi trường cạnh tranh toàn cầu

Hướng dẫn học lập trình web

đăng 10:06, 14 thg 8, 2014 bởi Mẫn Lê Viết   [ đã cập nhật 05:44, 3 thg 9, 2018 ]

Hướng dẫn học lập trình web

Điều đầu tiên, tôi nghĩ cần nói thật với các bạn rằng kinh nghiệm về lập trình web của tôi gần như là con số 0. Thời đại học, học phần Kiến tập là học phần duy nhất mà tôi có thể tiếp cận với lập trình web, nhưng do tôi bận tập trung vào lập trình phần mềm Windows nên tôi giao hẳn cho bạn tôi xử lý đề án của học phần đó. Sau đó, cũng có vài lần tôi xem xét việc học lập trình web, nhưng thật sự thì với công nghệ thời đó (thời kỳ trước web 2.0), lập trình web không hấp dẫn tôi bằng lập trình Windows và các thuật toán. Đến năm 2004, 2005, web 2.0 nở rộ, lập trình AJAX lên hương, web đã có sức mạnh gần ngang bằng với lập trình Windows và nó đã có nhiều điều thú vị để quyến rũ tôi. Tuy nhiên, do công việc quá nhiều, nên tôi một lần nữa chưa học được gì nhiều. Và giờ, tôi lại một lần nữa quay lại với lập trình web. Sau một thời gian không ngắn cũng không quá dài tìm hiểu, tôi đã lựa chọn một con đường để học lập trình web. Và tôi viết bài này là để chia sẻ nó cho các bạn.

Con đường học lập trình web

Hiện nay, để lập trình web bạn có rất nhiều lựa chọn. Bạn có thể dùng Notepad tự gõ từng dòng lệnh HTML, CSS, Javascript để tạo nên các trang web. Bạn có thể dùng các phần mềm hỗ trợ từ A đến Z như Dreamweaver. Bạn có thể dùng ASP.NET hay JSP, Servlet, Zend Framework để tạo ra các ứng dụng web mạnh mẽ. Hay bạn có thể dùng các nên tảng mới và hiệu năng lập trình cao như Rails, Node.js, Dart để tạo ra các ứng dụng web linh hoạt. Cho nên, để lựa chọn lấy một con đường để học lập trình web thật không đơn giản. Con đường mà tôi đã chọn dựa trên hai tiêu chuẩn sau:
  1. Nền tảng lập trình ứng dụng web phải mạnh mẽ, linh hoạt, hỗ trợ các tính năng cao cấp nhất hiện nay và quan trọng nhất là cung cấp một hệ thống hiệu năng cao
  2. Số lượng ngôn ngữ và nền tảng cần học là ít nhất

Do đó, tôi đã chọn học HTML & CSS, Javascript và nền tảng Dart.

Lý do tôi chọn HTML, CSS và Javascript thì khá là đơn giản. Bởi vì đó chính là tất cả những thứ cần thiết để lập trình web phần client-side mà mọi người học lập trình web đều nên học. Các nền tảng lập trình như ASP.NET, JSP, Joomla, Drupal đã hỗ trợ cho bạn khá nhiều, nhưng nếu bạn muốn tuỳ chỉnh từ những template có sẵn thì bạn vẫn cần biết 3 thứ trên.

Còn với Dart, tôi chọn nền tảng này vì nó thoả mãn hai tiêu chuẩn đã nói ở trên. Thứ nhất, nền tảng này mới ra đời gần đây và được hỗ trợ mạnh mẽ bởi Google. Thứ hai, nó chính là ngôn ngữ hỗ trợ tốt nhất cho hệ điều hành Chromium của Google, một nền tảng ứng dụng web khá mạnh hiện nay. Thứ ba, theo các đánh giá về hiệu năng thì Dart này ngang ngửa với thư viện Node.js, một thư viện cho hiệu năng cao nhất hiện nay. Thứ tư, Dart là nền tảng Javascript nên tôi sẽ không cần phải học thêm một ngôn ngữ nào nữa ngoài ba thứ HTML, CSS và Javascript.

Học như thế nào ?

Như vậy, chúng ta đã xác định được những gì cần học. Bây giờ, tôi sẽ trình bày những bước bạn nên đi để học những thứ trên :

  1. Đầu tiên, để học những thứ cơ bản của HTML & CSS, bạn nên tham gia khoá học HTML & CSS của codecademy.
  2. Sau khi xong phần cơ bản đó, bạn nên thực hiện dự án Make a Website và About you cũng trên codecademy. Các dự án này sẽ giúp bạn định hình các bước để tạo ra một trang web tĩnh.
  3. Cũng tương tự như khi học HTML & CSS, bạn nên tham gia khoá học Javascript của codecademy. Khoá học này sẽ giúp bạn nắm vững những thành phần cơ bản của ngôn ngữ lập trình Web Javascript.
  4. Tiếp đến bạn nên học JQuery. JQuery là bộ thư viện Javascript nhỏ gọn, cung cấp những tính năng mạnh mẽ giúp cho trang web của bạn tương tác với người dùng. Bạn nên tham gia khoá học JQuery của codecademy để học những thứ cơ bản của JQuery.
  5. Sau đó, dự án Make an Interactive Website sẽ giúp bạn kết hợp tất cả những gì đã học để tạo ra một trang web động đặc sắc.
  6. Hai dự án Animate Your NameSun, Earth, and Code cung cấp cơ hội để ôn lại những gì đã học.
  7. Đến đây, bạn đã có thể xây dựng những trang web động đơn giản. Bạn muốn biết nhiều hơn về HTML & CSS và Javascript, cũng như HTML5 và CSS3, bạn nên nghiên cứu các tutorial của w3school, hoặc của guru99.
  8. Khoá học HTML5 Tutorial Programming from Scratch cũng cung cấp những kiến thức HTML5 và CSS3 hữu ích.
    1. Đến ngang đây thì sách là thứ sẽ mang lại cho bạn nhiều kiến thức bổ ích. Bạn nên đọc quyển HTML5 for web designers - Jeremy KeithCSS3 for web designers - Dan Cederholm. Dan Cederholm là nhà thiết kế web khá nổi tiếng với hai trang web SimpleBitsDribbble.
    2. Hai quyển sách khác, cũng của Dan Cederholm, đáng để đọc là Web Standard SolutionsBulletproof Web Design. Hai quyển này tập hợp những hướng dẫn cho việc thiết kế các trang web linh động và đúng chuẩn web (web standard).
    3. Các hướng dẫn của thoughbot về HTML & CSS, cũng như Javascript cũng rất hữu ích cho mọi đối tượng.
    4. Tương tự, Webmaker cũng cung cấp nhiều tài nguyên, công cụ, hướng dẫn hữu ích cho người lập trình web.
    5. Nếu bạn quan tâm đến việc thiết kế các trang web vừa chạy tốt với màn hình máy PC, vừa chạy tốt với màn hình nhỏ của mobile, thì bạn nên đọc quyển Responsive Web Design của Ethan Marcotte.
    6. Nếu bạn đã nắm vững CSS và thấy rằng nó quá khó để quản lý các dòng CSS. Bạn muốn một CSS có khả năng sử dụng lại. Một CSS có những khả năng của một ngôn ngữ lập trình. Lúc này, bạn nên học SASS qua cuốn Sass for Web designers của Dan Cederholm hoặc nghiên cứu các tài liệu của thoughtbot. Bạn cũng có thể sử dụng công cụ miễn phí Bourbon để việc lập trình Sass dễ thở hơn.
  9. Tiếp theo, tôi nghĩ bạn nên bắt đầu nghiên cứu nền tảng Dart thông qua các tutorial và tài liệu trên trang chủ của Dart. Hai sách Dart: Up and RunningDart for Hipsters là hai cẩm nang tốt để bạn có thể tra cứu.
  10. Tạp chí A List Apart cung cấp rất nhiều bài viết hay về thiết kế web. Nếu bạn chịu khó đọc tiếng Anh thì bạn sẽ tìm được nhiều bài về web standard bổ ích, cũng như những kinh nghiệm làm web hay.

Các sách được liệt kê ở trên, các bạn có thể lấy ở đây.

Tôi cung cấp tất cả các sách dưới một file nén để tránh bị Google chặn vì bản quyền. Đợt vừa rồi phát hiện một loạt file PDF của các quyển sách bị Google chặn như thế.

Kết

Con đường của tôi đi chỉ mới tới đó. Nếu tôi nghiên cứu thêm được cái gì hữu ích, tôi sẽ chia sẻ với các bạn sau. Chúc các bạn thành công!

Đôi điều về em Yến (ngôn ngữ lập trình Swift)

đăng 00:30, 6 thg 8, 2014 bởi Mẫn Lê Viết   [ đã cập nhật 01:29, 7 thg 11, 2016 ]

Đôi điều về em Yến

Sau một thời gian tìm hiểu kha khá dài thì nay mình xin viết vài dòng (vài trăm dòng thì đúng hơn) về em Yến (ngôn ngữ lập trình Swift) để thoả lòng tò mò của những ai quan tâm. Vì kiến thức của mình về các ngôn ngữ lập trình cũng còn non nên nếu có gì sai sót các đại ca đi qua xin nhẹ tay. Thêm điều nữa là đây là bài viết tổng hợp nên mình không đi vào chi tiết vấn đề cú pháp câu lệnh của em Yến lắm. Mình cũng sẽ tập trung phân tích những cái khác biệt của em í chứ không nói nhiều đến những thứ giống. Mọi người muốn biết thêm về Swift thì tham khảo trên trang web của Apple.


Vì sao em Yến ra đời

Trước khi đi vào chi tiết từng góc cạnh của em Yến, thiết nghĩ nên tìm hiểu vì sao em ấy lại được sinh ra. Nói về sự ra đời của em Yến thì dễ nhất là trích lời giới thiệu của Apple (được dịch một cách sơ sài nhất) đó là : “…để cung cấp một ngôn ngữ mới, mạnh mẽ hơn, linh hoạt hơn (như Python, Ruby, các ngôn ngữ script,…), nhưng đồng thời cũng lưu giữ lại những điểm mạnh của Objective C.”

Còn nói cho hiểu rõ ngọn ngành của vấn đề thì là thế này : 

  • Thứ nhất, bởi vì Objective C (ngôn ngữ chính để lập trình cho iOS và OS X) hơi lạ so với các ngôn ngữ lập trình phổ biến khác như C#, Java, PHP, nên những lập trình viên khi chuyển từ các ngôn ngữ lập trình đó sang (để viết ứng dụng cho iDevices) thì phải mất thời gian tìm hiểu để nắm vững nguyên lý làm việc. Do đó, Swift đã vứt bỏ nhiều thứ là căn bản của Objective C và dùng nguyên lý của các ngôn ngữ C#, Java. Nên giờ, Swift đã gần với các ngôn ngữ này hơn. 
  • Thứ hai là các ngôn ngữ mới sau này hiện đại hơn các ngôn ngữ kiểu đa năng hồi xưa (C, C++, C#, Java) ở chỗ nó hỗ trợ nâng cao hiệu năng lập trình. Kiểu như một bên cần viết 100 dòng lệnh thì bên này giờ chỉ cần viết 1 dòng. Ví dụ: để thực hiện nhân ma trận, C# cần ít nhất là 3 dòng lệnh, thì Python chỉ cần 1 dòng (A * B). Nói chung, giờ Swift hiện đại hơn so với Objective C nhiều.
Hai điều trên cũng là hai lý do khiến mình vừa không thích, vừa thích em Yến.

Như vậy, em Yến ra đời với mục tiêu rất rõ ràng rồi. Tuy nhiên, có nhiều người sẽ tự hỏi là sao Apple không cải tiến ngôn ngữ Objective C luôn, mà phải tạo ra một ngôn ngữ mới làm gì. Về vấn đề này thì mình xin đưa ra hai luận giải như sau :

  1. Thực sự thì Objective C qua từng năm một thì đều có nhiều cải tiến, cả về hiệu năng cũng như cú pháp câu lệnh. Tuy nhiên, Swift mới hơn, linh động hơn, cởi mở hơn, an toàn hơn, hiện đại hơn nhiều. Nếu áp dụng ngay vào Objective C thì tất cả các lập trình viên đều bị tác động. Do đó, hiện nay Apple hỗ trợ cả hai để ai thích thì bắt đầu với Swift, các dự án cũ với Objective C vẫn có thể tiếp tục được phát triển. Hai ngôn ngữ này sống chung với nhau trong một dự án vẫn được.
  2. Swift sử dụng nhiều nguyên lý từ các ngôn ngữ C++, C#, Java, các nguyên lý này khác rất nhiều so với Objective C. Do đó, để đùng một cái đổi Objective C quay 180 độ thì ai mà theo cho kịp. Cộng thêm tốn thời gian công sức của lập trình viên để học cái mới thì cũng không ổn.

Có một điểm phải làm rõ trước khi so sánh em Yến với các đàn chị C++, C# và Java. Đó là em Yến được thừa kế, phát triển lên từ C và Objective C, một nhánh rất khác so với C# và Java. Do đó, trong tài liệu của Apple, họ so em Yến với C và Objective C. Cũng chính từ điều này, nên nhiều phần Apple có vẻ ca ngợi em Yến, nhưng thực tế các ngôn ngữ lập trình khác đã hỗ trợ rồi.

Nói như vậy thì có vẻ em Yến chẳng có gì mới. Xin thưa là không đúng, vì em ấy có nhiều cái mới so với Objective C, cũng như nhiều cải tiến khá mới lạ so với C#, Java.

Thôi mình không dài dòng nữa mà sẽ đi vào từng đường cong của em í.

Cơ bản

  • Đầu tiên, em Yến tỏ ra linh hoạt trong điều nhỏ nhất của một ngôn ngữ lập trình. Đó là, dấu chấm phẩy. Dấu chấm phẩy được đại đa số các ngôn ngữ lập trình sử dụng để làm ký tự kết thúc một dòng lệnh. Còn với em Yến, dấu chấm phẩy vẫn với tác dụng là kết thúc một dòng lệnh đó. Tuy nhiên, các bạn muốn hay không muốn dùng dấu chấm phẩy đều được. Mà nếu đã có thể bỏ đi thì thôi ta bỏ đi (hãy đừng là người hoài cổ - :)).
Thực tế, thì vẫn có một số trường hợp bạn cần dùng đến dấu chấm phẩy. Ví dụ: viết nhiều lệnh trên cùng 1 dòng. Do đó, việc cho phép dùng hoặc không dùng dấu chấm phẩy là điều rất tuyệt vời.

Biến và hằng

  • Tương tự các ngôn ngữ lập trình khác, em Yến cũng dùng biến và hằng để lưu dữ liệu. Tuy nhiên, em ấy giống các ngôn ngữ dạng script hơn vì cho phép khai báo biến và hằng theo kiểu động, nghĩa là không cần xác định kiểu dữ liệu của biến và hằng (Xem ví dụ bên dưới).

Khai báo biến thì dùng từ khoá var, khai báo hằng thì dùng từ khoá let
  • Tuy nhiên, mọi người đừng nghĩ rằng em í đang dùng 1 kiểu dữ liệu chung cho tất cả. Không phải là như thế, mà em í sẽ tự động định kiểu cho biến, hằng theo kiểu dữ liệu của giá trị được gán.
Với ví dụ ở trên thì cả hai biến và hằng đều có kiểu dữ liệu là Int

Lúc này, có người sẽ nghĩ đến trường hợp khi người ta khai báo biến nhưng chưa gán giá trị thì làm sao em Yến chọn kiểu dữ liệu cho biến. Với trường hợp này, em Yến tạm thời để biến đó chưa cấp phát vùng nhớ vội (bởi vì kích thước vùng nhớ của biến phụ thuộc vào kích thước vùng nhớ mà mỗi kiểu dữ liệu hỗ trợ) chờ đến khi biến đó được gán giá trị đầu tiên thì mới định kiểu dữ liệu và cấp phát bộ nhớ cho biến.

Em Yến cũng áp dụng điều trên cho hằng. Tức là, không giống các ngôn ngữ khác, em Yến cho phép khai báo hằng mà không cần phải khởi gán ngay giá trị của hằng, giá trị của hằng có thể được gán ngay cả khi chương trình đang chạy. Nhưng sau khi đã gán giá trị khởi tạo cho hằng thì không thể thay đổi giá trị của hằng được nữa. Điều này là hữu ích vì thực tế có nhiều hằng số chỉ khi chạy chương trình thì mới xác định được giá trị.

Tuy nhiên, bạn vẫn có thể xác định kiểu dữ liệu cho biến, hằng ngay khi khai báo sử dụng cú pháp như ví dụ sau:

  • Em Yến được thiết kế để trở thành một ngôn ngữ an toàn, nên em í chỉ cho sử dụng biến, hằng sau khi đã khởi gán giá trị cho biến, hằng. Nếu bạn cố sử dụng biến, hằng khi nó chưa được khởi gán thì chương trình sẽ phát sinh lỗi. Điều này là tương tự C#, khác so với C, C++.
  • Để chèn giá trị của biến và hằng vào chuỗi, em Yến hỗ trợ kỹ thuật tương tự token giữ chỗ của C#, nhưng thay vì đánh số thứ tự các token giữ chỗ như C# thì em Yến cho phép dùng thẳng tên biến, hằng luôn (như ví dụ dưới đây).

Kỹ thuật của em Yến có một vài điểm mạnh hơn so với token giữ chỗ. Đầu tiên, token giữ chỗ của C# thì bắt buộc phải thông qua hàm Format của lớp String hoặc hàm Write và WriteLine của lớp Console. Còn bên em Yến thì cú pháp tự nhiên hơn rất nhiều. Thứ hai, việc dùng tên của biến thay cho các số 0, 1, 2... sẽ giúp cho lập trình viên dễ hiểu, dễ kiểm soát câu lệnh hơn.

Kiểu dữ liệu

  • Em Yến cung cấp đầy đủ các kiểu dữ liệu cơ bản như : Int, Double, Float, Bool, String, Character
  • Với em Yến, tất cả các kiểu dữ liệu (ngoại trừ kiểu lớp) đều là kiểu giá trị. Duy nhất kiểu lớp là kiểu tham chiếu. Như vậy, kiểu String, Array, Dictionary, Enum, Structure đều là kiểu giá trị. Đây là một khác biệt rất lớn so với các ngôn ngữ khác và nó tạo ra nhiều thuận tiện, dễ dàng trong việc xử lý cho người lập trình.

Kiểu giá trị là kiểu mà khi thực hiện gán hoặc truyền tham số cho hàm thì giá trị của biến, hằng sẽ được sao chép. Do đó, một biến mảng khi được gán thì với em Yến nó sẽ sao chép cả mảng sang biến mới.

Kiểu tham chiếu là kiểu mà khi thực hiện gán hoặc truyền tham số cho hàm thì địa chỉ của biến sẽ được sao chép.

  • Giống C# và Java, em í cung cấp đầy đủ các kiểu dữ liệu như Int32, UInt8, Int16, UInt64, Double, Float,... Tuy nhiên, khi làm việc với số nguyên, nếu bạn không quan tâm đến kích thước vùng nhớ lắm thì nên dùng kiểu dữ liệu toàn năng được em Yến cung cấp là : Int, UInt. Các kiểu dữ liệu này là các kiểu dữ liệu thông minh, nó sẽ tự thay đổi kích thước theo nền tảng mà bạn đang chạy chương trình - tức là, nếu nền tảng là 32 bit thì kích thước sẽ là 32 bit, trên nền tảng 64 bit thì kích thước sẽ là 64 bit.
  • Em Yến là an toàn kiểu, nên nó sẽ không cho phép bạn gán một số nguyên cho một biến kiểu String, hoặc gán một ký tự (kiểu Character) cho một biến kiểu Int (như C, C++). Em í còn an toàn hơn nữa ở chỗ, em loại bỏ cú pháp ép kiểu ngầm định.
Em í bắt các anh phải ép kiểu một cách rõ ràng (ép kiểu tường minh).

Cú pháp ép kiểu tường mình của em í cũng rất khác biệt (khác so với C, C++, Objective C, C#, Java), xem ví dụ bên dưới. 

Thật ra, đây là cú pháp gọi hàm cấu tử hay còn gọi là hàm cấu tử chuyển đổi kiểu.

Đỡ tốn công phải biên dịch câu lệnh
  • Để cho dễ đọc các số lớn, em í hỗ trợ cách phân tách phần ngàn như sau :

Các anh thấy dễ đọc chưa ?
  • Em í hỗ trợ tạo bí danh cho các kiểu dữ liệu. Điều này rất hay vì nó sẽ cung cấp các tên kiểu dữ liệu mới phù hợp hơn với ngữ cảnh. Ví dụ: bạn dùng tên kiểu dữ liệu là điểm để tạo các biến chứa điểm thì sẽ dễ hiểu hơn là bạn khai báo các biến đó với kiểu dữ liệu là UInt8.

typealias Diem = UInt8

var diemToan : Diem

Kiểu String và kiểu ký tự

  • Điểm khác biệt đáng quan tâm nhất về String là khả năng sửa đổi (mutability). Trong Objective C cũng như C#, các chuỗi đều là chuỗi bất di bất dịch (immutable), có nghĩa là sau khi gán một chuỗi cho biến thì chúng ta không thể chèn thêm ký tự, xoá ký tự, thay đổi ký tự trong chuỗi đó. Nếu bạn muốn làm những việc như vậy, bạn phải dùng kiểu NSMutableString (của Objective C) hay kiểu StringBuilder (của C#). Điều này đôi lúc gây ra sự bất tiện và làm gia tăng việc xử lý chuỗi. Còn với em Yến, việc mutable hay immutalbe được quyết định thông qua bạn gán chuỗi cho biến hay hằng. Nếu bạn gán chuỗi cho hằng thì chuỗi đó là immutable và bạn gán cho biến thì bạn có thể sửa đổi chuỗi đó tuỳ thích. Một cách xử lý khá là giản dị.
  • Như ở phần trên đã đề cập tới, String của em Yến là kiểu giá trị và khi bạn thực hiện gán hoặc truyền tham số thì chuỗi sẽ được sao chép. Cách xử lý này gần với tự nhiên hơn, nên người lập trình sẽ dễ dàng hơn để sử dụng. Tuy nhiên, có người sẽ bảo rằng như vậy thì sẽ tốn hiệu năng xử lý hơn rất nhiều (vì sao chép luôn cả chuỗi như vậy vừa tốn bộ nhớ, vừa tốn hiệu năng xử lý sao chép chuỗi). Em Yến đã xử lý vấn đề này ngầm bên dưới bằng cách : đầu tiên, vẫn chỉ gán địa chỉ (không sao chép nguyên chuỗi ra vùng nhớ khác), chỉ cho đến khi gặp dòng lệnh thực hiện sửa đổi nội dung của chuỗi thì chuỗi đó mới được sao chép ra vùng nhớ mới và các thao tác sửa đổi được thực hiện trên vùng nhớ này.
  • Có thể dùng phép toàn + để nối chuỗi, ký tự; phép toán so sánh ==, != để so sánh hai chuỗi, hai ký tự; phép toán += để bổ sung chuỗi, ký tự vào cuối.
Bên C#, Java thì chỉ dùng các hàm

Kiểu dữ liệu Tuple

  • Kiểu tuple là một trong hai kiểu dữ liệu mới của em Yến.
  • Kiểu tuple nhóm nhiều giá trị vào bên trong một giá trị kết hợp duy nhất. Các giá trị trong tuple có thể thuộc bất kỳ kiểu dữ liệu nào và không cần phải giống nhau.

Bên dưới là câu lệnh khai báo một hằng kiểu tuple và gán một tuple gồm hai giá trị (một số nguyên, một chuỗi) cho hằng này. Tuple (404, "Not Found") nhóm một số nguyên và một chuỗi lại với nhau.

Một nguyên mẫu tuple được bao lại trong cặp dấu ngoặc

Bạn có thể tách một tuple ra các biến, hằng dùng lệnh dạng như sau :

statusCode và statusMessage là hai hằng. Sau khi thực hiện lệnh này, bạn có thể tách hai hằng này để sử dụng.

hoặc nếu bạn không muốn nhận giá trị của thành phần nào của tuple thì bạn có thể dùng dấu gạch chân để bỏ qua.

  • Bạn có thể truy xuất vào các thành phần của tuple thông qua chỉ mục :

hoặc khi gán giá trị thì bạn đặt tên cho các thành phần để sau đó sử dụng :

  • Kiểu tuple phát huy sự hiệu quả trong khá nhiều trường hợp, như với kiểu trả về của hàm, dùng với câu lệnh switch, dùng với lệnh lặp.

Ví dụ dưới đây sử dụng tuple để so sánh hai biến bool có cùng giá trị hay không :

switch (a, b) {

       case (true, true), (false, false): return true

    default: return false

}

Với hàm, thông thường, chúng ta chỉ có thể trả ra một giá trị thông qua câu lệnh return. Nếu muốn trả ra nhiều hơn thì chúng ta phải sử dụng danh sách tham số và truyền tham số theo tham chiếu (còn gọi là tham biến). Tuy nhiên, với cách dùng này thì câu lệnh khá tối nghĩa vì vừa là dùng tham số đề truyền tham số vào vừa để nhận giá trị ra. Chưa kể đến việc các biến trước khi được truyền dạng tham chiếu còn phải bắt buộc được khởi gán giá trị (ví dụ trong C#), nhưng đâu phải lúc nào bạn cũng biết giá trị để khởi tạo. Do đó, tuple là hình thức tự nhiên nhất để trả ra nhiều giá trị thông qua câu lệnh return.

  • Nếu suy nghĩ thêm chút về cách thức nào em Yến hỗ trợ tuple thì mình nghĩ em Yến dùng structure, nhưng có điều structure này hỗ trợ trường hợp khá đặc biệt là structure này không có tên.

Kiểu dữ liệu Optional

  • Đây là kiểu dữ liệu mới thứ hai
  • Bạn dùng optional khi một biến (tại thời điểm nào đó trong chu trình sống của nó) có thể không chứa giá trị nào. Khi bạn lấy giá trị của một biến optional thì bạn có thể nhận được câu trả lời kiểu như : Có chứa giá trị và nó là x hoặc Hoàn toàn không chứa giá trị nào.
  • Optional gần giống như con trỏ nil (hoặc null của C#). Tuy nhiên, nil hay null chỉ dùng với kiểu dữ liệu lớp, còn optional thì dùng với mọi kiểu dữ liệu. Có nghĩa là ngay cả một biến kiểu Int cũng có thể có lúc không nhận được giá trị nào. Em Yến cũng dùng từ khoá nil, nhưng nó không phải là con trỏ (trong C nó là con trỏ trỏ về vùng đầu tiên của bộ nhớ heap), nó chỉ là từ khoá để chỉ tình trạng không chứa giá trị.
  • Để khai báo một kiểu optional, ta thêm dấu chấm hỏi vào cuối tên kiểu dữ liệu. Ví dụ : Int?, String?
  • Có thể dùng câu lệnh if để kiểm tra xem một biến optional có chứa dữ liệu hay không bằng cách so sánh với giá trị nil:

Khi đã xác định được biến optional có chứa giá trị thì bạn thêm dấu chấm cảm vào sau tên biến để lấy giá trị của biến đó. Lấy giá trị của biến optional như thế này người ta gọi là mở gói ép buộc (forced unwrapping).

Bạn có thể sử dụng cú pháp Optional Binding để vừa kiểm tra sự tồn tại của giá trị vừa lấy giá trị ra nếu có.

Nếu có giá trị thì biểu thức điều kiện sẽ nhận được giá trị true và giá trị của biến possibleNumber sẽ được gán cho hằng actualNumber.

Vị trí từ khoá let có thể thay bằng var.

Optional Binding có thể dùng trong cả vòng lặp while.

  • Trong một số trường hợp, bạn biết rằng một biến optional sẽ luôn có giá trị sau khi được khởi gán. Lúc này, việc kiểm tra và mở gói là không cần thiết nữa. Do đó, bạn có thể sử dụng kiểu optional mở gói ngầm định (implicit unwrapped optional). Để khai báo, bạn dùng dấu ! thay cho dấu hỏi. Ví dụ : Int!

Để lấy giá trị của biến optional mở gói ngầm định thì không cần dấu chấm cảm theo sau.

Trường hợp bạn sẽ dùng kiểu optional mở gói ngầm định : Quan hệ giữa thủ đô (capital city) và nước (country). Vì mỗi nước có 1 thủ đô, 1 thủ đô phải thuộc về một nước. Do đó, sau khi khởi tạo xong một nước thì sẽ có một liên kết (không nil) đến một thủ đô và ngược lại.

  • Objective C là ngôn ngữ thuần hướng đối tượng - có nghĩa là mọi thứ trong Objective C đều là đối tượng (không như các ngôn ngữ hướng đối tượng khác như C#, Java thì vẫn có cái không phải là đối tượng). Ngay cả con trỏ nil cũng là đối tượng. Bởi vì, nó là đối tượng nên có thể truyền thông điệp cho nó. Em Yến được phát triển lên từ Objective C, nhưng để gần với C#, Java, nó đã chuyển sang dùng nguyên lý lời gọi hàm. Tuy nhiên, để khắc phục việc gọi hàm không có, kiểu optional và kỹ thuật optional chaining là giải pháp cực kỳ hữu hiệu.
Trong Objective C, người ta dùng nguyên lý truyền thông điệp chứ không phải là lời gọi hàm. Hai nguyên lý này rất khác biệt và nó ảnh hưởng đến cách thức hoạt động của hệ thống. Theo lời gọi hàm, thì hàm đó bắt buộc phải có, chương trình mới có thể gọi. Nếu không có thì chương trình bị lỗi. Còn theo nguyên lý truyền thông điệp thì dù thông điệp chưa được cụ thể hoá bằng hàm xử lý thì thông điệp đó sẽ vẫn được thụ lý bằng hàm khác (hàm xử lý chung). Do đó, với nguyên lý truyền thông điệp thì chương trình sẽ không bao giờ phát sinh lỗi.

Dùng dấu chấm hỏi đặt sau biến optional để kiểm tra và mở gói.

Toán tử

  • Phép gán = không trả ra giá trị, nên không gán giá trị cho nhiều biến đồng thời như trong C# được. Việc ngăn không cho phép gán trả ra giá trị cũng giúp các biểu thức điều kiện đỡ phức tạp, đỡ sai sót hơn.
Ví dụ: bạn muốn dùng phép so sánh ==, nhưng lở tay chỉ chấm 1 dấu bằng thì trong C, C++ câu lệnh đó vẫn đúng và không báo lỗi
  • Toán tử % có thể dùng cho cả số thực và số âm
  • Các phép toán số học (+, -, *, /) mặc định không xử lý trường hợp tràn số. Nếu tràn số thì chương trình sẽ phát sinh lỗi.
Tràn số là khi mà giá trị gán cho biến, hằng vượt quá phạm vi lưu trữ của kiểu dữ liệu của biến, hằng đó.

Trong đại đa số các ngôn ngữ lập trình, thì nếu tràn số xảy ra, giá trị của biến, hằng sẽ nhận được là giá trị nhỏ nhất trong miền giá trị của kiểu dữ liệu của biến, hằng đó.

  • Để xử lý trường hợp tràn số, em Yến hỗ trợ 5 toán tử xử lý tình huống tràn số là &+, &-, &*, &/, &%. Bên dưới là ví dụ sử dụng phép toán &+.

Phép toán &/ đã xử lý cả trường hợp chia cho 0.

  • Toán tử ?? (Nil Coalescing) được dùng để mở gói một biến kiểu optional nếu biến đó có giá trị, nếu không sẽ trả ra giá trị mặc định (nằm sau toán tử ??).

  • Toán tử phạm vi ..< và ... Ví dụ, ta viết a...b để lấy các giá trị trong đoạn từ a đến b. Ta viết a..<b để lấy các giá trị từ a đến cận dưới của b. Hai toán tử này khá hữu ích, có thể dùng thay thế cho các cú pháp câu lệnh for đã cũ và không rõ ràng.

Array và Dictionary

  • Giống như Objective C, em Yến vẫn chỉ cung cấp hai kiểu collection là Array và Dictionary.

Array (còn gọi là mảng) lưu trữ một danh sách có thứ tự các giá trị có cùng kiểu. Các giá trị giống nhau có thể xuất hiện nhiều lần tại những vị trí khác nhau.

Dictionary lưu trữ một tập không thứ tự các giá trị cùng kiểu mà nó có thể được tham chiếu và tìm kiếm thông qua một định danh duy nhất (hay còn gọi là khoá). Dictionary chính là kiểu tập hợp Map hoặc Hashtable trong C# và các ngôn ngữ khác.

Bạn dùng dictionary khi bạn cần tìm kiếm các giá trị dựa trên định danh (khoá) của chúng, giống như cách bạn tìm kiếm định nghĩa của các từ trong một quyển từ điển.

Objective C chỉ cung cấp đúng hai kiểu collection này. Các kiểu collection như List, Stack, Queue là không có. Nhưng trong XCode, người ta dùng hai kiểu này khá linh hoạt để thay thế cho những kiểu thiếu trên. Có trường hợp khá thú vị và hay là người ta dùng Dictionary để tổ chức dữ liệu cho các TableView. Mỗi hàng là một thành phần trong Dictionary được phân biệt với nhau thông qua key là id của hàng dữ liệu. Việc tổ chức bằng Dictionary như trên mạnh mẽ và linh hoạt hơn tổ chức bằng Array rất nhiều.

  • Array và dictionary của em Yến phải được định kiểu rõ ràng. Khác so với Objective C là kiểu nào cũng chơi.
  • Thật sự thì array và dictionary của em Yến được cài đặt là các tập hợp generic (xem phần Khuôn mẫu (generic) bên dưới). Do đó, nó có hiệu năng cao hơn so với array và dictionary của Objective C rất nhiều.
  • Tương tự như kiểu string thì array và dictionary là có thể sửa đổi (mutable) khi được chứa trong biến và không thể sửa đổi (immutable) khi được chứa trong hằng. Điều này giải phóng người lập trình ra khỏi cái mớ bòng bong giữa NSArray và NSMutableArray hay NSDictionary và NSMutableDictionary của Objective C.
  • Các thao tác thêm, sửa, xoá của em Yến khá là tự nhiên. Bạn có thể dùng phép toán += để thêm một hoặc vài thành phần mới vào array hoặc dictionary. Bạn có thể dùng toán tử phạm vi để sửa đổi cùng lúc cả nhóm các thành phần liền kề nhau (xem ví dụ dưới).

  • Để duyệt qua các thành phần của array, dictionary thì bạn dùng lệnh for-in.

Câu lệnh trên giống lệnh foreach của C#, nhưng mạnh hơn một xí. Ví dụ :

Sử dụng tuple và hàm enumerate để lấy cả giá trị và vị trí của giá trị. Hữu ích hơn việc dùng câu lệnh for duyệt qua từng thành phần, vì nó không rõ ràng, dễ hiểu.
  • Trong thực tế thì string, array và dictionary được cài đặt sử dụng struct (xem phần Class và structure bên dưới để hiểu về struct). Điều đó có nghĩa rằng chúng sẽ được sao chép khi thực hiện gán cho một biến, hằng mới, hoặc khi được truyền tham số cho hàm.

Hành vi này là khác biệt so với các kiểu này trong Objective C, cũng như C#, Java.

Các cấu trúc điều khiển

  • Em Yến cung cấp đầy đủ các câu lệnh điều khiển if, switch, for, while, do.
  • Các biểu thức điều kiện không cần đặt trong cặp dấu ngoặc, nhưng cặp dấu ngoặc nhọn để bao phần thân là bắt buộc.
Mình thấy điều này cũng như không và đôi lúc không hữu ích. Ví dụ trường hợp phần thân chỉ có 1 câu lệnh, nhưng vẫn phải bắt buộc có cặp dấu ngoặc nhọn.
  • Các biểu thức điều kiện phải có kết quả kiểu bool. Điều này là an toàn hơn, giống với C#, Java, nhưng khác so với C.
  • Câu lệnh switch là câu lệnh có nhiều cải tiến nhất trong các câu lệnh điều khiển và nó có cú pháp chung như sau :

Thứ nhất, một case có thể xử lý cho nhiều giá trị (khác với C#). Nếu nhiều case thoả điều kiện thì case đầu tiên (ở trên cùng) sẽ được lựa chọn thực hiện.

Thứ hai, không cần câu lệnh break vì mặc định em Yến không cho phép tự chạy xuống case tiếp theo mà sẽ thoát ra khỏi câu lệnh switch. Điều này ngăn người lập trình khỏi các lỗi do quên câu lệnh break (Nhưng nếu muốn bạn vẫn có thể dùng câu lệnh break ở đây). 

Thứ ba, mỗi case phải có ít nhất một câu lệnh thực thi nào đó. 

Thứ tư, có thể dùng toán tử phạm vi để so khớp một dãy giá trị liên tục. 

Thứ năm, có thể dùng kiểu tuple để mở rộng khả năng của switch.

Thứ sau, có thể dùng value binding trong switch. Ví dụ: 

Cuối cùng và mạnh mẽ nhất là nó còn hỗ trợ mệnh đề where. Ví dụ:

  • Câu lệnh for-in là câu lệnh mới được bổ sung trong nhóm các câu lệnh lặp. Câu lệnh này giống câu lệnh foreach của C#, nhưng mạnh mẽ hơn. Vì một số phần ở trên cũng đã đề cập đến câu lệnh này rồi, nên ở đây chỉ nhắc lại cách dùng đơn giản nhất của for-in, như sau:

index trong câu lệnh trên là một hằng. Bạn không cần phải khai báo trước khi sử dụng, em Yến đã tự động làm cho bạn rồi. Hằng này chỉ tồn tại bên trong vòng lặp for-in đó.

Bạn có thể bỏ qua các index trên sử dụng dấu gạch chân như sau:

  • Em Yến vẫn hỗ trợ hai câu lệnh break và continue. Ngoài ra, còn hỗ trợ thêm câu lệnh fallthrough (trong câu lệnh switch) để cho phép thực thi xuống tiếp case tiếp theo, giống như cách làm việc của switch của các ngôn ngữ C, C++, C#.
  • Em Yến hỗ trợ một thứ gọi là labelled statement. Cái này nó giống như nhãn và lệnh goto trong C, C++. Tuy nhiên, labelled statement được dùng trong các trường hợp như : khi bạn có nhiều vòng lặp lồng nhau, hoặc khi bạn có các câu lệnh lặp lồng trong câu lệnh switch,... Trong các trường hợp này, việc câu lệnh break, continue có thể xác định được thoát ra khỏi vòng lặp, câu lệnh switch nào thì sẽ hữu dụng hơn. Như vậy cách dùng labelled statement an toàn hơn so với lệnh goto của C, C++ rất nhiều.

Hàm

Hàm là một nhóm các dòng lệnh được nhóm lại trong một tên hàm, nhằm thực hiện một nhiệm vụ cụ thể nào đó. Ví dụ: hàm để tính tổng hai số nguyên, hàm để in một số nguyên dưới dạng số nhị phân, hàm để tìm kiếm một số trong mảng các số,...
  • Cú pháp khai báo hàm như sau :

Đến ngang đây thì mọi người gần hiểu vì sao khai báo kiểu dữ liệu thì cú pháp lại đặt kiểu dữ liệu nằm sau tên biến.
  • Có thể dùng kiểu tuple làm kiểu của tham số cũng như giá trị trả về.
  • Với em Yến, tham số của hàm theo khai báo thông thường được gọi là tham số cục bộ (local parameter name). Các tham số này chỉ được dùng bên trong phạm vi của hàm. Ngoài ra, em í còn hỗ trợ tham số ngoài (external parameter name) được dùng khi gọi hàm. Mục đích thứ nhất của nó là để chỉ định chức năng của các giá trị khi truyền cho hàm, mục đích thứ hai là để lời gọi hàm gần giống như một câu văn.
Cái này tương tự như tham số đặt tên trong C#, nhưng mục đích của C# là để cho phép việc truyền tham số không cần theo thứ tự trong định nghĩa hàm, chứ không phải để lời gọi hàm trông giống một câu văn.

string, toString, withJoiner là các tên tham số ngoài, còn s1, s2, joiner là tên tham số cục bộ dùng trong hàm. Lúc này, lời gọi hàm sẽ gần như một câu văn “Nối chuỗi hello vào chuỗi world với từ nối là “,””. Lời gọi hàm lúc này rõ ràng, dễ đọc, dễ hiểu hơn.

Một đặc điểm nổi bật và khác người của Objective C chính là những lời gọi hàm (Objective C gọi là truyền thông điệp). Nó khác biệt bởi vì thứ nhất nó theo nguyên lý truyền thông điệp, thứ hai các lời gọi hàm giống như một câu văn (hoặc có thể diễn đạt dễ dàng thành câu văn mà chỉ cần thêm rất ít từ bổ trợ vào). Ví dụ với lời gọi hàm [counter incrementBy: 5 numberOfTimes: 3]; có thể diễn đạt thành "Biến counter tăng lên 5 đơn vị 3 lần liên tục".

Em Yến cũng được thiết kế giữ lại những điểm mạnh này. Tuy nhiên, cặp dấu ngoặc trong lời gọi hàm làm cho câu văn không được đẹp lắm.

  • Em Yến cung cấp dạng tham số biến đổi (variadic parameters) tương tự như tham số param trong C#. Xem ví dụ để hiểu.
  • Các tham số của hàm mặc định được xem là các hằng. Trình biên dịch sẽ báo lỗi khi bạn cố gắng thay đổi giá trị của các tham số này. Đây là điều khá đặc biệt.
Trong các ngôn ngữ khác thường dùng khái niệm truyền tham trị và truyền tham biến (tham chiếu) (như Pascal, C, C#, Java). Tuy nhiên, các ngôn ngữ này dùng các biến để nhận giá trị (với truyền theo tham trị) nên vẫn giá trị có thể bị thay đổi mà trình biên dịch không phát sinh lỗi.

Khi bạn muốn giá trị truyền vào cho một biến thì bạn thêm từ khoá var vào phía trước tên tham số.

  • Em Yến cũng hỗ trợ dạng truyền tham chiếu (tham biến) thông qua tham số in-out. Sử dụng tham số này bằng cách thêm từ khoá inout ở phía trước tên tham số.

Kiểu hàm
  • Mỗi hàm đều có một kiểu dữ liệu gọi là kiểu hàm và được kết hợp từ kiểu của các tham số và kiểu trả về. 

Hai hàm trên đều có chung kiểu hàm là (Int, Int) -> Int
  • Kiểu hàm này có thể sử dụng như các kiểu dữ liệu khác của em Yến, có nghĩa là có thể khai báo biến, hằng, dùng làm kiểu của tham số, kiểu trả về của hàm,…
Kỹ thuật bên dưới của cái này gọi là con trỏ hàm, nhưng với em Yến thì dễ hiểu và dễ sử dụng hơn.

Ví dụ trên tạo ra một biến kiểu hàm (Int, Int) -> Int có tên là mathFunction và gán hàm addTwoInts cho nó.

Sau đó, có thể dùng biến mathFunction để gọi hàm.
  • Kiểu hàm chính là cơ sở cho các kỹ thuật như delegate và event (sự kiện)

Closure

  • Closure giống với inline function của Pascal, C, C++, giống lambda của C# và chính là block trong Objective C.
  • Closure là dạng hàm đặc biệt mà phần cài đặt của nó nằm ngay trong lời gọi hàm đến một hàm khác. Closure không có tên và thường là các hàm nhỏ (chỉ 1, 2 dòng lệnh). Closure có thể truy xuất các hằng, biến nằm trong ngữ cảnh nơi nó được định nghĩa.
  • Để hiểu rõ cách dùng closure thì chúng ta nên đi qua ví dụ ngắn sau. Đầu tiên, chúng ta có mảng sau:

Chúng ta dùng hàm sorted để sắp xếp mảng này. Hàm này nhận hai tham số :

  1. Mảng cần sắp xếp,
  2. Đối số kiểu hàm (<kiểu thành phần>, <kiểu thành phần>) -> Bool

Đối số kiểu hàm này nhận một hàm/closure có cùng kiểu nhằm chỉ định cách so sánh hai thành phần trong mảng, hàm trả ra true nếu thứ tự sắp xếp là đúng, false nếu thứ tự sắp xếp không đúng. <kiểu thành phần> là kiểu thành phần của mảng. Với ví dụ này thì kiểu hàm sẽ là (String, String) -> Bool.

Chúng ta viết đoạn code sau để sắp xếp:

Hàm backwards định nghĩa cách sắp xếp giảm dần. Dòng lệnh 4 gọi hàm sorted và truyền tên hàm backwards vào.

Bây giờ, chúng ta thay hàm backwards bằng closure, thì đoạn code sẽ thành như sau:

Phần nằm trong cặp dấu ngoặc nhọn chính là closure và nó tuân theo cú pháp sau:

hàm backwards và closure này có phần xử lý giống nhau, khác nhau là một bên cài đặt riêng, một bên cài đặt nằm ngay trên dòng lệnh gọi hàm sorted. Thứ hai, là closure không cần tên hàm.

Em Yến cho phép trích kiểu dữ liệu từ ngữ cảnh - tức là ở ví dụ trên, bởi vì mảng names có kiểu thành phần là String, nên đương nhiên hai tham số đầu vào của closure phải có kiểu là String, còn kiểu trả về thì do hàm sorted đã quy định là Bool rồi. Do đó, lệnh gọi hàm sorted trên có thể viết đơn giản như sau:

Câu lệnh return có vẻ cũng không cần thiết, nên có thể viết thành:

Hoặc dùng cú pháp tên đối số dạng ngắn (shorthand argument name), câu lệnh trên có thể viết thành:

Hoặc dùng hàm toán tử (operator function), câu lệnh sẽ thành:

Cú pháp khai báo closure bên trong hàm có vẻ hơi khó hiểu (khác biệt). Do đó, có thể sử dụng dạng closure treo (trailing closure) như sau :

  • Closure còn được dùng để tính toán giá trị khởi tạo cho các thuộc tính (Xem phần Thuộc tính ở dưới để hiểu thuộc tính là gì).

Kỹ thuật liệt kê (enumeration)

Từ enumeration có nghĩa là liệt kê. Nhiều sách dạy lập trình dùng từ kiểu liệt kê để dịch từ này. Tuy nhiên, đây không phải là một kiểu vì bản thân nó đứng trơ trọi thì không phải là một kiểu. Nó chỉ là một cách thức để tạo ra các kiểu dữ liệu mới. Cho nên, mình tự ý đặt tên lại cho nó thành Kỹ thuật liệt kê. :)

Còn khi muốn nói đến enumeration type thì mới dịch là kiểu liệt kê và từ này để chỉ chung cho các kiểu được tạo ra bằng kỹ thuật liệt kê này.

  • Một kiểu liệt kê định nghĩa một kiểu chung cho một nhóm các giá trị có liên quan với nhau. Ví dụ, kiểu thứ để quản lý các thứ trong tuần, kiểu tháng để quản lý tên các tháng, kiểu nhóm thuốc để quản lý tên của các nhóm thuốc...

Cú pháp khai báo một kiểu liệt kê của em Yến có hơi khác một chút. Nó yêu cầu phải có từ case đứng trước mỗi dòng, tên đi theo sau case là giá trị thành phần của kiểu liệt kê.
Bạn có thể viết cách giá trị thành phần trên cùng một dòng, cách nhau bằng dấu phẩy.
  • Mỗi định nghĩa kiểu liệt kê sẽ tạo ra một kiểu dữ liệu mới. Do đó, bạn có thể dùng kiểu mới đó để khai báo biến, hằng, khai báo kiểu của tham số.

Khai báo biến directionToHead kiểu CompassPoint và nhận giá trị West.
  • Trong C, Objective C, C#, trình biên dịch gán cho mỗi tên trong kiểu liệt kê một giá trị số nguyên (0, 1, 2,...). Còn em Yến thì linh hoạt hơn, không cần thiết phải cung cấp một giá trị cho mỗi thành phần của kiểu liệt kê. Còn nếu muốn cung cấp một giá trị cho mỗi thành phần (em Yến gọi giá trị này là giá trị thô - raw value) thì giá trị đó có thể là một chuỗi, một ký tự, một số.
  • Em Yến còn linh hoạt hơn nữa khi hỗ trợ khả năng gắn thêm các giá trị vào các thành phần đã được định nghĩa của kiểu liệt kê. Có nghĩa là các giá trị này được gắn thêm vào kiểu liệt kê sau khi đã định nghĩa kiểu liệt kê. Xem ví dụ bên dưới.

Khai báo kiểu liệt kê Barcode với hai giá trị thành phần là UPCA và QRCode. Mỗi thành phần có thể gắn thêm các giá trị kiểu Int hoặc String.

Khai báo biến productBarcode và nó nhận giá trị là một barcode của một sản phẩm nào đó. Khá là thú vị với cách dùng này nhỉ !
  • Với em Yến, kiểu liệt kê còn có những tính năng mạnh như kiểu lớp, như : thêm các thuộc tính tính toán (computed property) - để cung cấp thông tin về giá trị hiện tại của kiểu liệt kê, thêm các hàm thành phần - để cung cấp chức năng cho giá trị mà kiểu liệt kê đang thể hiện, định nghĩa thêm cấu tử để cung cấp giá trị khởi tạo, có thể mở rộng tính năng của kiểu liệt kê đã có bằng cách áp dụng các protocol hoặc sử dụng kỹ thuật mở rộng (extension). Các đặc tính này sẽ được nói rõ bên dưới.

Class và structure

  • Lớp (class) và struct (structure) là các cấu trúc linh hoạt, cho phép bạn tổ chức ra các kiểu dữ liệu mới bằng cách thêm vào các thuộc tính, hàm chức năng,...
  • Em Yến không yêu cầu bạn tách phần khai báo và cài đặt ra hai file như đa số các ngôn ngữ lập trình (C++, Objective C).
  • Giống trong C#, Java, struct của em Yến có nhiều tính năng của lớp, như : thuộc tính, hàm thành phần, chỉ mục, cấu tử, cài đặt giao diện và sử dụng kỹ thuật mở rộng (extension). Tuy nhiên, struct của em Yến cũng giống các ngôn ngữ khác ở chỗ nó thuộc kiểu giá trị (Xem phần Kiểu dữ liệu để hiểu kiểu giá trị).
Thực tế thì các kiểu dữ liệu như Bool, Int, Double, string, array, dictionary của em Yến đều được tạo ra sử dụng struct.
  • Cú pháp khai báo lớp và struct khá giống nhau, ví dụ :

width, height, resolution,... không được gọi là biến thành phần, mà em Yến gọi là thuộc tính lưu trữ (stored property). Có tên này cũng vì mục đích của thuộc tính này là lưu trữ dữ liệu, tên này cũng để phân biệt với các loại thuộc tính khác. Sau này, mình sẽ gọi tắt thuộc tính lưu trữ bằng từ thuộc tính.

Với em Yến, bạn không cần phải tạo biến thành phần để lưu trữ dữ liệu, rồi sau đó mới tạo các thuộc tính để quy định cách truy xuất đến các thành phần. Bạn chỉ cần tạo thuộc tính, em Yến sẽ tự động lo phần biến thành phần cho bạn luôn rồi.

Trong C#, bạn có thể dùng thuộc tính tự động để tạo ra các thuộc tính mà không cần tạo biến thành phần. Các biến thành phần của các thuộc tính này cũng được tạo ngầm bên dưới cho bạn và nó là ẩn. Cho nên, bạn không thể truy xuất vào các biến thành phần này. Do đó, bạn không thể áp dụng các kiểm tra dữ liệu (business rules) cho các thuộc tính này được. Với em Yến, bạn có thể kiểm tra dữ liệu cho các thuộc tính này.

  • Các thuộc tính này có thể là biến hoặc hằng bằng cách dùng từ khoá var hoặc let, có thể khởi gán giá trị cho chúng và có thể truy xuất bằng cách dùng toán tử dấu chấm.

Như vậy, trong em Yến không có khái niệm khả năng truy xuất (private, public, protected) như đại đa số các ngôn ngữ lập trình. Thật ra là có những nó được bổ sung sau này và khác hơi nhiều so với khái niệm gốc. Xem phần Khả năng truy xuất ở dưới cùng bài này.
  • Các struct được cung cấp hàm cấu tử mặc định và có thể dùng tên các thuộc tính để xác định giá trị truyền vào thuộc về thuộc tính nào của struct, ví dụ:

  • Lớp là kiểu tham chiếu duy nhất trong em Yến. Tham chiếu ở đây cũng tương tự như con trỏ trong Pascal, C, C++, Objective C. Tuy nhiên, khi khai báo thì không cần dùng toán tử *.
Như vậy, em Yến đã gần với chị C# rồi.
  • Em Yến hỗ trợ toán tử === và !== để kiểm tra xem hai biến/hằng có tham chiếu hay không tham chiếu đến cùng một đối tượng.
Điều này đã loại bỏ sự nhập nhằng của phép toán == là để kiểm tra giống nhau về nội dung hay cùng tham chiếu đến một đối tượng trong các ngôn ngữ lập trình khác.

Thuộc tính (property)

  • Thuộc tính là nơi để lưu trữ dữ liệu trong các lớp, struct và kiểu liệt kê.
  • Em Yến hỗ trợ hai loại thuộc tính : thuộc tính lưu trữ (stored property), thuộc tính tính toán (computed property). 
  • Thuộc tính lưu trữ (viết tắt là thuộc tính), như đã nói ở trên, là nơi để lưu trữ dữ liệu bên trong các đối tượng của lớp, struct. Kiểu liệt kê không hỗ trợ thuộc tính lưu trữ. Thuộc tính lưu trữ có thể là biến hoặc hằng. Bạn có thể cung cấp giá trị mặc định cho các thuộc tính lưu trữ ngay trong định nghĩa của thuộc tính. Giá trị của thuộc tính có thể thay đổi trong quá trình khởi tạo (trong hàm cấu tử) ngay cả khi thuộc tính đó là hằng.
  • Còn thuộc tính tính toán là các thuộc tính trả ra các giá trị sử dụng các dữ liệu đã có (từ các thuộc tính lưu trữ, hoặc từ công thức tính toán). Thuộc tính này thực tế là không lưu trữ dữ liệu. Nó cung cấp một getter và một setter tuỳ chọn để lấy và gán giá trị cho các thuộc tính một cách gián tiếp.

center là thuộc tính tính toán, newValue là từ khoá mặc định sẽ nhận giá trị ở vế phải của phép gán cho thuộc tính tính toán (bạn có thể thay đổi tên newValue này thành bất kỳ tên nào bạn muốn).

Cú pháp của getter và setter gần như giống với C#. Tuy nhiên, chỉ có setter là tuỳ chọn, còn getter thì luôn luôn phải được cài đặt. Trong trường hợp setter không được cung cấp thì có thể viết lại thuộc tính tính toán như ví dụ sau:

  • Kiểu liệt kê có thể cài đặt các thuộc tính tính toán.
  • Em Yến còn cung cấp thêm một loại thuộc tính khác, gọi là thuộc tính lưu trữ lười (lazy stored property). Đây là thuộc tính lưu trữ mà giá trị khởi tạo của nó chỉ được tính toán khi thuộc tính đó được truy xuất lần đầu tiên. Thuộc tính loại này phù hợp cho các thuộc tính chứa các đối tượng phụ thuộc vào các nhân tố bên ngoài, ví dụ : đối tượng chứa liên kết mạng, đối tượng đọc ghi file,... Để sử dụng thì thêm từ khoá lazy như ví dụ dưới đây.

  • Giá trị khởi tạo của thuộc tính lưu trữ có thể là kết quả của một hàm hoặc một closure.

Thuộc tính boardColors được khởi gán bằng closure.

Cặp dấu mở ngoặc, đóng ngoặc ở cuối closure cần phải có để trình biên dịch thực thi ngay closure khi được gán. Nếu không có boardColors sẽ có kiểu hàm và nhận giá trị là closure đó.

Bên trong closure hoặc hàm dạng này, bạn không thể truy xuất vào các thuộc tính khác, thuộc tính self, cũng như các hàm thành phần.

Bộ theo dõi thuộc tính (property observer)

  • Bộ theo dõi thuộc tính theo dõi và phản ứng lại những thay đổi trong giá trị của các thuộc tính. Bộ theo dõi sẽ được thực thi mỗi khi giá trị thuộc tính được gán, thậm chí cả khi giá trị mới giống giá trị cũ.
Đây là tính năng khá đặc sắc. Nó giống với các trigger trong cơ sở dữ liệu. Nó cũng giống với event (sự kiện) và delegate trong nhiều ngôn ngữ lập trình (C#, Java). Tuy nhiên, bộ theo dõi này chỉ hoạt động cho thuộc tính, event thì rộng hơn theo dõi cả những tương tác trên hệ thống. Thêm nữa, bộ theo dõi là tự động, còn event là phải tự phát sinh.
  • Bạn có thể thêm bộ theo dõi vào các thuộc tính lưu trữ, vào bất kỳ các thuộc tính nào (kể cả thuộc tính tính toán) được thừa kế từ lớp cha bằng cách nạp chồng thuộc tính ở lớp con. Xem phần Thừa kế ở dưới để hiểu về thừa kế và nạp chồng.
  • Em Yến cung cấp hai loại bộ theo dõi là: willSet và didSet. willSet được gọi trước khi giá trị được gán, didSet được gọi sau khi giá trị được gán.

  • Nhờ bộ theo dõi này, bạn có thể kiểm tra dữ liệu được gán vào cho các thuộc tính. Vì vậy, thuộc tính của em Yến mạnh mẽ hơn so với thuộc tính trong C# khá nhiều.

Thuộc tính cấp kiểu (type property)

Trong các ngôn ngữ như C++, C#, Objective C, chỉ hỗ trợ tính năng này trong lớp, nên người ta còn gọi chúng là thành phần cấp lớp. Em Yến thì hỗ trợ cho cả struct và kiểu liệt kê, nên không thể dùng từ thuộc tính cấp lớp được mà phải dịch là thuộc tính cấp kiểu.

Từ kiểu ở đây là viết tắt của từ kiểu dữ liệu.

  • Trong lập trình hướng đối tượng, đối tượng là đối tượng rồi và các kiểu cũng cần phải được xem như một đối tượng. Đối tượng có thuộc tính, hàm thành phần, thì kiểu cũng phải có thuộc tính và hàm thành phần của kiểu. Người ta gọi là thuộc tính cấp kiểu và hàm thành phần cấp kiểu.

Từng đối tượng có thông tin riêng của đối tượng thì kiểu dữ liệu cũng có thông tin riêng của kiểu.

Trong C++, C#, các thành phần được tạo ra với từ khoá static chính là các thành phần cấp kiểu. Muốn truy xuất các thành phần này thì phải thông qua kiểu, tức là viết <tên kiểu> + "." + <tên thành phần>. Ví dụ : Console.Write();

  • Các thuộc tính cấp kiểu được dùng để cung cấp các giá trị mà tất cả các đối tượng của kiểu đó đều có thể cần dùng. Ví dụ: số đối tượng đã được tạo ra từ kiểu đó, giá trị max, min mà các đối tượng chỉ thuộc vào đó,...
  • Đối với các kiểu giá trị (như struct, kiểu liệt kê) bạn có thể định nghĩa cả thuộc tính lưu trữ và tính toán cấp kiểu. Còn với kiểu lớp thì bạn chỉ có thể định nghĩa thuộc tính tính toán cấp kiểu.
  • Thuộc tính lưu trữ cấp kiểu bắt buộc phải có có giá trị khởi tạo, bởi vì không có hàm cấu tử cấp kiểu để khởi gán cho thuộc tính cấp kiểu.
  • Cú pháp khai báo thuộc tính cấp kiểu như sau:

Dùng từ khoá static với struct và kiểu liệt kê, dùng từ khoá class với lớp.

Mình cũng không hiểu vì sao phải dùng hai từ khoá khác nhau.

Hàm thành phần (method)

  • Hàm thành phần là các hàm thuộc về một kiểu cụ thể nào đó. Với em Yến, kiểu ở đây có thể là kiểu lớp, struct hoặc kiểu liệt kê.
Việc có thể định nghĩa các hàm thành phần trong kiểu liệt kê là khác biệt rất lớn so với C, Objective C, C# và Java. C#, Java chỉ hỗ trợ thêm hàm thành phần trong struct.

Cú pháp khai báo giống hàm (Xem phần Hàm ở trên), chỉ có vị trí thì nằm bên trong phần khai báo của lớp hoặc struct hoặc kiểu liệt kê.
  • Giống như hàm, các hàm thành phần vẫn có tham số cục bộ và tham số ngoài. Tuy nhiên, tham số ngoài lại hoạt động hơi khác so với hàm và cách hoạt động của hàm thành phần của Objective C. Cụ thể, trong Objective C, tên hàm thành phần cũng chính là tên tham số đầu tiên của hàm kết hợp với 1 giới từ (with, for, by). Ví dụ với câu lệnh [counter incrementBy: 5 numberOfTimes: 3]; thì hàm thành phần là incrementBy. Việc sử dụng giới từ ở đây cho phép lời gọi hàm giống như một câu văn. Do đó, theo mặc định, em Yến cung cấp tham số ngoài cho tất cả các tham số ngoại trừ tham số đầu tiên. Nếu bạn không cung cấp tham số ngoài thì tên của tham số cục bộ sẽ được sử dụng làm tên tham số ngoài.

Lớp counter có hàm thành phần incrementBy. Khi gọi hàm thành phần thì câu lệnh như sau:

Chỉ có tham số thứ hai là có thể dùng tham số ngoài.

Câu lệnh trông giống như một câu văn. Nhưng dấu chấm và dấu đóng, mở ngoặc có vẻ làm hỏng cú pháp câu văn đó.

  • Structure và enum là các kiểu giá trị, nên mặc định em Yến không cho phép thay đổi dữ liệu của các thuộc tính của nó từ các hàm thành phần. Còn nếu muốn thay đổi thì bạn phải thêm từ khoá mutating cho định nghĩa hàm đó.

Ngoài ra, còn có thể thay đổi cả bản thân đối tượng thông qua thuộc tính self (giống con trỏ this của C#).

Thuộc tính self này dùng được cho cả struct và kiểu liệt kê. Con trỏ this thì chỉ dùng cho kiểu lớp.
  • Giống các ngôn ngữ khác, em Yến cũng cung cấp các hàm cấp kiểu. Bạn khai báo hàm cấp kiểu cho lớp thì dùng từ khoá class và khai báo cho struct và kiểu liệt kê thì dùng từ khoá static.
Tương tự thuộc tính cấp kiểu, em Yến không chỉ cung cấp hàm cấp kiểu cho kiểu lớp, mà cho cả struct và kiểu liệt kê.
  • Vẫn có thể sử dụng thuộc tính self trong hàm cấp kiểu. Tuy nhiên, lúc này, nó tham chiếu đến chính bản thân kiểu đó.

Chỉ mục (subscript)

  • Kiểu lớp, struct và kiểu liệt kê có thể định nghĩa các chỉ mục (subscript). Giống như thuộc tính chỉ mục trong C#.
Trong các ngôn ngữ mình biết thì chỉ có C# và em Yến là hỗ trợ cái này.
  • Cú pháp của một chỉ mục khá đơn giản, như sau:

Chỉ mục cũng hỗ trợ getter và setter giống như thuộc tính tính toán. Nếu muốn chỉ mục là chỉ đọc thì có thể loại bỏ phần setter giống như làm với thuộc tính tính toán chỉ đọc.
  • Giống C#, bạn có thể khai báo nhiều chỉ mục cho một kiểu.
  • Bạn cũng không bị giới hạn về số lượng tham số, kiểu của tham số, cũng như kiểu trả về khi định nghĩa chỉ mục. Tuy nhiên, bạn không thể sử dụng tham số in-out và cung cấp giá trị mặc định cho tham số.

Thừa kế

  • Thừa kế là khả năng một lớp thừa kế các hàm thành phần, thuộc tính và các đặc tính khác từ một lớp khác. Lớp thừa kế gọi là lớp con, lớp được thừa kế gọi là lớp cha.
Thừa kế chỉ áp dụng cho lớp và nó tạo sự khác biệt giữa lớp với các kiểu khác.
  • Em Yến không cung cấp một superbase class (giống như lớp Object của C#) và không bắt buộc lớp của bạn phải thừa kế từ lớp khác.

Lớp Vehicle không có thừa kế từ lớp nào. Em Yến gọi các lớp không thừa kế từ lớp nào là lớp cơ sở (base class).

Thuộc tính tính toán description ở đây có chức năng giống hàm ToString của C#, nhưng nó không được tự động gọi như ToString. Cách làm việc này được sao chép từ mẫu lập trình của Objective C.

  • Cú pháp thừa kế thì vẫn giống với các ngôn ngữ hướng đối tượng khác, nhưng không cần các toán tử truy xuất (public, private, protected,...) như C++, C#, Java.
  • Lớp con cũng có thể nạp chồng hàm thành phần, hàm cấp lớp, thuộc tính thành phần, thuộc tính cấp lớp, chỉ mục của lớp cha bằng cách sử dụng từ khoá override.
Từ khoá override ở đây giúp người lập trình tránh các lỗi không mong muốn, như : sai định nghĩa của thành phần cần nạp chồng.

Ví dụ nạp chồng hàm thành phần makeNoise của Vehicle.

Cấu tử

  • Khởi tạo (Initialization) là tiến trình chuẩn bị một đối tượng của lớp, struct, kiểu liệt kê để sử dụng. Tiến trình này bao gồm việc gán giá trị khởi tạo cho mỗi thuộc tính lưu trữ của kiểu và thực thi các thiết lập hoặc tiến trình khởi tạo khác cần thiết trước khi một đối tượng mới sẵn sàng để sử dụng. Bạn cài đặt tiến trình này trong một hàm đặc biệt, gọi là cấu tử. Hàm cấu tử của em Yến có tên là init, chứ không phải là cùng tên với tên kiểu như các ngôn ngữ hướng đối tượng khác.
Cấu tử của em Yến khác so với Objective C ở chỗ nó không cần câu lệnh return trả ra một giá trị (giống với C#). Nhiệm vụ chủ yếu của nó là đảm bảo các đối tượng mới được khởi tạo trước chúng được sử dụng.
  • Em Yến yêu cầu tất cả các thuộc tính lưu trữ của kiểu lớp và struct đều phải được khởi gán giá trị ngay khi một đối tượng mới của kiểu đó được tạo ra. Bạn có thể thực hiện khởi gán trong hàm cấu tử hoặc thông qua gán giá trị mặc định ngay khi khai báo thuộc tính.

Trong struct Fahrenheit có thuộc tính lưu trữ temperature, nên trong hàm cấu tử (init), chúng ta phải khởi gán cho thuộc tính này.

Việc gán giá trị mặc định và khởi gán giá trị bên trong hàm cấu tử không làm phát sinh lời gọi đến bộ theo dõi của thuộc tính.

Nếu thuộc tính lưu trữ thuộc kiểu optional thì có thể không cần khởi gán giá trị.

Bạn có thể thay đổi giá trị của các thuộc tính hằng trong quá trình khởi tạo của lớp khai báo thuộc tính hằng đó. Các lớp thừa kế không thể thay đổi giá trị của thuộc tính hằng của lớp cha.

  • Cấu tử cũng là một hàm, nên em Yến cũng hỗ trợ tham số ngoài cho cấu tử. Mặc định, tham số ngoài sẽ được tạo ra tự động cho tất cả các tham số.

struct Color khai báo hai cấu tử: một nhận ba tham số red, green, blue; một là tham số white. Em Yến tự động tạo ra các tham số ngoài cùng tên với tham số cục bộ.

Tham số ngoài là bắt buộc phải dùng khi truyền tham số cho cấu tử, nếu không có thì sẽ phát sinh lỗi trong quá trình biên dịch.
  • Một hàm cấu tử có thể gọi một cấu tử khác để thực hiện một phần của tiến trình khởi tạo đối tượng.

Hàm cấu tử init(center, size) gọi hàm cấu tử init(origin, size) sử dụng thuộc tính self.
  • Với lớp, do có thừa kế, nên các lớp còn có trách nhiệm phải đảm bảo các thuộc tính được thừa kế phải được khởi gán. Do đó, trong hàm cấu tử của lớp con phải gọi hàm cấu tử của lớp cha. Tuy nhiên, để tránh những lỗi không lường trước, em Yến chia các hàm cấu tử thành hai loại (designated init và convenience init - chưa biết dịch thế nào) và yêu cầu việc gọi hàm cấu tử của nhau phải tuân theo các quy định.
  • Designated init là các hàm cấu tử chính cho một lớp. Nó đảm bảo hai việc : 1. khởi tạo tất cả các thuộc tính do lớp đó cung cấp, 2. gọi một hàm cấu tử thích hợp của lớp cha (một designated init của lớp cha). Một lớp có thể có nhiều và ít nhất một designated init.
  • Convenience init là các cấu tử phụ, bổ trợ - thông thường vài thuộc tính sẽ giữ nguyên giá trị mặc định. Convenience init chỉ gọi một designated init hoặc convenience init của cùng lớp. Convenience init có thể có hoặc không.
  • Quá trình khởi tạo đối tượng của em Yến dùng hai pha như trong Objective C. Trong pha thứ nhất, mỗi thuộc tính được gán giá trị khởi tạo bởi lớp cung cấp thuộc tính đó. Sau đó, pha thứ hai, quá trình khởi tạo mới cho phép thay đổi các giá trị này (giống cách làm của C#). Cách làm này an toàn hơn, nó ngăn ngừa việc sử dụng biến trước khi cấp phát. Điểm khác biệt với Objective C, là em Yến linh hoạt hơn bằng cách thay vì gán giá trị mặc định của kiểu dữ liệu của thuộc tính thì nó gán bằng giá trị tuỳ chọn luôn.
  • Các lớp con của em Yến mặc định không thừa kế các cấu tử từ lớp cha. Điều này nhằm ngăn chặn việc thừa kế một cấu tử khởi tạo thiếu các thuộc tính của lớp con. Bạn bắt buộc phải nạp chồng nếu muốn cài đặt các tuỳ chọn.

Lớp Bicycle phải nạp chồng hàm cấu tử của Vehicle để thiết lập giá trị thuộc tính numberOfWheels là 2.

Tuy nhiên, lớp con sẽ tự động thừa kế tất cả các hàm cấu tử của lớp cha khi tất cả các thuộc tính đều được cung cấp giá trị mặc định và :

    1. Nếu lớp con không định nghĩa designated init nào, nó sẽ tự động thừa kế tất cả các hàm cấu tử của lớp cha
    2. Nếu lớp con nạp chồng tất cả các designated init của lớp cha thì nó sẽ thừa kế tất cả các convenience init của lớp cha.
Việc ngăn chặn và tự động thừa kế này cho phép em Yến vừa an toàn vừa linh động, nhưng sẽ hơi khó hiểu một xí.
  • Muốn ép buộc lớp con phải nạp chồng hàm cấu tử nào thì bạn thêm từ khoá required ở trước khai báo hàm cấu tử đó.

Huỷ tử

  • Huỷ tử sẽ được gọi ngay khi một đối tượng của lớp bị huỷ. Các hàm huỷ tử chỉ có trên các lớp và cài đặt hàm deinit để cài đặt tiến trình huỷ tử.

  • Em Yến quản lý bộ nhớ các đối tượng thông qua ARC (automatic reference counting - bộ đếm tham chiếu tự động).
  • Với em Yến, hàm huỷ tử sẽ được tự động thực hiện khi có một đối tượng cần bị huỷ bộ nhớ. Bạn không được phép tự gọi hàm huỷ tử. Tuy nhiên, bạn vẫn cần có hàm huỷ tử để làm một số việc, ví dụ : đóng file, ngắt kết nối đến cơ sở dữ liệu,… Hàm huỷ tử của lớp cha sẽ được lớp con thừa kế và sẽ tự động được gọi ở cuối phần cài đặt của hàm huỷ tử của lớp con. Hàm huỷ tử của lớp cha luôn được gọi ngay cả khi lớp con không cài đặt hàm huỷ tử nào.

Objective C và C# đều hỗ trợ cả không tự động và tự động cho quá trình huỷ tử trên.

C, C++ thì hoàn toàn giao cho người lập trình quản lý.

ARC (automatic reference counting - bộ đếm tham chiếu tự động)

  • Giống Objective C, em Yến cũng dùng ARC để theo dõi và quản lý việc sử dụng bộ nhớ của ứng dụng. ARC tự động giải phóng bộ nhớ của các đối tượng lớp khi những đối tượng này là không còn cần thiết. Việc huỷ bộ nhớ của các đối tượng chỉ xảy ra khi không còn 1 thuộc tính, biến, hằng nào còn tham chiếu đến đối tượng đó. Việc kiểm tra xem có còn 1 thuộc tính, biến, hằng nào đó tham chiếu đến đối tượng thì có nhiều cách. Em Yến dùng cách Objective C đã dùng khá hiệu quả là sử dụng một biến đếm tham chiếu (reference counting). Mỗi khi một đối tượng được tạo ra thì biến đếm cho đối tượng đó sẽ được gán bằng 1. Nếu trong quá trình hoạt động lại có một biến khác nhận địa chỉ tham chiếu đến đối tượng đó (thông qua phép gán) thì biến đếm của đối tượng đó sẽ tăng lên 2. Khi một biến nào đó không tham chiếu đến đối tượng đó nữa (gán giá trị nil cho biến) thì biến đếm tham chiếu của đối tượng đó sẽ giảm đi 1. Cho đến khi nào biến đếm về bằng 0 thì ARC sẽ chạy quá trình huỷ tử. Hình dưới đây minh hoạ quy trình này một cách đơn giản dễ hiểu nhất (alloc - lệnh cấp phát bộ nhớ, retain - lệnh tăng đếm lên 1, release - lệnh giải phóng).

Hình trên minh hoạ quy trình làm việc của ARC. alloc - lệnh cấp phát bộ nhớ, retain - lệnh tăng đếm lên 1, release - lệnh yêu cầu giải phóng bộ nhớ. Đây là các lệnh của Objective C.

ARC chỉ làm việc với các đối tượng kiểu lớp.

Em Yến dùng cách này nhưng không dùng đống lệnh alloc, retain, release, mà toàn bộ việc này sẽ được thực hiện tự động. Khi bạn muốn release thì chỉ cần gán nil cho biến tham chiếu đến đối tượng muốn giải phóng. Cách làm tự động này tương tự Trình gom rác của .NET.

  • Với ARC, khi bạn gán một đối tượng cho một biến, hằng, thuộc tính thì biến, hằng, thuộc tính sẽ tạo ra một tham chiếu mạnh (strong reference) đến đối tượng đó.
  • Một vấn đề đối với các hệ thống dọn dẹp bộ nhớ tự động của các nền tảng lập trình là vấn đề tham chiếu vòng (reference cycle). Đó là khi hai đối tượng tham chiếu lẫn nhau. Lúc đó, trình dọn dẹp bộ nhớ tự động sẽ không thể giải phóng bộ nhớ của cả hai đối tượng này.

Em Yến gọi tham chiếu vòng là tham chiếu vòng mạnh (strong reference cycle).

Theo như mình tìm hiểu được thì C# cũng có cách giải quyết vấn đề này, nhưng theo mình thì vẫn chưa ổn lắm. Cách của Objective C và em Yến là tốt hơn.

  • Em Yến, cung cấp thêm hai loại tham chiếu là tham chiếu yếu (weak reference) và tham chiếu không sở hữu (unowned reference). Và luật mới là ARC sẽ chỉ xoá bộ nhớ một đối tượng khi số tham chiếu mạnh (strong reference) là bằng 0.
  • Sử dụng tham chiếu yếu khi bạn biết rằng trong vòng đời của biến đó có thể có lúc nó không tham chiếu đến đối tượng nào (nil).

  • Sử dụng tham chiếu không sở hữu khi bạn biết rằng trong vòng đời của nó, nó sẽ không bao giờ nil một khi nó đã được khởi tạo.

Objective C chỉ hổ trợ weak reference.

Theo như mình hiểu, thì weak reference được dùng cho các đối tượng mà có 1 biến tham chiếu vào, còn unowned reference (unowned - không có ai làm chủ) được dùng cho các đối tượng mà không có biến nào tham chiếu vào, ngoại trừ để các đối tượng khác tham chiếu.

  • Với các lớp có dùng closure để khởi tạo các thuộc tính thì cũng có thể gặp trường hợp trên. Lúc đó, có thể dùng tham chiếu không sở hữu để giải quyết.

Chuyển đổi kiểu (type casting)

  • Em Yến cung cấp hai toán tử is và as tương tự trong C# để kiểm tra và ép kiểu dữ liệu. Ngoài ra còn có thêm as? cho kiểu optional.
  • Em Yến còn cung cấp thêm hai kiểu AnyObject và Any cho các kiểu chưa xác định. AnyObject thể hiện cho bất kỳ một kiểu lớp nào. Any thể hiện cho tất cả các kiểu, kể cả kiểu hàm.

Kỹ thuật mở rộng (Extension)

  • Kỹ thuật mở rộng cho phép bổ sung thêm các chức năng cho các lớp, struct, kiểu liệt kê đã có. Việc bổ sung chức năng này là không cần truy cập vào mã nguồn của lớp, struct, kiểu liệt kê đã có đó.
Tương tự category của Objective C. Tuy nhiên, trong em Yến, các extension không có tên. C# cũng có khả năng tương tự, nhưng chỉ bổ sung hàm thành phần, không thêm vào được cho cùng lớp, cách cài đặt và sử dụng không tự nhiên.
  • Kỹ thuật mở rộng của em Yến cho phép: thêm thuộc tính tính toán, hàm thành phần, hàm cấp lớp, hàm huỷ tử, chỉ mục, kiểu mới lồng bên trong, cài đặt một giao thức (protocol).
  • Dưới đây là một extension mở rộng lớp Double và sử dụng lớp Double mới.
  • Một ví dụ mở rộng lớp Int và sử dụng

  • Một ví dụ mở rộng lớp Character.

Giao thức (protocol)

  • Giao thức định nghĩa nguyên mẫu của các hàm thành phần, thuộc tính, chỉ mục và những yêu cầu khác cho một nhiệm vụ cụ thể nào đó. Giao thức không cung cấp cài đặt cho các yêu cầu đó, nó chỉ mô tả một cài đặt sẽ trông như thế nào. Sau đó, các lớp, struct, kiểu liệt kê có thể áp dụng (adopt) giao thức đó để cung cấp một cài đặt cho những yêu cầu của giao thức. Các kiểu thoả mãn những yêu cầu của một giao thức thì được gọi là phù hợp (conform) với giao thức đó.

Protocol chính là Interface trong C++, C#, Java. Do xuất phát từ từ interface, nên người ta dùng thuật ngữ giao diện để gọi kỹ thuật này. Còn theo mình thì bản thân chữ giao diện nó đã không thể hiện đúng bản chất của kỹ thuật này. Thứ nhất, bởi vì bản thân một lớp cũng đã cung cấp một giao diện cho người dùng của lớp đó, nên nếu ở đây cũng dùng từ giao diện thì bị trùng. Thứ hai, những gì chúng ta định nghĩa giống các giao thức mà các lớp phải tuân theo. Có nghĩa là nó bao hàm luôn cả cách thức làm việc, chứ không chỉ cái nguyên mẫu hàm. Do đó, theo tôi chữ giao thức thể hiện đúng kỹ thuật này hơn cả.

Từ nguyên mẫu xuất phát từ từ nguyên mẫu hàm dùng trong C, C++. Nguyễn mẫu hàm chính là dòng khai báo tên hàm, các tham số và kiểu trả về của hàm.

    • Một giao thức có thể yêu cầu các kiểu phù hợp với nó phải có các hàm thành phần, thuộc tính, hàm cấp kiểu, toán tử và chỉ mục cụ thể nào đó.
    • Cú pháp khai báo giao thức như sau :

    Một kiểu muốn áp dụng một giao thức thì viết theo cú pháp sau :

    Nếu lớp có thừa kế từ lớp khác thì viết tên lớp cha trước các giao thức.

    Em Yến cho phép lớp, structure, kiểu liệt kê đều có thể áp dụng giao thức.

    • Ví dụ:

    Giao thức FullyNamed yêu cầu các lớp áp dụng giao thức này phải có một thuộc tính trả ra tên đầy đủ (full name).

    Lớp Starship áp dụng giao thức FullyNamed đã cài đặt thuộc tính fullName để trả ra tên đầy đủ.
    • Giao thức cũng là một kiểu dữ liệu, nên có thể khai báo biến, hằng, thuộc tính có kiểu giao thức, có thể dùng kiểu giao thức là kiểu của tham số hay kiểu trả ra của hàm, hàm thành phần, cấu tử.
    • Em Yến còn cho phép các giao thức thừa kế từ giao thức khác và cho phép tạo ra giao thức bằng cách kết hợp nhiều giao thức vào với nhau (gọi là giao thức kết hợp - protocol composition).
    • Dùng toán tử is, as, as? để kiểm tra và ép kiểu về giao thức.

    Delegation

    • Trong C# và Java, người ta cung cấp hai kiểu dữ liệu là delegation và event. Delegation giống như "trao trách nhiệm cho ai đó". Ví dụ, bạn giao trách nhiệm cho một biến nào đó có khả năng thụ lý (gọi) một hàm nào đó. Vậy thì từ biến đó ta có thể gọi thực hiện hàm đó. Event dịch ra là sự kiện. Đây là kiểu mở rộng của delegation. Có thể diễn dịch kiểu này thành "khi có điều gì xảy ra thì gọi hàm nào để xử lý". Ở đây, có thể có nhiều hàm cùng xử lý khi một sự kiện xảy ra, nên event được tạo ra để hổ trợ cho điều đó.
    • Còn với em Yến, delegation chỉ là một mẫu thiết kê (design pattern) cho phép một lớp, struct uỷ thác một số trách nhiệm của nó cho một kiểu khác. Mẫu thiết kế này được cài đặt bằng cách định nghĩa một giao thức mà nó đóng gói những trách nhiệm được uỷ thác đó. Một kiểu áp dụng giao thức đó sẽ phải đảm bảo cung cấp những chức năng được uỷ thác đó. Delegation được sử dụng để hồi đáp lại một hành vi cụ thể hoặc để trích rút dữ liệu từ bên ngoài mà không cần biết kiểu của nguồn dữ liệu.
    Em Yến không tạo ra một kiểu riêng cho delegation, mà dùng ngay luôn kiểu giao thức.
    • Ví dụ:

    Khai báo một delegation (một protocol được dùng làm delegation).

    Lớp DiceGameTracker áp dụng delegation trên và cài đặt ba hàm để xử lý cho ba sự kiện game bắt đầu, game chơi một lượt và game kết thúc.

    Lớp SnakesAndLadders khai báo một biến kiểu delegation. Hàm play dùng biến này để gọi các hàm xử lý các sự kiện tương ứng.
    • Em Yến hầu như không nhắc gì đến event, nhưng có đưa ra cách dùng một mảng các delegation. 

    Khuôn mẫu (Generic)

    • Khuôn mẫu cho phép bạn tạo ra các hàm, các kiểu có thể làm việc với mọi kiểu dữ liệu. Ví dụ: bạn viết 1 hàm tính tổng duy nhất nhưng bạn có thể truyền cho nó kiểu dữ liệu số nguyên, số thực gì cũng được. Hoặc bạn viết một lớp Stack nhận các thành phần của tất cả các kiểu dữ liệu.

    Trong C#, lớp List<T> là một generic.

    Khuôn mẫu của em Yến mạnh mẽ hơn khuôn mẫu của C++ và generic của C#.

    Hầu hết thư viện chuẩn của em Yến được viết sử dụng khuôn mẫu. Ví dụ: array và dictionary có thể dùng với kiểu gì cũng được.

    • Ví dụ khuôn mẫu hàm :

    Đây là ví dụ thể hiện phải viết nhiều hàm để thực hiện công việc hoán đổi giá trị hai biến, mỗi hàm xử lý cho một kiểu giá trị khác nhau.

    Dùng khuôn mẫu với cú pháp tham số kiểu (type parameter) <T>, ta chỉ cần cài đặt một hàm duy nhất:

    • Em Yến cho phép dùng nhiều tham số kiểu và đặt tên cho tham số kiểu để dễ phân biệt.
    • Ví dụ khuôn mẫu kiểu (kiểu generic tức là khuôn mẫu lớp, khuôn mẫu struct, khuôn mẫu kiểu liệt kê):

    • Em Yến còn cho phép thêm các hạn chế kiểu (type constraint) vào khuôn mẫu để các khuôn mẫu kiểu, khuôn mẫu hàm hoạt động tốt hơn. Các hạn chế kiểu xác định các tham số kiểu (type parameter) phải thừa kế từ một kiểu nào đó hoặc áp dụng một giao thức nào đó.

    <T: Equatable> thể hiện kiểu dùng với hàm findIndex phải là kiểu có áp dụng giao thức Equatable.
    • Còn có thể thêm cả mệnh đề where (điều kiện) vào trong phần khai báo hạn chế kiểu.

    Kiểu kết hợp (associated type) trong giao thức

    • Đôi khi, khi khai báo một giao thức bạn cần thêm một hoặc vài kiểu kết hợp. Đó là các kiểu chưa xác định cụ thể khi khai báo giao thức. Đến khi giao thức được áp dụng thì mới gán kiểu cụ thể vào. Em Yến cung cấp các bí danh cho các kiểu đó. Bí danh này sẽ được dùng trong phạm vi của giao thức.
    • Cú pháp khai báo tương tự như sau:

    ItemType là bí danh cho kiểu được dùng trong giao thức.

    Áp dụng giao thức Container:

    Nạp chồng toán tử

    • Chỉ có lớp và struct là có thể nạp chồng toán tử.
    Với em Yến, struct cũng đã có thể nạp chồng toán tử.
    • Các toán tử được nạp chồng bằng các hàm toàn cục. Không bị phân tán cái trong lớp, cái ngoài lớp như C++ và C#.

    Nạp chồng toán tử + cho struct Vector2D.

    • Sử dụng từ khoá prefix và postfix để quy định vị trí xuất hiện trước hay sau của toán tử với toán hạng.
    • Em Yến cho phép tạo ra các toán tử mới. Lệnh sau đây khai báo toán tử +++:

    Ban đầu thì nó chưa có ý nghĩa gì, bạn phải nạp chồng để cung cấp ý nghĩa cho nó. Ví dụ:

    • Khi tạo ra toán tử mới, bạn có thể thiết lập thứ tự ưu tiên cho toán tử đó:

    Khả năng truy xuất

    Khả năng truy xuất chỉ mới được thêm vào với bản beta 5 của ngôn ngữ lập trình này.
    • Khả năng truy xuất giới hạn truy xuất vào các phần trong mã nguồn của bạn từ những mã nguồn trong file khác hoặc module khác.
    • Bạn có thể chỉ định cấp độ truy xuất cho các kiểu cụ thể (lớp, struct, kiểu liệt kê), cũng như cho thuộc tính, hàm thành phần, cấu tử, chỉ mục của kiểu đó.
    • Thực tế, khi bạn tạo ra một app riêng lẻ thì không cần phải chỉ định khả năng truy xuất làm gì. Nên em Yến cung cấp khả năng truy xuất mặc định (internal) và bạn không cần phải viết rõ nó ra trong mã nguồn.
    • Em Yến chia cấp độ mã nguồn của bạn ra hai cấp độ là module và tập tin mã nguồn.
      • Một module là một đơn vị riêng lẻ của mã nguồn được phân phối, ví dụ: một framework hay một ứng dụng. Nó được biên dịch và chuyển giao như một đơn vị riêng lẻ và có thể được sử dụng bởi module khác.
      • Một tập tin mã nguồn là một tập tin mã nguồn Swift riêng lẻ trong một module.
    • Từ đó, em Yến cung cấp 3 cấp độ truy xuất: public, internal và private. Trong đó, private chỉ cho phép truy xuất trong một tập tin mã nguồn và internal chỉ cho phép truy xuất trong một module.

    Kết luận

    Có thể nói em Yến như một cô gái lai. Em ấy sở hữu động cơ mạnh mẽ, hiệu quả và cú pháp gọi hàm dạng câu văn của Objective C; sử dụng các cú pháp câu lệnh ít khác người của C#, Java; bổ sung và cải tiến các cú pháp linh động, hiệu năng cao và hiện đại của các ngôn ngữ dạng script (Python,…); và cuối cùng là gia tăng thêm tính an toàn cho câu lệnh. Và giờ, em ấy sở hữu một thân hình thật quyến rũ đủ để mê hoặc tất cả các lập trình viên.

    1-4 of 4