Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Эволюция антипаттернов в Java и Kotlin. Михаил Горюнов

CocoaHeads
November 15, 2019

Эволюция антипаттернов в Java и Kotlin. Михаил Горюнов

Kotlin — это логическое продолжение Java и в некотором смысле результат её эволюции.
Антипаттерны сильно зависят от языка и трансформируются вместе с ним. Я рассмотрю, как антипаттерны изменились, какие исчезли и какие появились новые.

CocoaHeads

November 15, 2019
Tweet

More Decks by CocoaHeads

Other Decks in Technology

Transcript

  1. Эволюция антипаттернов в Java и Kotlin @Miha-x64 @miha_x64 @Harmonizr at

    android_ru, kotlin_mobile, spbpeerlab Михаил Горюнов
  2. 5 «Ленивый» синглтон public final class Single { private static

    Single INSTANCE; private Single() {} public static Single getInstance() { return INSTANCE != null ? INSTANCE : (INSTANCE = new Single()); } }
  3. 6 Многопоточный «ленивый» синглтон public final class Single { private

    static final Object LOCK = new Object(); private static volatile Single INSTANCE; private Single() {} public static Single getInstance() { Single inst = INSTANCE; if (inst != null) return inst; synchronized(LOCK) { if ((inst = INSTANCE) != null) return inst; return INSTANCE = new Single(); } } }
  4. 7 Синглтон из enum public enum Singleton { INSTANCE; }

    Effective Java, Third Edition by Joshua Bloch Item 3: Enforce the singleton property with a private constructor or an enum type
  5. 8 Синглтон из enum public enum Singleton { INSTANCE; }

    implements Comparable, Serializable name(), ordinal() values(), valueOf() Effective Java, Third Edition by Joshua Bloch Item 3: Enforce the singleton property with a private constructor or an enum type
  6. 9 Синглтон здорового человека public final class Single { private

    static final Single INSTANCE = new Single(); private Single() {} public static Single getInstance() { return INSTANCE; } }
  7. 10 Синглтон здорового человека public final class Single { private

    static final Single INSTANCE; static { INSTANCE = new Single(); } private Single() {} public static Single getInstance() { return INSTANCE; } }
  8. 12 «Синглтон» с параметром public final class Single { private

    static Single INSTANCE; public static Single getInstance(Context context) { return INSTANCE != null ? INSTANCE : (INSTANCE = new Single(context)); } private final Context context; private Single(Context context) { this.context = context; } }
  9. 13 «Синглтон» с параметром class Single private constructor(private val context:

    Context) { companion object { private var INSTANCE: Single? = null fun getInstance(context: Context): Single { INSTANCE!.let { return it } return Single(context).also { INSTANCE = it } } } }
  10. 14 «Синглтон» с параметром companion object { @Volatile private var

    INSTANCE: UsersDatabase? = null fun getInstance(context: Context): UsersDatabase = INSTANCE !: synchronized(this) { INSTANCE !: buildDatabase(context).also { INSTANCE = it } } private fun buildDatabase(context: Context) = Room.databaseBuilder(context.applicationContext, UsersDatabase!:class.java, "Sample.db") .build() }
  11. 15 Класс с параметром — ок public final class NotSingle

    { private final Context context; public NotSingle(Context context) { this.context = context; } } class NotSingle( private val context: Context )
  12. 16 Nullability hell var callback: Callback? = null override fun

    onAttach(context: Context?) { !/ 1 this.callback = context as? Callback !/ 2 } override fun onClick(v: View?) { !/ 3 (v!.tag !/ 4 as? SomeObj)!.let { obj !> !/5 callback!.objClicked(obj) !/ 6 } }
  13. 17 Nullability hell var callback: Callback? = null override fun

    onAttach(context: Context?) { !/ 1 this.callback = context as? Callback !/ 2 } override fun onClick(v: View?) { !/ 3 (v!.tag !/ 4 as? SomeObj)!.let { obj !> !/5 callback!.objClicked(obj) !/ 6 } }
  14. 18 Nullability hell var callback: Callback? = null override fun

    onAttach(context: Context?) { !/ 1 this.callback = context as? Callback !/ 2 } override fun onClick(v: View?) { !/ 3 (v?.tag !/ 4 as? SomeObj)?.let { obj !> !/5 callback?.objClicked(obj) !/ 6 } }
  15. 19 По всей строгости — ок lateinit var callback: Callback

    fun onAttach(context: Context) { this.callback = context as Callback } override fun onClick(v: View) { callback.objClicked(v.tag as SomeObj) }
  16. 21 Исключения val el = element !/ Java PsiTreeUtil.getParentOfType(el, PsiMethodCallExpression!:class.java)!.methodExpression

    !.takeIf { it.qualifierExpression !!= this } !.let { (it.reference!.resolve() as PsiMethod?)!.name } !.let { return it } !/ Kotlin PsiTreeUtil.getParentOfType(el, KtDotQualifiedExpression!:class.java) !.takeIf { it.receiverExpression.references.any { it.element != el } } !.let { (it.selectorExpression as? KtCallExpression)!.calleeExpression!.references } !.forEach { (it.resolve() as? PsiMethod)!.name!.let { return it } } return null
  17. 22 enum в роли тупой константы enum Role { User,

    Admin, } @DrawableRes int iconOf(Role role) { switch (role) { case User: return R.drawable.icon_user; case Admin: return R.drawable.icon_admin; default: throw new AssertionError(); } }
  18. 23 enum в роли тупой константы enum Role { User,

    Admin, Anonymous, } @DrawableRes int iconOf(Role role) { switch (role) { case User: return R.drawable.icon_user; case Admin: return R.drawable.icon_admin; default: throw new AssertionError(); } }
  19. 24 enum не такой уж и тупой — ок enum

    Role { User(R.drawable.icon_user), Admin(R.drawable.icon_admin), ; @DrawableRes public final int icon; Role(@DrawableRes int icon) { this.icon = icon; } }
  20. 25 Exhaustive when — ок! enum class Role { User,

    Admin, } @DrawableRes fun iconOf(role: Role): Int = when (role) { Role.User !> R.drawable.icon_user Role.Admin !> R.drawable.icon_admin }
  21. 27 data class The compiler automatically derives the following members

    from all properties declared in the primary constructor:  equals()/hashCode() pair;  toString() of the form "User(name=John, age=42)";  componentN() functions corresponding to the properties in their order of declaration;  copy() function.
  22. 29 val user = Pair("John", 42) !!. val (name, age)

    = user val name = user.component1() val age = user.component2()
  23. 30 val user = Pair("John", 42) !!. val (name, age)

    = user val name = user.component1() val age = user.component2() user.copy(second = 43)
  24. 31 data class data class User(val name: String, val age:

    Int) @GET("/me") fun me(): Call<User>
  25. 32 data class data class User(val name: String, val age:

    Int) @GET("/me") fun me(): Call<User>
  26. 34 Destructuring не-кортежей val fullName = FullName("John", "Doe") !!. val

    name = fullName.component1() val surname = fullName.component2()
  27. 35 Destructuring не-кортежей val fullName = FullName("John", "Doe") !!. val

    surname = fullName.component1() val name = fullName.component2()
  28. 40 Свалка констант public interface Constants { String IMAGE_BASE_URL =

    "https:!/upload.wikimedia.org/wikipedia/commons/"; String KEY_NAME = "name"; }
  29. 42 Магические константы в точке входа — ок new ImageLoader(

    "https:!/upload.wikimedia.org/wikipedia/commons/" ) new ImageLoader(BuildConfig.IMAGE_BASE_URL) TODO getString, getInt, etc
  30. 43 Типизированные ключи — ок private static final StringKey KEY_NAME

    = new StringKey("name"); https://matklad.github.io/2018/05/24/typed-key-pattern.html
  31. 44 class StringKey { private final String key; public StringKey(String

    key) { this.key = key; } public String get(Bundle from) { return from.getString(key); } public void set(Bundle to, String value) { to.putString(key, value); } }
  32. 46 • избыточная «ленивость» синглтона, потокоопасная инициализация, излишняя синхронизация, синглтон

    из enum, наличие параметра • -NullPointerException +nullability hell • -switch (enum) • +data class DTO • +destructuring не-кортежей • нестрогие, изменяемые, нуллабельные DTO с параметрами по умолчанию • свалка констант • нетипизированные ключи