Như những gì tôi đã nói ở trong các buổi hội nghị gần đây, cuốn sách Refactoring to Patterns luôn được nhắc đến trong các buổi nói chuyện ưa thích của tôi. Và cuối cùng thì tôi cũng đã có thời gian để đọc nó (sau một đợt WWDC bận rộn), và tôi muốn tổng hợp lại những gì tôi đọc được tại đây để tiện tham khảo cho tương lai. Trong quá trình đọc thì tôi cũng ngộ ra được một cách ghi nhớ rất tốt, đó là dịch từ ngôn ngữ Java sang Swift.

Hôm nay chúng ta sẽ nói về pattern đầu tiên, đó là Creation Method.

Trước khi refactor

Giả dụ bạn có một model, có tên gọi là Loan( các khoản cho vay), nó có rất nhiều thuộc tính và các hàm initializer khác nhau. Ở trong trường hợp này mỗi hàm initializer được khai báo với một mục đích khác nhau - tương ứng với từng loại hình cho vay:

struct Loan {
    let commitment: NSDecimalNumber
    let riskRating: Float
    let maturity: Int
    let expiry: NSDate?
    let capitalStrategy: String?
    let outstanding: NSDecimalNumber?
    
    init(commitment: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate?, capitalStrategy: String?, outstanding: NSDecimalNumber?) {
        self.commitment = commitment
        self.riskRating = riskRating
        self.maturity = maturity
        self.expiry = expiry
        self.capitalStrategy = capitalStrategy
        self.outstanding = outstanding
    }
    
    init(commitment: NSDecimalNumber, riskRating: Float, maturity: Int) {
        self.init(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: nil, capitalStrategy: nil, outstanding: nil)
    }
    
    init(commitment: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) {
        self.init(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: nil, outstanding: nil)
    }
    
    init(commitment: NSDecimalNumber, outstanding: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) {
        self.init(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: nil, outstanding: outstanding)
    }
    
    init(capitalStrategy: String, commitment: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) {
        self.init(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: capitalStrategy, outstanding: nil)
    }
    
    init(capitalStrategy: String, commitment: NSDecimalNumber, outstanding: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) {
        self.init(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: capitalStrategy, outstanding: outstanding)
    }
}

Nhìn vào trong đoạn code trên, bạn sẽ khá hoang mang không biết phải sử dụng intializer nào. Dĩ nhiên là đối với người mới thì sẽ khá lẫn lộn vì họ không nắm được rõ về nghiệp vụ này, vì vậy sẽ dẫn đến việc sử dụng nhầm intializer. Do đó chúng ta cần phải refactor lại.

Tham khảo các khóa học lập trình online, onlab, và thực tập lập trình tại TechMaster

Sau khi refactor

Đoạn code dưới đây được gọi là refactor theo Creation Method

struct Loan {
    let commitment: NSDecimalNumber
    let riskRating: Float
    let maturity: Int
    let expiry: NSDate?
    let capitalStrategy: String?
    let outstanding: NSDecimalNumber?
 
    // the original initializer can now be private
    private init(commitment: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate?, capitalStrategy: String?, outstanding: NSDecimalNumber?) {
        self.commitment = commitment
        self.riskRating = riskRating
        self.maturity = maturity
        self.expiry = expiry
        self.capitalStrategy = capitalStrategy
        self.outstanding = outstanding
    }
    
    static func createTermLoan(commitment: NSDecimalNumber, riskRating: Float, maturity: Int) -> Loan {
        return Loan(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: nil, capitalStrategy: nil, outstanding: nil)
    }
    
    static func createTermLoan(capitalStrategy: String, commitment: NSDecimalNumber, outstanding: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) -> Loan {
        return Loan(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: capitalStrategy, outstanding: outstanding)
    }
    
    static func createRevolverLoan(commitment: NSDecimalNumber, outstanding: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) -> Loan {
        return Loan(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: nil, outstanding: outstanding)
    }
    
    static func createRevolverLoan(capitalStrategy: String, commitment: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) -> Loan {
        return Loan(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: capitalStrategy, outstanding: nil)
    }
    
    static func createRCTL(commitment: NSDecimalNumber, riskRating: Float, maturity: Int, expiry: NSDate) -> Loan {
        return Loan(commitment: commitment, riskRating: riskRating, maturity: maturity, expiry: expiry, capitalStrategy: nil, outstanding: nil)
    }
}

Sau khi refactor xong thì nhìn code trở nên sáng sủa hơn hẳn, và quan trọng nhất là phân loại rõ được các hàm initializer. Bên cạnh pattern này thì còn có nhiều kiểu khởi tạo khác trong Swift tùy thuộc vào các trường hợp (ví dụ: enums), do vậy tôi sẽ sử dụng pattern này trong trường hợp muốn code của mình trở nên dễ đọc và tường minh hơn.