あなたがすぐに採用することができる単純であるが、強力なコーディング慣行:クラスと機能


機能は、我々が開発者として我々の仕事をしなければならない最高で最も重要なツールのうちの1つです.おそらく、私たちは重複したコードを削除するためにそれらを使用することを学びました.
クラスは一般的に関連するプロパティと関数をグループ化するために使用されます.
これらの2つのツールは、より良いコード化の原則に従って、よりよく、より整理されたコードを作成するのに役立ちます.
話しましょう.

純粋関数はよりよい
関数は、結果を変更できる外部依存関係を持たない場合は純粋と見なされます.それらの出力値は、同じ入力で呼ばれるとき、常に等しくなければなりません.
例を考えてみましょう.
func sum(_ a: Int, plus b: Int) -> Int {
    return a + b
}
func add(value: Int) -> Int {
    return totalValue + value
}

上記の機能が純粋かどうかを確認できますか?
sum関数は純粋なものです.なぜなら、何度も呼び出したとしても、同じ入力で呼び出されたときと同じ結果を生成するからです.
func sum(_ a: Int, plus b: Int) -> Int {
    return a + b
}
sum(15, plus: 20) // 35
sum(15, plus: 20) // 35
sum(100, plus: 20) // 120
sum(100, plus: 20) // 120
しかし、add関数は純粋ではありません.なぜなら、すべての呼び出しで異なる“totalValue”という名前の外部変数を使用するからです.この関数は依存関係に何が起こっているかを制御することができず、結果を保証することはできません.
var totalValue = 0

func add(value: Int) -> Int {
    return totalValue + value
}

add(value: 10) // 10
totalValue = 20
add(value: 10) // 30
totalValue = add(value: 20) // 40
add(value: 20) // 60
見ることができるように、不純な関数はバグを追跡するためにハードを隠すことができます.
これは非常にテストし、純粋な機能を維持する簡単です.

できるだけ小さくしておきなさい
何かをコーディングし始めるとき、すべてをする大きくて混乱している機能を作成するのは、全く一般的です.我々は問題を解決しようとしている間、これは正常ですが、まだどのようにそれを正確に知っていない.私たちは、それがそうするべきであることを確認するために、考え、実装し、テストする必要があります.
しかし、コードが予想通りに動作している場合、作業はまだ完了していません.今、それは小さなものにそれらを壊すことによって、大混乱の機能をクリーニングリファクタリングする時です.
次の架空の問題を考えてください:アクメ会社はその日の労働時間の従業員を支払う.平日は全額30ドルでなければならない.土曜日に20 %のボーナスを追加し、日曜日には、支払いを2倍にする必要があります.コード化しましょう
func calculateHourlyPay(for workingHours: Int, at dayOfWeek: String) -> Double {
    let paymentPerHour: Double = 30

    if dayOfWeek == "sun" {
        return Double(workingHours) * paymentPerHour * 2
    }
    if dayOfWeek == "sat" {
        return Double(workingHours) * paymentPerHour * 1.2
    }

    return Double(workingHours) * paymentPerHour
}

let sunPayment = calculateHourlyPay(for: 10, at: "sun") // 600
let monPayment = calculateHourlyPay(for: 10, at: "mon") // 300
let tuePayment = calculateHourlyPay(for: 10, at: "tue") // 300
let wedPayment = calculateHourlyPay(for: 10, at: "wed") // 300
let thuPayment = calculateHourlyPay(for: 10, at: "thu") // 300
let friPayment = calculateHourlyPay(for: 10, at: "fri") // 300
let satPayment = calculateHourlyPay(for: 10, at: "sat") // 360
良い.しかし、現在、顧客は仕事の最初の8時間を上回る残業時間を計算するよう頼みました.残業時間は50 %を支払う.
OKです.
func calculateHourlyPay(for workingHours: Int, at dayOfWeek: String) -> Double {
    let paymentPerHour: Double = 30

    let normalHours: Double = workingHours > 8 ? 8 : Double(workingHours)
    let overtimeHours = workingHours > 8 ? Double(workingHours - 8) : 0

    var payment: Double = 0

    if dayOfWeek == "sun" {
        payment = normalHours * paymentPerHour * 2
        payment += overtimeHours * paymentPerHour * 2 * 1.5
    } else if dayOfWeek == "sat" {
        payment = normalHours * paymentPerHour * 1.2
        payment += overtimeHours * paymentPerHour * 1.2 * 1.5
    } else {
        payment = normalHours * paymentPerHour
        payment += overtimeHours * paymentPerHour * 1.5
    }

    return payment
}

let sunPayment = calculateHourlyPay(for: 10, at: "sun") // 660
let monPayment = calculateHourlyPay(for: 10, at: "mon") // 330
let tuePayment = calculateHourlyPay(for: 10, at: "tue") // 330
let wedPayment = calculateHourlyPay(for: 10, at: "wed") // 330
let thuPayment = calculateHourlyPay(for: 10, at: "thu") // 330
let friPayment = calculateHourlyPay(for: 10, at: "fri") // 330
let satPayment = calculateHourlyPay(for: 10, at: "sat") // 396
すごい!準備ができて船、右?いいえ.ではなく.
今機能は大きく、あなたが正確に何が起こっているかを理解していない場合は読むことが困難な計算の完全です.私はあなたを保証します:あなたが将来このコードに変更をする必要があるならば、あなたは理解するためにこれを読んで数分を必要とします.時間の無駄.リファクターにとって、それがまだ我々の心にまだ新鮮であるということを今よりよく書くことはよりよいです.
これは、次のチップに私たちをもたらします.

クラスと関数には
sのSOLID principles 単一の責任を表します.つまり、1つだけ物事を行う.
最後のコードを見てください.我々の機能はたった今多くのことをします:
  • 一定の値として1時間あたりの支払いを維持する(更新するのは難しい);
  • 正常と残業時間を計算する
  • 曜日の確認
  • 計算通常時間支払い;
  • 残業時間支払い計算
  • 合計正常と返済する残業.
  • つの責任.我々はより良い行うことができます!再設定しましょう:
    class Payment {
        let paymentPerHour: Double
    
        init(paymentPerHour: Double) {
            self.paymentPerHour = paymentPerHour
        }
    
        func calculateHourlyPay(for workingHours: Int, at dayOfWeek: String) -> Double {
            let normalHours: Double = workingHours > 8 ? 8 : Double(workingHours)
            let overtimeHours = workingHours > 8 ? Double(workingHours - 8) : 0
    
            var payment: Double = 0
    
            if dayOfWeek == "sun" {
                payment = normalHours * paymentPerHour * 2
                payment += overtimeHours * paymentPerHour * 2 * 1.5
            } else if dayOfWeek == "sat" {
                payment = normalHours * paymentPerHour * 1.2
                payment += overtimeHours * paymentPerHour * 1.2 * 1.5
            } else {
                payment = normalHours * paymentPerHour
                payment += overtimeHours * paymentPerHour * 1.5
            }
    
            return payment
        }
    }
    
    最初に、我々はクラスであるためにリファクタリングしpaymentPerHour プロパティである定数.これで、必要に応じてこのプロパティの値を簡単に変更することができますpaymentPerHour 値.続きましょう
    class Payment {
        let paymentPerHour: Double
    
        init(paymentPerHour: Double) {
            self.paymentPerHour = paymentPerHour
        }
    
        private func normalHours(from workingHours: Int) -> Double {
            return workingHours > 8 ? 8 : Double(workingHours)
        }
    
        private func overtimeHours(from workingHours: Int) -> Double {
            return workingHours > 8 ? Double(workingHours - 8) : 0
        }
    
        func calculateHourlyPay(for workingHours: Int, at dayOfWeek: String) -> Double {
            let normalHours = self.normalHours(from: workingHours)
            let overtimeHours = self.overtimeHours(from: workingHours)
    
            var payment: Double = 0
    
            if dayOfWeek == "sun" {
                payment = normalHours * paymentPerHour * 2
                payment += overtimeHours * paymentPerHour * 2 * 1.5
            } else if dayOfWeek == "sat" {
                payment = normalHours * paymentPerHour * 1.2
                payment += overtimeHours * paymentPerHour * 1.2 * 1.5
            } else {
                payment = normalHours * paymentPerHour
                payment += overtimeHours * paymentPerHour * 1.5
            }
    
            return payment
        }
    }
    
    第二に、我々は通常と残業時間を計算するために2つのプライベートメソッドを作成しました.あなたは、それに気がつきましたcalculateHourlyPay 方法は、その責任を失います?動きましょう:
    class Payment {
        let paymentPerHour: Double
    
        init(paymentPerHour: Double) {
            self.paymentPerHour = paymentPerHour
        }
    
        private func normalHours(from workingHours: Int) -> Double {
            return workingHours > 8 ? 8 : Double(workingHours)
        }
    
        private func overtimeHours(from workingHours: Int) -> Double {
            return workingHours > 8 ? Double(workingHours - 8) : 0
        }
    
        private func calculateNormalHoursPayment(for hours: Double, withBonus bonus: Double = 1) -> Double {
            return hours * self.paymentPerHour * bonus
        }
    
        private func calculateOvertimeHoursPayment(for hours: Double, withBonus bonus: Double = 1) -> Double {
            return hours * self.paymentPerHour * bonus * 1.5
        }
    
        func calculateHourlyPay(for workingHours: Int, at dayOfWeek: String) -> Double {
            let normalHours = self.normalHours(from: workingHours)
            let overtimeHours = self.overtimeHours(from: workingHours)
    
            var payment: Double = 0
    
            if dayOfWeek == "sun" {
                payment = calculateNormalHoursPayment(for: normalHours, withBonus: 2)
                payment += calculateOvertimeHoursPayment(for: overtimeHours, withBonus: 2)
            } else if dayOfWeek == "sat" {
                payment = calculateNormalHoursPayment(for: normalHours, withBonus: 1.2)
                payment += calculateOvertimeHoursPayment(for: overtimeHours, withBonus: 1.2)
            } else {
                payment = calculateNormalHoursPayment(for: normalHours)
                payment += calculateOvertimeHoursPayment(for: overtimeHours)
            }
    
            return payment
        }
    }
    
    第三に、我々は、通常と残業時間の支払いを計算するための2つのプライベートメソッドを作成しました.さて、私たちのコードは、現在の行の数が大きくなりますが、これは我々が考慮すべきメトリックではありません:私たちは今、読みやすく理解している私たちの主な方法calculateHourlyPay より少ない責任*
  • 曜日の確認
  • 他のメソッドの計算を委任します
  • 合計正常と返済する残業.
  • 今ちょうど3つの責任.私は、あなたが我々がここで作った改善を見るようになることを本当に望みます.それを終えましょうか.
    class Payment {
        let paymentPerHour: Double
    
        init(paymentPerHour: Double) {
            self.paymentPerHour = paymentPerHour
        }
    
        private func normalHours(from workingHours: Int) -> Double {
            return workingHours > 8 ? 8 : Double(workingHours)
        }
    
        private func overtimeHours(from workingHours: Int) -> Double {
            return workingHours > 8 ? Double(workingHours - 8) : 0
        }
    
        private func calculateNormalHoursPayment(for hours: Double, withBonus bonus: Double = 1) -> Double {
            return hours * self.paymentPerHour * bonus
        }
    
        private func calculateOvertimeHoursPayment(for hours: Double, withBonus bonus: Double = 1) -> Double {
            return hours * self.paymentPerHour * bonus * 1.5
        }
    
        private func calculateHorlyPay(for workingHours: Int, withBonus bonus: Double = 1) -> Double {
            let normalHours = self.normalHours(from: workingHours)
            let overtimeHours = self.overtimeHours(from: workingHours)
    
            let normalPay = self.calculateNormalHoursPayment(for: normalHours, withBonus: bonus)
            let overtimePay = self.calculateOvertimeHoursPayment(for: overtimeHours, withBonus: bonus)
    
            return normalPay + overtimePay
        }
    
        func bonus(for dayOfWeek: String) -> Double {
            if dayOfWeek == "sun" {
                return 2
            }
            if dayOfWeek == "sat" {
                return 1.2
            }
            return 1
        }
    
        func calculateHourlyPay(for workingHours: Int, at dayOfWeek: String) -> Double {
            return calculateHorlyPay(
                for: workingHours,
                withBonus: self.bonus(for: dayOfWeek)
            )
        }
    }
    
    let payment = Payment(paymentPerHour: 30)
    
    let sunPayment = payment.calculateHourlyPay(for: 10, at: "sun") // 660
    let monPayment = payment.calculateHourlyPay(for: 10, at: "mon") // 330
    let tuePayment = payment.calculateHourlyPay(for: 10, at: "tue") // 330
    let wedPayment = payment.calculateHourlyPay(for: 10, at: "wed") // 330
    let thuPayment = payment.calculateHourlyPay(for: 10, at: "thu") // 330
    let friPayment = payment.calculateHourlyPay(for: 10, at: "fri") // 330
    let satPayment = payment.calculateHourlyPay(for: 10, at: "sat") // 396
    
    はい!私は、現在このコードで非常に満足です.私たちのメイン(および一意ではない)プライベートメソッドは非常に小さく、1つだけのことです:それは他の特定のメソッドの計算を委譲し、結果を返します.

    結論
    機能とクラスは、気にせずに書かれるならば、厄介なバグの源でありえます.しかし、この記事で議論された原則に従うならば、彼らは我々のコードで最高のものでありえます.
    あなたがこの記事を読んでからいくつかの洞察と学習をしたことを心から願っています.今までのところ、私は自分自身を書くことに最も献身的な記事です!
    あなたがまだそれをまだ持っていないならば、私は強くあなたが読むことを勧めますClean Code アフィリエイトリンク集.それは開発者のための必読です、そして、この記事の内容の大部分は私がそれから学んだものから来ました.
    この記事は、コーディングのベストプラクティスについて書いているシリーズの第2です.
    何か質問がある場合は、以下のコメントを自由に感じるフィードバックを与える.私は、あなたとこれを議論して満足です.
    あなたが私の仕事を楽しむならば、あなたが新しい記事を発表するとき、あなたが通知されるために購読することができます.
    ありがとう!良い一日を!