Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

つくってあそぼ Kotlin DSL 第2版

10/12モバイルメソッド勉強会
「つくってあそぼ Kotlin DSL」

  • Login to see the comments

  • Be the first to like this

つくってあそぼ Kotlin DSL 第2版

  1. 1. Kotlin DSL 2018 10/12 Kamedon
  2. 2. • • Kotlin • •
  3. 3. • DSL • • DSL Kotlin
  4. 4. DSL
  5. 5. DSL • domain-specific language/ ) • DSL (wikipedia ) • 

  6. 6. • DSL 

  7. 7. Builder //Java Human human= new Human.Builder() .setName("Kamedon") .setAge(30) .build(); //Kotlin class Human(val name: String, val age: Int) Human(age = 30, name = "Kamedon") if(human.isAdult()){ //成人だったらなにかする }
  8. 8. Build DSL buildscript { ext.kotlin_version = '1.2.70' repositories { google() jcenter() maven { url 'http://kamedon.github.com/Validation/repository' } } dependencies { classpath 'com.android.tools.build:gradle:3.1.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin: $kotlin_version" } } •
  9. 9. Layout Anko) verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } } • • LinearLayout
  10. 10. DI DSL(Kodein) Kodein { bind<Presenter>() with singleton { Presenter(instance(), inetance()) } } val presenter: Presenter by instance() • •
  11. 11. DSL • • • IDE • Sansan Kotlin DSL
  12. 12. DSL • • • Kotlin 
 Java DSL • DSL Kotlin
  13. 13. DSL
  14. 14. • Validation DSL • DSL • Validation 
 https://github.com/kamedon/Validation
  15. 15. Step1.
  16. 16. • 
 • 
 [ ]
 5 10 
 [ ]
 20
  17. 17. • • • •
  18. 18. Step2. DSL
  19. 19. DSL (1) • • 5 • • A be B
  20. 20. DSL (2) • • 5 
 • A be B 
 not • A be B not Message
  21. 21. DSL be not be not be not be not be not
  22. 22. DSL be not be not be not be not be not
  23. 23. Validation<User> { "name"{ be { name.isNotBlank() } not "入力してください" be { name.length >= 5 } not "5文字以上入力" be { name.length <= 10 } not "10文字以下で入力" } "age"{ be { age >= 0 } not "入力してください" be { age >= 20 } not "20歳以上で入力" } } •
  24. 24. Step3.
  25. 25. be be not be not
  26. 26. • • Pair Triple 
 Build • 
 DSL • DSL
  27. 27. data class User(val name: String, val age: Int) class ValidationTest { @Test fun dslTest() { Validation<User> {} } }
  28. 28. Validation<User> {} • operator invoke companion object 
 • companion object Java static • •
  29. 29. operator invoke • Kotlin +,-, a..b , a in b, 
 • invoke class K { operator fun invoke(value: Int) = value * 1000 } val k = K() k.invoke(1) k(1) // invokeは省略可能
  30. 30. fun Int.k() = this * 1000 val kg = 1.k() //1000 public static final int k(int $receiver) { return $receiver * 1000; } } • • static • 

  31. 31. + operator invoke • operator invoke operator fun String.invoke(){} "name"()
 operator fun String.invoke(f: ()->Unit){} "name"({}) "name"{}
  32. 32. • Lambda) • Syntax Suger val kg: (Int) -> Int = fun(value: Int): Int { return value * 1000 } val kg = { value: Int -> value * 1000 } kg.invoke(1) //invokeメソッドが作られる kg(1) //invokeは省略可能
  33. 33. class Sample { fun task(f: (String) -> Unit) { //taskする f("Hello") } fun task2(f: String.() -> Unit) { //taskする "Hello".f() } }
  34. 34. class Test { fun task() { val sample = Sample() sample.task { print(this::class.java.name) print(it.length) } sample.task2 { print(this::class.java.name) print(length) } } }
  35. 35. 
 // f: String.() -> Unit) public static final String f(@NotNull String $receiver) // f: (String) -> Unit) public static final String f($receiver: Test, @NotNull String it) • • {} this
  36. 36. Validation{} class Validation<T> { companion object { operator fun <T> invoke(init: Validation<T>.() -> Unit): Validation<T> { return Validation<T>().apply(init) } } } public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this }
  37. 37. @Test fun dslTest() { Validation<User> { "name"{} } } • • {} • +
  38. 38. {} class Validation<T> { operator fun String.invoke(init: () -> Unit) } • Validation 
 Validation •
  39. 39. {} • • ChildValidation • {} ChildValidation • ChildValidation • ChildValidation Validation
  40. 40. be not be not be not be not
  41. 41. {} class Validation<T> { val validations = mutableMapOf<String, ChildValidation<T>>() operator fun String.invoke( init: ChildValidation<T>.() -> Unit ) { validations.put(this, ChildValidation<T>().apply(init)) } } • • String.invoke ChildValidation • ChildValidation Validations 

  42. 42. be {} @Test fun dslTest() { Validation<User> { "name"{ be { name.isNotBlank() } } } } • be • name
  43. 43. "name"{ be { name.isNotBlank() } this.be { name.isNotBlank() } this.be({ name.isNotBlank() }) this.be({ this.name.isNotBlank() }) } • 
 ßbe
  44. 44. class Validation<T> { operator fun String.invoke(init: ChildValidation<T>.() -> Unit) } class ChildValidation<T> { fun be(f: T.() -> Boolean) {} } • • String{} ChildValidation • be ChildValidation • User(T) • T boolean
  45. 45. (infix) infix fun Int.k(str: String): String { return "${this * 1000}$str" } val kg = 1.k("g") //1000g val kg = 1 k "g" //1000g • • 1 infix •
  46. 46. be {} not message @Test fun dslTest() { Validation<User> { "name"{ be { name.isNotBlank() } not "入力してください" } } } • be not not
  47. 47. val validation = be { name.isNotBlank() } not "入力してください" if(!validation(user)) { return errorMessage } • 
 1 • {} not • be {} /
  48. 48. class ChildValidation<T> { fun be(f: T.() -> Boolean) = f infix fun (T.() -> Boolean).not(message: String) } • • be • not DSL
  49. 49. Validation<User> { "name"{ be { name.isNotBlank() } not "入力してください" be { name.length >= 5 } not "5文字以上入力" be { name.length <= 10 } not "10文字以下で入力" } "age"{ be { age >= 0 } not "入力してください" be { age >= 20 } not "20歳以上で入力" } } •
  50. 50. class Validation<T> (val validations: Map<String, ChildValidation<T>>) class ChildValidation<T> { val validations: MutableList<Pair<T.() -> Boolean, String>> = mutableListOf() } • ChildValidation Validation • <Pair<T.() -> Boolean, String> ChildValidation
  51. 51. ChildValidation "name"{ be { name.isNotBlank() } not "入力してください" be { name.length >= 0 } not "5文字以上で入力" } class ChildValidation<T> { val validations = mutableListOf<Pair<T.() -> Boolean, String>>() infix fun be(f: T.() -> Boolean) = f infix fun (T.() -> Boolean).not(message: String) { validations.add(Pair(this, message)) } }
  52. 52. Validation ChildValidation Validation<User> { "name"{ be { name.isNotBlank() } not "入力してください" } } class Validation<T> { val validations = mutableMapOf<String, ChildValidation<T>>() operator fun String.invoke(init: ChildValidation<T>.() -> Unit) { validations.put(this, ChildValidation<T>().apply(init)) } } •
  53. 53. DSL Validation<User> { "name"{ } } class Validation<T> { val validations = mutableMapOf<String, ChildValidation<T>>() operator fun String.invoke(init: ChildValidation<T>.() -> Unit) { validations.put(this, ChildValidation<T>().apply(init)) } companion object { inline operator fun <T> invoke( init: Validation<T>.() -> Unit): Validation<T> { return Validation<T>().apply(init) } } }
  54. 54. DSL be { name.isNotBlank() } not "入力してください" class ChildValidation<T> { val validations = mutableListOf<Pair<T.() -> Boolean, String>>() infix fun be(f: T.() -> Boolean) = f infix fun (T.() -> Boolean).not(message: String) { validations.add(Pair(this, message)) } } •
  55. 55. DSL Validation<User> { "name"{ be { name.isNotBlank() } not "入力してください" be { name.length >= 5 } not "5文字以上入力" be { name.length <= 10 } not "10文字以下で入力" } "age"{ be { age >= 0 } not "入力してください" be { age >= 20 } not "20歳以上で入力" } } • validate
  56. 56. Validate class Validation<T> { val validations = mutableMapOf<String, ChildValidation<T>>() fun validate(value: T): Map<String, List<String>> { val messages = mutableMapOf<String, List<String>>() validations.forEach { map -> val errors = map.value.validations.asSequence() .filter { !it.first.invoke(value) } .map { it.second } .filter { it.isNotEmpty() } .toList() if(errors.isNotEmpty()){ messages.put(map.key, errors) } } return messages } }
  57. 57. DSL OK val user = User("kamedon", 30) val v = Validation<User> { "name"{ be { name.isNotBlank() } not "入力してください" be { name.length >= 5 } not "5文字以上入力" be { name.length <= 10 } not "10文字以下で入力" } "age"{ be { age >= 0 } not "入力してください" be { age >= 20 } not "20歳以上で入力" } } v.validate(user) •
  58. 58. DSL NG val user = User("k", 10) val v = Validation<User> { "name"{ be { name.isNotBlank() } not "入力してください" be { name.length >= 5 } not "5文字以上入力" be { name.length <= 10 } not "10文字以下で入力" } "age"{ be { age >= 0 } not "入力してください" be { age >= 20 } not "20歳以上で入力" } } v.validate(user) {name=[5文字以上入力], age=[20歳以上で入力]} •
  59. 59. Step4.
  60. 60. • Pair • Builder var build val • mutable immutable • DSL 

  61. 61. DSL Builder class Validation<T> { val validations = mutableMapOf<String, ChildValidation<T>>() operator fun String.invoke(init: ChildValidation<T>.() -> Unit) { validations.put(this, ChildValidation<T>().apply(init)) } companion object { inline operator fun <T> invoke( init: Validation<T>.() -> Unit): Validation<T> { return Validation<T>().apply(init) } } fun validate(value: T): Map<String, List<String>> }
  62. 62. DSL Builder class ValidationBuilder<T> { val validations = mutableMapOf<String, ChildValidation<T>>() operator fun String.invoke(init: ChildValidation<T>.() -> Unit) { validations.put(this, ChildValidation<T>().apply(init)) } fun build(): Validation<T> { return Validation(validations) } } class Validation<T>(val validations: Map<String, ChildValidation<T>>) { companion object { inline operator fun <T> invoke( init: ValidationBuilder<T>.() -> Unit): Validation<T> { val builder = ValidationBuilder<T>().apply(init) return builder.build() } } }
  63. 63. Step5.
  64. 64. • object DSL • inline GC • • interface
  65. 65. Validation<User> { "name"{ be { name.length >= 5 } not "5文字以下" be { name.length <= 10 } not "10文字以下" } "age"{ be { age >= 20 } not error message { "20歳以上" } be { age <= 150 } not with callback ("150歳以下"){ print("callback") } } } •
  66. 66. 11/2 

  67. 67. • Java • DSL • IDE DSL • DSL
  68. 68. • var val : Builder • {} : lambda lambda • () • : operator invoke • : (Extensions Functions or Properties) • : infix)
  69. 69. • DSL • • 
 Validation GitHub 
 11/2

×