「詳解 Swift 改訂版」のChapter3、構造体のパートを読んで、構造体の「タイプ・メソッド」の復習をしていました。しかし、萩原さんの説明の矛盾点に悩んでいます。
タイプ・メソッド
- タイプ・メソッドは、個々のインスタンス機能を実装するものではなく、全インスタンス共通して利用される機能の提供
- インスタンスの作り方(個数の制限や再利用)のコントロール
- この型を利用するうえで便利な機能の提供
タイプ・メソッドの説明があり、例文として次の構造体を挙げています。「static」キーワードが付いているメソッドがタイプ・メソッドで、2つ指定されています。「isLeap」メソッドは「うるう年」かどうか判定する計算式です。「西暦年が4で割り切れる年ですが、100で割り切れない年でないといけません。しかし、400で割り切れる場合はうるう年になります」なので、式にしたのが「isLeap」メソッド内の式です。
そして、ある年のある月が何日あるか返すメソッドが「daysOfMonth」メソッドです。これはisLeapメソッドを利用しています。
struct Date {
var year, month, day: Int
static func isLeap(y: Int) -> Bool {
return (y % 4 == 0) && (y % 100 != 0 || y % 400 == 0)
}
static func daysOfMonth(m: Int, year: Int) -> Int {
switch m {
case 2: return self.isLeap(y: year) ? 29 : 28
case 4, 6, 9, 11: return 30
default: return 31
}
}
}
さて、悩んでいるのはこのタイプメソッドの利用法です。萩原さんは次のコードを載せています。
Date.isLeap(y: 2000) // trueが返る
Date.daysOfMonth(m: 2, year: 2000) // 29が返る
実はこういう利用法は、上記のタイプ・メソッドの説明に合ってないから納得できないんです。最初の説明に書いているように「全インスタンス共通して利用される機能の提供」として考えるなら、次のような例文の方が合っているような気がします。
var someDate = Date(year: 2050, month: 11, day: 29)
var anyDate = Date(year: 2016, month: 1, day: 17)
// 上記のようにインスタンスを作って、
// これに共通する機能として次のように書く方が
// 望ましいんじゃないでしょうか?
Date.isLeap(y: anyDate.year) // trueが返る
Date.isLeap(y: someDate.year) // falseが返る
萩原さんの利用法で説明するのなら、**構造体Dateのプロパティ「year」「month」「day」が意味を成さないような気がします。**というのも、萩原さんの利用法を推奨するなら、次のような例文もできます。
var day = 31
Date.isLeap(y: day)
こうなってしまったら、タイプメソッドの説明とはかけ離れてしまいます。いっそのことプロパティを排除して、タイプ・メソッドだけで構成した次のような共通メソッド群とした方がいいような気がします。
struct ComonFunctions {
static func isLeap(y: Int) -> Bool {
return (y % 4 == 0) && (y % 100 != 0 || y % 400 == 0)
}
static func daysOfMonth(m: Int, year: Int) -> Int {
switch m {
case 2: return isLeap(y: year) ? 29 : 28
case 4, 6, 9, 11: return 30
default: return 31
}
}
}
プログラマーの皆さん、どうなんでしょうか?