- Tổng quan Tổng quan
- Tối thiểu scope của biến (5 sao) Tối thiểu scope của biến (5 sao)
- Biến instance Biến instance
- Biến cục bộ Biến cục bộ
- Nên viết code giải thích biến ra Nên viết code giải thích biến ra
- Tính đơn nhất của code Tính đơn nhất của code
- Cách đặt tên biến Cách đặt tên biến
- Nên sử dụng object.function() thay function(object) Nên sử dụng object.function() thay function(object)
- Thay vì kế thừa nên sử dụng interface Thay vì kế thừa nên sử dụng interface
- Làm đơn giãn cấu trúc rẽ nhánh Làm đơn giãn cấu trúc rẽ nhánh
- không nên chồng các if quá nhiều không nên chồng các if quá nhiều
- sử dụng return sớm sử dụng return sớm
- ngắt logic ngắt logic
- Không phân nhánh ở chổ gọi ra Không phân nhánh ở chổ gọi ra
- Nên sử dụng tính đa hình Nên sử dụng tính đa hình
- Truyền xử lý tiếp theo qua tham số Truyền xử lý tiếp theo qua tham số
- Phân chia command và query ra Phân chia command và query ra
- Không tạo property dạng flag (4 sao) Không tạo property dạng flag (4 sao)
- Không sử dụng khóa cho data là chuỗi Không sử dụng khóa cho data là chuỗi
- Không truyền data dạng mãng Không truyền data dạng mãng
- Không comment out Không comment out
- Giá trị trong code nên sử dụng enum Giá trị trong code nên sử dụng enum
- Viết commnent cho code (3 sao) Viết commnent cho code (3 sao)
- Hạn chế tối đa sử dụng if, switch Hạn chế tối đa sử dụng if, switch
- Lập kế hoạch trước khi viết code Lập kế hoạch trước khi viết code
Tổng quan
Bài viết này tập trung vào hướng dẫn việc viết code như thế nào để nâng cao tính dễ đọc từ đó nâng cao khả năng phát triển lên cao hơn của ứng dụng đặc biệt là đối với người mới cũng có thể dễ dàng hiểu được.
Các ví dụ trong bài viết sẽ sử dụng ngôn ngữ lập trình hướng đối, và dùng ngôn ngữ Swift để làm mẫu.
Mục đích
- Nâng cao khả năng phát triễn ứng dụng, giảm chi phí bảo trì.
- Tránh bug phát sinh.
- Dễ hiểu đối với các team member (đặc biệt là với người mới).
- Hướng dẫn về cách viết code cho các bạn mới lập trình.
Chú ý
Trong tiêu đề mỗi mục chính sẽ có đánh dấu bao nhiều sao, thể hiện mức độ quan trọng của mục đó.
Những cái được đánh sao cao thì ảnh hưởng lớn đến ứng dụng, còn những cái đánh sao thấp sẽ ảnh hưởng ít và dễ sửa hơn.
Tối thiểu scope của biến (5 sao)
Về cơ bản những loại biến bên dưới sẽ có phạm vi rộng vì vậy nên tránh sử dụng nó
- Biến toàn cục.
- Biến instance (biến member nằm trong class).
- Biến cục bộ
Biến toàn cục
Đối với biến toàn cục, không nên sử dụng kiểu data đơn thuần như Int, String. Cần phải đưa nó vào trong object có liên quan, sau đó lưu nó dưới dạng Singleton hoặc shared object.
Ví dụ không tốt:
var userName: String = ""
var loginPassword: String = ""
Ví dụ tốt:
class AccountInfo {
static var shared = AccountInfo()
var userName: String = ""
var loginPassword: String = ""
}
Tuy nhiên, giữa Singleton và shared object thì chúng tôi khuyến khích nên sử dụng shared object sẽ đạt hiệu quả cao hơn.
Biến instance
Cách để giảm instance trong class, chú ý 3 cái bên dưới:
- Không instance data có sẵn.
- không instance giá trị tính toán được.
- Không instance thông tin có thể get được.
Không instance data có sẵn
Ví dụ không tốt:
class Foo {
var user: User?
func setUser(user: User) {
self.user = user
printName()
printEmail()
}
func printName() {
print(user?.name)
}
func printEmail() {
print(user?.email)
}
}
Ví dụ tốt:
class Foo {
func setUser(user: User) {
printName(user: user)
printEmail(user: user)
}
func printName(user: User) {
print(user.name)
}
func printEmail(user: User) {
print(user.email)
}
}
không instance giá trị tính toán được
Ví dụ sau đây, itemsA và itemsB được tính toán từ items, nên không tốt.
class Foo {
var items = ["A-1", "A-2", "B-1", "B-2"]
let itemsA: [String]
let itemsB: [String]
init() {
itemsA = items.filter { $0.hasPrefix("A-") }
itemsB = items.filter { $0.hasPrefix("B-") }
}
}
Ví dụ sau đây tốt hơn:
class Foo {
var items = ["A-1", "A-2", "B-1", "B-2"]
func itemsA() -> [String] {
return items.filter { $0.hasPrefix("A-") }
}
func itemsB() -> [String] {
return items.filter { $0.hasPrefix("B-") }
}
}
Không instance thông tin có thể get được
Ví dụ không tốt:
class Before {
var dataType: DataType?
func fetchData(dataType: DataType) {
self.dataType = dataType // インスタンス変数に保存
APIConnection(dataType).getData(onComplete: { response in
self.setResponse(response)
})
}
func setResponse(_ response: Response) {
print("\(dataType) がセットされた")
}
}
Ví dụ tốt:
class After {
func fetchData(dataType: DataType) {
APIConnection(dataType).getData(onComplete: { response in
// クロージャ内でdatTypeを使うことで、API通信完了までdataTypeを保持することができる
self.setResponse(response, dataType: dataType)
})
}
func setResponse(_ response: Response, dataType: DataType) {
print("\(dataType) がセットされた")
}
}
Biến cục bộ
Sử dụng cũng được, tuy nhiên cần chú ý giới hạn phạm vi sử dụng của nó lại
Ví dụ không tốt:
var num = 0
for i in list {
num = i
print(num)
}
Ví dụ tốt
for i in list {
let num = i
print(num)
}
Nên viết code giải thích biến ra
Ví dụ không tốt:
let totalPrice = ((orangePrice * orangeQuantity) + (applePrice * appleQuanitity)) * (1 + taxPercentage / 100)
Ví dụ tốt:
// 以下の3つは説明変数
let orangePriceSum = orangePrice * orangeQuantity
let applePriceSum = applePrice * appleQuanitity
let includesTaxRate = 1 + taxPercentage / 100
let totalPrice = (orangePriceSum + applePriceSum) * includesTaxRate
Tính đơn nhất của code
Ví dụ bên dưới cho thấy code bị lập lại, không tốt.
class Person {
var age = 17
var ageText = "17歳"
}
Ví dụ tốt
class Person {
var age = 17
var ageText: String {
return "\(age)歳"
}
}
Trùng lập dữ liệu DB
Ví dụ
class Foo {
var records: [DBRecord]?
func readDBRecord(dbTable: DBTable) {
records = dbTable.selectAllRecords()
}
}
Không nên code hóa thông tin đã biết
Ví dụ không tốt
func getName(index: Int) -> String? {
let map = [0: "佐藤", 1: "品川", 2: "鈴木"]
return map[index]
}
Ví dụ tốt
func getName2(index: Int) -> String? {
let names = ["佐藤", "品川", "鈴木"]
if 0 <= index && index < names.count {
return names[index]
}
return nil
}
Cách đặt tên biến
Đặt tên biến, class, hàm... cũng rất quan trọng, chú ý những cái bên dưới khi đặt tên biến
- Sao cho ai cũng có thể hiện được ý nghĩa
- Xử lý và tên của nó khớp nhau
- Không đặt những gì không thuộc xử lý vào tên
Không gắn ID hoặc con số vào tên
Ví dụ bên dưới không tốt:
var code1 = "a"
func func001() {}
enum VieID {
case vol_01, vol_02, vol_03
}
Không viết tắt biến
Ví dụ không tốt
func chkDispFlg(){}
Tên không ý nghĩa
Ví dụ không tốt
let yen = "円"
Trường hợp ở đây thay đổi không sử dụng yên mà dùng $ thì tên biến này rất tệ.
Cách đặt tên cho biến boolean
Nên sử dụng những cách đặt tên bên dưới:
- is + Tính từ (isEmpty..)
- is + Quá khứ (isHidden...)
- is + Chủ từ + Quá khứ (isViewLoaded..)
- has + Danh từ (hasParent...)
- can + Động từ (canLoad)
- Động từ (exists、contains...)
- should + Động từ (shouldLoad...)
Nên sử dụng object.function() thay function(object)
Ví dụ không tốt:
if StringUtils.isEmpty(string) {}
Ví dụ tốt:
if string.isEmpty() {}
Thay vì kế thừa nên sử dụng interface
sử dụng kế thừa thì khi sửa đổi dẫn đến thay đổi nhiều hơn, hơn nữa cũng không thể kế thừa được từ nhiều cha.
Làm đơn giãn cấu trúc rẽ nhánh
if, swich sẽ làm tăng khả năng khó đọc của source code vì vậy nên làm giảm mức độ phức tạp của nó tối đa có thể.
không nên chồng các if quá nhiều
Ví dụ không tốt
if text != nil {
if text == "A" {
// 処理1
} else {
// 処理2
}
}
sử dụng return sớm
Ví dụ trên có thể sửa lại như bên dưới
if text == nil {
return
}
if text == "A" {
// 処理1
} else {
// 処理2
}
ngắt logic
Ví dụ tốt:
if text != nil {
doSomething(text)
}
func doSomething(_ text: String?) {
if text == "A" {
// 処理1
} else {
// 処理2
}
}
Không phân nhánh ở chổ gọi ra
Ví dụ không tốt
class BaseViewController: UIViewController {
func doSomething() {
if viewId == "home" {
// ホーム画面の処理
} else if viewId == "login" {
// ログイン画面の処理
} else if viewId == "setting" {
// 設定画面の処理
}
}
}
Nên sử dụng tính đa hình
Ví dụ trên có thể được giải quyết bằng tính đa hình như sau:
class BaseViewController {
func doSomething() {}
}
class HomeViewController: BaseViewController {
override func doSomething() {
// ホーム画面の処理
}
}
class LoginViewController: BaseViewController {
override func doSomething() {
// ログイン画面の処理
}
}
class SettingViewController: BaseViewController {
override func doSomething() {
// 設定画面の処理
}
}
Truyền xử lý tiếp theo qua tham số
Hoặc ví dụ ở trên cũng có thể được giải quyết như sau bằng cách truyền xử lý tiếp theo như là tham số:
class BaseViewController: UIViewController {
func doSomething(something: () -> Void) {
something()
}
}
Phân chia command và query ra
Ví dụ không tốt
func nextAction() {
if userName.isNotEmpty && sessionId.isNotEmpty {
showNextPage()
}
}
Ví dụ tốt
// コマンド
func nextAction() {
if isLoggedIn() {
showNextPage()
}
}
// クエリ
func isLoggedIn() {
return userName.isNotEmpty && sessionId.isNotEmpty
}
Không tạo property dạng flag (4 sao)
Ví dụ không tốt
class Foo {
var hasReceived: Bool = false // 不必要なフラグ
var data: Data?
func setData(data: Data) {
self.data = data
hasReceived = true
}
}
Không sử dụng khóa cho data là chuỗi
Ví dụ không tốt
func title(index: Int) -> String {
switch index {
case 0:
return "A"
case 1:
return "B"
case 2:
return "C"
default:
return ""
}
}
Ví dụ tốt
let titles = ["A", "B", "C"]
func title(index: Int) -> String {
return titles.indices.contains(index) ? titles[index] : ""
}
Không truyền data dạng mãng
thay vì vậy chúng ta nên tạo object
Ví dụ không tốt
func getUserData(): [String: String] {
var userData: [String] = []
userData[0] = "山田 正男"
userData[1] = "171-0001"
userData[2] = "東京都豊島区"
return userData
}
Ví dụ tốt
struct UserData {
let name: String
let postalCode: String
let address: String
}
func getUserData(): UserData {
return UserData(name: "山田 正男",
postalCode: "171-0001",
address: "東京都豊島区")
}
Không comment out
Code không sử dụng nên bỏ đi không nên comment out rồi để đó.
Giá trị trong code nên sử dụng enum
Ví dụ không tốt
func setStatus(status: String) {
if status == "0" {
// 成功時の処理
}
}
Ví dụ tốt
enum Status: String {
case success = "0"
case error = "1"
}
func setStatus(status: Status) {
if status == .success {
// 成功時の処理
}
}
Viết commnent cho code (3 sao)
Code sau khi viết 1 thời gian rất dễ quên xử lý đó làm cho mục đích gì vì vậy nên cố gắng comment lại nhiều nhất có thể.
Hạn chế tối đa sử dụng if, switch
Ví dụ không tốt
if flag {
label.text = "aaa"
} else {
label.text = "bbb"
}
Ví dụ tốt:
label.text = flag ? "aaa" : "bbb"
Lập kế hoạch trước khi viết code
Trước khi viết code có thể sử dụng mã giả để viết trước và lên kế hoạch về việc sẽ sử dụng class gì, tên biến, hàm và các interface ra sao.
Ví dụ
func createSession(request) -> Session? {
let user = userTable.getByUserName(request.userName)
if user.account đang bị lock {
throw lỗi gì đó
}
if user.passward != request.password {
userTable.đếm tăng số lần login thất bại
return nil
}
Xuất thông tin user đã login
return Session()
}