1. Tổng quan Tổng quan
    1. Mục đích Mục đích
    2. Chú ý Chú ý
  2. Tối thiểu scope của biến (5 sao) Tối thiểu scope của biến (5 sao)
    1. Biến toàn cục Biến toàn cục
  3. Biến instance Biến instance
    1. Không instance data có sẵn Không instance data có sẵn
    2. không instance giá trị tính toán được không instance giá trị tính toán được
    3. Không instance thông tin có thể get được Không instance thông tin có thể get được
  4. Biến cục bộ Biến cục bộ
  5. Nên viết code giải thích biến ra Nên viết code giải thích biến ra
  6. Tính đơn nhất của code Tính đơn nhất của code
    1. Trùng lập dữ liệu DB Trùng lập dữ liệu DB
    2. Không nên code hóa thông tin đã biết Không nên code hóa thông tin đã biết
  7. Cách đặt tên biến Cách đặt tên biến
    1. Không gắn ID hoặc con số vào tên Không gắn ID hoặc con số vào tên
    2. Không viết tắt biến Không viết tắt biến
    3. Tên không ý nghĩa Tên không ý nghĩa
    4. Cách đặt tên cho biến boolean Cách đặt tên cho biến boolean
  8. Nên sử dụng object.function() thay function(object) Nên sử dụng object.function() thay function(object)
  9. Thay vì kế thừa nên sử dụng interface Thay vì kế thừa nên sử dụng interface
  10. Làm đơn giãn cấu trúc rẽ nhánh Làm đơn giãn cấu trúc rẽ nhánh
    1. không nên chồng các if quá nhiều không nên chồng các if quá nhiều
    2. sử dụng return sớm sử dụng return sớm
    3. ngắt logic ngắt logic
    4. Không phân nhánh ở chổ gọi ra Không phân nhánh ở chổ gọi ra
    5. Nên sử dụng tính đa hình Nên sử dụng tính đa hình
    6. Truyền xử lý tiếp theo qua tham số Truyền xử lý tiếp theo qua tham số
    7. Phân chia command và query ra Phân chia command và query ra
  11. Không tạo property dạng flag (4 sao) Không tạo property dạng flag (4 sao)
  12. Không sử dụng khóa cho data là chuỗi Không sử dụng khóa cho data là chuỗi
  13. Không truyền data dạng mãng Không truyền data dạng mãng
  14. Không comment out Không comment out
  15. Giá trị trong code nên sử dụng enum Giá trị trong code nên sử dụng enum
  16. Viết commnent cho code (3 sao) Viết commnent cho code (3 sao)
  17. Hạn chế tối đa sử dụng if, switch Hạn chế tối đa sử dụng if, switch
  18. Lập kế hoạch trước khi viết code Lập kế hoạch trước khi viết code
質問
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ập nhật: 

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ó

  1. Biến toàn cục.
  2. Biến instance (biến member nằm trong class).
  3. 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()
}
この記事が気に入ったら応援お願いします🙏
19
ツイート
LINE
Developer
Price Rank Dev
I use Next.js (React) and Firebase (Firestore / Auth) for development. We are also developing APIs for Ruby on Rails and GraphQL. Our team members are 6 Vietnamese and Japanese engineers.