Chào các bạn, là một lập trình viên Android chắc hẳn bạn đã từng sử dụng qua Library (Thư viện) trong Android. Nó có thể là một thư viện mở được chia sẻ trên internet, hoặc một thư viện ra chính bạn tạo ra.
Hôm nay mình sẽ chia sẻ kiến thức nhỏ mà mình biết được liên quan đến việc publish thư viện Android dưới lên Remote (Online) và Local. Với những khái niệm như Publish Library, Local Maven,….
I. Khái niệm
Đầu tiên, Android Library là gì?
Android Library có cấu trúc giống như Module ứng dụng Android.
Android Library có thể bao gồm mọi thứ cần thiết để xây dựng một ứng dụng xây dựng: Source code, Resource file và một Android Manifest.
Thay vì, tệp App build thành APK file, thì Library build thành AAR file(Android Archive) và sử dụng như một phần phụ thuộc của module ứng dụng Android.
II. Sử dụng và Lợi ích
Vậy, khi nào sử dụng Android Library?
Khi sử dụng nhiều ứng dụng mà bạn có một số thành phần giống nhau, chẳng hạn như activity, service hoặc UI layout người dùng.
Một trong những lợi ích của việc sử dụng Library mà chúng ta có thể kể đến như là:
Tăng tốc developemnt time.
Tái sử dụng lại source code được phân chia chức năng cụ thể.
III. Truy cập
Tạo và keep Library trong Project
Tạo and Publish it to global to truy cập chúng một cách remote hoặc share chúng tới các project khác. Một số cách thông dụng để publish Library lên remote. Một số nơi có thể kể tới
Tạo Library:
Để tạp library module bạn làm theo cách sau: Vào File > New > New Module. Trong cửa sổ Create New Module xuất hiện, nhấp vào Thư viện Android, sau đó nhấp vào Tiếp theo. Ở màn hình tạo Module mới xuất hiện, bạn chọn Thư viện Android, sau đó nhấn Next.
Sau khi hoàn tất các thao tác tạo Lib và implement functions cần thiết
Bạn cũng có thể tạo 1 project thuần tuý là thư viện mà không có App Module kèm theo bằng cách đổi plugin trong Gradle file thành
<pre>
plugins {
id ‘com.android.library’
}
</pre>
Vì bài này tập trung vào các publish Library nên mình chỉ làm một ví dụ đơn giản về nội dung của Library này về chức năng tính toán.
Như mọi người đã biết, hầu hết các ứng dụng ngày nay đều hỗ trợ tính năng đa ngôn ngữ, nhằm mục đích tiếp cận được nhiều người dùng hơn, cho mọi người sử dụng ứng dụng dễ dàng hơn. Tuy nhiên hiện nay hầu hết các dự án đều làm một cách tự phát, chưa có một quy trình chuẩn. Điều này làm cho quá trình phát triển sinh ra nhiều bug UI sai Message, sai chính tả, nó làm tốn thời gian không đáng có của các bên.
Trong quá trình làm việc ở các dự án, khi tài liệu được cập nhật đồng nghĩa với việc các Dev phải quay lại kiểm tra các thay đổi message, rồi bắt đầu loay hoay tìm kiếm trong source code để thực hiện thay đổi.
Hay mỗi khi code một màn hình mới việc phải định nghĩa một đống key, định nghĩa enum/constant và copy/paste nó sang các file localization thật nhàm chán và tiềm tàng nhiều rủi ro về lỗi sai chính tả, copy nhầm. Trước đây, có một member trong dự án của mình chỉ vì copy sai text làm thừa một dấu “;” làm lỗi cả file đa ngôn ngữ, việc này làm cho toàn bộ text trên ứng dụng khi release cho bị sai. Do file đa ngôn ngữ là string nên những lỗi sai chính tả như này mất rất nhiều thời gian để điều tra lỗi.
Sau nhiều năm làm việc với các dự án khác nhau, cùng các anh em, cộng sự hoàn thành các ứng dụng to nhỏ khác nhau. Mình đã đúc kết được một quy trình giúp cải thiện năng suất làm việc của mọi người, giúp giảm các bug UI về sai message, sai chính tả, giảm thời gian thực hiện tính năng đa ngôn ngữ, giúp quá trình bảo trì, thay đổi message trở nên dễ dàng hơn.
Quy trình
Brainstoming
Trước tiên, để đạt được hiệu quả Leader của các bên gồm BA, Mobile, Web … ngồi lại với nhau để thống nhất về cách làm việc theo quy trình trên. Sau đó phổ biến lại cho member và đảm bảo việc member thực hiện đúng quy trình làm việc.
Tạo file excel chung lưu các message cần làm đa ngôn ngữ
Trong file này mình có định nghĩa sẵn các trường cần nhập và có sẵn công thức để gen ra code của swift và android. Nếu dự án của các bạn sử dụng ngôn ngữ khác thì có thể chỉnh lại công thức cho phù hợp với dự án của mình.
Nhập nội dung
Thêm/sửa message
Screen/Feature: Điền vào tiên màn hình hoặc tính năng.
MessageKey: Điền vào tên item trên màn hình, nếu SRS có mô tả thì ưu tiên sử dụng key trong SRS, nếu không hãy đặt tên sao cho đúng ý nghĩa của item.
Tiếng việt: Text được hiển thị khi ngôn ngữ là Tiếng Việt
Tiếng anh: Text được hiển thị khi ngôn ngữ là Tiếng Anh
Note: TH1. Tài liệu đã định nghĩa sẵn các message cho các ngôn ngữ: thì BA có thể cử 1 bạn ra điền hết vào file này trước khi bên dev thực hiện code. Trường hợp này thì việc làm đa ngôn ngữ khá nhàn, mình sẽ giải thích chi tiết ở các phần phía dưới. TH2: Tài liệu chưa định nghĩa message cho các ngôn ngữ(các dự án thường rơi vào trường hợp này): Khi này khi các Dev thực hiện code sẽ tự insert thêm message vào file trên teams, để file tự tạo ra code tương ứng với các ngôn ngữ.
Công thức tạo code tự động
Công thức tạo code từ excel
Các Developers sẽ không phải làm các thao tác lặp đi lặp lại nhàm chán. Ngoài ra nó giúp giảm lỗi đánh máy, lỗi copy paste, lỗi sai chính tả và giúp việc làm đa ngôn ngữ nhanh hơn. Trên file excel mình có tạo ra công thức để tự tạo ra code của swift/android. Khi bạn điền thông tin vào sheet MessageList thì code sẽ được gen tự động, việc của mọi người là chỉ cần copy code vào project và sử dụng.
Đưa ra các luật lệ
Tạo ra các luật yêu cầu member phải tuân thủ như sau:
Đặt tên màn hình theo quy định của dự án, cách đặt tên cần thống nhất giữa tài liệu SRS và các class trong code của Dev.
Dev của các bên(mobile, web) và BA khi thực hiện thêm mới, hay chỉnh sửa sẽ thực hiện trực tiếp trên files Localize chung của dự án, khi chỉnh sửa cần tìm đúng tên màn hình, message để sửa, nếu thêm mới thì thêm xuống cuối cùng của của danh sách list message của màn hình đó.
Để tránh việc conflict hoặc giẫm chân nhau khi các bên chạy song song, thì mỗi màn hình những member liên quan tới màn hình đó sẽ cử ra một bạn điển vào file message trước.
Cách thực hiện code
TH1: đối với dự án không cho sử dụng thư viện mã nguồn mở
Khi thực hiện chúng ta chỉ cần vào file excel và copy code vào project của mình rồi sử dụng bình thường.
TH2:Dự án cho phép sử dụng thư viện mã nguồn mở
Chúng ta sẽ sử dụng thư viện để tự tạo ra enum nhanh gọn lẹ hơn, ví dụ bên iOS Swift thì chúng ta có thể sử dụng Swiftgen để thực hiện. Link hướng dẫn sử dụng Swiftgen mình cũng có viết môt bài rồi các bạn có thể tham khảo link dưới đây:
Giảm thời gian làm việc, tăng năng suất làm việc của team
Giảm khả năng bị lỗi UI/Message hay các lỗi về copy/paste khi thực hiện
Giảm thời gian bảo trì ứng dụng khi thay đổi message, khi có thay đổi BA sẽ update file message list và báo lại cho Devs, lúc này Dev không cần phải tìm xem thay đổi ở đâu, mà chỉ cần copy code từ file message list là xong.
Giảm thời gian implement khi có yêu cầu hỗ trợ thêm ngôn ngữ khác. Vì khi này chúng ta chỉ cần thêm 1 cột nữa ở file excel rồi copy code sang project là xong.
Giúp tối ưu được effort khi các team không start cùng một thời điểm. Vì team start sau sẽ sử dụng lại được phần đã làm của team start trước.
Nhược điểm
Cần quản lí, phân chia công việc tốt, member có khả năng làm việc nhóm.
Thường chỉ hiệu quả cao đối với các dự án mới
Tổng kết
Phía trên là nội dung chia sẻ về cách tối ưu khi làm ứng dụng hỗ trợ đa ngôn ngữ, mình hi vọng bài viết có thể giúp mọi người phần nào trong quá trình thực hiện các ứng dụng có hỗ trợ đa ngôn ngữ. Cảm ơn mọi người đã dành thời gian đọc bài viết này.
Tìm hiểu cách mà SwiftGen giúp dễ dàng loại bỏ các chuỗi ma thuật(magic strings) trong các dự án iOS của bạn.
Là một nhà phát triển ứng dụng trên thiết bị di động, bạn có thể cảm thấy như bạn gặp phép thuật hàng ngày. Các dòng mã bạn viết sẽ được chuyển đổi thành các ứng dụng mà mọi người trên khắp thế giới có thể sử dụng. Các công cụ mà Apple cung cấp giúp biến điều kỳ diệu đó thành hiện thực và làm cho cuộc sống của bạn dễ dàng hơn. Khi tiến xa hơn vào lĩnh vực phát triển phần mềm, bạn có thể nhận ra có một thứ ma thuật mà bạn không thích: Chuỗi thần kỳ(magic strings).
Loại an toàn, khái niệm rằng các biến chỉ có thể thuộc một loại cụ thể, cung cấp cho các nhà phát triển các rào chắn(guardrails) để giữ cho các chương trình của họ an toàn. Tuy nhiên, chuỗi ma thuật đưa mã không an toàn vào các ứng dụng đó. Chuỗi ma thuật là gì? Trong quá trình phát triển iOS, bạn đã gặp phải những điều này nhiều lần. Một ví dụ trông giống như sau:
let color = UIColor(named: "green-apple")
self.title = "Welcome!"
Ví dụ này hiển thị “green-apple” và “Welcome!” được viết dưới dạng chuỗi trực tiếp trong mã của bạn. Không quá khi nói rằng tất cả các nhà phát triển đôi khi nhận thấy mình có lỗi với hành vi này.
Trên thực tế, trong quá trình phát triển iOS, bạn không có nhiều lựa chọn. Ngoài ra, Xcode không cung cấp cách nào để tránh việc này.
Những người đã làm việc trong Android có thể thấy mình run sợ trước những đoạn mã như thế này. Môi trường phát triển Android có cơ chế chuyển đổi tài nguyên ứng dụng, chẳng hạn như chuỗi, màu sắc, hình ảnh và phông chữ, thành các kiểu biến an toàn. Có rất nhiều lợi ích từ việc này, và đó là:
Giảm lỗi sai chính tả
Ngăn chặn sự trùng lặp tài nguyên không cần thiết
Cung cấp kiểm tra tài nguyên tại thời điểm biên dịch
Giúp dọn dẹp tài nguyên cũ
Và nhiều hơn thế nữa
Như đã nêu, các nhà phát triển iOS và macOS không có quyền truy cập vào hệ thống cung cấp sự an toàn cho loại tài nguyên này.
May mắn thay, có SwiftGen, một trình tạo mã để loại bỏ các chuỗi ma thuật trong ứng dụng của bạn. Có sẵn dưới dạng thư viện mã nguồn mở trên GitHub, bạn có thể thêm tính năng này vào các dự án iOS và macOS của mình để mang lại sự an toàn về loại và kiểm tra thời gian biên dịch của tất cả các Assets của bạn.
Trong hướng dẫn này bạn sẽ học được cách:
Thiết lập dự án của bạn với SwiftGen
Xác định các nội dung mà bạn muốn chuyển đổi
Xác định nơi mà code được generated
Tạo các templates cho phép SwiftGen generate khi dùng SwiftUI cho Fonts và màu sắc.
Getting Started
Để bắt đầu, hãy nhấp vào nút Tải xuống tài liệu dưới đây:
Có một số cách bạn có thể cài đặt SwiftGen để hoạt động với môi trường của mình như sau:
CocoaPods
Homebrew
Mint
Directly download a zipped release(Nên dùng cách này)
Link hướng dẫn cài ở đây: https://github.com/SwiftGen/SwiftGen
Trong hướng dẫn này, bạn sẽ sử dụng CocoaPods để quản lý SwiftGen.
Lưu ý: Nếu bạn không có CocoaPods, đừng lo lắng – dự án khởi động và dự án cuối cùng đã được tải xuống phần phụ thuộc. :]
Mở workspace, có tên là DrinksUp! .Xcworkspace. Vì dự án này sử dụng CocoaPods nên bạn sẽ không thể làm việc trực tiếp với DrinksUp! .Xcodeproj.
Hãy dành một chút thời gian để xem xét xung quanh trong Xcode. Dự án đã ở trạng thái hoàn thành nhưng sử dụng chuỗi để tham chiếu phông chữ, màu sắc, hình ảnh và chuỗi. Bạn sẽ chuyển đổi tất cả những thứ này vào cuối hướng dẫn.
Xây dựng và chạy và làm quen với ứng dụng.
Ứng dụng, DrinksUp !, là một cách để theo dõi các loại đồ uống thú vị mà bạn và gia đình đã thử khi đến nhà hàng hoặc ở nhà.
Thiết lập SwiftGen
Bắt đầu bằng cách mở Terminal và điều hướng đến thư mục gốc của dự án khởi động của bạn. Tiếp theo, nhập lệnh sau vào Terminal:
./Pods/SwiftGen/bin/swiftgen config init
Thao tác này sẽ tạo một tệp cấu hình, có tên là swiftgen.yml, tại thư mục gốc dự án của bạn. Nếu tệp này tự động mở trong Xcode, hãy tiếp tục và tắt nó đi.
Tiếp theo, trong workspace dự án của bạn, đi tới Files ▸ Add new file vào “DrinksUp!”…. Tìm swiftgen.yml. Đảm bảo bỏ chọn Sao chép các mục nếu cần và chọn Tạo tham chiếu thư mục.
Nhấp vào nút Thêm. Khi hoàn tất, bạn sẽ thấy swiftgen.yml ở đầu trình điều hướng Dự án, như bên dưới:
Lưu ý: Bạn có thể di chuyển tệp này đến vị trí giống như hình minh họa, nếu Xcode không thêm nó theo cách tương tự.
Tệp này là nơi bạn sẽ đặt các hướng dẫn cho SwiftGen biết tệp nào bạn muốn chuyển đổi thành mã được tạo. Loại tệp, YML , cho biết nó đang sử dụng YAML cho cú pháp của nó. Nếu bạn chưa sử dụng YAML trước đây, đây chỉ đơn giản là một cách dễ đọc hơn để xem dữ liệu được tuần tự hóa. Bạn có thể nghĩ về nó như là JSON , được đơn giản hóa.
Bây giờ, thay thế toàn bộ nội dung của swiftgen.yml bằng nội dung sau:
Bạn đã khai báo một biến input_dir thư mục đầu vào. Điều này cho SwiftGen biết thư mục gốc để điều hướng đến tất cả các đường dẫn tệp mà bạn sẽ sớm thêm vào.
Một biến khác xác định thư mục đầu ra của các tệp Swift được tạo. Bằng cách này, bạn sẽ dễ dàng theo dõi tất cả các tệp SwiftGen hơn.
Thêm Build Phase
Để chạy SwiftGen, bạn sẽ cần thêm một giai đoạn xây dựng mới vào dự án của mình. Để thực hiện việc này, hãy chọn dự án của bạn trong trình điều hướng Dự án, chọnBuild Phases. Chọn + và chọnNew Run Script Phase.
Đổi tên script thành SwiftGen bằng cách nhấp đúp vào tên hiện tại, Run Script . Tiếp theo, thêm phần sau vào trường văn bản của tập lệnh:
if [[ -f "${PODS_ROOT}/SwiftGen/bin/swiftgen" ]]; then
"${PODS_ROOT}/SwiftGen/bin/swiftgen"
else
echo "warning: SwiftGen is not installed. Run 'pod install --repo-update' to install it."
fi
Cuối cùng, sắp xếp lại thứ tự tập lệnh để ngồi ngay sau tên tập lệnh [CP] Check Pods Manifest.lock . Các Build Phases của bạn bây giờ sẽ giống như sau:
Build và Run. Nếu mọi thứ được thiết lập đúng cách, bạn sẽ không có bất kỳ lỗi nào. Bạn sẽ không có bất kỳ thứ gì trong thư mục Đã tạo của mình. Được rồi chúng ta đến bước tiếp theo.
Chuyển đổi XCAssets
Bây giờ, bạn đã sẵn sàng để bắt đầu xóa các chuỗi khỏi dự án của mình! Bước đầu tiên sẽ là SwiftGen tạo mã cho các tệp XCAsset trong dự án. Mở swiftgen.yml và thêm phần sau vào cuối tệp:
Đây là ý nghĩa của mỗi dòng trong số những dòng này:
Mỗi loại tệp hoặc mẫu, bạn muốn chuyển đổi bằng SwiftGen yêu cầu một mục nhập ở cấp gốc là swiftgen.yml . Ở đây, điều này cho biết bạn muốn SwiftGen chuyển đổi các tệp là XCAsset .
Danh sách này cho biết những tệp SwiftGen nên giới hạn chuyển đổi của nó.
Bạn cần cho SwiftGen biết cách tạo đầu ra.
Bạn phải cung cấp tên mẫu. Ở đây, swift5 là một mẫu mặc định được cung cấp bởi nhóm SwiftGen. Bạn sẽ học cách sử dụng các mẫu của riêng mình sau này.
Cuối cùng, bạn cung cấp tên tệp mà bạn muốn mã Swift mới của mình tạo ra. Hãy nhớ rằng, bạn đã xác định output_dir ở đầu tệp, có nghĩa là nó sẽ xuất thành Generated / XCAssets + Generated.swift .
Build và Run. Nếu bạn không gặp bất kỳ lỗi nào, thì quá trình tạo mã của bạn đã hoạt động!
Thêm tệp
Mở rộng folder Generated trong trình Project navigator. Hiện tại, bạn vẫn sẽ không tìm thấy tệp mới của mình. Để thêm nó, nhấp chuột phải vào Generated và chọn Add Files to “DrinksUp!”… .
Chọn XCAssets + Generated.swift . Đảm bảo Copy items if needed không được chọn, sau đó nhấp vào Add . Bây giờ, hãy mở XCAssets + Generate.swift và quan sát. Bạn sẽ thấy enumAsset. Trong enum, bạn sẽ tìm thấy các bảng liệt kê khác được xác định phù hợp với các danh mục XCAsset mà bạn đã xác định. Ví dụ:
Assets: Mỗi hình ảnh trong dự án bây giờ có một thuộc tính tĩnh được xác định.
Colors: Tất cả các màu của ứng dụng cũng có các thuộc tính tĩnh để tham khảo.
Mở Assets.xcassets . Lưu ý rằng có một nhóm hình ảnh có tên là nội dung khởi chạy . Nhưng Assets đã khai báo tất cả các thuộc tính ảnh tĩnh ở cùng một mức. SwiftGen có thể duy trì tổ chức này cho bạn nhưng không làm như vậy theo mặc định. Mở swiftgen.yml và thay thế toàn bộ mục nhập xcassets bằng mục sau:
Tại đây, bạn tận dụng khả năng của SwiftGen để tùy chỉnh đầu ra mã của bạn bằng cách thực hiện như sau:
Xác định params trên outputs của bạn.
forceProvidesNamespaces: Điều này sẽ duy trì không gian tên của bạn được tìm thấy trong danh mục nội dung.
Tham số bổ sung này đảm bảo cho dù bạn đã cung cấp bao nhiêu tên tệp inputs, SwiftGen sẽ duy trì các bảng liệt kê riêng biệt để đại diện cho từng danh mục nội dung.
Xây dựng dự án, sau đó quay lại XCAssets + Generated.swift . Bây giờ bạn sẽ thấy Assets có một enum tên mới LaunchAssets để đại diện cho cấu trúc thư mục của bạn.
Bây giờ, đã đến lúc sử dụng mã mới được tạo này để xóa bất kỳ tham chiếu chuỗi nào đến hình ảnh. Mở DrinksListView.swift . Bạn sẽ thấy Image("milkshake")bên trong các mục trên thanh công cụ được thêm vào chế độ xem. Thay thế dòng bằng dòng sau:
Image(Asset.Assets.milkshake.name)
Ở đây, bạn đã tham chiếu tên hình ảnh cho milkshake. Hiện tại, SwiftGen không hỗ trợ làm việc trực tiếp với SwiftUI. Bạn sẽ học cách tự thêm cái này sau. Hiện tại, bạn vẫn có thể sử dụng những gì có sẵn để tải nội dung hình ảnh mà không cần tham chiếu trực tiếp đến chuỗi.
Sử dụng các mẫu cơ bản bổ sung
Có một số mẫu bổ sung mà bạn có thể tận dụng mà không cần tùy chỉnh.
Làm việc với trình tạo giao diện
Ứng dụng bạn đang làm việc đang sử dụng SwiftUI. Tuy nhiên, để giới thiệu khả năng của SwiftGen để làm việc với Trình tạo giao diện, dự án mẫu bao gồm một bảng phân cảnh và một số bộ điều khiển chế độ xem. Bắt đầu bằng cách tạo mã để hỗ trợ Trình tạo giao diện hoặc Bảng phân cảnh bằng cách thêm phần sau vào swiftgen.yml :
Cho SwiftGen biết rằng bạn muốn nó tìm kiếm bất kỳ tệp nào được hỗ trợ trình tạo giao diện trong thư mục gốc của dự án của bạn.
Một điều tuyệt vời về SwiftGen là nó tách biệt khái niệm Cảnh khỏi Segues . Điều này cho biết tệp mà cảnh của bạn sẽ xuất ra.
Cuối cùng, điều này cho biết tất cả thông tin giả mạo sẽ được xuất ra ở đâu.
Xây dựng dự án của bạn. Thêm IB-Scenes + Generate.swift và IB-Segues + Generated.swift vào nhóm Generated , giống như bạn đã làm đối với XCAssets + Generated.swift .
Giờ đây, bạn có thể thay thế thông tin cảnh hoặc thông tin xác thực trong ứng dụng.
Bắt đầu bằng cách mở InformationViewController.swift . Trong InformationViewController, thay thế việc triển khai showAbout()bằng những điều sau:
Tất cả các segues sẽ tạo ra dưới dạng các enum trường hợp thực tế, điều này giúp mọi thứ dễ dàng hơn khi bạn kiểm tra xem segue nào được kích hoạt. Vì đơn giản, mã bạn đã thêm chỉ đang kích hoạt segue.
Tiếp theo, bên trong InformationView, thay thế makeUIViewController(context:)bằng những thứ sau:
func makeUIViewController(context: Context) -> some UIViewController {
StoryboardScene.Main.initialScene.instantiate()
}
Ở đây, bạn đã đơn giản hóa mã cần thiết để khởi tạo bộ điều khiển chế độ xem ban đầu của bảng phân cảnh. SwiftGen cung cấp một phương thức trợ giúp để nhanh chóng truy cập cảnh ban đầu của bạn.
Xây dựng và chạy. Nhấn vào Tìm hiểu thêm ở trên cùng bên phải. Bạn sẽ thấy một phương thức xuất hiện, hiển thị cho bạn một số thông tin về ứng dụng. Điều này được gọi vào makeUIViewController(context:)để biết chế độ xem nào cần tải.
Bây giờ, hãy nhấn vào nút About . Bạn sẽ kích hoạt segue mà bạn đã sửa đổi.
Làm việc với JSON
Cuối cùng chúng ta sẽ thêm phần hỗ trợ cho JSON. Thêm phần sau vào cuối swiftgen.yml :
Điều này hiện cung cấp một cách để tham chiếu đến bất kỳ tệp JSON nào bạn có trong tài nguyên của dự án. Bạn sẽ sử dụng điều này để chuyển đổi dữ liệu giả đi kèm với ứng dụng.
Xây dựng dự án, sau đó thêm JSON + Generated.swift vào nhóm Đã tạo của bạn .
Bây giờ, hãy mở Drink.swift . Loại bỏ hoàn toàn các struct tên được đặt tên MockData. Sau đó, thay thế phần mở rộng cho DrinkStore, được tìm thấy ở dưới cùng, bằng phần mở rộng sau:
extension DrinkStore {
static var mockData: [Drink] {
do {
let data = try JSONSerialization.data(
withJSONObject: JSONFiles.starterDrinks,
options: [])
let mockDrinks = try JSONDecoder().decode([Drink].self, from: data)
return mockDrinks
} catch {
print(error.localizedDescription)
return []
}
}
}
Tại đây, bạn sẽ thấy các tham chiếu mã của mình JSONFiles.starterDrinks. Mở MockData.json và nhận thấy khóa đầu tiên trong tệp, có tên là starterDrinks . SwiftGen đã lấy đối tượng cấp cao nhất này và cung cấp nó dưới dạng thuộc tính tĩnh trên JSONFiles để bạn tham khảo khi cần.
Xây dựng và chạy. Bạn sẽ không nhận thấy bất cứ điều gì khác so với trước đây – chỉ là đồ uống hiển thị trong danh sách.
Làm việc với chuỗi
Có lẽ một trong những tiện ích lớn nhất mà SwiftGen cung cấp là khả năng sử dụng các biến để tham chiếu các chuỗi được bản địa hóa(Localized Strings). Đó là một thực tiễn tuyệt vời để đặt bất kỳ văn bản nào bạn sẽ trình bày cho người dùng ứng dụng của mình bên trong các tệp strings hoặc tệp stringsdict được bản địa hóa . Nhưng nếu bạn đã làm điều này, bạn biết rằng một khi bạn vượt quá một số chuỗi, sẽ trở nên khó khăn để nhớ những chuỗi nào có sẵn. Cũng có cảm giác thừa rằng bạn có một chuỗi trong tệp chuỗi và… một chuỗi trong mã của bạn.
Dự án này chứa các tệp chuỗi sau:
Localizable.strings : Tệp chuỗi run of the-mill của bạn, được tạo bằng các khóa và giá trị.
Localizable.stringsdict : Bạn nên sử dụng các tệp stringdict bất cứ khi nào bạn cần lo lắng về các chuỗi đa nguyên. Loại tệp này không chỉ hỗ trợ dịch các chuỗi mà còn hỗ trợ cách đa hóa các từ cho bất kỳ biến thể nào mà một ngôn ngữ yêu cầu.
Để chuyển đổi tất cả các tệp chuỗi của bạn, hãy thêm phần sau vào swiftgen.yml :
Bạn nên biết một số điều quan trọng về những gì bạn đã thêm ở đây:
Khi bạn chuyển đổi các tệp chuỗi của mình, bạn chỉ nên sử dụng một trong các thư mục được bản địa hóa. Đối với mỗi ngôn ngữ được thêm vào, một thư mục bản địa hóa mới sẽ được tạo. Trong dự án này, bản địa hóa duy nhất là tiếng Anh. Nếu bạn thêm nhiều ngôn ngữ hơn, không cần phải sửa đổi mục nhập này để nhận các bản dịch bổ sung đó. Bởi vì tệp chuỗi phải có một tập hợp các khóa và giá trị phù hợp, bạn sẽ tham chiếu các bản dịch giống như bạn sẽ làm nếu bạn không sử dụng SwiftGen.
Bạn đã thêm một tham số mới, được đặt tên publicAccess. Nếu bạn nhìn xung quanh bất kỳ tệp nào đã tạo mà bạn đã thêm, bạn sẽ thấy tất cả các loại đều có công cụ sửa đổi quyền truy cập internal. Bằng cách sử dụng tham số này, bạn có thể thay đổi công cụ sửa đổi quyền truy cập của các bảng kê đã tạo thành công khai.
Build dự án, sau đó thêm Strings + Generated.swift vào nhóm Generated . Trước khi bạn chuyển đổi tất cả các chuỗi trong ứng dụng, điều quan trọng là phải hiểu tệp này hơi khác một chút như thế nào.
Hiểu tệp
Mở Strings + Generated.swift . Bạn sẽ thấy kiểu cha được tạo, được đặt tên L10n. Trong phần này enum, bạn sẽ thấy một số kiểu con được tạo bên trong nó DrinkDetail: Navigationvà DrinkList. Chúng tương ứng với các chuỗi được khai báo trong Localizable.strings .
Mở Localizable.strings và xem cách nó khai báo mục nhập đầu tiên:
"DrinkList.Navigation.Title" = "Drinks";
Lưu ý cách khai báo khóa bằng ký hiệu không gian tên bằng dấu chấm:
DrinkList : Điều này cho biết chuỗi này thuộc về màn hình Danh sách đồ uống.
Navigation : Cho biết chuỗi này sẽ được sử dụng trong thanh điều hướng.
Title : Cuối cùng, điều này cho biết đó là tiêu đề trong thanh điều hướng.
Bây giờ, bạn có thể chọn tổ chức và đặt tên cho các chuỗi của mình theo cách khác. Không có gì sai với điều đó – kiểu đặt tên này được sử dụng để hiển thị cách SwiftGen sẽ chuyển đổi và tổ chức mã của bạn. Đối với mỗi khoảng thời gian bạn đặt trong chuỗi của mình, SwiftGen sẽ tạo thêm một kiểu con.
Quay lại Strings + Generated.swift , bạn sẽ thấy hàm tĩnh drinksCount. SwiftGen giúp bạn dễ dàng làm việc với các chuỗi đa nguyên. Thay vì phải tạo tham chiếu đến các chuỗi được bản địa hóa và sử dụng trình định dạng chuỗi, các hàm được tạo này giúp bạn dễ dàng sử dụng một hàm lấy các giá trị của chuỗi đa nguyên của bạn.
Bây giờ, chuyển đổi tất cả các chuỗi bản địa hóa được sử dụng trong ứng dụng để trỏ đến các loại được tạo. Bắt đầu bằng cách mở DrinksListView.swift . Tiếp theo, tìm dòng mã:
Text("DrinkList.Navigation.Title")
Đổi nó thành
Text(L10n.DrinkList.Navigation.title)
Chờ một chút… L10n là gì? Đây là một phím tắt cho “bản địa hóa”. Bạn cũng có thể thấy “quốc tế hóa” được viết tắt là i18n . Nếu bạn đếm các chữ cái giữa chữ cái đầu tiên và chữ “n” cuối cùng trong một trong hai từ, bạn sẽ tìm thấy 10 hoặc 18 chữ cái tương ứng. Mặc dù điều này có ý nghĩa, sẽ không tốt nếu sử dụng một tên khác cho loại chuỗi cấp cao nhất của bạn phải không?
Mở swiftgen.yml và thêm một thuộc tính vào mục nhập chuỗi của bạn, ngay sau publicAccessđó, nó trông giống như sau:
Ở đây, bạn đã thêm tham số enumName. Điều này cho phép bạn thay đổi loại từ “L10n” thành “Strings”.
Xây dựng và chạy. Lần này, bạn sẽ có một lỗi biên dịch. Điều này là do loại L10n không còn nữa. Truy cập DrinksListView.swift và tìm:
Text(L10n.DrinkList.Navigation.title)
Thay thế nó bằng
Text(Strings.DrinkList.Navigation.title)
Bây giờ, ứng dụng của bạn đang sử dụng tên loại mới mà bạn đã cung cấp ở bước trước.
Build ứng dụng của bạn. Bạn sẽ không còn gặp bất kỳ lỗi biên dịch nào nữa.
Lưu ý : Nếu bạn vẫn gặp lỗi, bạn có thể cần phải làm sạch dự án của mình. Chọn Product ▸ Clean and build folders , sau đó tạo lại.
Tiếp theo tìm tài sản drinkCountString. Đây là mã sử dụng tệp stringdict để xử lý cách hiển thị số lượng đồ uống trong danh sách. Thay thế nó bằng những thứ sau:
private var drinkCountString: String {
Strings .drinksCount (drinkStore.drinks.count)
}
Nếu bạn so sánh nó với mã ở đó trước đây, bạn có thể thấy đây là cách nhanh hơn nhiều để tham chiếu các chuỗi đa nguyên.
Bạn nên chuyển đổi tất cả các chuỗi trong dự án khỏi sử dụng chuỗi. Mở Localizable.strings và xem tất cả các khóa chuỗi. Bạn nên tìm từng cách sử dụng các khóa này trong một tệp Swift và hoán đổi nó cho các biến được tạo bởi SwiftGen.
Khi bạn hoán đổi văn bản xếp hạng trong DrinkDetailView.swift , nó sẽ sử dụng một hàm để cung cấp chuỗi, giống như cách bạn xử lý số lượng đồ uống.
Tạo mẫu tùy chỉnh
Cho đến thời điểm này, tệp swiftgen.yml của bạn đã sử dụng các mẫu mặc định do SwiftGen cung cấp. Tất cả những thứ này được lưu trữ trong SwiftGen pod. Nếu các mẫu này không cung cấp đầy đủ chức năng bạn muốn, bạn có thể tạo các mẫu của riêng mình để tạo mã theo cách bạn muốn. Các mẫu được xây dựng bằng Stencil , một dự án mã nguồn mở cung cấp ngôn ngữ tạo mẫu cho Swift. Trong phần này, bạn sẽ học cách sửa đổi các mẫu hiện có và sử dụng chúng để tạo mã của bạn.
Nếu bạn nhìn trong trình điều hướng Dự án, bạn sẽ thấy có một thư mục có tên là Mẫu . Trong đó, có hai thư mục con: Fonts và xcassets . Với những điều này, bạn sẽ được SwiftGen cung cấp hỗ trợ để sử dụng màu sắc và phông chữ trực tiếp trong SwiftUI.
Hỗ trợ màu SwiftUI
Để thêm Colorhỗ trợ SwiftUI, hãy mở asset_swift5_swiftui.stencil . Lúc đầu, mọi thứ có thể hơi choáng ngợp vì tệp không có bất kỳ hỗ trợ cú pháp mã nào.
Trên dòng 13, thêm dòng mã sau:
import SwiftUI
Tiếp theo, quay lại swiftgen.yml . Trong mục nhập đầu tiên của bạn, đối với xcassets , hãy tìm dòng nơi bạn xác định tên mẫu:
Tại đây, bạn đã thay đổi từ sử dụng templateNamesang templatePath. Điều này yêu cầu SwiftGen sử dụng mẫu tùy chỉnh của bạn thay vì mẫu tích hợp sẵn.
Xây dựng dự án, sau đó truy cập XCAssets + Generated.swift . Ở trên cùng, bây giờ bạn sẽ thấy:
import SwiftUI
Vì bạn đã thêm nhập vào tệp Stencil nên khi mã được tạo, mã sẽ chọn thay đổi này và thêm nhập mới. Khá tuyệt, phải không?
Mở asset_swift5_swiftui.stencil và thay thế:
// Add Support For SwiftUI Here
Như dưới đây:
{{accessModifier}} private(set) lazy var color: Color = {
Color(systemColor)
}()
Trong đoạn mã này, bạn có thể xem cách sử dụng Stencil nhiều hơn một chút. Đây là những gì bạn đã thêm:
{{accessModifier}}: Đây là cách bạn nói với Stencil cách thay thế bằng thứ gì đó được cung cấp trong quá trình tạo mã. Nếu bạn nhìn vào dòng 11 của tệp này, bạn sẽ thấy accessModifier được định nghĩa là một biến. Theo mặc định, công cụ sửa đổi quyền truy cập là internal. Nếu bạn nhớ từ trước, bạn đã thấy cách bạn có thể thay đổi điều này thànhpublic
Phần còn lại của điều này thực sự chỉ là mã tiêu chuẩn. Nó tạo ra màu SwiftUI từ màu UIKit.
Lưu ý : Có một sửa đổi khác được thực hiện đối với mẫu như một phần của vật liệu khởi động. Nó thay đổi loại Colorthành SystemColor.
Build lại dự án, sau đó quay lại XCAssets + Generated.swift . Trên dòng 62, bạn sẽ thấy mã từ mẫu của mình, hiện được tạo dưới dạng mã thực.
Bây giờ, bạn cần hoán đổi bất kỳ tham chiếu được mã hóa cứng nào thành một màu để sử dụng chức năng mới của mình. Mở DrinksListView.swift và tìm vị trí đặt màu nền trước trên văn bản tiêu đề điều hướng. Thay thế nó như sau:
Đây systemColor là tham chiếu đến loại màu cụ thể của nền tảng. Sẽ như UIColor vậy nếu bạn đang sử dụng iOS và NSColor nếu bạn đang sử dụng macOS.
Hai tệp có màu được khai báo bằng cách sử dụng chuỗi:
AppMain.swift
DrinkListView.swift
Hoàn tất chuyển đổi các màu còn lại từ việc sử dụng các chuỗi trong mỗi tệp này theo ví dụ trên.
Hỗ trợ Phông chữ SwiftUI
Cũng như SwiftUI Color hiện không được hỗ trợ trong SwiftGen, Font cũng không được hỗ trợ. Bắt đầu bằng cách mở font_swift5_swiftui.stencil và thêm nhập sau vào đầu tệp:
import SwiftUI
Tiếp theo, thay thế khối mã này được tìm thấy gần cuối tệp:
// Add Support For SwiftUI here
fileprivate extension Font {
}
Bằng đoạn code dưới đây:
fileprivate extension Font {
// 1
static func mappedFont(_ name: String, textStyle: TextStyle) -> Font {
let fontStyle = mapToUIFontTextStyle(textStyle)
let fontSize = UIFont.preferredFont(forTextStyle: fontStyle).pointSize
return Font.custom(name, size: fontSize, relativeTo: textStyle)
}
// 2
static func mapToUIFontTextStyle(
_ textStyle: SwiftUI.Font.TextStyle
) -> UIFont.TextStyle {
switch textStyle {
case .largeTitle:
return .largeTitle
case .title:
return .title1
case .title2:
return .title2
case .title3:
return .title3
case .headline:
return .headline
case .subheadline:
return .subheadline
case .callout:
return .callout
case .body:
return .body
case .caption:
return .caption1
case .caption2:
return .caption2
case .footnote:
return .footnote
@unknown default:
fatalError("Missing a TextStyle mapping")
}
}
}
Đây là những gì bạn đã thêm:
mappedFont(_:textStyle:)tạo một phông chữ tùy chỉnh từ tên và TextStyle. Kiểu được sử dụng để lấy kích thước phông chữ tiêu chuẩn, mặc định cho mỗi phông chữ.
mapToUIFontTextStyle(_:)chỉ đơn giản là cung cấp ánh xạ 1: 1 của SwiftUI TextStyletới UIKitTextStyle
Khối mã này tương tự như những gì bạn đã thêm để cung cấp Color hỗ trợ. Sự khác biệt duy nhất ở đây là nó dành riêng cho việc cung cấp phông chữ trực tiếp trong SwiftUI.
Ứng dụng này sử dụng hai phông chữ, cả hai đều nằm trong nhóm Tài nguyên :
NotoSans
NotoSans-Bold
Mục nhập mới này chỉ cần biết thư mục mẹ của phông chữ bạn muốn hỗ trợ ở đâu. Mọi thứ khác tương tự như tất cả các mục nhập khác mà bạn đã thêm trước đó.
Xây dựng ứng dụng của bạn và thêm Fonts+Generated.swift vào Generated . Sau khi thực hiện, hãy mở Fonts+Generated.swift . Tại đây, bạn có thể thấy cách tổ chức họ phông chữ. Bạn sẽ thấy rằng NotoSans có sẵn các biến thể sau:
Thường
In đậm
In đậm nghiêng
In nghiêng
Giống như tất cả các mã được tạo khác trong ứng dụng, nó khá dễ sử dụng. Mở AppMain.swift và thay thế dòng đầu tiên application(_:didFinishLaunchingWithOptions:)bằng dòng sau:
let buttonFont = FontFamily.NotoSans.bold.font(size: 16)
Tại đây, bạn đặt trực tiếp kích thước phông chữ thành phông chữ NotoSans Bold(in đậm) .
Tiếp theo, truy cập DrinksListView.swift , tìm tham chiếu đầu tiên đến một phông chữ, trong NavigationLink và thay thế nó bằng như sau:
Tại đây, bạn tận dụng mã của mẫu tùy chỉnh của mình để có thể tạo phông chữ SwiftUI tùy chỉnh, kích thước phù hợp với mặc định TextStyle: trong trường hợp này body.
Cuối cùng, hoàn tất việc chuyển đổi tất cả các cách sử dụng của phông chữ trong toàn bộ ứng dụng. Trong cả DrinksListView.swift và DrinkDetailView.swift , bạn sẽ tìm thấy một số nơi đặt phông chữ. Theo ví dụ trên, bạn có thể chuyển đổi mã từ việc sử dụng một chuỗi sang trọng số thích hợp của NotoSans . Mỗi vị trí trong số này đã cho biết TextStylechúng nên có vị trí nào.
Build và Run. Ứng dụng của bạn trông vẫn giống như cách nó hoạt động khi bạn bắt đầu. Nhưng bây giờ bạn sẽ có tất cả các tài nguyên được tham chiếu theo cách an toàn về kiểu loại!
Tổng kết
Bây giờ bạn có thể sử dụng SwiftGen để:
Loại bỏ nhu cầu sử dụng chuỗi để tham chiếu tài nguyên trong ứng dụng của bạn, cho dù bạn sử dụng SwiftUI hay UIKit.
Tùy chỉnh các tệp đầu ra bằng cách sử dụng các thông số cài sẵn.
Sử dụng các mẫu của riêng bạn để tạo mã.
Để tìm hiểu thêm về nó, hãy xem SwiftGen trên GitHub . Bạn cũng có thể tìm hiểu thêm về Stencil trên GitHub .
Chúng tôi hy vọng bạn thích hướng dẫn này. Nếu bạn có bất kỳ câu hỏi hoặc ý kiến nào, hãy tham gia thảo luận của diễn đàn bên dưới!
Nội dung bài viết được dịch từ link: https://www.raywenderlich.com/23709326-swiftgen-tutorial-for-ios
Trong thời đại công nghệ 4.0, các công ty đua nhau chuyển đổi số, vì vậy có rất nhiều những ứng dụng di động được phát triển để giúp tiếp cận người dùng một cách dễ dàng hơn. Để những ứng dụng có thể vươn xa ra tầm thế giới, tiếp cận được với những người dùng nước ngoài, thì ứng dụng đó cần phải hỗ trợ đa ngôn ngữ. Vì vậy hôm nay mình sẽ hướng dẫn các bạn một số cách thực hiện một ứng dụng iOS hỗ trợ đa ngôn ngữ.
Cài đặt dự án hỗ trợ đa ngôn ngữ
Bước 1: Thực hiện thêm ngôn ngữ hỗ trợ bằng cách Chọn Project -> Info(thông tin) -> Bấm nút +
Thêm ngôn ngữBỏ tích ở các file storyboard để xCode không gen ra các file String cho các files storyboard
Bước 2: Tạo file String để chứa nội dung theo các ngôn ngữ New File… -> tìm string và chọn Strings File -> Next
Bước 3: Localize file string vừa mới tạo Chọn File vừa tạo -> Bấm vào nút Localize… -> Popup hiển thị lên thì chọn Localize
Bước 4: Tích vào ngôn ngữ bạn hỗ trợ, Chọn File String localize vừa được tạo -> Ở menu bên phải mục Localization tích vào những ngôn ngữ mà ứng dụng của bạn hỗ trợ.
Chọn ngôn ngữ hỗ trợ
Vậy là việc cài đặt đa ngôn ngữ cho ứng dụng của bạn đã hoàn thành.
Thực hiện đa ngôn ngữ với các chuỗi (Localize String)
Để tránh các lỗi sai chính tả và việc thực hiện đa ngôn ngữ trở nên dễ dàng hơn thì chúng ta sẽ tạo ra một enum Localization để liệt kê các item/string dưới dạng keyword để sử dụng như sau:
enum Localization {
static let helloWorld: String = "Hello"
static let buttonChangeLanguageTitle: String = "btn.changeLanguage"
}
Với mỗi 1 key của string chúng ta cần tạo ra value tương ứng với nó ở trong file Localizable mà chúng ta đã tạo.
Thêm key/value cho các file ngôn ngữ tương ứng:
Thêm key/value cho ngôn ngữ Tiếng AnhThêm key/value cho ngôn ngữ Tiếng Việt
NOTE: – Kết thúc của dòng code phải là dấu chấm phẩy “;” nếu 1 dòng code bị thiếu nó sẽ khiến ứng dụng của bạn hiển thị không đúng ngôn ngữ. – Key phải trùng với giá trị của enum Localization
2. Tạo một file chung để quản lí ngôn ngữ như đoạn code dưới đây
// for manage language
class LanguageManager {
static let shared = LanguageManager()
private init(){}
// save language to UserDefault
func changeLanguage(_ language: Language) {
UserDefaults.standard.set(language.rawValue, forKey: "APP_LANGUAGE")
}
/// Get language of set on app, if nil use device language
/// - Returns: current language of application
func getAppLanguage() -> Language {
if let language = UserDefaults.standard.value(forKey: "APP_LANGUAGE") as? String {
return Language(rawValue: language) ?? .english
} else {
// default lan is english
let currentLanguage: String = Locale.current.languageCode ?? Language.english.rawValue
return Language(rawValue: currentLanguage) ?? .english
}
}
// define enum language
enum Language: String {
case vietnamese = "vi"
case english = "en"
}
}
Ở đây mình tạo ra một file quản lí ngôn ngữ nhằm mục đích tập trung tất cả những tính năng liên quan tới ngôn ngữ: thay đổi, lấy ra ngôn ngữ …
Chúng ta cần lưu ngôn ngữ của ứng dụng vào Local Storage để khi người dùng tắt ứng dụng vào lại thay đổi vẫn được áp dụng. Trong trường hợp này mình chọn cách dùng UserDefault vì những lí do sau: – Dễ sử dụng – Dữ liệu cần lưu không yêu cầu bảo mật – Dữ liệu lưu có dung lượng nhỏ
3. Vậy là chúng ta đã tạo xong file quản lí ngôn ngữ. Tiếp đến để việc sử dụng localization dễ dàng chúng ta sẽ tạo ra một var localized trong extension String như sau:
Sau khi tạo xong extension thì việc thực hiện code localized trở nên rất dễ dàng. Khi cần sử dụng chúng ta chỉ cần .localized là xong.
4. Demo và cách sử dụng:
Trong ví dụ này mình sẽ tạo ra 1 label hiển thị text và một nút để thay đổi ngôn ngữ của ứng dụng. Sau đó mình tạo ra một func setDataForUI() nhằm mục đích set tất cả các data liên quan tới localization ở đây. Nếu sau đó đổi ngôn ngữ ta chỉ cần gọi lại hàm này để thực hiện set lại.
Vậy là chúng ta đã hoàn thành việc thực hiện localize String cho ứng dụng. Mình hi vọng bài viết này có thể giúp được chút gì đó cho các bạn. Cảm ơn các bạn đã đọc bài viết này, chúc các bạn thành công!
Microservice là một cách tiếp cận đối với các hệ thống phân tán nhằm thúc đẩy việc sử dụng các dịch vụ chi tiết có vòng đời riêng của chúng, các dịch vụ này cộng tác với nhau. Bởi vì các microservice chủ yếu được mô hình hóa xung quanh các lĩnh vực kinh doanh, chúng tránh được các vấn đề của kiến trúc phân cấp truyền thống. Microservices cũng tích hợp các công nghệ và kỹ thuật mới đã xuất hiện trong thập kỷ qua, giúp họ tránh được sự sụp đổ của nhiều deploy kiến trúc hướng dịch vụ.
Cuốn sách này có đầy đủ các ví dụ cụ thể về việc sử dụng microservice trên khắp thế giới, bao gồm các tổ chức như Netflix, Amazon, Gilt và nhóm REA, những người đều nhận thấy rằng sự tự chủ ngày càng tăng mà kiến trúc này mang lại cho các nhóm trong công ty đó thuận lợi rất lớn.
Ai nên đọc cuốn sách này
Phạm vi của cuốn sách này rất rộng, vì hàm ý của các kiến trúc microservice chi tiết cũng rất rộng. Do đó, nó sẽ thu hút những người quan tâm đến các khía cạnh của thiết kế, phát triển, deploy, kiểm thử và bảo trì hệ thống. Những người trong số các bạn đã bắt đầu hành trình hướng tới các kiến trúc chi tiết hơn, cho dù là ứng dụng greenfield (một hệ thống hoàn toàn mới) hay là một phần của việc phân rã một hệ thống hiện có, kiểu giống monolithic, sẽ tìm thấy rất nhiều lời khuyên thiết thực để giúp bạn. Nó cũng sẽ giúp những người trong số các bạn muốn biết mọi rắc rối là gì, để bạn có thể xác định xem microservices có phù hợp với mình hay không.
Tại sao tôi viết cuốn sách này
Tôi bắt đầu nghĩ về chủ đề kiến trúc ứng dụng từ nhiều năm trước, khi làm việc để giúp mọi người cung cấp phần mềm của họ nhanh hơn. Tôi nhận ra rằng mặc dù các kỹ thuật tự động hóa, kiểm thử và Continuous Delivery trên cơ sở hạ tầng có thể hữu ích, nhưng nếu thiết kế tinh thần cơ bản của hệ thống không giúp bạn dễ dàng thực hiện các thay đổi, thì có những giới hạn đối với những gì có thể hoàn thành.
Đồng thời, nhiều tổ chức đang kiểm thử các kiến trúc lưu trữ chi tiết hơn để đạt được các mục tiêu tương tự, nhưng cũng để đạt được những thứ như cải thiện quy mô, tăng quyền tự chủ của các nhóm hoặc dễ dàng nắm bắt các công nghệ mới hơn. Kinh nghiệm của riêng tôi, cũng như của các đồng nghiệp của tôi tại ThoughtWorks và các nơi khác, buộc phải thực tế rằng việc sử dụng số lượng lớn các dịch vụ hơn với vòng đời độc lập của chúng dẫn đến nhiều vấn đề đau đầu hơn phải giải quyết. Theo nhiều cách, cuốn sách này được hình dung như một cửa hàng tổng hợp có thể giúp bao gồm nhiều loại tài liệu hàng đầu cần thiết để hiểu về microservices — điều gì đó đã giúp tôi rất nhiều trong quá khứ!
Một từ để nói về Microservices hôm nay
Microservices là một chủ đề nóng, mọi thứ đều thay đổi nhanh chóng. Mặc dù ý tưởng không phải là mới (ngay cả khi chính thuật ngữ microservice cũng đã xuất hiện rất lâu), nhưng kinh nghiệm của mọi người trên khắp thế giới, cùng với sự xuất hiện của các công nghệ mới, đang có ảnh hưởng sâu sắc đến cách chúng được sử dụng. Do tốc độ thay đổi nhanh chóng, tôi đã cố gắng tập trung cuốn sách này để nói về các ý tưởng hơn là các công nghệ cụ thể, biết rằng các chi tiết deploy luôn thay đổi nhanh hơn những suy nghĩ đằng sau chúng. Tuy nhiên, tôi hoàn toàn mong đợi rằng trong một vài năm nữa kể từ bây giờ, chúng ta sẽ học được nhiều hơn nữa về vị thế phù hợp của microservices và cách sử dụng tốt microservice.
Vì vậy, trong khi tôi đã cố gắng hết sức để chắt lọc ra những tinh túy nhất của chủ đề trong cuốn sách này, nếu chủ đề này khiến bạn hứng thú, hãy chuẩn bị cho nhiều năm học hỏi liên tục để luôn dẫn đầu về lĩnh vực nghệ thuật!
Đọc quyển sách này như nào
Cuốn sách này chủ yếu được sắp xếp theo định dạng dựa trên chủ đề. Do đó, bạn có thể muốn chuyển sang các chủ đề cụ thể mà bạn quan tâm nhất. Mặc dù tôi đã cố gắng hết sức để tham khảo các điều khoản và ý tưởng trong các chương trước, nhưng tôi muốn nghĩ rằng ngay cả những người tự cho mình là có kinh nghiệm cũng sẽ tìm thấy điều gì đó quan tâm trong tất cả các chương ở đây. Tôi chắc chắn khuyên bạn nên xem qua Chương 2, phần này đề cập một cách bao quát nhất chủ đề microservicecũng như cung cấp một số khung cho cách tôi tiếp cận mọi thứ trong trường hợp nếu bạn muốn đi sâu hơn vào một số chủ đề sau này.
Đối với những người mới làm quen với chủ đề này, tôi đã cấu trúc các chương theo cách mà tôi hy vọng sẽ có ý nghĩa khi đọc từ đầu đến cuối.
Dưới đây là tổng quan về những gì chúng tôi đề cập:
Chương 1, Microservice
Chúng ta sẽ bắt đầu bằng phần giới thiệu về microservices, bao gồm những lợi ích chính cũng như một số nhược điểm.
Chương 2, Sự tiến hoá của Kiến trúc phần mềm
Chương này thảo luận về những khó khăn mà chúng ta sẽ gặp phải và đánh đổi, cân nhắc với tư cách là kiến trúc sư của phần mềm và trình bày cụ thể về bao nhiêu điều chúng ta cần suy nghĩ với microservices.
Chương 3, Làm thế nào để Mô hình hóa Dịch vụ
Ở đây, chúng ta sẽ bắt đầu xác định ranh giới của microservices, sử dụng các kỹ thuật từthiết kế theo hướng nghiệp vụ để giúp tập trung suy nghĩ của chúng tôi.
Chương 4, Tích hợp
Đây là nơi chúng ta bắt đầu tìm hiểu sâu hơn một chút về các công nghệ cụ thể, khi chúng ta thảo luận về những loại kỹ thuật cộng tác dịch vụ nào sẽ giúp ích nhiều nhất cho chúng ta. Chúng tôi cũng sẽ đi sâu vào chủ đề giao diện người dùng và tích hợp với các sản phẩm thương mại (COTS) cũ và thương mại.
Chương 5, Tách phầnmềm monolithic
Nhiều người quan tâm đến microservices như lời giải cho các hệ thống monolithic lớn, khó thay đổi và đây chính xác là những gì chúng tôi sẽ đề cập chi tiết trong phần này
Chương 6, Deploy
Mặc dù cuốn sách này chủ yếu là lý thuyết, nhưng một số chủ đề trong cuốn sách đã bị ảnh hưởng bởi những thay đổi gần đây trong công nghệ cũng như việc deploy, chúng ta sẽ khám phá những thứ liên quan đến vấn đề này ở đây
Chương 7, Thử nghiệm
Chương này đi sâu vào chủ đề kiểm thử, một lĩnh vực đặc biệt quan tâm khi xử lý việc deploy nhiều dịch vụ rời rạc. Đặc biệt lưu ý là vai trò của các hợp đồng do khách hàng định hướng có thể giúp chúng tôi đảm bảo chất lượng phần mềm của mình.
Chương 8, Giám sát
Kiểm tra phần mềm của chúng tôi trước khi đến môi trường production sẽ không hữu ích nếu sự cố xảy ra khi chúng tôi golive và chương này khám phá cách chúng tôi có thể giám sát các hệ thống chi tiết của mình và đáp ứng với một số sự phức tạp nổi bật của hệ thống phân tán.
Chương 9, Bảo mật
Ở đây, chúng tôi sẽ kiểm tra các khía cạnh bảo mật của microservices và xem xét cách xử lý xác thực và ủy quyền giữa người dùng với dịch vụ và giữa các dịch vụ. Bảo mật là một chủ đề rất quan trọng trong ngành khoa học máy tính, một chủ đề quá dễ bị xem nhẹ. Mặc dù tôi không phải là một chuyên gia bảo mật, nhưng tôi hy vọng rằng chương này ít nhất sẽ giúp bạn xem xét một số khía cạnh bạn cần lưu ý khi xây dựng hệ thống, và hệ thống microservice nói riêng.
Chương 10, ĐịnhluậtConway và Thiết kế Hệ thống
Chương này tập trung vào sự tác động lẫn nhau của cơ cấu tổ chức và kiến trúc. Nhiều tổ chức đã nhận ra rằng rắc rối sẽ xảy ra nếu bạn không giữ hai bên hòa hợp. Chúng tôi sẽ cố gắng giải quyết tận cùng tình thế khó xử này và xem xét một số cách khác nhau để điều chỉnh thiết kế hệ thống với cấu trúc của nhóm của bạn.
Chương 11, Microservice khi mở rộng
Đây là lúc chúng tôi bắt đầu xem xét việc thực hiện tất cả những điều này trên quy mô lớn, để chúng tôi có thể xử lý nguy cơ lỗi gia tăng có thể xảy ra với số lượng lớn dịch vụ cũng như lưu lượng truy cập lớn.
Chương 12, Kết hợp tất cả lại với nhau
Chương cuối cùng cố gắng chắt lọc bản chất cốt lõi của những gì làm cho các microservice trở nên khác biệt. Nó bao gồm một danh sách bảy nguyên tắc microservices, cũng như mộttóm tắt những điểm chính của cuốn sách.
Các quy ước được sử dụng trong cuốn sách này
Các quy ước về kiểu chữ sau đây được sử dụng trong cuốn sách này:
In nghiêng
Cho biết các thuật ngữ mới, URL, địa chỉ email, tên tệp và phần mở rộng tệp.
Độ rộng chữ không đổi
Được sử dụng cho danh sách chương trình, cũng như trong các đoạn văn để chỉ các phần của chương trình như tên biến hoặc hàm, cơ sở dữ liệu, kiểu dữ liệu, biến môi trường, câu lệnh và từ khóa.
Độ rộng chữ không đổi in đậm
Hiển thị các lệnh hoặc văn bản khác mà người dùng phải nhập theo nghĩa đen.
Độ rộng chữ không đổi in nghiêng
Hiển thị văn bản cần được thay thế bằng các giá trị do người dùng cung cấp hoặc bằng các giá trị được xác định theo ngữ cảnh.
Sự nhìn nhận
Cuốn sách này dành riêng cho Lindy Stephens, nếu không có cô ấy thì quyển sách này đã không hình thành. Cô ấy đã khuyến khích tôi bắt đầu cuộc hành trình này, hỗ trợ tôi trong suốt quá trình viết lách thường xuyên căng thẳng và là đối tác tốt nhất mà tôi có thể yêu cầu. Tôi cũng muốn dành điều này cho bố tôi, Howard Newman, người đã luôn ở bên tôi. Điều này là cho cả hai người.
Tôi muốn chọn ra Ben Christensen, Vivek Subramaniam và Martin Fowler vì đã đưa cho tôi những phản hồi chi tiết trong suốt quá trình viết, giúp định hình cuốn sách này. Tôi cũng muốn gửi lời cảm ơn đến James Lewis, người mà đã cùng tôi uống bia cùng thảo luận về những ý tưởng được trình bày trong cuốn sách này. Cuốn sách này sẽ chỉ là một cái bóng của chính nó nếu không có sự giúp đỡ và hướng dẫn của họ.
Ngoài ra, nhiều người khác đã giúp đỡ và phản hồi về các phiên bản đầu tiên của cuốn sách. Cụ thể, tôi muốn cảm ơn (không theo thứ tự cụ thể) Kane Venables, Anand Krishnaswamy, Kent McNeil, Charles Haynes, Chris Ford, Aidy Lewis, Will Thames, Jon Eaves, Rolf Russell, Badrinath Janakiraman, Daniel Bryant, Ian Robinson, Jim Webber, Stewart Gleadow, Evan Bottcher, Eric Sword, Olivia Leonard, và tất cả các đồng nghiệp khác của tôi tại ThoughtWorks và trong toàn ngành, những người đã giúp tôi đạt được điều này.
Cuối cùng, tôi muốn cảm ơn tất cả những người tại O’Reilly, bao gồm Mike Loukides vì đã đưa tôi vào hội đồng quản trị, biên tập viên Brian MacDonald, Rachel Monaghan, Kristen Brown, Betsy Waliszewski và tất cả những người khác đã giúp đỡ tôi theo cách có thể không bao giờ biết về.
CHƯƠNG 1 Microservices
Trong nhiều năm nay, chúng tôi đã và đang tìm ra những cách tốt hơn để xây dựng hệ thống. Chúng tôi đã học hỏi từ những gì đã có trước đó, áp dụng các công nghệ mới và quan sát cách một làn sóng công ty công nghệ mới hoạt động theo những cách khác nhau để tạo ra các hệ thống CNTT giúp làm cho cả khách hàng và nhà phát triển của chính họ hạnh phúc hơn.
Cuốn sách Domain-Driven Design (Addison-Wesley) của Eric Evans đã giúp chúng tôi hiểu tầm quan trọng của việc thể hiện thế giới thực trong mã nguồn của chúng tôi và chỉ cho chúng tôi những cách tốt hơn để lập mô hình hệ thống của chúng tôi. Khái niệm Continuous Delivery cho thấy cách chúng tôi có thể đưa phần mềm của mình lên môi trường production một cách ngày càng hiệu quả hơn, truyền cho chúng tôi ý tưởng rằng chúng tôi nên coi mọi thay đổi là một ứng cử viên release. Sự hiểu biết của chúng tôi về cách hoạt động của Web đã giúp chúng tôi phát triển những cách tốt hơn để máy giao tiếp với cácmáy. Khái niệm kiến trúc lục giác (hexagonal architecture) của Alistair Cockburn đã hướng dẫn chúng tôi thoát khỏi kiến trúc nhiều lớp nơi logic kinh doanh có thể ẩn giấu. Nền tảng ảo hóa cho phép chúng tôi cung cấp và thay đổi kích thước các server của mình theo ý muốn, với cơ sở hạ tầng tự động hóa cung cấp cho chúng tôi cách xử lý những máy này trên quy mô lớn. Một số tổ chức lớn, thành công như Amazon và Google tán thành quan điểm về các nhóm nhỏ sở hữu toàn bộ vòng đời dịch vụ của họ. Và, gần đây hơn, Netflix đã chia sẻ với chúng tôi các cách xây dựng hệ thống chống phân mảnh ở quy mô mà chỉ 10 năm trước khó có thể hiểu được.
Domain-driven. Continuous Delivery. Ảo hóa theo yêu cầu. Tự động việc deploy cơ sở hạ tầng. Các team nhỏtự trị. Hệ thống ở quy mô lớn. Microservices đã xuất hiện từ thế giới này. Chúng không được phát minh hoặc mô tả trước thực tế; chúng nổi lên như một xu hướng hoặc một kiểu mẫu, từ việc sử dụng trong thế giới thực. Nhưng chúng tồn tại chỉ vì tất cả những gì đã qua trước đó. Trong suốt cuốn sách này, tôi sẽ rút ra những điểm mấu chốt từ công việc trước đây để giúp vẽ nên bức tranh về cách xây dựng, quản lý và phát triển microservices.
Nhiều tổ chức đã phát hiện ra rằng bằng cách áp dụng một cách chi tiết kiến trúc microservice, họ có thể cung cấp phần mềm nhanh hơn và nắm bắt các công nghệ mới hơn. Microservicecho phép chúng ta tự do hơn đáng kể để phản ứng và đưa ra các quyết định khác nhau, cho phép chúng ta phản ứng nhanh hơn với sự thay đổi không thể tránh khỏi tác động đến tất cả chúng ta.
Microservices là gì?
Microservices là các dịch vụ nhỏ, tự trị hoạt động cùng nhau. Hãy làm rõ quan điểm đó xuống sâu một chút và xem xét các đặc điểm làm cho các microservice trở nên khác biệt.
Nhỏ và tập trung vào việc làm tốt một việc
Lượng mã nguồn tăng lên khi chúng tôi viết mã nguồn để thêm các tính năng mới. Theo thời gian, có thể khó biết nơi cần thực hiện thay đổi vì khối lượng mã nguồn quá lớn. Mặc dù có một định hướng cho các mã nguồn kiểumonolithic được chia mô-đun rõ ràng, nhưng tất cả các ranh giới trong quá trình tùy ý này thường bị chia nhỏ. Mã nguồnliên quan đến các chức năng tương tự nhau bắt đầu lan rộng khắp nơi, khiến việc sửa lỗi hoặc deploy khó khăn hơn.
Trong một hệ thống monolithic, chúng tôi chiến đấu chống lại những lực lượng này bằng cách cố gắng đảm bảo mã của chúng tôi gắn kết hơn, thường bằng cách tạo ra các mô-đun hoặc trừu tượng. Sự liên kết — động lực để có các mã liên quan được nhóm lại với nhau — là một khái niệm quan trọng khi chúng ta nghĩ về microservices. Điều này được củng cố bởi định nghĩa của Robert C. Martin về Nguyên tắc trách nhiệm duy nhất, trong đó nêu rõ "Tập hợp những thứ thay đổi vì cùng một lý do và tách những thứ thay đổi vì những lý do khác nhau."
Microservices áp dụng cách tiếp cận tương tự đối với các dịch vụ độc lập. Chúng tôi tập trung vào việc đặt ranh giới dịch vụ của mình vào ranh giới về nghiệp vụ, làm cho nó rõ ràng là nơi mã sống cho một phần chức năng nhất định. Và bằng cách giữ cho dịch vụ này tập trung vào một ranh giới rõ ràng, chúng tôi tránh xa những cám dỗ để nó tăng trưởng quá lớn, và tất cả những khó khăn liên quan mà điều này có thể gây ra.
Câu hỏi tôi thường được hỏi lànhỏ như thế nào là nhỏ_? Đưa ra một số dòng mã nguồn là cảmột vấn đề, vì một số ngôn ngữ dễbiểu đạt hơn những ngôn ngữ khác và do đó có thể làm được nhiều hơn với ít dòng mã hơn. Chúng ta cũng phải xem xét thực tế rằng chúng ta có nhiều dependenc, bản thân chúng chứa nhiều dòng mã. Ngoài ra, một số phần trong domain của bạn có thể phức tạp về mặt pháp lý, đòi hỏi nhiều mã nguồn hơn. Jon Eaves tại RealEstate.com.au ở Úc mô tả một microservice là thứ có thể được viết lại sau hai tuần, một quy tắc chung có ý nghĩa đối với bối cảnh cụ thể của anh ấy.
Một câu trả lời hơi sáo rỗng khác mà tôi có thể đưa ra làđủ nhỏ và không nhỏ hơn_. Khi phát biểu tại các hội nghị, tôi gần như luôn đặt câu hỏi rằngai có một hệ thống quá lớn và bạn muốn chia_nhỏ nó_? Gần như tất cả mọi người đều giơ tay. Chúng tôi dường như có cảm giác rất tốt về những gì là quá lớn và vì vậy có thể lập luận rằng một khi một đoạn mã không còncảm thấyquá lớn nữa, thì có lẽ nó đã đủ nhỏ.
Một yếu tố lớn trong việc giúp chúng tôi trả lờinhỏ như thế nào_? Làdịch vụ phù hợp với cấu trúc nhóm như thế nào. Nếu mã nguồn cơ sở quá lớn để được quản lý bởi một nhóm nhỏ, tìm cách chia nhỏ nó là rất hợp lý. Chúng ta sẽ nói thêm về việc sắp xếp tổ chức ở phần sau.
Khi nói đến việc nhỏ đến mức nào là đủ nhỏ, tôi muốn nghĩ theo các thuật ngữ sau: dịch vụ càng nhỏ, bạn càng tối đa hóa lợi ích và mặt trái của kiến trúc vi mô. Khi bạn có những service nhỏ hơn, những lợi ích xung quanh việc dependency lẫn nhau sẽ tăng lên. Nhưng một số phức tạp nổi lên từ việc ngày càng có nhiều bộ phận cùng phát triển, điều mà chúng ta sẽ khám phá trong suốt cuốn sách này. Khi bạn xử lý tốt hơn vớisự phức tạp này, bạn có thể cố gắng cho các dịch vụ nhỏ hơn và nhỏ hơn.
Tự chủ
Microservice của chúng tôi là một thực thể riêng biệt. Nó có thể được deploy như một dịch vụ biệt lập trên nền tảng như một dịch vụ (PAAS), hoặc nó có thể là quy trình hệ điều hành của riêng nó. Chúng tôi cố gắng tránh đóng gói nhiều dịch vụ vào cùng một máy, mặc dù định nghĩa vềmáytrong thế giới ngày nay khá mơ hồ! Như chúng ta sẽ thảo luận ở phần sau, mặc dù sự cô lập này có thể thêm một số chi phí, nhưng kết quả là sự đơn giản khiến hệ thống phân tán của chúng tôi dễ dàng lập luận hơn nhiều và các công nghệ mới hơn có thể giảm thiểu nhiều rủi ro liên quan đến hình thức deploy này.
Bản thân tất cả các giao tiếp giữa các dịch vụ đều thông qua các cuộc gọi mạng, để thực thi sự tách biệt giữa các dịch vụ và tránh các nguy cơ kết nối chặt chẽ.
Các dịch vụ này cần có khả năng thay đổi độc lập với nhau và được tự deploy mà không yêu cầu khách hàng thay đổi. Chúng tôi cần suy nghĩ về những gì dịch vụ của chúng tôi nên để lộ và những gì chúng nên cho phép được ẩn. Nếu có quá nhiều sự chia sẻ, các dịch vụ mà chúng tôi cung cấp sẽ lại trở nên liên kết chặt chẽ hơn với phần nội tại của dịch vụ. Điều này làm giảm quyền tự chủ của chúng tôi, vì nó đòi hỏi sự phối hợp bổ sung với người dùng khi thực hiện các thay đổi.
Dịch vụ của chúng tôi có giao diện lập trình ứng dụng (API) và các dịch vụ cộng tác giao tiếp với chúng tôi thông qua các API đó. Chúng tôi cũng cần phải suy nghĩ về thuật ngữ công nghệ nào phù hợp để đảm bảo rằng bản thân điều này không ảnh hưởng đến khách hàng. Điều này có thể có nghĩa là chọn các API không liên quan gì về công nghệ để đảm bảo rằng chúng tôi không bịhạn chế bởi các lựa chọn công nghệ. Chúng ta sẽ quay lại nhiều lần với tầm quan trọng của các API tốt, được tách biệt trong suốt cuốn sách này.
Nếu không tách rời, mọi thứ sẽ bị chia nhỏ đối với chúng tôi. Quy tắc vàng: bạn có thể thực hiện thay đổi đối với một dịch vụ và tự deploy dịch vụ đó mà không cần thay đổi bất kỳ điều gì khác không? Nếu câu trả lời là không, thì bạn sẽ khó đạt được nhiều lợi thế mà chúng ta thảo luận trong suốt cuốn sách này.
Để thực hiện tốt việc phân tách, bạn sẽ cần phải lập mô hình dịch vụ của mình phù hợp và nhận các API một cách đúng đắn. Tôi sẽ nói về điều đó rất nhiều.
Các lợi ích chính
Các lợi ích của microservices rất nhiều và đa dạng. Nhiều lợi ích trong số này có thể thấy ở bất kỳ hệ thống phân tán nào. Tuy nhiên, microservices có xu hướng đạt được những lợi ích này ở một mức độ lớn hơn chủ yếu là do chúng tiếp nhận các khái niệm đằng sau hệ thống phân tán và kiến trúc hướng dịch vụ đến đâu.
Tự do về công nghệ
Với một hệ thống bao gồm nhiều dịch vụ cộng tác với nhau, chúng tôi có thể quyết định sử dụng các công nghệ khác nhau bên trong mỗi dịch vụ. Điều này cho phép chúng tôi chọn công cụ phù hợp cho từng công việc, thay vì phải chọn cách tiếp cận tiêu chuẩn hóa hơn, một cách làm phù hợp với tất cả thường trở thành mẫu số chung thấp nhất.
Nếu một phần trong hệ thống của chúng tôi cần cải thiện hiệu suất của nó, chúng tôi có thể quyết định sử dụng một công nghệ khác có khả năng đạt được mức yêu cầu hiệu suất tốt hơn. Chúng tôi cũng có thể quyết định rằng cách chúng tôi lưu trữ dữ liệu của mình cần phải thay đổi đối với các phần khác nhau trong hệ thống của chúng tôi. Ví dụ: đối với mạng xã hội, chúng tôi có thể lưu trữ các tương tác của người dùng trong cơ sở dữ liệu hướng biểu đồ (GraphQL) để phản ánh bản chất có tính liên kết cao của biểu đồ xã hội, nhưng có lẽ các bài đăng mà người dùng thực hiện có thể được lưu trữ trong cơ sở dữ liệu kiểu tài liệu, tạo ra một kiến trúc không đồng nhất như thể hiện trong Hình 1-1.
Hình 1-1 Microservices có thể cho phép bạn dễ dàng nắm bắt các công nghệ khác nhau
Với microservices, chúng tôi cũng có thể áp dụng công nghệ nhanh hơn và hiểu rõ những tiến bộ mới có thể giúp chúng tôi như thế nào. Một trong những rào cản lớn nhất đối với việc thử và áp dụng công nghệ mới là những rủi ro đi kèm với nó. Với một ứng dụng monolithic, nếu tôi muốn thử một ngôn ngữ lập trình, cơ sở dữ liệu hoặc framework mới, thì bất kỳ thay đổi nào cũng sẽ ảnh hưởng đến một lượng lớn hệ thống của tôi. Với một hệ thống bao gồm nhiều dịch vụ, tôi có nhiều chỗ mới để thử một phần công nghệ mới. Tôi có thể chọn một dịch vụ có lẽ là rủi ro thấp nhất và sử dụng công nghệ ở đó, cầnbiết rằng tôi có thể hạn chế mọi tác động tiêu cực tiềm ẩn. Nhiều tổ chức nhận thấy khả năng tiếp thu nhanh hơn các công nghệ mới là một lợi thế thực sự của họ.
Tất nhiên, việc áp dụng nhiều công nghệ không phải là không có rủi ro hay chi phí. Một số tổ chức chọn đặt một số ràng buộc đối với lựa chọn ngôn ngữ. Netflix và Twitter, chẳng hạn, chủ yếu sử dụng Máy ảo Java (JVM) làm nền tảng, vì họ hiểu rất rõ về độ tin cậy và hiệu suất của hệ. Họ cũng phát triển các thư viện và công cụ cho JVM giúp hoạt động trên quy mô lớn dễ dàng hơn nhiều, nhưng lại gây khó khăn hơn cho các dịch vụ hoặc ứng dụng khách (client) không dựa trên Java. Nhưng cả Twitter và Netflix đều không chỉ sử dụng một stack công nghệ cho tất cả các công việc. Một điểm khác đối với những lo ngại về việc sử dụng các công nghệ khác nhau là khối lượng công việc, khối lượng mã nguồn. Nếu tôi thực sự có thể viết lại microservice của mình trong hai tuần, bạn có thể giảm thiểu rủi ro khi sử dụng công nghệ mới.
Như bạn sẽ thấy trong suốt cuốn sách này, cũng giống như nhiều điều liên quan đến microservices, đó là tất cả về việc tìm kiếm sự cân bằng phù hợp. Chúng ta sẽ thảo luận về cách lựa chọn công nghệ trong Chương 2, chương này tập trung vào việctiến hóakiến trúc; và trong Chương 4, liên quan đến tích hợp, bạn sẽ học cách đảm bảo rằng các dịch vụ của bạn có thể phát triển công nghệ của chúng một cách độc lập với nhau mà không có sự kết hợp quá mức.
Khả năng phục hồi
Một khái niệm quan trọng trong kỹ thuật phục hồi là vách ngăn (vách ngăn ở khoang tàu thuỷ). Nếu một thành phần của hệ thống bị lỗi, nhưng lỗi đó không rò rỉ và lan ra, bạn có thể cô lập sự cố và phần còn lại của hệ thống có thể tiếp tục hoạt động. Ranh giới dịch vụ trở thành vách ngăn rõ ràng của bạn. Trong một dịch vụ monolithic, nếu dịch vụ bị lỗi, mọi thứ sẽ ngừng hoạt động. Với một hệ thống monolithic, chúng ta có thể chạy trên nhiều máy để giảm nguy cơ hỏng hóc và đổ vỡ hệ thống, nhưng với microservices, chúng ta có thể xây dựng các hệ thống xử lý lỗi toàn bộ của các dịch vụ và làm suy giảm chức năng tương ứng.
Tuy nhiên, chúng tôi cần phải cẩn thận. Để đảm bảo các hệ thống microservice của chúng tôi có thể nắm bắt đúng cách khả năng phục hồi được cải thiện này, chúng tôi cần hiểu các nguồn lỗi mới mà các hệ thống phân tán phải đáp ứng. Hệ thống mạng có thể và sẽ thất bại, và máy móc cũng thế. Chúng tôi cần biết cách xử lý vấn đề này và tác động (nếu có) của nó đối với người dùng cuối đang sử dụng phần mềm của chúng tôi.
Chúng ta sẽ nói thêm về khả năng xử lý tốt hơn và cách xử lý các chế độ lỗi trong Chương 11.
Mở rộng quy mô
Với một dịch vụ lớn, monolithic, chúng tôi phải mở rộng mọi thứ cùng với nhau. Một phần nhỏ của hệ thống tổng thể của chúng tôi bị hạn chế về hiệu suất, nhưng nếu hành vi đó bị khóa trong một ứng dụng monolithic khổng lồ, chúng tôi phải xử lý việc mở rộng mọi thứ thay vì một phần nhỏ. Với các dịch vụ nhỏ hơn, chúng ta chỉ có thể mở rộng các dịch vụ cần mở rộng quy mô, cho phép chúng ta chạy các phần khác của hệ thống trên phần cứng nhỏ hơn, cầnít sức mạnh hơn, như trong Hình 1-2.
Hình 1-2. Bạn có thể nhắm mục tiêu mở rộng quy mô chỉ ở những dịch vụ nhỏ cần nó
Gilt, một nhà bán lẻ thời trang trực tuyến, đã sử dụng microservices vì lý do chính xác này. Bắt đầu từ năm 2007 với một ứng dụng Rails kiểu monolithic, đến năm 2009 hệ thống của Gilt đã không thể đáp ứng với số lượng người dùng sử dụng nó. Bằng cách tách nhỏra các phần cốt lõi của hệ thống, Gilt chắc chắn có thể đáp ứng với lượng truy cập tăng đột biến và ngày nay có hơn 450 microservices, mỗi microservices chạy trên nhiều máy riêng biệt.
Khi sử dụng các hệ thống cung cấp theo yêu cầu như hệ thống được cung cấp bởi Amazon Web Services, chúng tôi thậm chí có thể áp dụng quy mô này theo yêu cầu cho những phần cần nó. Điều này cho phép chúng tôi kiểm soát chi phí của mình hiệu quả hơn. Không phải thường xuyên mà cách tiếp cận kiến trúc có thể tương quan chặt chẽ đến mức tiết kiệm chi phí gần như ngay lập tức.
Dễ deploy
Thay đổi một dòng mã nguồn đối với ứng dụng monolithic dài hàng triệu dòng yêu cầu toàn bộ ứng dụng phải được deploy để release thay đổi. Đó có thể là một lầndeploy có tác động lớn, rủi ro cao. Trong thực tế, việc deploy có tác động lớn, rủi ro cao sẽ không thường xuyên xảy ra do nỗi sợ hãi mà ai cũng có thể hiểu được. Thật không may, điều này có nghĩa là các thay đổi của chúng tôi được xây dựng và tích lũy giữa các bản release, cho đến khi phiên bản ứng dụng mới của chúng tôi bắt đầu được đưa lên môi trường production có hàng loạt thay đổi. Và sự khác nhau giữa các lần release càng lớn, thì nguy cơ chúng tôi gặp sự cố càng cao!
Với microservices, chúng ta có thể thực hiện thay đổi đối với một dịch vụ duy nhất và deploy nó một cách độc lập với phần còn lại của hệ thống. Điều này cho phép chúng tôi deploy mã nguồncủa mình nhanh hơn. Nếu sự cố xảy ra, nó có thể được cô lập nhanh chóng cho một dịch vụ riêng lẻ, giúp dễ dàng đạt được quá trình khôi phục nhanh. Điều đó cũng có nghĩa là chúng tôi có thể đưa chức năng mới của mình đến với những khách hàng nhanh hơn. Đây là một trong những lý do chính tại sao các tổ chức như Amazon và Netflix sử dụng các kiến trúc này — để đảm bảo họ loại bỏ nhiều trở ngại nhất có thể để đưa phần mềm ra thế giới thật.
Công nghệ trong lĩnh vực này đã thay đổi rất nhiều trong vài năm qua và chúng ta sẽ xem xét sâu hơn chủ đề deploy trong thế giới microservice trong Chương 6.
Liên kết với tổ chức
Nhiều người trong chúng tôi đã gặp phải các vấn đề liên quan đến các nhóm lớn vàmã nguồn cơ sở. Những vấn đề này có thể trở nên trầm trọng hơn khi nhóm bị phân tán. Chúng tôi cũng biết rằng các nhóm nhỏ hơn làm việc trên các mã nguồn cơ sở nhỏ hơn có xu hướng hiệu quả hơn.
Microservices cho phép chúng tôi điều chỉnh kiến trúc phù hợp hơn với tổ chức của mình, giúp chúng tôi giảm thiểu số lượng người làm việc trên bất kỳ mã nguồn cơ sở nào để đạt được điểm tốt về quy mô nhóm và năng suất. Chúng tôi cũng có thể chuyển quyền sở hữu dịch vụ giữa các nhóm để cố gắng giữ cho mọi người làm việc trên một dịch vụ được tập trung vào vị trí. Chúng ta sẽ đi vào chi tiết hơn về chủ đề này khi chúng ta thảo luận về định luật Conway trong Chương 10.
Khả năng kết hợp
Một trong những tiềm năng quan trọng của hệ thống phân tán và kiến trúc hướng dịch vụ là chúng tôi mở ra cơ hội tái sử dụng chức năng. Với microservices, chúng tôi cho phép sử dụng chức năng của mình theo những cách khác nhau cho các mục đích khác nhau. Điều này có thể đặc biệt quan trọng khi chúng ta nghĩ về cách khách hàng sử dụngphần mềm. Đã qua rồi cái thời mà chúng ta có thể suy nghĩ hạn hẹp về trang web trên máy tính để bàn hoặc ứng dụng di động của mình. Bây giờ chúng ta cần nghĩ đến vô số cách mà chúng ta có thể muốn kết hợp các khả năng với nhau cho Web, ứng dụng native, web di động, ứng dụng máy tính bảng hoặc cho thiết bị đeo. Khi các tổ chức chuyển từ suy nghĩ về các kênh hẹp sang các khái niệm toàn diện hơn về sự tương tác của khách hàng, chúng tôi cần các kiến trúc có thể theo kịp.
Với microservices, hãy nghĩ đến việc chúng tôi mở các đường nối trong hệ thống của mình mà các bên bên ngoài có thể giải quyết được. Khi hoàn cảnh thay đổi, chúng ta có thể xây dựng mọi thứ theo những cách khác nhau. Với ứng dụng monolithic, tôi thường có một đường may thô có thể được sử dụng từ bên ngoài. Nếu tôi muốn chia nhỏ điều đó để kiếm thứ gì đó hữu ích hơn, tôi sẽ cần một cái búa! Trong Chương 5, tôi sẽ thảo luận về các cách để bạn chia nhỏ các hệ thống monolithic hiện có và hy vọng thay đổi chúng thành một số microser có thể tái sử dụng và có thể kết hợp với nhau
Tối ưu hóa khả năng thay thế
Nếu bạn làm việc tại một tổ chức quy mô trung bình hoặc lớn hơn, rất có thể bạn biết đến một hệ thống mang tính di sản lớn và khó chịu nào đó đang nằm trong góc. Cái mà không ai muốn chạm vào. Điều quan trọng đối với cách công ty của bạn điều hành, nhưng điều đó tình cờ được viết trong một số biến thể Fortran kỳ lạ và chỉ chạy trên phần cứng đã hết tuổi thọ 25 nămtrước. Tại sao nó vẫn chưa được thay thế? Bạn biết lý do tại sao: đó là một công việc quá lớn và đầy rủi ro.
Với các dịch vụ riêng lẻ của chúng tôi có quy mô nhỏ, chi phí để thay thế chúng bằng cách deploy tốt hơn, hoặc thậm chí xóa chúng hoàn toàn, sẽ dễ quản lý hơn nhiều. Bạn có thường xuyên xóa hơn một trăm dòng mã chỉ trong một ngày và không lo lắng quá nhiều về điều đó? Với các microservice thường có kích thước tương tự, các rào cản đối với việc viết lại hoặc xóa hoàn toàn dịch vụ là rất thấp.
Các nhóm sử dụng phương pháp tiếp cận microservice cảm thấy thoải mái với các dịch vụ viết lại hoàn toàn khi được yêu cầu và chỉ giết dịch vụ khi không còn cần thiết nữa. Khi một mã nguồn cơ sở chỉ dài vài trăm dòng, rất khó để mọi người nảy sinh tình cảm gắn bó với nó và hơn nữa chi phí thay thế nó là khá nhỏ.
Còn về Kiến trúc Hướng Dịch vụ (SOA)?
Kiến trúc hướng dịch vụ (SOA) là một cách tiếp cận thiết kế trong đó nhiều dịch vụ hợp tác để cung cấp một số khả năng cuối cùng. Dịch vụ ở đây thường có nghĩa là một quy trình hoàn toàn riêng biệt chạy trong hệ điều hành. Giao tiếp giữa các dịch vụ này thường thông qua các cuộc gọi trên mạng chứ không phải là bằng cách gọi các phương thức trong một ranh giới quy trình (method call hoặc function call).
SOA nổi lên như một cách tiếp cận để loại bỏ những thách thức của những nền tảng monolithic lớn. Đó là một cách tiếp cận nhằm mục đích thúc đẩy khả năng tái sử dụng của phần mềm; Ví dụ: hai hoặc nhiều ứng dụng người dùng cuối đều có thể sử dụng các dịch vụ giống nhau. Nó nhằm mục đích giúp việc bảo trì hoặc viết lại phần mềm dễ dàng hơn, vì về mặt lý thuyết, chúng tôi có thể thay thế một phần mềm này bằng một phần mềm khác mà không ai biết, miễn là ngữ nghĩa của dịch vụ không thay đổi quá nhiều.
SOA và bản thân của nó là một ý tưởng rất hợp lý. Tuy nhiên, dù có nhiều nỗ lực nhưng vẫn chưa có sự đồng thuận caovề một cách làm SOA_tốt_. Theo quan điểm của tôi, phần lớn ngành công nghiệp phần mềm đã không thể nhìn nhận vấn đề một cách tổng thể và đưa ra một giải pháp thay thế hấp dẫn cho câu chuyện của các nhà cung cấp khác nhau trong lĩnh vực này.
Nhiều vấn đề đặt ra trước cửa SOA thực sự là các vấn đề với những thứ như giao thức truyền thông (ví dụ: SOAP), phần mềm trung gian của nhà cung cấp, thiếu hướng dẫn về mức độ chi tiết của dịch vụ hoặc hướng dẫn sai về việc chọn vị trí để phân chia hệ thống của bạn. Chúng tôi sẽ lần lượt giải quyết từng vấn đề này trong suốt phần còn lại của cuốn sách. Người hoài nghi có thể ám chỉ rằng các nhà cung cấp đã đồng ý (và trong một số trường hợp thúc đẩy) phong trào SOA như một cách để bán được nhiều sản phẩm hơn và những sản phẩm tự đặt tên đó cuối cùng đã làm suy yếu mục tiêu của SOA.
Phần lớn sự hiểu biết thông thường về SOA không giúp bạn hiểu được cách chia điều gì đó lớn thành điều gì đó nhỏ. Nó không nói về việc lớn như thế nào là quá lớn. Nó không nói đủ về những cách thực tế, thực tế để đảm bảo rằng các dịch vụ không trở nên quá khớp với nhau. Số lượng những điều không được giải đáp là nơi bắt nguồn của nhiều cạm bẫy liên quan đến SOA.
Phương pháp tiếp cận microservice đã xuất hiện từ việc sử dụng trong thế giới thực, giúp chúng ta hiểu rõ hơn về hệ thống và kiến trúc để thực hiện tốt SOA. Vì vậy, thay vì bạn nên nghĩ microservices như một cách tiếp cận cụ thể cho SOA giống như XP hoặc Scrum là những cách tiếp cận (framework) cụ thể để phát triển phần mềm theo phương pháp Agile.
Các kỹ thuật phân rã khác
Khi bạn nắm bắt được nó, nhiều lợi thế của một kiến trúc dựa trên microservice đến từ bản chất chi tiết của nó và thực tế là nó cung cấp cho bạn nhiều lựa chọn hơn về cách giải quyết vấn đề. Nhưng liệu các kỹ thuật phân tách tương tự có thể đạt được những lợi ích tương tự không?
Thư viện được chia sẻ
Một kỹ thuật phân tách rất chuẩn được tích hợp vào hầu như bất kỳ ngôn ngữ nào là chia nhỏ một mã nguồn cơ sở thành nhiều thư viện. Các thư viện này có thể được cung cấp bởi các bên thứ ba hoặc được tạo ra trong tổ chức của riêng bạn.
Thư viện cung cấp cho bạn một cách để chia sẻ chức năng giữa các nhóm và dịch vụ. Ví dụ, tôi có thể tạo một tập hợp các tiện ích thu thập hữu ích, hoặc có thể là một thư viện thống kê có thể được sử dụng lại.
Các nhóm có thể tự tổ chức xung quanh các thư viện này và bản thân các thư viện có thể được sử dụng lại. Nhưng có một số hạn chế.
Đầu tiên, bạn mất đi tính không đồng nhất của công nghệ thực sự. Thư viện thường phải sử dụng cùng một ngôn ngữ, hoặc ít nhất là chạy trên cùng một nền tảng. Thứ hai, sự dễ dàng mà bạn có thể mở rộng các phần của hệ thống của mình một cách độc lập với nhau bị hạn chế. Tiếp theo, trừ khi bạn đang sử dụng các thư viện được liên kết động, bạn không thể deploy một thư viện mới mà không deploy lại toàn bộ quy trình, do đó, khả năng deploy các thay đổi riêng biệt của bạn bị giảm. Và có lẽ nguyên nhân chính là bạn thiếu các liên kết rõ ràng xung quanh để xây dựng các biện pháp an toàn kiến trúc nhằm đảm bảo khả năng phục hồi của hệ thống.
Thư viện được chia sẻ vẫn có giá trị của nó. Bạn sẽ thấy mình đang tạo mã cho các nhiệm vụ phổ biến không dành riêng cho bất kì lĩnh vực nào của doanh nghiệp mà bạn muốn sử dụng lại trong toàn tổ chức, đây là một ứng cử viên rõ ràng để trở thành một thư viện có thể tái sử dụng. Tuy nhiên, bạn cần phải cẩn thận. Mã dùng chung được sử dụng để giao tiếp giữa các dịch vụ có thể trở thành điểm kết hợp (khiến mọi thứ kém linh hoạt), điều mà chúng ta sẽ thảo luận trong Chương 4.
Các dịch vụ có thể và nên sử dụng nhiều thư viện của bên thứ ba để tái sử dụng chung mã nguồn. Nhưng đókhông hẳn đã là giải pháp cho tất cả.
Mô-đun
Một số ngôn ngữ cung cấp các kỹ thuật phân rã mô-đun của riêng chúng vượt ra ngoài các thư viện đơn giản. Chúng cho phép một số quản lý vòng đời của các mô-đun, để chúng có thể được deploy thành một quy trình đang chạy, cho phép bạn thực hiện các thay đổi mà không cần gỡ bỏ toàn bộ quy trình.
Sáng kiến Cổng thông tin Nguồn mở (OSGI) đáng được gọi là một phương pháp tiếp cận công nghệ cụ thể để phân rã mô-đun. Bản thân Java không có khái niệm thực sự về mô-đun và chúng ta sẽ phải đợi ít nhất cho đến khi Java 9 thấy điều này được thêm vào ngôn ngữ. OSGI, nổi lên như một framework để cho phép cài đặt các trình cắm thêm (plugin) trong Eclipse Java IDE, hiện được sử dụng như một cách để trang bị thêm một khái niệm mô-đun trong Java thông qua một thư viện.
Vấn đề với OSGI là nó đang cố gắng thực thi những thứ như quản lý vòng đời mô-đun mà không có đủ sự hỗ trợ bằng chính ngôn ngữ đó. Điều này dẫn đến việc các tác giả mô-đun phải thực hiện nhiều công việc hơn để phân lập mô-đun thích hợp. Trong một ranh giới quy trình, họ cũng dễ rơi vào bế tắc khi làm cho các mô-đun kết hợp quá chặt chẽ với nhau, gây ra đủ loại vấn đề. Kinh nghiệm của bản thân tôi với OSGI, được đúc kết bởi các đồng nghiệp trong ngành, là ngay cả với những đội giỏi, OSGI vẫn dễ dàng trở thành một thứ phức tạp hơn nhiều so với những lợi ích của nó mang lại
Erlang theo một cách tiếp cận khác, trong đó các mô-đun được đưa vào ngôn ngữ ở runtime. Vì vậy, Erlang là một cách tiếp cận rất thuần thục để phân rã mô-đun. Các mô-đun Erlang có thể dừng, khởi động lại và nâng cấp mà không gặp vấn đề gì. Erlang thậm chí còn hỗ trợ chạy nhiều hơn một phiên bản của mô-đun tại một thời điểm nhất định, cho phép nâng cấp mô-đun một cách uyển chuyển hơn.
Khả năng của các mô-đun của Erlang thực sự rất ấn tượng, nhưng ngay cả khi chúng ta đủ may mắn để sử dụng một nền tảng khác có các khả năng này, vẫn có một điều gì đó thiếu sót giống như cách chúng ta làm với các thư viện được chia sẻ thông thường. Chúng tôi bị hạn chế nghiêm ngặt về khả năng sử dụng các công nghệ mới, bị hạn chế về cách chúng tôi có thể mở rộng quy mô một cách độc lập, có thể hướng tới các kỹ thuật tích hợp quá chặt chẽ và thiếu các liên kết dành cho các biện pháp an toàn kiến trúc.
Có một suy nghĩ cuối cùng rất đáng để chia sẻ. Về mặt kỹ thuật, có thể tạo ra các mô-đun độc lập, được kiểm chứng tốt trong một quy trình monolithic duy nhất. Và chúng ta hiếm khi thấy điều này xảy ra. Bản thân các mô-đun này sớm trở nên kết hợp chặt chẽ với phần còn lại của mã nguồn, và từ bỏ một trong những lợi ích chính của chúng. Một lần thực hiện việc chia nhỏ theo ranh giới của tiến trình sẽ vệ sinh sạch sẽ về mặt này (hoặc ít nhất là làm cho người ta khó có thể làm sai hơn!). Tất nhiên, tôi không gợi ý rằng đây phải là động lực chính cho việc phân tách quy trình, nhưng điều thú vị là những hứa hẹn về việc phân tách theo mô-đun trong ranh giới tiến trình hiếm khi được đưa ra trong thế giới thực.
Vì vậy, mặc dù việc phân rã mô-đun trong một ranh giới tiến trình có thể là điều bạn muốn làm cũng như phân rã hệ thống của bạn thành các dịch vụ, nhưng bản thân nó sẽ không thể giải quyết mọi thứ. Nếu bạn chỉ sử dụng Erlang, lợi ích việc sử dụng mô-đun của Erlang có thể giúp bạn đạt được một chặng đường rất dài, nhưng tôi nghi ngờ rằng nhiều người trong số bạn không thể làm điều đó. Đối với phần còn lại của chúng ta, chúng ta sẽ thấy các mô-đun cung cấp các loại lợi ích tương tự như các thư viện được chia sẻ.
Không có viên đạn bạc nào cả
Trước khi chúng ta kết thúc, tôi nên nói rằng microservices không phải là bữa trưa miễn phí hay viên đạn bạc, hay là một lựa chọn tồi như một chiếc búa vàng. Chúng có tất cả sự phức tạp liên quan của các hệ thống phân tán, và mặc dù chúng tôi đã học được rất nhiều về cách quản lý tốt các hệ thống bị phân tán (mà chúng tôi sẽ thảo luận trong suốt cuốn sách) thì vẫn còn nhiều khó khăn. Nếu bạn bảo lưu quan điểm hệ thống theo monolithic, bạn sẽ phải cải thiện nhiều hơn trong việc xử lý deploy, kiểm thử và giám sát để đạt được những lợi ích mà chúng tôi đã đề cập cho đến nay. Bạn cũng sẽ cần suy nghĩ khác về cách bạn mở rộng quy mô hệ thống của mình và đảm bảo rằng chúng có khả năng phục hồi. Cũng đừng ngạc nhiên nếu những thứ như giao dịch phân tán hoặc định lý CAP bắt đầu khiến bạn đau đầu!
Mọi công ty, tổ chức và hệ thống đều khác nhau. Một số yếu tố sẽ ảnh hưởng đến việc liệu microservices có phù hợp với bạn hay không và mức độ tích cực của bạn trong việc áp dụng chúng. Xuyên suốt mỗi chương của cuốn sách này, tôi sẽ cố gắng cung cấp cho bạn hướng dẫn làm nổi bật những cạm bẫy tiềm ẩn, những cạm bẫy này sẽ giúp bạn vạch ra một con đường ổn định.
Tóm tắt
Hy vọng rằng bây giờ bạn đã biết microservice là gì, điều gì làm cho nó khác với các kỹ thuật tổng hợp khác và một số ưu điểm chính là gì. Trong mỗi chương sau, chúng ta sẽ đi vào chi tiết hơn về cách đạt được những lợi ích này và cách tránh một số cạm bẫy phổ biến.
Có một số chủ đề cần đề cập, nhưng chúng ta cần bắt đầu từ đâu đó. Một trong những thách thức chính mà microservices đưa ra là sự thay đổi vai trò của những người thường dẫn dắt sự phát triển của hệ thống của chúng ta: các kiến trúc sư. Tiếp theo, chúng ta sẽ xem xét một số cách tiếp cận khác nhau đối với vai trò này có thể đảm bảo chúng ta tận dụng tối đa kiến trúc mới này.
Xin chào! Bài viết này mình muốn chia sẻ cho mọi người về một số cách để code SWIFT giống với Adobe xD và hạn chế phần nào việc bị bắt bug UI không đáng.
Trong nỗi trăn trở ở mỗi dự án mobile có hàng trăm, hàng ngàn các bug UI được log. Tự nhiên mình lại nghĩ phải làm việc gì đó để giúp cho anh em code UI ngon hơn, đỡ tạo ra bug UI hơn. Vì vậy mình đã viết bài viết này hi vọng sẽ giúp anh em được phần nào trong việc tránh dính phải những bug UI.
Khi làm việc với xD các anh em thường bỏ qua các chỉ số của xD mà hay tự thực hiện code để nhìn sao cho giống UI nhất có thể, nếu không hiểu rõ bản chất nó khiến cho anh em mất khá nhiều thời gian để có thể làm giống được với file thiết kế cụ thể ở đây là file Adobe xD.
Drop shadow
Để giúp mọi người làm việc dễ dàng hơn nên mình đã tạo ra một hàm trong CALayer để giúp anh em đổ bóng bao chuẩn, bao giống xD :v Việc của anh em là lấy chỉ số ở xD và truyền vào func để setup là xong.
extension CALayer {
/// make shadow like Adobe xD, all prameters using same value with Adobe xD
/// - Parameters:
/// - color: shadow color
/// - opacity: alpha of shadow color (0-100)
/// - x: x
/// - y: y
/// - b: shadow radius
func dropShadowLikeXD(color: UIColor = .black,
opacity: Int = 50,
x: CGFloat = 0,
y: CGFloat = 3,
b: CGFloat = 6) {
masksToBounds = false
shadowColor = color.cgColor
shadowOpacity = Float(opacity) / 100.0
shadowOffset = CGSize(width: x, height: y)
shadowRadius = b / 2.0
shadowPath = nil
}
}
Để anh em dễ hiểu hơn thì mình xin giải thích như sau: – color: đây là shadow color, nó là màu shadow trên xD, cái này khá đơn giản mọi người chỉ cần lấy màu trên xD và fill vào là xong – opacity: đây là độ trong suốt của shadow, trên Adobe xD không có thuộc tính này mà giá trị này sẽ là Opacity của shadow color – x: độ lệch của shadow so với view, tính từ trái qua phải. x > 0 thì shadow lệch qua phải và ngược lại – y: độ lệch của shadow so với view, tính từ trên xuống dưới, x > thì shadow lệch xuống dưới và ngược lại. – b: là thuộc tính blur trên xD, nhưng trong Swift không có thuộc tính này, mà chỉ có shadowRadius nó là bán kính của shadow, và nó bằng 1/2 blur trên xD. – masksToBounds: thuộc tính này bằng true sẽ không thể tạo được shadow vì nó sẽ cắt mất view shadow đi. vì trong hàm mình đã set lại giá trị này bằng false.
Lưu ý: Để vừa đổ bóng được kết hợp với bo góc chúng ta cần thực hiện bo góc trước khi gọi hàm dropShadowLikeXD()
Border
Border trong Adobe xD cho phép custom khá nhiều thuộc tính, tuy nhiên mấy ông Dev tạo ra CALayer của Apple lại chỉ cho set mỗi 2 thuộc tính là borderWidth và borderColor. Vì vậy để làm giống Adobe xD chúng ta sẽ mất công hơn 1 chút. Cụ thể chúng ta sẽ cần thêm 1 enum và một func trong extension của UIView như sau:
Lưu ý: Do hàm này thực hiện thêm mới sublayer nên mọi người không nên gọi nó thực hiện ở func có thể gọi nhiều lần như: viewWillAppear(), viewDidAppear() …
Line spacing
Do định nghĩa về line spacing của Apple và Adobe xD khác nhau nên chúng ta không thể sử dụng cùng chỉ số được, vì vậy chúng ta cần tạo ra một phương thức để sửa lại công thức sao cho khớp với Adobe xD.
Adobe xD định nghĩa line spacing: là khoảng cách từ Top của dòng trên so với Top của dòng dưới liền kề.
Apple định nghĩa line spacing: là khoảng cách giữa Bot của dòng trên so với Top của dòng dưới liền kề.
Line spacing
Chúng ta có thể nhận ra sự chênh lệch giá trị line spacing của Apple so với Adobe chính là chiều cao của 1 dòng. Vậy nên mình có tạo ra một func giúp mọi người set lại giá trị line spacing giống xD mà không phải đau đầu tính toán nữa.
extension UILabel {
/// Set line spacing for label
///
/// - Parameter lineSpacing: Line spacing
func setLineSpacing(_ lineSpacing: CGFloat) {
// Check label text empty
guard let labelText: String = self.text,
let font = self.font else {
return
}
let constraintRect: CGSize = CGSize(width: self.bounds.width, height: .greatestFiniteMagnitude)
let boundingBox: CGRect = "Ok".boundingRect(with: constraintRect,
options: .usesLineFragmentOrigin,
attributes: [NSAttributedString.Key.font: font],
context: nil)
let heightLabel: CGFloat = ceil(boundingBox.height)
let paragraphStyle: NSMutableParagraphStyle = NSMutableParagraphStyle()
// line spacing on xD - height of one line
paragraphStyle.lineSpacing = lineSpacing - heightLabel
let attributedString: NSMutableAttributedString
if let labelattributedText: NSAttributedString = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
// Line spacing attribute
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0,
length: attributedString.length))
self.attributedText = attributedString
}
}
Trên đây là những gì mình muốn chia sẻ lại cho mọi người, hi vọng nó sẽ giúp được mọi người phần nào trong công việc. Nếu mọi người có câu hỏi hay thắc mắc gì có thể đặt câu hỏi ở dưới comment mình sẽ cố gắng giải đáp những thắc mắc của mọi người.
Bản chất của các cấu trúc dữ liệu cơ bản (trong các Ngôn ngữ lập trình) là phục vụ cho 2 việc: Lưu trữ dữ liệu và Truy xuất dữ liệu.
Để lưu trữ dữ liệu thì cần các Ô nhớ (trong Memory). Để Truy xuất dữ liệu thì cần các Địa chỉ của các Ô nhớ nói trên.
Mỗi cấu trúc dữ liệu sẽ triển khai các phương pháp Lưu trữ và Truy xuất khác nhau. Array và Linked List là 2 cấu trúc dữ liệu đơn giản nhất.
Hầu hết các ngôn ngữ lập trình bậc cao hiện nay đều triển khai rất nhiều cấu trúc dữ liệu hữu ích. Một trong những cấu trúc dữ liệu Quan trọng và Mạnh mẽ nhất là Dictionaries.
Trong Python thì Dictionaries được áp dụng ở mọi nơi (Classes, Modules, Namespaces, Sets, Dicts,…)
Tìm hiểu sâu bên trong về cách triển khai Dictionaries tức là ta đang nghiên cứu về một trong những vấn đề quan trọng nhất của Khoa học máy tính, đó là: Hashing
Trong Swift thì chúng ta nên sử dụng value type như là struct hay là enum.
Tuy nhiên, có một số trường hợp bắt buộc phải dùng reference type như là class thì chúng ta phải hết sức cẩn thận khi sử dụng để tránh reference cycle.
Reference type trong Swift được quản lý bộ nhớ thông qua ARC (Autoatic Reference Counting).
Trong bài viết này, chúng ta sẽ cùng tìm hiểu ARC làm việc như nào trong các version sắp tới, và một vài điểm chú ý khi sử dụng reference type.
Object’s lifetimes
An object’s lifetime in Swift begins at initialization and ends at last use.
ARC automatically manages memory, by deallocating an object after its lifetime ends.
It determines an object’s lifetime by keeping track of its reference counts.
ARC is mainly driven by the Swift compiler which inserts retain and release operations.
At runtime, retain increments the reference count and release decrements it.
When the reference count drops to zero, the object will be deallocated.
Từ trước đến giờ chúng ta vẫn luôn hiểu rằng ARC sẽ release một vùng nhớ khi không còn con trỏ nào trỏ vào nó. Ví dụ những vùng nhớ được khai báo trong block code, như function, sẽ được release sau khi content của function này kết thúc.
Tuy nhiên, trên thực tế, mọi thứ còn hơn thế. Trong một function thì Object’s life time được tính từ khi nó được khởi tạo cho đến lần cuối cùng nó được dùng. Sau đó, lifetimes của nó sẽ kết thúc, và nó sẽ được xóa đi.
Ví dụ, với những function giả sử dài 100 LOC, và chúng ta có object myObject nào đó được sử dụng trong khoảng 30 LOC đầu, thì sau khi lần cuối cùng nó được dùng, nó sẽ bị release đi, tức là trong khoảng thời gian 70 LOC còn lại được thực thi, thì vùng nhớ của myObject đã không còn tồn tại.
Tham khảo đoạn source code bên dưới:
class Traveller {
var name: String
var destination: String?
init(name: String) {
self.name = name
}
}
func test() {
let traveller1 = Traveller(name: "NhatHM") // traveller1 Object lifetime begins
let traveller2 = traveller1
traveller2.destination = "WWDC21-10216" // traveller1 Object lifetime ends
print("Done travelling \(traveller2.destination)")
}
Ở ví dụ này, ngay sau khi object traveller1 được gán cho traveller2 thì nó đã không còn được sử dụng nữa, cho nên, đó cũng chính là thời điểm kết thúc lifetime của object traveller. Và lúc này, traveller1 đã sẵn sàng để bị release.
Việc release object traveller1 là do Swift compiler tự xử lý, và thời điểm release là do ARC thực hiện.
Như hiện tại, với XCode13 thì đã có option để optimize object life time. Tức là, có thể những bug trước đây chưa xảy ra vì coding logic observer đến object life time, thì bây giờ hoàn toàn có thể xảy ra. Bản thân Apple cũng khuyến cáo là khi coding, tránh các logic mà phụ thuộc vào object life time quá nhiều, vì với mỗi version Swift mới thì Swift compiler và ARC optimization có thể thay đổi, dẫn đến những bug tiềm ẩn có thể xảy ra.
Note: ở đa số các ngôn ngữ lập trình khác, thì object chỉ bị release khi kết thúc body của function.
Obserable object lifetimes và vấn đề của nó
Thông qua:
weak và unowned reference
Deinitializer (deinit)
Dùng weak và unowned không sai, tuy nhiên trong một số trường hợp, nó rất có thể gây ra bug tiềm ẩn (phụ thuộc vào việc ARC được optimize như nào trong các phiên bản tiếp theo).
Because relying on observed object lifetimes may work today, but it is only a coincidence.
Observed object lifetimes are an emergent property of the Swift compiler and can change as implementation details change.
Xem ví dụ dưới (captured from Apple WWDC21 video)
Ở ví dụ trên, ngay sau đoạn code traveler.account = account kết thúc thì lifetimes của traveler đã kết thúc, và với các version Swift compiler sau này thì có thể xảy ra bug, vì lúc này, traveler bị release, do đó reference count đến vùng nhớ của Traveler đã bị release, dẫn đến đoạn code trong func printSummary sẽ bị crash, nếu dùng if let để unwrap value ra thì bản thân function này cũng sẽ bị sai logic, vì lúc này logic mong muốn print ra thông tin của traveler đã không còn được thực hiện.
Đương nhiên, hiện tại code như trên sẽ chưa thể bug ngay được, vì Swift compiler đang chưa optimize đến mức là vừa kết thúc lifetimes của object thì object sẽ bị release đi luôn. Tuy nhiên, với sections này thì Apple đang nhắc nhở chúng ta vì việc tương lai, chắc chắn là việc xử lý memory của object lifetimes sẽ được thực hiện mạnh tay hơn, có thể ngay sau dòng code cuối cùng mà object được gọi thì nó sẽ bị release, và như vậy, source code của chúng ta chưa bug ở thời điểm này, nhưng tương lai có thể sẽ bị bug.
Thay đổi cách code tránh các object reference lẫn nhau (khuyến khích)
Kết luận:
Object lifetimes không tồn tại suốt vòng đời của function, mà chỉ tồn tại từ khi khởi tạo object -> lần cuối cùng object được sử dụng.
ARC optimization có thể thay đổi sau mỗi version của Swift (và Swift compiler), do đó, việc implement source code phụ thuộc quá nhiều vào object lifetimes tiềm ẩn bug.
Python Deep Dive: Hiểu closures, decorators và các ứng dụng của chúng – Phần 4
Ở bài viết này, tôi sẽ giới thiệu một kỹ thuật gọi là decorator. Nhìn chung, nếu ai đã từng làm việc với các python web framework như Django, Flask, FastAPI,… thì sẽ sử dụng kỹ thuật này thường xuyên, nhưng không phải ai cũng hiểu rõ bản chất của nó.
Thừa nhận rằng, khi sử dụng các decorators có sẵn mà các framework cung cấp, đã đủ để chúng ta làm việc như một web developer. Tuy nhiên, việc nắm được bản chất của kỹ thuật này cũng giúp cho chúng ta sử dụng decorators hiệu quả hơn, có thể tùy chỉnh và tạo ra các decorators của riêng mình, như vậy là ta đã trở thành một lập trình viên chuyên nghiệp hơn.
Nhắc lại một ví dụ trong Phần 3, chúng ta đã áp dụng Closure để maintain một bộ đếm – đếm số lần gọi một hàm bất kỳ.
def counter(fn):
cnt = 0 # số lần chạy fn, khởi tạo là 0
def inner(*args, **kwargs):
nonlocal cnt
cnt = cnt + 1
print('Hàm {0} đã được gọi {1} lần'.format(fn.__name__, cnt))
return fn(*args, **kwargs)
return inner
Hàm counter nhận một function làm đầu vào – fn. Trong hàm counter, ta khởi tạo 1 biến cục bộ cnt – biến này sẽ đến số lần gọi hàm fn, mỗi khi goị đến hàm inner thì biến cnt được tăng lên 1 đơn vị.
Việc sử dụng *args và **kwargs trong hàm inner giúp ta có thể gọi hàm fn với bất kỳ sự kết hợp positional arguments và keyword-only arguments nào. Lấy ví dụ:
def mult(x, y=10):
"""
return the products of two values
"""
return x * y
mult = counter(mult) # trả về inner function - closure
Nhớ rằng, ban đầu mult là một label trỏ đến hàm mult được định nghĩa ở trên. Goị hàm counter(mult) sẽ trả về một closure và gán vào một label là mult, thì lúc này mult sẽ trỏ đến closure đó, rõ ràng là khác so với nơi mà mult ban đầu trỏ đến.
Tuy nhiên, inner thực hiện gọi hàm mult ban đầu cho chúng ta và trả về kết quả của nó, chính vì vậy mà kết quả trả về khi gọi hàm inner không khác so với việc gọi hàm mult ban đầu. Nhưng thực sự, hàm inner đã làm thêm một số việc trước khi gọi và trả về kết quả của hàm mult, đó là đếm số lần hàm mult được gọi.
mult(3, 5)
# In ra: Hàm mult đã được gọi 1 lần
# return 15
Chúng ta đã thực sự sửa đổi hàm mult ban đầu, bằng cách wrapping nó bên trong một hàm khác – bổ sung thêm chức năng cho nó –> chúng ta đã decorated hàm mult với hàm counter và chúng ta gọi counter là decorator function.
Nhìn chung, một decorator function sẽ:
lấy 1 function như một đối số
return một closure
closure nói trên sẽ nhận bất kỳ sự kết hợp đầu vào nào (sử dụng *args và **kwargs)
chạy một vài chức năng bên trong closure
closure function sẽ gọi original function sử dụng các đối số được truyền vào closure
return kết quả được trả về từ function call nói trên
The @ Symbol
Như đã thấy, chúng ta hoàn toàn có thể sử dụng decorator function như sau:
def my_func(*args, **kwargs):
# some code here
# ...
my_func = func(my_func)
Trong đó, func là decorator function, còn my_func là hàm được decorated.
Tuy nhiên, có một cách khác thanh lịch hơn:
@func
def my_func(*args, **kwargs):
# some code here
# ...
Introspection
Sử dụng lại counter như một decorator cho hàm mult:
import inspect
@counter
def mult(x, y=10):
"""
return the products of two values
"""
return x * y
print(f'name = {mult.__name__}') # name = inner
Ta thấy rằng, câu lệnh print ở trên sẽ in ra màn hình name = inner. Rõ ràng, tên của hàm mult không phải là mult nữa, mà nó là inner – tên của một closure, điều này chứng tỏ một điều rằng, hàm mult lúc này chính là một closure.
Vì vậy, cầu lưu ý rằng, khi chúng ta decorate một function, tức là chúng ta đã làm cho function đó thay đổi (về bản chất).
Như vậy, khi gọi inspect.signature(mult), chúng ta đã nhìn thấy rõ chữ ký của hàm inner được trả về.
Việc decorate một function làm thay đổi docstring, signature, name khiến cho việc debugging trở nên khó khăn hơn rất nhiều.
The wraps function
Để giải quyết vấn đề mới nói ở trên, functools module cung cấp một wraps function có thể fix metadata của inner function trong decorator. Bản thân wraps function này là một decorator.
Hàm wraps này sẽ decorate inner function để thay đổi metadata (docstring, signature, name,…) của nó. Thêm nữa, nó phải sử dụng hàm mult như một đối số đầu vào, để có thể thay thế metadata của inner function bằng metadata của mult function (hàm được decorated)
from functools import wraps
import inspect
def counter(fn):
cnt = 0 # số lần chạy fn, khởi tạo là 0
@wraps(fn)
def inner(*args, **kwargs):
nonlocal cnt
cnt = cnt + 1
print('Hàm {0} đã được gọi {1} lần'.format(fn.__name__, cnt))
return fn(*args, **kwargs)
return inner
@counter
def mult(x, y=10):
"""
return the products of two values
"""
return x * y
print(f'name = {mult.__name__}') # name = mult
print(f'signature: {inspect.signature(mult)}') # signature: (x, y=10)
Chúng ta không nhất thiết phải sử dụng wraps function trong khi sử dụng decorator, nhưng việc sử dụng nó sẽ giúp cho việc debugging trở nên dễ dàng hơn.
Summary
Decorators trong Python là cú pháp cho phép một function sửa đổi một function khác tại thời gian chạy (runtime)
Ký pháp @ giúp cho việc sử dụng decorator thanh lịch hơn (mặc dù có thể không cần)
Hãy sử dụng wraps function giúp cho việc debugging dễ dàng hơn khi làm việc với decorators.