SlideShare ist ein Scribd-Unternehmen logo
1 von 90
Downloaden Sie, um offline zu lesen
Kotlinアンチパターン
Naoto Nakazato @DroidKaigi2018
自己紹介
● Naoto Nakazato
○ Android開発歴 7年くらい
● Recruit Lifestyle
○ 2017年6月〜
○ HOT PEPPER Beauty
● アカウント
○ Twitter: @oxsoft
○ Facebook: naoto.nakazato
○ GitHub: oxsoft
○ Qiita: oxsoft
KotlinとAndroid
普段のアプリ開発でKotlin使ってますか?
KotlinとAndroid
2017年5月にGoogle I/Oで公式サポートが発表されたKotlin
Javaよりもシンプルで安全なのは間違いない!けど……
● 色々な書き方があって悩む
● 使い方次第ではバグが発生するかも
KotlinとAndroid
公式ドキュメントに「文法」は書いてあるが、
それ以外に「アンチパターン」のようなものがありそう
このセッションでは、30分で10個紹介します!
今日お話しすること
その前に……
ホットペッパービューティー
ホットペッパービューティーは、
国内最大級のサロン検索・予約サービス
ヘアサロン以外にも、
ネイル、まつげ、リラクゼーション、エステがあります
Androidアプリ
2010年 Androidアプリリリース
徐々に秘伝のタレ化
2016年05月 フルリニューアルを決意
2017年02月 フルKotlinに方針転換
2017年12月 リニューアル版リリース
今日お話しすること
KotlinでAndroidアプリを開発して感じたアンチパターン
● リリース済みの巨大なアプリのフルリニューアル
● メンバーのAndroid開発経験が様々
● 最大11人という大人数のチーム開発
プロダクトや開発メンバーによって賛否両論ありそう
→ブースでは実際のソースコードを展示しているので、
 開発メンバーとディスカッションしに来てください!
今日お話しすること
今回はアンチパターンを10個話しますが、
それぞれ以下の4つの構成で話していこうと思います
● 言語機能の説明
● アンチパターン
● 何が良くないか
● 解決策の例
Kotlinを普段使っている人は、
「言語機能の説明」は聞かなくても良いかもしれないです
#01 lateinitとnull初期化
lateinitとは
Javaでは、初期値がない変数宣言は null などが入る
Kotlinでは、基本的に初期値を書かないといけない
TextView message; // nullが入る
var message: TextView // errorになる
var message: TextView? = null // OK
言語機能の説明
lateinitとは
Androidでは、onCreate以降で初期化するものが多い
実質NonNullなものが、全部Nullableになってしまう
言語機能の説明
var message: TextView? = null
fun clear() {
message!!.text = "" // 怪しい
message?.text = "" // 面倒
}
lateinitとは
そこでlateinitを使うと、宣言時に初期値を入れなくて良い
ただし、値を代入していない状態で値を参照すると
UninitializedPropertyAccessException が投げられる
lateinit var message: TextView // OK
言語機能の説明
lateinitの使いどころ
インスタンス作成時には値が定まらないが、
onCreateやonCreateViewで代入されるもの
例えば、
● findViewByIdしたView
● DataBindingのbinding
● DaggerなどのDIによってinjectされるもの
ただし、プリミティブ型やNullableには使えない
言語機能の説明
アンチパターン
通信後に得られる情報をlateinitにする
lateinit var profile: Profile
fun init() {
fetchProfile().subscribe { profile ->
this.profile = profile
}
}
何が良くないか
あらかじめリスナーをセットしている場合に、
通信中や通信エラー時にアクセスして、
UninitializedPropertyAccessException
button.setOnClickListener {
textView.text = profile.name
}
解決策の例
Nullableにして、
常に「情報未取得時どうするか」を考えさせる
var profile: Profile? = null
fun init() {
fetchProfile().subscribe { profile ->
this.profile = profile
}
}
解決策の例
onCreate / onCreateView で初期化可能
 →  lateinitで良さそう
それ以降で値が決まる
 → Nullableにする
     or
   メンバ変数にするのを避ける
isInitialized
Kotlin 1.2から、isInitializedが追加された
値が代入されたかどうかを確認することができる
lateinit var str: String
fun foo() {
val before = ::str.isInitialized // false
str = "hello"
val after = ::str.isInitialized // true
}
補足
isInitialized
元々はテストコードでの利用を想定して追加された
これを日常的に使うともはやnull安全じゃなくなるので、
プロダクションコードでの使用は避けたほうが良さそう
補足
参考:https://youtrack.jetbrains.com/issue/KT-9327
private lateinit var file: File
@After
fun tearDown() {
// ファイル作成前にfailするとエラーになる
file.delete()
}
#02 スコープ関数
スコープ関数
let/run/also/apply/withの5つがあるが、
withを除くとザックリ以下のように分類できる
it this
結果 let run
自身 also apply
戻り値
レシーバ
言語機能の説明
戻り値 = 自身.スコープ関数 {
レシーバ.method()
結果
}
使いどころ1
null関連の制御に便利
str?.let {
// strがnullじゃない場合
}
言語機能の説明
val r = str ?: run {
// strがnullの場合
}
使いどころ2
初期化処理をまとめる
言語機能の説明
val intent = Intent().apply {
putExtra("key", "value")
putExtra("key", "value")
putExtra("key", "value")
}
アンチパターン
apply内でプロパティアクセス形式の処理を書く
val button = Button(context)
button.text = "hello" // JavaのsetText(...)が呼ばれる
// ... buttonの設定が続く ...
val button = Button(context).apply {
text = "hello"
// ... buttonの設定が続く ...
}
↓ apply を使って処理をまとめよう!
何が良くないか
ローカル変数を定義すると、アクセス先が変わる
fun init() {
var text = "" // 後々この行が追加されると……
val button = Button(context).apply {
text = "hello"
}
button.text // "hello"にならない!
}
解決策の例1
apply 内ではプロパティアクセス形式を使わず、
通常の関数呼び出しにする
ただし、ローカル関数が定義されると同じ問題が起きる
val button = Button(context).apply {
setText("hello")
}
解決策の例2
this 必須というルールにする
alsoと似たような感じになってしまう
val button = Button(context).apply {
this.text = "hello"
}
解決策の例3
apply 禁止( also を使う)
我々のチームでは、let と also のみに限定
val button = Button(context).also {
it.text = "hello"
}
#03 Nullable と NonNull
Nullable と NonNull
Nullable / NonNull はKotlinの大きな魅力の1つ
言語機能の説明
val nullable: String? = null // OK
val nonNull: String = null // NG
nullable.length // NG
nullable!!.length // OK
nullable?.length // OK
nonNull.length // OK
アンチパターン1
Nullableのままデータを引き回す
data class User(
val id: Long? = null,
val name: String? = null,
val age: Int? = null
)
API
Domain
UI
Nullable
Nullable
何が良くないか1
Nullableのデータを引き回すと、
至る所でnullチェックやsafe callするハメになる
→ 実質的には条件分岐が増え、挙動把握が困難になる
→ 「 null って何だっけ?」を毎回考えることになる
return team?.user?.name?.length
アンチパターン2
全てNonNullにするために無効なデータを入れる
val response = ...
return User(
id = response.id ?: 0L,
name = response.name.orEmpty(),
age = response.age ?: 0
)
何が良くないか2
全てNonNullにするために無効なデータを入れると……
→ 無効なデータかどうかチェックするハメになる
→ チェックを忘れて表示崩れが起きる
if (user.name.isNotEmpty()) {
// ↑しんどい or 忘れる
}
解決策の例
「nullが何を表すか」で処理するレイヤを決める
● APIが返してくれないからnull → APIのレイヤで処理
● ユーザーが設定してないからnull → UIのレイヤで処理
● 自明じゃなくてレイヤをまたぐ → クラスで明確化
データ表現に困った時、安易にnullに頼らない
(例外を投げる、別のクラスにする、など)
#04 data class
data classとは
● equals/hashCodeやtoStringをよしなにoverride
● componentNやcopyなどのメソッドを生成
言語機能の説明
data class User(val name: String, val age: Int)
val alice = User("Alice", 27)
alice == User("Alice", 27) // true
alice.toString() // "User(name=Alice, age=27)"
val (name, age) = alice
val nextYear = alice.copy(age = 28)
アンチパターン
インスタンス生成用のメソッドで制約を保証したい
data class Range(val min: Int, val max: Int) {
companion object {
fun getRange(a: Int, b: Int): Range {
return Range(minOf(a, b), maxOf(a, b))
}
}
}
何が良くないか
data classにはcopyメソッドが生成される
→ 任意のデータを持つインスタンスが生成可能
val range = Range.getRange(3, 5)
val illegal = range.copy(max = 0)
↑ Range(min=3, max=0) となり、制約が壊れる
解決策の例
値がまとまっているからといってdata classにしない
「値の内部表現」=「クラスに期待される振る舞い」
→ 値のまとまりに名前を付けているだけ
→ data class
「値の内部表現」!=「クラスに期待される振る舞い」
→ 通常のクラスが良さそう
#05 interfaceとabstract class
interfaceのデフォルト実装
Kotlinではinterfaceにデフォルト実装ができる
Java8にもある機能だが、Androidで使うためには、
minSdkVersionを24以上にする必要がある
言語機能の説明
interface Downloadable {
fun download() {
// 共通の処理など
}
}
abstract classと何が違う?
interface abstract class
状態 持てない 持てる
継承元 Interfaceのみ classとInterface
多重継承 できる できない
default method class method
final できない できる
protected できない できる
言語機能の説明
アンチパターン
Javaの頃から言われていたアンチパターンで、
処理を共通化するためだけにBaseクラスを肥大化させる
abstract class BaseActivity : AppCompatActivity() {
protected fun showNetworkError() {
// エラー表示(エラーがないページもあるのに……)
}
}
何が良くないか
何千行レベルの親クラスになり、手に負えなくなる
解決策の例
Javaでは「継承よりも委譲」などと言われてきた
Kotlinではインタフェースのデフォルト実装も良さそう
interface NetworkErrorView {
// エラー用のViewとリロード処理は子クラスで用意
val networkErrorView: View
fun onClickReload()
fun showNetworkError() {
// リスナーのセット、エラーの表示などの共通処理
}
}
解決策の例
もちろん「Baseクラス=悪」ではない
abstractクラスが有効な例
● onCreateなどの継承部分で処理を共通化したい
● クラス外から呼ばれたくない
● 子クラスでoverrideされたくない
解決策の例
デフォルト実装が有効な例
● 前述以外で、処理を共通化させたい場合
● 多重継承したい場合
状態を持ちたい場合も、class delegationを使うとできる
class UserModel() : DefaultImpl by State() { ... }
class UserModel(s: State) : DefaultImpl by s { ... }
状態を持ちつつも、一部の処理は子クラスで実装させたい場合
interface IState {
var state: String
}
interface DefaultImpl : IState {
fun abstract(): String
fun default() { ... }
}
class State : IState {
override var state: String = ""
}
class UserModel : DefaultImpl, IState by State() {
override fun abstract(): String {
return "concrete"
}
}
IState
State
DefaultImpl
UserModel
状態をインタフェースとして切り出し、
状態を実装したクラスを用意する
#06 トップレベル関数と拡張関数
トップレベル関数
ファイルに直接関数を書くことができる
そうすると、どこからでも呼び出すことができる
fun throwOrLog(t: Throwable) {
// 開発はクラッシュ、本番はログ送信
}
fun foo() {
throwOrLog(e)
}
言語機能の説明
拡張関数
クラスに関数を追加する(ように見せる)ことができる
こちらも、どこからでも呼び出せるようになる
fun Any?.log() {
Log.d("DEBUG", this.toString())
}
fun foo() {
123.log()
"hello".log()
}
言語機能の説明
アンチパターン1
一般性がない・局所的にしか使わないメソッドを追加する
fun String.decorate() =
"_人人人人人_n" +
"> $this <n" +
" ̄Y^Y^Y^Y^Y ̄"
↑”decorate”の意味が広く、
 文字幅も考慮されていない
アンチパターン2
公式でありそうな名前なのに雑に実装する
fun String.isInteger() =
this.all { it in '0'..'9' }
↑空文字や負の数が考慮されていない
何が良くないか
拡張関数が便利なので乱発しがち
ライブラリよりもバグが混入している可能性が高い
JavaでUtilクラスを作りまくるのと同種の問題だが、
Kotlinだと通常の関数っぽく見えるので被害が大きい
開発メンバーが多いと特に問題になる
解決策の例
チーム内で拡張関数を作る基準・感覚を揃える
Interfaceのデフォルト実装でスコープを限定する
interface BookmarkableActivity {
fun bookmark() {
// 共通のブックマーク処理
}
fun String.toLabel() = "★ $this"
}
#07 lazyとcustom getter
lazyとは
最初にアクセスがあった時に値が計算され、
以降はその値を返す、delegated propertyの1つ
言語機能の説明
private val userId by lazy {
intent.getStringExtra("USER_ID")
}
通常の代入とcustom getter
● 通常の代入は、インスタンス生成時に計算される
● lazyは、最初にアクセスがあった時に計算される
● custom getterは、アクセスがあるたびに計算される
言語機能の説明
val isAdmin = userId == ADMIN_ID
val isAdmin by lazy { userId == ADMIN_ID }
val isAdmin get() = userId == ADMIN_ID
アンチパターン
通常の代入、lazy、custom getterを、曖昧に使い分ける
何が良くないか
クラッシュしたり、
古い値が返ってきたり、
無駄な計算が何度も走ったりする
private val button = findViewById<Button>(R.id.button)
val area by lazy { width * height }
val user: User get() = User(userId)
解決策の例
値の性質応じて、適切に使い分ける
クラス初期化時に値が確定し、不変 → 通常の代入
ある時点を過ぎるまで値が取れないが、不変 → lazy
状態が変わると値が変わる → custom getter
delegated property
プロパティのget(とset)を別のクラスに委譲できる
→ 普通の値のように見えて分かりやすい
● Intentのextra
● Fragmentのarguments
● savedInstanceState
● SharedPreferences
● FirebaseRemoteConfig   などに使える
ついでに紹介
var userId: String by MyClass()
delegated propertyの使用例
ついでに紹介
class Extra<out T> : ReadOnlyProperty<Activity, T> {
override fun getValue(
thisRef: Activity,
property: KProperty<*>
): T = thisRef.intent.extras.get(property.name) as T
}
fun Intent.put(
prop: KProperty1<*, String>, value: String
): Intent = this.putExtra(prop.name, value)
delegated propertyの使用例
ついでに紹介
class ProfileActivity : AppCompatActivity() {
val userId: String by Extra()
companion object {
fun createIntent(context: Context, userId: String): Intent {
return Intent(context, ProfileActivity::class.java)
.put(ProfileActivity::userId, userId)
}
}
}
かなりの部分を隠蔽することができる
参考:https://speakerdeck.com/sakuna63/kotlins-delegated-properties-x-android
#08 Fragmentとlazy
lazyとは(再掲)
最初にアクセスがあった時に値が計算され、
以降はその値をキャッシュして返す
private val userId by lazy {
intent.getStringExtra("USER_ID")
}
言語機能の説明
アンチパターン
(アンチパターンとして有名ですが)
FragmentのViewをlazyにする
val button by lazy {
view!!.findViewById<Button>(R.id.button)
}
何が良くないか
Fragmentでは同じインスタンスに対して
onCreateViewが再び呼ばれる
          ↓
lazyが初回のviewをずっと保持するため、
再びonCreateViewされても値が更新されない
出典:https://developer.android.com/guide/components/fragments.html
解決策の例
lateinit を使い、onCreateView で代入する
Data Bindingの場合も、binding自体はlateinitが良い
lateinit var button: Button
override fun onCreateView(...): View? {
val view = ...
button = view.findViewById(R.id.button)
return view
}
#09 custom getter
custom getterとは
valやvarの返り値は、カスタマイズすることができる
文法上は、引数がない関数は全てcustom getterにできる
var userId: String = ""
val isAdmin get() = userId == ADMIN_ID
言語機能の説明
アンチパターン1
値を取得する過程で副作用がある
private val itemCount: Int
get() {
if (recyclerView.adapter == null) {
initXXX() // 副作用
}
return recyclerView.adapter.itemCount
}
アンチパターン2
計算量が多い
private val total: Int
get() = countView(root)
fun countView(view: View): Int = if (view is ViewGroup) {
(0 until view.childCount).map {
countView(view.getChildAt(it))
}.sum()
} else {
1
}
何が良くないか
呼び出す側からは通常の変数と同じように見える
そのため、状態が変化したり、計算が重いことが予期できず、
予想外のバグやパフォーマンス低下を引き起こす
if (this.itemCount > 20) {
// ↑のように無邪気にアクセスしてしまう
}
解決策の例
副作用がなくて、計算量が少ない → custom getter
副作用があるか、計算量が多い  → 関数
上記の分類で実装していけば、関数名よりも明確に、
呼び出し側が副作用の有無や計算量を予想することができる
公式のリファレンスにも同様の記述がある
https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties
#10 custom setter
custom setterとは
Kotlinでは通常のvarをカスタマイズすることができる
var text: String = ""
set(value) {
field = value
notifyDataSetChanged()
}
言語機能の説明
custom setterの使いどころ
● 値をセットするついでに更新処理などを行う
● 変数の委譲
言語機能の説明
変数の委譲
メンバー変数への委譲を簡単に書くことができる
(この場合はバッキングフィールドも生成されない)
private val owner = Person()
var ownerName: String
get() = owner.name
set(value) {
owner.name = value
}
言語機能の説明
※バッキングフィールド:Javaに変換された時に生成されるメンバ変数
変数の委譲
もちろん以下のように書くこともできるが、やや冗長
private val owner = Person()
var ownerName: String by object : ReadWriteProperty<Any, String> {
override fun getValue(thisRef: Any, property: KProperty<*>): String = owner.name
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
owner.name = value
}
}
言語機能の説明
アンチパターン
常に必要とは限らない処理をしている
例:値が更新されたら、XXXを更新して、YYYに通知して、ZZZ
の値も更新して……
var person: Person = Person()
set(value) {
field = value
updateXXX()
notifyYYY()
ZZZ = value.name
}
何が良くないか
値だけ変えることができない(たとえクラス内からでも)
反映や計算を後でまとめてやるとかができない
値を代入しただけだと思ったら予想外の挙動になる
解決策の例
当たり前だけど、varの責務は値の保持にとどめておくべき
varは普通にprivateで持っておいて、更新用の関数を公開する
というJavaのパターンの方が混乱が少ないケースも多い
最後にもう1つ
最後にもう1つだけ……
Kotlinの機能を無理やり使わないように注意が必要かも
● operator overload
● infix
● tailrec etc...
キレイに書けると超気持ちいいが、
実際のプロダクトで有効な例はそんなに多くないはず
特にチーム開発では、分かりやすさも大事
まとめ
Kotlinは最高!だけど書き方の自由度が高い
→チーム開発の場合は、基準・感覚をこまめに話し合う
まずは今回話した10個のテーマで
ブースにて実際のソースコードを展示しているので、
開発メンバーとディスカッションしに来てください!

Weitere ähnliche Inhalte

Was ist angesagt?

オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ
増田 亨
 

Was ist angesagt? (20)

Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり
 
ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方
 
RLSを用いたマルチテナント実装 for Django
RLSを用いたマルチテナント実装 for DjangoRLSを用いたマルチテナント実装 for Django
RLSを用いたマルチテナント実装 for Django
 
Web エンジニアが postgre sql を選ぶ 3 つの理由
Web エンジニアが postgre sql を選ぶ 3 つの理由Web エンジニアが postgre sql を選ぶ 3 つの理由
Web エンジニアが postgre sql を選ぶ 3 つの理由
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!
 
Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版
 
Cognitive Complexity でコードの複雑さを定量的に計測しよう
Cognitive Complexity でコードの複雑さを定量的に計測しようCognitive Complexity でコードの複雑さを定量的に計測しよう
Cognitive Complexity でコードの複雑さを定量的に計測しよう
 
ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説
 
Yahoo!ニュースにおけるBFFパフォーマンスチューニング事例
Yahoo!ニュースにおけるBFFパフォーマンスチューニング事例Yahoo!ニュースにおけるBFFパフォーマンスチューニング事例
Yahoo!ニュースにおけるBFFパフォーマンスチューニング事例
 
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかDDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
 
ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開
 
オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
リッチなドメインモデル 名前探し
リッチなドメインモデル 名前探しリッチなドメインモデル 名前探し
リッチなドメインモデル 名前探し
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計
 
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
 
TDD のこころ
TDD のこころTDD のこころ
TDD のこころ
 

Ähnlich wie Kotlinアンチパターン

ユーザーストーリー:ファースト・ジェネレーション
ユーザーストーリー:ファースト・ジェネレーションユーザーストーリー:ファースト・ジェネレーション
ユーザーストーリー:ファースト・ジェネレーション
Masanori Kado
 
D言語会議#1
D言語会議#1D言語会議#1
D言語会議#1
9rnsr
 
Tizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native apiTizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native api
Naruto TAKAHASHI
 
Deep dive into oss written in swift
Deep dive into oss written in swiftDeep dive into oss written in swift
Deep dive into oss written in swift
Yuki Asai
 

Ähnlich wie Kotlinアンチパターン (20)

Ecmascript2015とその周辺について
Ecmascript2015とその周辺についてEcmascript2015とその周辺について
Ecmascript2015とその周辺について
 
わんくま東京#32 「null ヤバイのでなんとかする」
わんくま東京#32 「null ヤバイのでなんとかする」わんくま東京#32 「null ヤバイのでなんとかする」
わんくま東京#32 「null ヤバイのでなんとかする」
 
ユーザーストーリー:ファースト・ジェネレーション
ユーザーストーリー:ファースト・ジェネレーションユーザーストーリー:ファースト・ジェネレーション
ユーザーストーリー:ファースト・ジェネレーション
 
GroovyなAndroidテスト #atest_hack
GroovyなAndroidテスト #atest_hackGroovyなAndroidテスト #atest_hack
GroovyなAndroidテスト #atest_hack
 
D言語会議#1
D言語会議#1D言語会議#1
D言語会議#1
 
Processing
ProcessingProcessing
Processing
 
Tokyo GTUG Bootcamp2010
Tokyo GTUG Bootcamp2010Tokyo GTUG Bootcamp2010
Tokyo GTUG Bootcamp2010
 
20180830 implement dqn_platinum_data_meetup_vol1
20180830 implement dqn_platinum_data_meetup_vol120180830 implement dqn_platinum_data_meetup_vol1
20180830 implement dqn_platinum_data_meetup_vol1
 
Tizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native apiTizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native api
 
とあるFlashの自動生成
とあるFlashの自動生成とあるFlashの自動生成
とあるFlashの自動生成
 
Pronama 0707 wf4
Pronama 0707 wf4Pronama 0707 wf4
Pronama 0707 wf4
 
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフトobjc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
 
Deep dive into oss written in swift
Deep dive into oss written in swiftDeep dive into oss written in swift
Deep dive into oss written in swift
 
Deep dive into oss written in swift
Deep dive into oss written in swiftDeep dive into oss written in swift
Deep dive into oss written in swift
 
Introduction of Python
Introduction of PythonIntroduction of Python
Introduction of Python
 
C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0
 
AozoraYomite @InfoTalk 2012/12/21
AozoraYomite @InfoTalk 2012/12/21AozoraYomite @InfoTalk 2012/12/21
AozoraYomite @InfoTalk 2012/12/21
 
プログラミングの基礎振り返りスライド1
プログラミングの基礎振り返りスライド1プログラミングの基礎振り返りスライド1
プログラミングの基礎振り返りスライド1
 
Zigbee Study
Zigbee StudyZigbee Study
Zigbee Study
 
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
 

Mehr von Recruit Lifestyle Co., Ltd.

Mehr von Recruit Lifestyle Co., Ltd. (20)

業務と消費者の体験を同時にデザインするリクルートの価値検証のリアル ー 「Airレジ ハンディ」セルフオーダーのブレない「価値」の確かめ方 ー
業務と消費者の体験を同時にデザインするリクルートの価値検証のリアル ー 「Airレジ ハンディ」セルフオーダーのブレない「価値」の確かめ方 ー業務と消費者の体験を同時にデザインするリクルートの価値検証のリアル ー 「Airレジ ハンディ」セルフオーダーのブレない「価値」の確かめ方 ー
業務と消費者の体験を同時にデザインするリクルートの価値検証のリアル ー 「Airレジ ハンディ」セルフオーダーのブレない「価値」の確かめ方 ー
 
分散トレーシングAWS:X-Rayとの上手い付き合い方
分散トレーシングAWS:X-Rayとの上手い付き合い方分散トレーシングAWS:X-Rayとの上手い付き合い方
分散トレーシングAWS:X-Rayとの上手い付き合い方
 
OOUIを実践してわかった、9つの大切なこと
OOUIを実践してわかった、9つの大切なことOOUIを実践してわかった、9つの大切なこと
OOUIを実践してわかった、9つの大切なこと
 
Flutter移行の苦労と、乗り越えた先に得られたもの
Flutter移行の苦労と、乗り越えた先に得られたものFlutter移行の苦労と、乗り越えた先に得られたもの
Flutter移行の苦労と、乗り越えた先に得られたもの
 
CTIサービスを支える裏側 〜物理デバイスとの戦い〜 | iOSDC Japan 2020
CTIサービスを支える裏側 〜物理デバイスとの戦い〜 | iOSDC Japan 2020CTIサービスを支える裏側 〜物理デバイスとの戦い〜 | iOSDC Japan 2020
CTIサービスを支える裏側 〜物理デバイスとの戦い〜 | iOSDC Japan 2020
 
「進化し続けるインフラ」のためのマルチアカウント管理
「進化し続けるインフラ」のためのマルチアカウント管理「進化し続けるインフラ」のためのマルチアカウント管理
「進化し続けるインフラ」のためのマルチアカウント管理
 
Air事業のデザイン組織とデザイナー
Air事業のデザイン組織とデザイナーAir事業のデザイン組織とデザイナー
Air事業のデザイン組織とデザイナー
 
リクルートライフスタイル AirシリーズでのUXリサーチ
リクルートライフスタイル AirシリーズでのUXリサーチリクルートライフスタイル AirシリーズでのUXリサーチ
リクルートライフスタイル AirシリーズでのUXリサーチ
 
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
 
データサイエンティストが力を発揮できるアジャイルデータ活用基盤
データサイエンティストが力を発揮できるアジャイルデータ活用基盤データサイエンティストが力を発揮できるアジャイルデータ活用基盤
データサイエンティストが力を発揮できるアジャイルデータ活用基盤
 
Real-time personalized recommendation using embedding
Real-time personalized recommendation using embeddingReal-time personalized recommendation using embedding
Real-time personalized recommendation using embedding
 
データから価値を生み続けるには
データから価値を生み続けるにはデータから価値を生み続けるには
データから価値を生み続けるには
 
データプロダクト開発を成功に導くには
データプロダクト開発を成功に導くにはデータプロダクト開発を成功に導くには
データプロダクト開発を成功に導くには
 
Jupyter だけで機械学習を実サービス展開できる基盤
Jupyter だけで機械学習を実サービス展開できる基盤Jupyter だけで機械学習を実サービス展開できる基盤
Jupyter だけで機械学習を実サービス展開できる基盤
 
SQLを書くだけでAPIが作れる基盤
SQLを書くだけでAPIが作れる基盤SQLを書くだけでAPIが作れる基盤
SQLを書くだけでAPIが作れる基盤
 
BtoBサービスならではの顧客目線の取り入れ方
BtoBサービスならではの顧客目線の取り入れ方BtoBサービスならではの顧客目線の取り入れ方
BtoBサービスならではの顧客目線の取り入れ方
 
The Design for Serverless ETL Pipeline データ分析基盤のレガシーなデータロードをサーバレスでフルリプレースするまで道のり
The Design for Serverless ETL Pipeline データ分析基盤のレガシーなデータロードをサーバレスでフルリプレースするまで道のりThe Design for Serverless ETL Pipeline データ分析基盤のレガシーなデータロードをサーバレスでフルリプレースするまで道のり
The Design for Serverless ETL Pipeline データ分析基盤のレガシーなデータロードをサーバレスでフルリプレースするまで道のり
 
リクルートライフスタイルにおける深層学習の活用とGCPでの実現方法
リクルートライフスタイルにおける深層学習の活用とGCPでの実現方法リクルートライフスタイルにおける深層学習の活用とGCPでの実現方法
リクルートライフスタイルにおける深層学習の活用とGCPでの実現方法
 
ビックデータ分析基盤の成⻑の軌跡
ビックデータ分析基盤の成⻑の軌跡ビックデータ分析基盤の成⻑の軌跡
ビックデータ分析基盤の成⻑の軌跡
 
Refactoring point of Kotlin application
Refactoring point of Kotlin applicationRefactoring point of Kotlin application
Refactoring point of Kotlin application
 

Kotlinアンチパターン