例えば以下のような User クラスとそのUserのオブジェクトを登録する関数が存在するとします。
class User(val id: Int, val firstName: String, val lastName: String) fun registerUser(user: User) { if (user.firstName.isEmpty()) { throw IllegalArgumentException("Invalid User id=${user.id} : firstName is Empty") } if (user.lastName.isEmpty()) { throw IllegalArgumentException("Invalid User id=${user.id} : lastName is Empty") } // Register to persistence area ... }
登録関数の registerUser
は実際の登録処理前に、Userの firstName と lastName が空文字列でないかのバリデーションを行い、
空であれば IllegalArgumentException
をスローします。
一応実行させてみます
fun main() { registerUser(User(100, "", "Yamada")) }
結果は以下の通りです
Exception in thread "main" java.lang.IllegalArgumentException: Invalid User id=100 : firstName is Empty at net.yyuki.sandbox.localfunc.LocalFuncKt.registerUser(LocalFunc.kt:10) at net.yyuki.sandbox.localfunc.LocalFuncKt.main(LocalFunc.kt:21) at net.yyuki.sandbox.localfunc.LocalFuncKt.main(LocalFunc.kt)
firstName と lastName のチェックを行っている処理は重複していて、DRY原則に従うのであれば、関数化して重複を取り除く必要があるでしょう。
こういった場合に、バリデーションの処理部分をローカル関数として配置する事で重複を取り除く事ができます。
validation というローカル関数を、 registerUser関数の中に配置する事でバリデーション処理の重複を取り除く事ができました。
class User(val id: Int, val firstName: String, val lastName: String) fun registerUser(user: User) { fun validation(user: User, value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("Invalid User id=${user.id} : $fieldName is Empty") } } validation(user, user.firstName, "firstName") validation(user, user.lastName, "lastName") // Register to persistence area ... }
先ほどと同様の呼び出しで実行してみると結果は以下の通りで、予定通りに例外がスローされています
Exception in thread "main" java.lang.IllegalArgumentException: Invalid User id=100 : firstName is Empty at net.yyuki.sandbox.localfunc.LocalFuncKt$registerUser$1.invoke(LocalFunc.kt:11) at net.yyuki.sandbox.localfunc.LocalFuncKt.registerUser(LocalFunc.kt:15) at net.yyuki.sandbox.localfunc.LocalFuncKt.main(LocalFunc.kt:22) at net.yyuki.sandbox.localfunc.LocalFuncKt.main(LocalFunc.kt)
ローカル関数は、外側の関数の全ての引数や変数にアクセスする事ができるため、validation 関数に Userのオブジェクトを渡す必要はありません。
そのため、以下のように書き換え可能です。
class User(val id: Int, val firstName: String, val lastName: String) fun registerUser(user: User) { fun validation(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("Invalid User id=${user.id} : $fieldName is Empty") } } validation(user.firstName, "firstName") validation(user.lastName, "lastName") // Register to persistence area ... }
ローカル関数へ Userのオブジェクトを渡さなくなったので、だいぶすっきりしました。
さらに、validation 関数をUserクラスの拡張関数へと移動する事もできます
class User(val id: Int, val firstName: String, val lastName: String) private fun User.validation() { fun validation(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("Invalid User id=$id : $fieldName is Empty") } } validation(firstName, "firstName") validation(lastName, "lastName") } fun registerUser(user: User) { user.validation() // Register to persistence area ... }
拡張関数にすることで、余分な修飾もなくUserオブジェクトのメンバにアクセスできます。