Menu
Coddy logo textTech

finalと階層構造

CoddyのSwiftジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 25/57。

finalは、それ以降のオーバーライドを禁止します。final funcはどのサブクラスでもオーバーライドできず、final classはまったくサブクラス化できません。

class Account {
    final func id() -> String { "ACC" }   // オーバーライドできません
}

final class Receipt { }                  // サブクラス化できません
// class GiftReceipt: Receipt { }        // エラー: finalクラスからの継承

サブクラスで変更させたくないメソッド(たとえば、ルールを適用するメソッド)には final を付けます。他のメソッドは引き続きオーバーライドして、詳細をカスタマイズできます。

class Checkout {
    final func total(_ prices: [Int]) -> Int {
        let sum = prices.reduce(0, +)
        return sum - discount(sum)
    }
    func discount(_ sum: Int) -> Int { 0 }
}

class SaleCheckout: Checkout {
    override func discount(_ sum: Int) -> Int { sum / 10 }
}

print(Checkout().total([50, 50]))
print(SaleCheckout().total([50, 50]))

出力:

100
90

階層には複数のレベルを持たせることができます。メソッドはオブジェクト自身のクラスから上位へ向かって検索され、最初に見つかった実装が実行されます。

class Animal {
    func move() -> String { "moves" }
    func eat() -> String { "eats" }
}
class Bird: Animal {
    override func move() -> String { "flies" }
}
class Penguin: Bird {
    override func move() -> String { "swims" }
}

let p = Penguin()
print(p.move())
print(p.eat())

出力:

swims
eats

階層は浅く保ちましょう。深いチェーンは追いにくくなります。これは、1つのオブジェクトの振る舞いが多くのクラスに分散するためです。型が is-a 関係なしに振る舞いを共有する場合は、通常、プロトコル(次の章)のほうが適したツールです。

// 深くて脆弱
class A { }
class B: A { }
class C: B { }
class D: C { }      // Dは何を継承し、どこから継承するのか?
challenge icon

チャレンジ

簡単

Discount には、価格から amount(for:) を引いた値を返し、0 未満にはならない final メソッド apply(to:) があります。また、amount(for:) は 0 を返します。PercentOff(percent を格納し、金額 = price × percent / 100)と FixedOff(value を格納し、金額 = value)をそれぞれ宣言し、各クラスにイニシャライザと amount(for:) のオーバーライドを実装してください。apply(to:) 自体は Discount に残します。

提供されたコードは価格を読み込み、その後 p,10 または f,300 による割引を読み取り、それぞれを価格に適用して 1000 -> 900 のように出力します。

コードは Discount.swift、PercentOff.swift、FixedOff.swift に記述します。main.swift には提供された入出力コードが含まれており、編集できません。

自分で試してみよう

// 提供された入出力コード: そのままにしておく
var input: [String] = []
while let line = readLine() { input.append(line) }
let price = Int(input[0])!
for line in input.dropFirst() {
    let p = line.split(separator: ",").map(String.init)
    let d: Discount = p[0] == "p" ? PercentOff(percent: Int(p[1])!) : FixedOff(value: Int(p[1])!)
    print("\(price) -> \(d.apply(to: price))")
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Swiftプレイグラウンド