why
SOLID の依存性の逆転のために真ん中に依存させる何かが欲しい!
そのために作りたい機能のクラスを作るための 「継承/実装」元の 3 つを比較してみる。
- 通常のクラスをオープンにして関数の本体を書く
- 抽象クラスに抽象関数のガワだけを書く
- インターフェースに関数のガワだけを書く
通常のクラスをオープンにして関数の本体を書く
open class Animal {
open fun walk() {
println("てくてく")
}
open fun eat() {
println("もぐもぐ")
}
}
class Human: Animal() {
override fun walk() {
println("二足歩行")
}
}
val kaede = Human()
kaede.walk()
kaede.eat()
二足歩行
もぐもぐ
Kotlin では open をつけたクラスは継承できる。
これを使って既に有るメソッドを上書きできる。
今回では Animal クラスを継承した Human クラスを使って
kaede というインスタンスを作成した。
上書きした walk を使うと 二足歩行が使える。
一方、上書きしてない eat も使える。
val fromOpen = Animal()
しかし、オープンクラス自体からもインスタンスをはやせてしまう。
open class Animal {
fun walk()
}
Function 'walk' without a body must be abstract
さらに、本体はいらないのに、関数の中身を必要とされてしまう。
抽象クラスに抽象関数のガワだけを書く
abstract class Animal {
abstract fun walk()
open fun eat() {
println("もぐもぐ")
}
}
抽象にすれば、中身を書かなくても定義できる。
これのほうが実装と分離して書ける。
val fromAbs = Animal()
Cannot create an instance of an abstract class
抽象クラスからはインスタンスが作れなくなる。
これでオープンクラスのときのように元から作れる問題は解決
class Human: Animal() {
override fun walk() {
TODO("Not yet implemented")
}
}
継承先で override で作るべき関数がない場合は赤線がでて
誘導に従えば TODO 状態で作ってくれる。かなり書きやすい。
abstract class Bisinessman {
abstract fun earnMoney()
}
class Human: Animal(),Bisinessman() {
}
Only one class may appear in a supertype list
また、kaede が Animal の walk,eat だけでなく
Bisinessman の earnMoney も使いたくなった時
クラスなので複数の継承元を使うことはできなくて困る。
インターフェースに関数のガワだけを書く
上記の全ての問題はインターフェースを使うことで解決する。
interface Animal {
fun walk()
fun eat()
}
interface Businessman {
fun earnMoney()
}
これを使ってクラスを作って中身を作る時は
インターフェースの中身を「実装」する。
抽象クラスの中身を「継承」するのではなく。
欠点を強いていえば、何かのクラス内部に作ってしまうと
'Animal' is an interface so it cannot be local. Try to use anonymous object or abstract class instead
インターフェースは外に出せとエラーになるくらい。
interface Animal {
fun walk() {
println(
"んぽ"
)
}
fun eat()
}
一方、上書きして実装しないときのデフォルトの挙動もインターフェースで書けてしまう。これをエラーにしたい時に何を使えばいいかは要検証。
Top comments (0)