From 961523ac45d6ce9b8ea04db3e18fdd0423d7082d Mon Sep 17 00:00:00 2001 From: buna Date: Thu, 7 Sep 2023 10:14:33 +0900 Subject: [PATCH 01/37] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20getParameterValues()=EB=A5=BC=20getArgs()=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/shopping/di/injector/DependencyInjector.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt index dffc7ae0b..44a4ca1d8 100644 --- a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt +++ b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt @@ -13,13 +13,13 @@ object ClassInjector { inline fun inject(): T { val primaryConstructor = validateHasPrimaryConstructor() - val parameterValues = getParameterValues(primaryConstructor.parameters) + val args = getArgs(primaryConstructor.parameters) - return primaryConstructor.call(*parameterValues.toTypedArray()) + return primaryConstructor.call(*args.toTypedArray()) } @OptIn(ExperimentalStdlibApi::class) - fun getParameterValues(parameters: List): MutableList { + fun getArgs(parameters: List): MutableList { val parameterTypes = parameters.map { it.type } val parameterValues = mutableListOf() From 5ac37cf40919f5e3b132b5c2e6281aca16c36df2 Mon Sep 17 00:00:00 2001 From: buna Date: Thu, 7 Sep 2023 11:40:31 +0900 Subject: [PATCH 02/37] =?UTF-8?q?feat:=20=ED=95=84=EB=93=9C=20=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/di/annotation/Inject.kt | 5 +++ .../di/injector/DependencyInjector.kt | 35 +++++++++++-------- .../shopping/di/lazy/ActivityViewModelLazy.kt | 4 +-- .../shopping/di/util/ExceptionUtil.kt | 5 +++ .../{UtilFunctions.kt => FunctionUtil.kt} | 0 5 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/woowacourse/shopping/di/annotation/Inject.kt create mode 100644 app/src/main/java/woowacourse/shopping/di/util/ExceptionUtil.kt rename app/src/main/java/woowacourse/shopping/di/util/{UtilFunctions.kt => FunctionUtil.kt} (100%) diff --git a/app/src/main/java/woowacourse/shopping/di/annotation/Inject.kt b/app/src/main/java/woowacourse/shopping/di/annotation/Inject.kt new file mode 100644 index 000000000..de66693a5 --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/di/annotation/Inject.kt @@ -0,0 +1,5 @@ +package woowacourse.shopping.di.annotation + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY_SETTER) +annotation class Inject diff --git a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt index 44a4ca1d8..7fc143c62 100644 --- a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt +++ b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt @@ -1,10 +1,12 @@ package woowacourse.shopping.di.injector +import woowacourse.shopping.di.annotation.Inject +import woowacourse.shopping.di.util.throwNotExistDependency import woowacourse.shopping.di.util.validateHasPrimaryConstructor import kotlin.reflect.KParameter import kotlin.reflect.javaType -object ClassInjector { +object DependencyInjector { val dependencies = mutableMapOf, Any>() inline fun inject(instance: T) { @@ -15,29 +17,32 @@ object ClassInjector { val primaryConstructor = validateHasPrimaryConstructor() val args = getArgs(primaryConstructor.parameters) - return primaryConstructor.call(*args.toTypedArray()) - } - - @OptIn(ExperimentalStdlibApi::class) - fun getArgs(parameters: List): MutableList { - val parameterTypes = parameters.map { it.type } - val parameterValues = mutableListOf() + val instance = primaryConstructor.call(*args.toTypedArray()) + injectFields(instance) - parameterTypes.forEach { paramType -> - val parameterType = paramType.javaType - val parameterValue = dependencies[parameterType] + return instance + } - requireNotNull(parameterValue) { "[ERROR] 주입할 의존성이 존재하지 않습니다." } - parameterValues.add(parameterValue) + inline fun injectFields(instance: T) { + instance::class.java.declaredFields.forEach { field -> + if (field.isAnnotationPresent(Inject::class.java)) { + val dependency = dependencies[field.type] ?: throwNotExistDependency(field.type) + field.isAccessible = true + field.set(instance, dependency) + } } + } - return parameterValues + @OptIn(ExperimentalStdlibApi::class) + fun getArgs(parameters: List): List = parameters.map { param -> + val type = param.type.javaType + dependencies[type] ?: throwNotExistDependency(type.javaClass) } } class ClassInjectorDsl { inline fun inject(instance: T) { - ClassInjector.inject(instance) + DependencyInjector.inject(instance) } } diff --git a/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt b/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt index 2c0b64210..7d91bfad8 100644 --- a/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt +++ b/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt @@ -3,7 +3,7 @@ package woowacourse.shopping.di.lazy import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelLazy -import woowacourse.shopping.di.injector.ClassInjector +import woowacourse.shopping.di.injector.DependencyInjector import woowacourse.shopping.di.util.viewModelFactory inline fun ComponentActivity.viewModel(): Lazy { @@ -15,5 +15,5 @@ inline fun ComponentActivity.viewModel(): Lazy { } inline fun createViewModel(): VM { - return ClassInjector.inject() + return DependencyInjector.inject() } diff --git a/app/src/main/java/woowacourse/shopping/di/util/ExceptionUtil.kt b/app/src/main/java/woowacourse/shopping/di/util/ExceptionUtil.kt new file mode 100644 index 000000000..d3a642f18 --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/di/util/ExceptionUtil.kt @@ -0,0 +1,5 @@ +package woowacourse.shopping.di.util + +fun throwNotExistDependency(clazz: Class<*>) { + throw IllegalArgumentException("[ERROR] 주입할 의존성이 존재하지 않습니다. parameterType: $clazz") +} diff --git a/app/src/main/java/woowacourse/shopping/di/util/UtilFunctions.kt b/app/src/main/java/woowacourse/shopping/di/util/FunctionUtil.kt similarity index 100% rename from app/src/main/java/woowacourse/shopping/di/util/UtilFunctions.kt rename to app/src/main/java/woowacourse/shopping/di/util/FunctionUtil.kt From f3db23987354f4ee69ec355f7dbf08898c6c9e4c Mon Sep 17 00:00:00 2001 From: buna Date: Thu, 7 Sep 2023 12:40:14 +0900 Subject: [PATCH 03/37] =?UTF-8?q?feat:=20=EC=A2=85=EC=86=8D=20=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=20=EB=A7=B5=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/shopping/di/injector/DependencyInjector.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt index 7fc143c62..1088af7b0 100644 --- a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt +++ b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt @@ -38,6 +38,10 @@ object DependencyInjector { val type = param.type.javaType dependencies[type] ?: throwNotExistDependency(type.javaClass) } + + fun clear() { + dependencies.clear() + } } class ClassInjectorDsl { From c95b7c0148444ad86238289841181ece7c93f99e Mon Sep 17 00:00:00 2001 From: buna Date: Thu, 7 Sep 2023 12:54:39 +0900 Subject: [PATCH 04/37] =?UTF-8?q?test:=20=EB=B7=B0=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90/=ED=95=84=EB=93=9C=20=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/injector/DependencyInjectorTest.kt | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 app/src/test/java/woowacourse/shopping/di/injector/DependencyInjectorTest.kt diff --git a/app/src/test/java/woowacourse/shopping/di/injector/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/shopping/di/injector/DependencyInjectorTest.kt new file mode 100644 index 000000000..2efedd26d --- /dev/null +++ b/app/src/test/java/woowacourse/shopping/di/injector/DependencyInjectorTest.kt @@ -0,0 +1,101 @@ +package woowacourse.shopping.di.injector + +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertNotNull +import junit.framework.TestCase.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import woowacourse.shopping.di.annotation.Inject +import woowacourse.shopping.di.lazy.viewModel + +class ConstructorTestActivity : AppCompatActivity() { + val viewModel: ConstructorTestViewModel by viewModel() +} + +class ConstructorTestViewModel( + val constructorDependency: ConstructorDependency, +) : ViewModel() + +class FieldTestActivity : AppCompatActivity() { + val viewModel: FieldTestViewModel by viewModel() +} + +class FieldTestViewModel : ViewModel() { + @Inject + lateinit var fieldWithInjectAnnotation: FieldDependency + lateinit var fieldWithoutInjectAnnotation: FieldDependency + + fun isFieldWithoutInjectAnnotationInitialized() = ::fieldWithoutInjectAnnotation.isInitialized +} + +class ConstructorDependency +class FieldDependency + +@RunWith(RobolectricTestRunner::class) +class DependencyInjectorTest { + + @Before + fun setup() { + DependencyInjector.clear() + } + + @Test + fun 뷰모델의_생성자_종속_항목을_자동_주입해준다() { + // given: 뷰모델 생성에 필요한 종속 항목을 주입한다. + val constructorDependency = ConstructorDependency() + DependencyInjector.inject(constructorDependency) + + // when: 액티비티를 생성한다. + val activity = Robolectric.buildActivity(ConstructorTestActivity::class.java) + .create() + .get() + + // then: 액티비티의 뷰모델에 종속 항목이 주입되어 있다. + assertNotNull(activity.viewModel) + activity.viewModel.run { + assertEquals(constructorDependency, this.constructorDependency) + } + } + + @Test + fun 뷰모델에서_Inject_애노테이션이_붙은_필드에_종속_항목을_자동_주입해준다() { + // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. + val fieldDependency = FieldDependency() + DependencyInjector.inject(fieldDependency) + + // when: 액티비티를 생성한다. + val activity = Robolectric.buildActivity(FieldTestActivity::class.java) + .create() + .get() + + // then: Inject 애노테이션이 포함된 뷰모델의 필드에 종속 항목이 주입되어 있다. + assertNotNull(activity.viewModel) + activity.viewModel.run { + assertEquals(fieldDependency, this.fieldWithInjectAnnotation) + } + } + + @Test + fun 뷰모델에서_Inject_애노테이션이_없는_필드에는_종속_항목을_주입하지_않는다() { + // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. + val fieldDependency = FieldDependency() + DependencyInjector.inject(fieldDependency) + + // when: 액티비티를 생성한다. + val activity = Robolectric.buildActivity(FieldTestActivity::class.java) + .create() + .get() + + // then: Inject 애노테이션이 포함된 뷰모델의 필드에 종속 항목이 주입되어 있다. + assertNotNull(activity.viewModel) + activity.viewModel.run { + assertFalse(isFieldWithoutInjectAnnotationInitialized()) + } + } +} From af93fd77f2319033416c12c643725b91ea79f7b5 Mon Sep 17 00:00:00 2001 From: buna Date: Thu, 7 Sep 2023 13:45:45 +0900 Subject: [PATCH 05/37] =?UTF-8?q?refactor:=20CartRepository=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Dao=EB=A5=BC=20=EC=A3=BC=EC=9E=85=EB=B0=9B=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/data/mapper/ProductMapper.kt | 10 +++++++++ .../data/repository/DefaultCartRepository.kt | 21 ++++++++++++------- .../shopping/repository/CartRepository.kt | 7 +++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt b/app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt index 03855de9e..8aae138d7 100644 --- a/app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt +++ b/app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt @@ -10,3 +10,13 @@ fun Product.toEntity(): CartProductEntity { imageUrl = imageUrl, ) } + +fun List.toDomain(): List { + return map { + Product( + name = it.name, + price = it.price, + imageUrl = it.imageUrl, + ) + } +} diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt index 4b5e25027..f8bc1a22c 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt @@ -1,18 +1,23 @@ package woowacourse.shopping.data.repository +import woowacourse.shopping.data.CartProductDao +import woowacourse.shopping.data.mapper.toDomain +import woowacourse.shopping.data.mapper.toEntity import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository -class DefaultCartRepository : CartRepository { - private val cartProducts: MutableList = mutableListOf() - - override fun addCartProduct(product: Product) { - cartProducts.add(product) +class DefaultCartRepository( + private val dao: CartProductDao, +) : CartRepository { + override suspend fun addCartProduct(product: Product) { + dao.insert(product.toEntity()) } - override fun getAllCartProducts(): List = cartProducts.toList() + override suspend fun getAllCartProducts(): List { + return dao.getAll().toDomain() + } - override fun deleteCartProduct(id: Int) { - cartProducts.removeAt(id) + override suspend fun deleteCartProduct(id: Long) { + dao.delete(id) } } diff --git a/domain/src/main/java/woowacourse/shopping/repository/CartRepository.kt b/domain/src/main/java/woowacourse/shopping/repository/CartRepository.kt index 5f8dd0bd0..bb823429e 100644 --- a/domain/src/main/java/woowacourse/shopping/repository/CartRepository.kt +++ b/domain/src/main/java/woowacourse/shopping/repository/CartRepository.kt @@ -2,9 +2,8 @@ package woowacourse.shopping.repository import woowacourse.shopping.model.Product -// TODO: Step2 - CartProductDao를 참조하도록 변경 interface CartRepository { - fun addCartProduct(product: Product) - fun getAllCartProducts(): List - fun deleteCartProduct(id: Int) + suspend fun addCartProduct(product: Product) + suspend fun getAllCartProducts(): List + suspend fun deleteCartProduct(id: Long) } From a2b640ccb58bf77ae2264a5293aae9fcf4815091 Mon Sep 17 00:00:00 2001 From: buna Date: Thu, 7 Sep 2023 15:15:29 +0900 Subject: [PATCH 06/37] =?UTF-8?q?refactor:=20=EC=9E=A5=EB=B0=94=EA=B5=AC?= =?UTF-8?q?=EB=8B=88=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20CartProduct?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=EC=9D=84=20=EA=B0=80=EC=A7=80=EA=B3=A0=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/ShoppingApplication.kt | 10 ++++++- .../shopping/data/ShoppingDatabase.kt | 4 +++ .../shopping/data/mapper/CartMapper.kt | 27 +++++++++++++++++++ .../shopping/data/mapper/ProductMapper.kt | 22 --------------- .../data/repository/DefaultCartRepository.kt | 3 ++- .../shopping/ui/cart/CartProductAdapter.kt | 12 ++++----- .../shopping/ui/cart/CartProductViewHolder.kt | 17 ++++++------ .../shopping/ui/cart/CartViewModel.kt | 20 +++++++++----- .../shopping/ui/main/MainViewModel.kt | 8 ++++-- app/src/main/res/layout/item_cart_product.xml | 7 ++++- .../woowacourse/shopping/model/CartProduct.kt | 11 ++++++++ .../woowacourse/shopping/model/Product.kt | 6 ++++- .../shopping/repository/CartRepository.kt | 3 ++- 13 files changed, 99 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt delete mode 100644 app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt create mode 100644 domain/src/main/java/woowacourse/shopping/model/CartProduct.kt diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index 9a50dc565..0e4e8891f 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -1,6 +1,8 @@ package woowacourse.shopping import android.app.Application +import androidx.room.Room +import woowacourse.shopping.data.ShoppingDatabase import woowacourse.shopping.data.repository.DefaultCartRepository import woowacourse.shopping.data.repository.DefaultProductRepository import woowacourse.shopping.di.injector.modules @@ -11,9 +13,15 @@ class ShoppingApplication : Application() { override fun onCreate() { super.onCreate() + val shoppingDatabase = Room.databaseBuilder( + applicationContext, + ShoppingDatabase::class.java, + ShoppingDatabase.DATABASE_NAME, + ).build() + modules { inject(DefaultProductRepository()) - inject(DefaultCartRepository()) + inject(DefaultCartRepository(shoppingDatabase.cartProductDao())) } } } diff --git a/app/src/main/java/woowacourse/shopping/data/ShoppingDatabase.kt b/app/src/main/java/woowacourse/shopping/data/ShoppingDatabase.kt index e898bbc43..4b154e9e7 100644 --- a/app/src/main/java/woowacourse/shopping/data/ShoppingDatabase.kt +++ b/app/src/main/java/woowacourse/shopping/data/ShoppingDatabase.kt @@ -6,4 +6,8 @@ import androidx.room.RoomDatabase @Database(entities = [CartProductEntity::class], version = 1, exportSchema = false) abstract class ShoppingDatabase : RoomDatabase() { abstract fun cartProductDao(): CartProductDao + + companion object { + const val DATABASE_NAME = "cart-database" + } } diff --git a/app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt b/app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt new file mode 100644 index 000000000..40707be38 --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt @@ -0,0 +1,27 @@ +package woowacourse.shopping.data.mapper + +import woowacourse.shopping.data.CartProductEntity +import woowacourse.shopping.model.CartProduct +import woowacourse.shopping.model.Product + +fun Product.toEntity(): CartProductEntity { + return CartProductEntity( + name = name, + price = price, + imageUrl = imageUrl, + ) +} + +fun List.toDomain(): List { + return map { + CartProduct( + product = Product( + name = it.name, + price = it.price, + imageUrl = it.imageUrl, + ), + id = it.id, + createdAt = it.createdAt, + ) + } +} diff --git a/app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt b/app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt deleted file mode 100644 index 8aae138d7..000000000 --- a/app/src/main/java/woowacourse/shopping/data/mapper/ProductMapper.kt +++ /dev/null @@ -1,22 +0,0 @@ -package woowacourse.shopping.data.mapper - -import woowacourse.shopping.data.CartProductEntity -import woowacourse.shopping.model.Product - -fun Product.toEntity(): CartProductEntity { - return CartProductEntity( - name = name, - price = price, - imageUrl = imageUrl, - ) -} - -fun List.toDomain(): List { - return map { - Product( - name = it.name, - price = it.price, - imageUrl = it.imageUrl, - ) - } -} diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt index f8bc1a22c..6ef136078 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt @@ -3,6 +3,7 @@ package woowacourse.shopping.data.repository import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.mapper.toDomain import woowacourse.shopping.data.mapper.toEntity +import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository @@ -13,7 +14,7 @@ class DefaultCartRepository( dao.insert(product.toEntity()) } - override suspend fun getAllCartProducts(): List { + override suspend fun getAllCartProducts(): List { return dao.getAll().toDomain() } diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartProductAdapter.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartProductAdapter.kt index 272acf333..bbf98ae86 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartProductAdapter.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartProductAdapter.kt @@ -2,18 +2,18 @@ package woowacourse.shopping.ui.cart import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import woowacourse.shopping.model.Product +import woowacourse.shopping.model.CartProduct class CartProductAdapter( - items: List, - onClickDelete: (position: Int) -> Unit, + items: List, + onClickDelete: (id: Long) -> Unit, private val dateFormatter: DateFormatter, ) : RecyclerView.Adapter() { - private val items: MutableList = items.toMutableList() + private val items: MutableList = items.toMutableList() - private val onClickDelete = { position: Int -> - onClickDelete(position) + private val onClickDelete = { id: Long, position: Int -> + onClickDelete(id) removeItem(position) } diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartProductViewHolder.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartProductViewHolder.kt index aef478fa5..6f04b3d70 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartProductViewHolder.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartProductViewHolder.kt @@ -4,31 +4,30 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import woowacourse.shopping.databinding.ItemCartProductBinding -import woowacourse.shopping.model.Product +import woowacourse.shopping.model.CartProduct class CartProductViewHolder( private val binding: ItemCartProductBinding, - private val dateFormatter: DateFormatter, - onClickDelete: (position: Int) -> Unit, + dateFormatter: DateFormatter, + onClickDelete: (id: Long, position: Int) -> Unit, ) : RecyclerView.ViewHolder(binding.root) { init { + binding.dateFormatter = dateFormatter binding.ivCartProductDelete.setOnClickListener { - val position = adapterPosition - onClickDelete(position) + onClickDelete(binding.item!!.id, adapterPosition) } } - fun bind(product: Product) { - binding.item = product - // TODO: Step2 - dateFormatter를 활용하여 상품이 담긴 날짜와 시간을 출력하도록 변경 + fun bind(cartProduct: CartProduct) { + binding.item = cartProduct } companion object { fun from( parent: ViewGroup, dateFormatter: DateFormatter, - onClickDelete: (position: Int) -> Unit, + onClickDelete: (id: Long, position: Int) -> Unit, ): CartProductViewHolder { val binding = ItemCartProductBinding .inflate(LayoutInflater.from(parent.context), parent, false) diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt index dc5af010a..ea4b34467 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt @@ -3,26 +3,32 @@ package woowacourse.shopping.ui.cart import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import woowacourse.shopping.model.Product +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch +import woowacourse.shopping.model.CartProduct import woowacourse.shopping.repository.CartRepository class CartViewModel( private val cartRepository: CartRepository, ) : ViewModel() { - private val _cartProducts: MutableLiveData> = + private val _cartProducts: MutableLiveData> = MutableLiveData(emptyList()) - val cartProducts: LiveData> get() = _cartProducts + val cartProducts: LiveData> get() = _cartProducts private val _onCartProductDeleted: MutableLiveData = MutableLiveData(false) val onCartProductDeleted: LiveData get() = _onCartProductDeleted fun fetchAllCartProducts() { - _cartProducts.value = cartRepository.getAllCartProducts() + viewModelScope.launch { + _cartProducts.value = cartRepository.getAllCartProducts() + } } - fun deleteCartProduct(id: Int) { - cartRepository.deleteCartProduct(id) - _onCartProductDeleted.value = true + fun deleteCartProduct(id: Long) { + viewModelScope.launch { + cartRepository.deleteCartProduct(id) + _onCartProductDeleted.value = true + } } } diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index 6d2ee66f4..d9db3524b 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -3,6 +3,8 @@ package woowacourse.shopping.ui.main import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository @@ -18,8 +20,10 @@ class MainViewModel( val onProductAdded: LiveData get() = _onProductAdded fun addCartProduct(product: Product) { - cartRepository.addCartProduct(product) - _onProductAdded.value = true + viewModelScope.launch { + cartRepository.addCartProduct(product) + _onProductAdded.value = true + } } fun fetchAllProducts() { diff --git a/app/src/main/res/layout/item_cart_product.xml b/app/src/main/res/layout/item_cart_product.xml index d09b4f553..c5715daa8 100644 --- a/app/src/main/res/layout/item_cart_product.xml +++ b/app/src/main/res/layout/item_cart_product.xml @@ -7,7 +7,11 @@ + type="woowacourse.shopping.model.CartProduct" /> + + + suspend fun getAllCartProducts(): List suspend fun deleteCartProduct(id: Long) } From ed8e7506b04a102a6e09d1c900591569e9d38bf7 Mon Sep 17 00:00:00 2001 From: buna Date: Fri, 8 Sep 2023 15:22:47 +0900 Subject: [PATCH 07/37] =?UTF-8?q?feat:=20Qualifier=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EA=B5=AC=EB=B6=84=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/ShoppingApplication.kt | 41 ++++-- .../data/repository/DefaultCartRepository.kt | 2 + .../shopping/di/annotation/Qualifier.kt | 5 + .../di/injector/DependencyInjector.kt | 120 +++++++++++++----- .../shopping/di/lazy/ActivityViewModelLazy.kt | 2 +- .../shopping/ui/common/di/Modules.kt | 1 + .../ui/common/di/qualifier/Qualifier.kt | 9 ++ .../shopping/ui/main/MainViewModel.kt | 6 +- 8 files changed, 142 insertions(+), 44 deletions(-) create mode 100644 app/src/main/java/woowacourse/shopping/di/annotation/Qualifier.kt create mode 100644 app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt create mode 100644 app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index 0e4e8891f..88a0c6c9d 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -1,27 +1,48 @@ package woowacourse.shopping import android.app.Application +import android.content.Context import androidx.room.Room +import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.ShoppingDatabase import woowacourse.shopping.data.repository.DefaultCartRepository import woowacourse.shopping.data.repository.DefaultProductRepository import woowacourse.shopping.di.injector.modules +import woowacourse.shopping.di.injector.type import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository +import woowacourse.shopping.ui.common.di.qualifier.DatabaseDao +import woowacourse.shopping.ui.common.di.qualifier.InMemoryDao class ShoppingApplication : Application() { override fun onCreate() { super.onCreate() - val shoppingDatabase = Room.databaseBuilder( - applicationContext, - ShoppingDatabase::class.java, - ShoppingDatabase.DATABASE_NAME, - ).build() - - modules { - inject(DefaultProductRepository()) - inject(DefaultCartRepository(shoppingDatabase.cartProductDao())) - } + // 2. 어플리케이션에서 프로바이더 모듈을 맵에 등록 + modules( + DaoModule(this@ShoppingApplication), + ) + type( + ProductRepository::class to DefaultProductRepository::class, + CartRepository::class to DefaultCartRepository::class, + ) } } + +interface Module + +// 1. 프로바이더 함수에 퀄리파리어 어노테이션 붙이기 +class DaoModule(private val context: Context) : Module { + @DatabaseDao + fun provideDatabaseCartProductDao(): CartProductDao = Room.databaseBuilder( + context, + ShoppingDatabase::class.java, + ShoppingDatabase.DATABASE_NAME, + ).build().cartProductDao() + + @InMemoryDao + fun provideInMemoryCartProductDao(): CartProductDao = Room.inMemoryDatabaseBuilder( + context, + ShoppingDatabase::class.java, + ).build().cartProductDao() +} diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt index 6ef136078..8b93fc604 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt @@ -6,8 +6,10 @@ import woowacourse.shopping.data.mapper.toEntity import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository +import woowacourse.shopping.ui.common.di.qualifier.InMemoryDao class DefaultCartRepository( + @InMemoryDao private val dao: CartProductDao, ) : CartRepository { override suspend fun addCartProduct(product: Product) { diff --git a/app/src/main/java/woowacourse/shopping/di/annotation/Qualifier.kt b/app/src/main/java/woowacourse/shopping/di/annotation/Qualifier.kt new file mode 100644 index 000000000..c2239a051 --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/di/annotation/Qualifier.kt @@ -0,0 +1,5 @@ +package woowacourse.shopping.di.annotation + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) +annotation class Qualifier diff --git a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt index 1088af7b0..cb3c455ec 100644 --- a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt +++ b/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt @@ -1,55 +1,111 @@ package woowacourse.shopping.di.injector +import woowacourse.shopping.Module import woowacourse.shopping.di.annotation.Inject -import woowacourse.shopping.di.util.throwNotExistDependency -import woowacourse.shopping.di.util.validateHasPrimaryConstructor -import kotlin.reflect.KParameter -import kotlin.reflect.javaType +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KType +import kotlin.reflect.full.declaredMemberFunctions +import kotlin.reflect.full.hasAnnotation +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.full.starProjectedType +import kotlin.reflect.jvm.jvmErasure -object DependencyInjector { - val dependencies = mutableMapOf, Any>() - - inline fun inject(instance: T) { - dependencies[T::class.java] = instance - } +// 3. Key : (어노테이션, 함수 반환 타입) +// Value : 함수 +data class Qualify( + val module: Module, + val type: KType, // 반환 타입 (인터페이스일 수도 있음) + val annotation: Annotation? = null, // 그런 경우를 대비해 어노테이션도 함께 저장 +) - inline fun inject(): T { - val primaryConstructor = validateHasPrimaryConstructor() - val args = getArgs(primaryConstructor.parameters) +object DependencyInjector { + val dependencies = mutableMapOf>() + val types = mutableMapOf() + private val cache = mutableMapOf() - val instance = primaryConstructor.call(*args.toTypedArray()) - injectFields(instance) + fun inject(clazz: KClass): T { + val primaryConstructor = + clazz.primaryConstructor ?: throw IllegalArgumentException("주생성자 없음") + val parameters = primaryConstructor.parameters + val arguments = parameters.map { parameter -> + val hasQualifiedAnnotation = parameter.annotations.any(::isAnnotationPresent) + val isSameType = isSameType(parameter.type) - return instance - } + if (hasQualifiedAnnotation && isSameType) { + val qualify = dependencies.keys.find { qualify -> + qualify.annotation == parameter.annotations.first() && + qualify.type == parameter.type + } + val cached = cache[qualify] + if (cached != null) { + return@map cached + } + val instance = dependencies[qualify]?.call(qualify!!.module) + cache[qualify!!] = instance!! + instance + } else if (isSameType) { + val qualify = dependencies.keys.find { qualify -> + qualify.type == parameter.type + } + val cached = cache[qualify] + if (cached != null) { + return@map cached + } + val instance = dependencies[qualify]?.call(qualify!!.module) + ?: throw IllegalArgumentException("의존성 주입 실패") + cache[qualify!!] = instance + instance + } else { + val childType = types[parameter.type] ?: throw IllegalArgumentException("의존성 주입 실패") + inject(childType.classifier as KClass<*>) + } + } - inline fun injectFields(instance: T) { - instance::class.java.declaredFields.forEach { field -> + val instance = primaryConstructor.call(*arguments.toTypedArray()) + clazz.java.declaredFields.forEach { field -> if (field.isAnnotationPresent(Inject::class.java)) { - val dependency = dependencies[field.type] ?: throwNotExistDependency(field.type) field.isAccessible = true - field.set(instance, dependency) + field.set(instance, inject(types[field.type.kotlin.starProjectedType]!!.jvmErasure)) } } + + return instance } - @OptIn(ExperimentalStdlibApi::class) - fun getArgs(parameters: List): List = parameters.map { param -> - val type = param.type.javaType - dependencies[type] ?: throwNotExistDependency(type.javaClass) + private fun isAnnotationPresent(annotation: Annotation): Boolean { + return dependencies.keys.any { qualify -> qualify.annotation == annotation } } - fun clear() { - dependencies.clear() + private fun isSameType(type: KType): Boolean { + return dependencies.keys.any { qualify -> qualify.type == type } } } -class ClassInjectorDsl { - inline fun inject(instance: T) { - DependencyInjector.inject(instance) +fun modules( + vararg modules: Module, +) { + modules.forEach { module: Module -> + module::class.declaredMemberFunctions.forEach { function -> + if (function.hasAnnotation()) { + val qualify = Qualify( + module, + function.returnType, + function.annotations.first(), + ) + DependencyInjector.dependencies[qualify] = function + } else { + val qualify = Qualify(module, function.returnType) + DependencyInjector.dependencies[qualify] = function + } + } } } -fun modules(block: ClassInjectorDsl.() -> Unit) { - ClassInjectorDsl().apply(block) +fun type( + vararg types: Pair, KClass<*>>, +) { + types.forEach { (parentType, childType) -> + DependencyInjector.types[parentType.starProjectedType] = childType.starProjectedType + } } diff --git a/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt b/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt index 7d91bfad8..125c950fa 100644 --- a/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt +++ b/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt @@ -15,5 +15,5 @@ inline fun ComponentActivity.viewModel(): Lazy { } inline fun createViewModel(): VM { - return DependencyInjector.inject() + return DependencyInjector.inject(VM::class) } diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt new file mode 100644 index 000000000..0c52d7331 --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt @@ -0,0 +1 @@ +package woowacourse.shopping.ui.common.di diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt new file mode 100644 index 000000000..a8beba96c --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt @@ -0,0 +1,9 @@ +package woowacourse.shopping.ui.common.di.qualifier + +import woowacourse.shopping.di.annotation.Qualifier + +@Qualifier +annotation class DatabaseDao + +@Qualifier +annotation class InMemoryDao diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index d9db3524b..748c143c5 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -5,14 +5,18 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch +import woowacourse.shopping.di.annotation.Inject import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository class MainViewModel( private val productRepository: ProductRepository, - private val cartRepository: CartRepository, ) : ViewModel() { + + @Inject + private lateinit var cartRepository: CartRepository + private val _products: MutableLiveData> = MutableLiveData(emptyList()) val products: LiveData> get() = _products From 970dc8b8310dc3892c5958ea487b2e8dd1ab376d Mon Sep 17 00:00:00 2001 From: buna Date: Fri, 8 Sep 2023 15:43:19 +0900 Subject: [PATCH 08/37] =?UTF-8?q?chore:=20di=20=EB=AA=A8=EB=93=88=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 4 +- .../shopping/ShoppingApplication.kt | 12 ++--- .../shopping/ui/cart/CartActivity.kt | 2 +- .../ui/common/di/qualifier/Qualifier.kt | 2 +- .../shopping/ui/main/MainActivity.kt | 2 +- .../shopping/ui/main/MainViewModel.kt | 4 +- bunadi/.gitignore | 1 + bunadi/build.gradle.kts | 14 ++++++ di/.gitignore | 1 + di/build.gradle.kts | 47 +++++++++++++++++++ di/consumer-rules.pro | 0 di/proguard-rules.pro | 21 +++++++++ .../com/buna/di/ExampleInstrumentedTest.kt | 24 ++++++++++ di/src/main/AndroidManifest.xml | 4 ++ .../java/com/buna/di}/DependencyInjector.kt | 27 +++++------ .../java/com/buna}/di/annotation/Inject.kt | 2 +- .../java/com/buna}/di/annotation/Qualifier.kt | 2 +- .../buna}/di/lazy/ActivityViewModelLazy.kt | 6 +-- di/src/main/java/com/buna/di/module/Module.kt | 3 ++ .../java/com/buna}/di/util/ExceptionUtil.kt | 2 +- .../java/com/buna}/di/util/FunctionUtil.kt | 2 +- .../com/buna}/di/util/ViewModelFactory.kt | 2 +- .../di/injector/DependencyInjectorTest.kt | 10 ++-- settings.gradle.kts | 1 + 24 files changed, 153 insertions(+), 42 deletions(-) create mode 100644 bunadi/.gitignore create mode 100644 bunadi/build.gradle.kts create mode 100644 di/.gitignore create mode 100644 di/build.gradle.kts create mode 100644 di/consumer-rules.pro create mode 100644 di/proguard-rules.pro create mode 100644 di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt create mode 100644 di/src/main/AndroidManifest.xml rename {app/src/main/java/woowacourse/shopping/di/injector => di/src/main/java/com/buna/di}/DependencyInjector.kt (83%) rename {app/src/main/java/woowacourse/shopping => di/src/main/java/com/buna}/di/annotation/Inject.kt (75%) rename {app/src/main/java/woowacourse/shopping => di/src/main/java/com/buna}/di/annotation/Qualifier.kt (76%) rename {app/src/main/java/woowacourse/shopping => di/src/main/java/com/buna}/di/lazy/ActivityViewModelLazy.kt (75%) create mode 100644 di/src/main/java/com/buna/di/module/Module.kt rename {app/src/main/java/woowacourse/shopping => di/src/main/java/com/buna}/di/util/ExceptionUtil.kt (81%) rename {app/src/main/java/woowacourse/shopping => di/src/main/java/com/buna}/di/util/FunctionUtil.kt (89%) rename {app/src/main/java/woowacourse/shopping => di/src/main/java/com/buna}/di/util/ViewModelFactory.kt (91%) rename {app/src/test/java/woowacourse/shopping => di/src/test/java/com/buna}/di/injector/DependencyInjectorTest.kt (94%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7f5f55408..e7961d5ef 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -72,8 +72,8 @@ dependencies { // Mockk testImplementation("io.mockk:mockk:1.13.5") androidTestImplementation("io.mockk:mockk-android:1.13.5") - // Reflection - implementation("org.jetbrains.kotlin", "kotlin-reflect", "1.8.21") + // DI Library + implementation(project(":di")) } kapt { diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index 88a0c6c9d..481e0d233 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -3,12 +3,13 @@ package woowacourse.shopping import android.app.Application import android.content.Context import androidx.room.Room +import com.buna.di.module.Module +import com.buna.di.modules +import com.buna.di.types import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.ShoppingDatabase import woowacourse.shopping.data.repository.DefaultCartRepository import woowacourse.shopping.data.repository.DefaultProductRepository -import woowacourse.shopping.di.injector.modules -import woowacourse.shopping.di.injector.type import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository import woowacourse.shopping.ui.common.di.qualifier.DatabaseDao @@ -18,20 +19,17 @@ class ShoppingApplication : Application() { override fun onCreate() { super.onCreate() - // 2. 어플리케이션에서 프로바이더 모듈을 맵에 등록 modules( DaoModule(this@ShoppingApplication), ) - type( + + types( ProductRepository::class to DefaultProductRepository::class, CartRepository::class to DefaultCartRepository::class, ) } } -interface Module - -// 1. 프로바이더 함수에 퀄리파리어 어노테이션 붙이기 class DaoModule(private val context: Context) : Module { @DatabaseDao fun provideDatabaseCartProductDao(): CartProductDao = Room.databaseBuilder( diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt index 5bf18d004..16d90a577 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt @@ -3,9 +3,9 @@ package woowacourse.shopping.ui.cart import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import com.buna.di.lazy.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityCartBinding -import woowacourse.shopping.di.lazy.viewModel class CartActivity : AppCompatActivity() { private val binding by lazy { ActivityCartBinding.inflate(layoutInflater) } diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt index a8beba96c..ca3173423 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt @@ -1,6 +1,6 @@ package woowacourse.shopping.ui.common.di.qualifier -import woowacourse.shopping.di.annotation.Qualifier +import com.buna.di.annotation.Qualifier @Qualifier annotation class DatabaseDao diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt index 20f81891c..971abc88e 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt @@ -5,9 +5,9 @@ import android.os.Bundle import android.view.Menu import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import com.buna.di.lazy.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityMainBinding -import woowacourse.shopping.di.lazy.viewModel import woowacourse.shopping.ui.cart.CartActivity class MainActivity : AppCompatActivity() { diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index 748c143c5..e04d98277 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -4,8 +4,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.buna.di.annotation.Inject import kotlinx.coroutines.launch -import woowacourse.shopping.di.annotation.Inject import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository @@ -14,7 +14,7 @@ class MainViewModel( private val productRepository: ProductRepository, ) : ViewModel() { - @Inject + @com.buna.di.annotation.Inject private lateinit var cartRepository: CartRepository private val _products: MutableLiveData> = MutableLiveData(emptyList()) diff --git a/bunadi/.gitignore b/bunadi/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/bunadi/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/bunadi/build.gradle.kts b/bunadi/build.gradle.kts new file mode 100644 index 000000000..1883501dc --- /dev/null +++ b/bunadi/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("java-library") + id("org.jetbrains.kotlin.jvm") +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 +} + +dependencies { + implementation("org.jetbrains.kotlin", "kotlin-reflect", "1.8.21") + implementation("androidx.activity:activity-ktx:1.7.2") +} diff --git a/di/.gitignore b/di/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/di/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/di/build.gradle.kts b/di/build.gradle.kts new file mode 100644 index 000000000..eb46920a8 --- /dev/null +++ b/di/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.buna.di" + compileSdk = 33 + + defaultConfig { + minSdk = 26 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.9.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + implementation("org.jetbrains.kotlin", "kotlin-reflect", "1.8.21") + implementation("androidx.activity:activity-ktx:1.7.2") + // Robolectric + testImplementation("org.robolectric:robolectric:4.9") +} diff --git a/di/consumer-rules.pro b/di/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/di/proguard-rules.pro b/di/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/di/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt b/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..82727a48d --- /dev/null +++ b/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.buna.di + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.buna.di.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/di/src/main/AndroidManifest.xml b/di/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/di/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/DependencyInjector.kt similarity index 83% rename from app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt rename to di/src/main/java/com/buna/di/DependencyInjector.kt index cb3c455ec..0f1c5feae 100644 --- a/app/src/main/java/woowacourse/shopping/di/injector/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/DependencyInjector.kt @@ -1,7 +1,6 @@ -package woowacourse.shopping.di.injector +package com.buna.di -import woowacourse.shopping.Module -import woowacourse.shopping.di.annotation.Inject +import com.buna.di.annotation.Inject import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KType @@ -11,12 +10,10 @@ import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.starProjectedType import kotlin.reflect.jvm.jvmErasure -// 3. Key : (어노테이션, 함수 반환 타입) -// Value : 함수 data class Qualify( - val module: Module, - val type: KType, // 반환 타입 (인터페이스일 수도 있음) - val annotation: Annotation? = null, // 그런 경우를 대비해 어노테이션도 함께 저장 + val module: com.buna.di.module.Module, + val type: KType, + val annotation: Annotation? = null, ) object DependencyInjector { @@ -29,7 +26,8 @@ object DependencyInjector { clazz.primaryConstructor ?: throw IllegalArgumentException("주생성자 없음") val parameters = primaryConstructor.parameters val arguments = parameters.map { parameter -> - val hasQualifiedAnnotation = parameter.annotations.any(::isAnnotationPresent) + val hasQualifiedAnnotation = + parameter.annotations.any(DependencyInjector::isAnnotationPresent) val isSameType = isSameType(parameter.type) if (hasQualifiedAnnotation && isSameType) { @@ -83,10 +81,11 @@ object DependencyInjector { } fun modules( - vararg modules: Module, + vararg modules: com.buna.di.module.Module, ) { - modules.forEach { module: Module -> - module::class.declaredMemberFunctions.forEach { function -> + modules.forEach { module -> + val providerFunctions = module::class.declaredMemberFunctions + providerFunctions.forEach { function -> if (function.hasAnnotation()) { val qualify = Qualify( module, @@ -102,9 +101,7 @@ fun modules( } } -fun type( - vararg types: Pair, KClass<*>>, -) { +fun types(vararg types: Pair, KClass<*>>) { types.forEach { (parentType, childType) -> DependencyInjector.types[parentType.starProjectedType] = childType.starProjectedType } diff --git a/app/src/main/java/woowacourse/shopping/di/annotation/Inject.kt b/di/src/main/java/com/buna/di/annotation/Inject.kt similarity index 75% rename from app/src/main/java/woowacourse/shopping/di/annotation/Inject.kt rename to di/src/main/java/com/buna/di/annotation/Inject.kt index de66693a5..0f5d19595 100644 --- a/app/src/main/java/woowacourse/shopping/di/annotation/Inject.kt +++ b/di/src/main/java/com/buna/di/annotation/Inject.kt @@ -1,4 +1,4 @@ -package woowacourse.shopping.di.annotation +package com.buna.di.annotation @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY_SETTER) diff --git a/app/src/main/java/woowacourse/shopping/di/annotation/Qualifier.kt b/di/src/main/java/com/buna/di/annotation/Qualifier.kt similarity index 76% rename from app/src/main/java/woowacourse/shopping/di/annotation/Qualifier.kt rename to di/src/main/java/com/buna/di/annotation/Qualifier.kt index c2239a051..ecdbba9bc 100644 --- a/app/src/main/java/woowacourse/shopping/di/annotation/Qualifier.kt +++ b/di/src/main/java/com/buna/di/annotation/Qualifier.kt @@ -1,4 +1,4 @@ -package woowacourse.shopping.di.annotation +package com.buna.di.annotation @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) diff --git a/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt b/di/src/main/java/com/buna/di/lazy/ActivityViewModelLazy.kt similarity index 75% rename from app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt rename to di/src/main/java/com/buna/di/lazy/ActivityViewModelLazy.kt index 125c950fa..284c9cbbd 100644 --- a/app/src/main/java/woowacourse/shopping/di/lazy/ActivityViewModelLazy.kt +++ b/di/src/main/java/com/buna/di/lazy/ActivityViewModelLazy.kt @@ -1,10 +1,10 @@ -package woowacourse.shopping.di.lazy +package com.buna.di.lazy import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelLazy -import woowacourse.shopping.di.injector.DependencyInjector -import woowacourse.shopping.di.util.viewModelFactory +import com.buna.di.DependencyInjector +import com.buna.di.util.viewModelFactory inline fun ComponentActivity.viewModel(): Lazy { return ViewModelLazy( diff --git a/di/src/main/java/com/buna/di/module/Module.kt b/di/src/main/java/com/buna/di/module/Module.kt new file mode 100644 index 000000000..c6d5df305 --- /dev/null +++ b/di/src/main/java/com/buna/di/module/Module.kt @@ -0,0 +1,3 @@ +package com.buna.di.module + +interface Module diff --git a/app/src/main/java/woowacourse/shopping/di/util/ExceptionUtil.kt b/di/src/main/java/com/buna/di/util/ExceptionUtil.kt similarity index 81% rename from app/src/main/java/woowacourse/shopping/di/util/ExceptionUtil.kt rename to di/src/main/java/com/buna/di/util/ExceptionUtil.kt index d3a642f18..7ceb3b19a 100644 --- a/app/src/main/java/woowacourse/shopping/di/util/ExceptionUtil.kt +++ b/di/src/main/java/com/buna/di/util/ExceptionUtil.kt @@ -1,4 +1,4 @@ -package woowacourse.shopping.di.util +package com.buna.di.util fun throwNotExistDependency(clazz: Class<*>) { throw IllegalArgumentException("[ERROR] 주입할 의존성이 존재하지 않습니다. parameterType: $clazz") diff --git a/app/src/main/java/woowacourse/shopping/di/util/FunctionUtil.kt b/di/src/main/java/com/buna/di/util/FunctionUtil.kt similarity index 89% rename from app/src/main/java/woowacourse/shopping/di/util/FunctionUtil.kt rename to di/src/main/java/com/buna/di/util/FunctionUtil.kt index 31b89afa4..515d3ec4c 100644 --- a/app/src/main/java/woowacourse/shopping/di/util/FunctionUtil.kt +++ b/di/src/main/java/com/buna/di/util/FunctionUtil.kt @@ -1,4 +1,4 @@ -package woowacourse.shopping.di.util +package com.buna.di.util import kotlin.reflect.KFunction import kotlin.reflect.full.primaryConstructor diff --git a/app/src/main/java/woowacourse/shopping/di/util/ViewModelFactory.kt b/di/src/main/java/com/buna/di/util/ViewModelFactory.kt similarity index 91% rename from app/src/main/java/woowacourse/shopping/di/util/ViewModelFactory.kt rename to di/src/main/java/com/buna/di/util/ViewModelFactory.kt index 563a01869..e779ed1ff 100644 --- a/app/src/main/java/woowacourse/shopping/di/util/ViewModelFactory.kt +++ b/di/src/main/java/com/buna/di/util/ViewModelFactory.kt @@ -1,4 +1,4 @@ -package woowacourse.shopping.di.util +package com.buna.di.util import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider diff --git a/app/src/test/java/woowacourse/shopping/di/injector/DependencyInjectorTest.kt b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt similarity index 94% rename from app/src/test/java/woowacourse/shopping/di/injector/DependencyInjectorTest.kt rename to di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt index 2efedd26d..d75c5adfc 100644 --- a/app/src/test/java/woowacourse/shopping/di/injector/DependencyInjectorTest.kt +++ b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt @@ -1,18 +1,18 @@ -package woowacourse.shopping.di.injector +package com.buna.di.injector import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel +import com.buna.di.DependencyInjector +import com.buna.di.annotation.Inject +import com.buna.di.lazy.viewModel import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotNull -import junit.framework.TestCase.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner -import woowacourse.shopping.di.annotation.Inject -import woowacourse.shopping.di.lazy.viewModel class ConstructorTestActivity : AppCompatActivity() { val viewModel: ConstructorTestViewModel by viewModel() @@ -27,7 +27,7 @@ class FieldTestActivity : AppCompatActivity() { } class FieldTestViewModel : ViewModel() { - @Inject + @com.buna.di.annotation.Inject lateinit var fieldWithInjectAnnotation: FieldDependency lateinit var fieldWithoutInjectAnnotation: FieldDependency diff --git a/settings.gradle.kts b/settings.gradle.kts index 3eaf0e50a..0ce4a5d38 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,3 +15,4 @@ dependencyResolutionManagement { rootProject.name = "android-di" include(":app") include(":domain") +include(":di") From 8175c122fccf5f19762f64b5f1eb7dd15ec4972e Mon Sep 17 00:00:00 2001 From: buna Date: Fri, 8 Sep 2023 15:57:28 +0900 Subject: [PATCH 09/37] =?UTF-8?q?refactor:=20=EB=B6=80=EB=AA=A8=20?= =?UTF-8?q?=EC=9E=90=EC=8B=9D=20=ED=83=80=EC=9E=85=EC=9D=84=20=EB=A7=A4?= =?UTF-8?q?=EC=B9=AD=ED=95=98=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/ShoppingApplication.kt | 4 ++-- .../shopping/ui/main/MainViewModel.kt | 1 - .../java/com/buna/di/DependencyInjector.kt | 23 ++++++++++++++----- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index 481e0d233..615e1eea9 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -3,9 +3,9 @@ package woowacourse.shopping import android.app.Application import android.content.Context import androidx.room.Room +import com.buna.di.matchTypes import com.buna.di.module.Module import com.buna.di.modules -import com.buna.di.types import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.ShoppingDatabase import woowacourse.shopping.data.repository.DefaultCartRepository @@ -23,7 +23,7 @@ class ShoppingApplication : Application() { DaoModule(this@ShoppingApplication), ) - types( + matchTypes( ProductRepository::class to DefaultProductRepository::class, CartRepository::class to DefaultCartRepository::class, ) diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index e04d98277..5ea3dde65 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.buna.di.annotation.Inject import kotlinx.coroutines.launch import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository diff --git a/di/src/main/java/com/buna/di/DependencyInjector.kt b/di/src/main/java/com/buna/di/DependencyInjector.kt index 0f1c5feae..d03686c82 100644 --- a/di/src/main/java/com/buna/di/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/DependencyInjector.kt @@ -18,7 +18,7 @@ data class Qualify( object DependencyInjector { val dependencies = mutableMapOf>() - val types = mutableMapOf() + private val typeMatches = mutableMapOf() private val cache = mutableMapOf() fun inject(clazz: KClass): T { @@ -55,7 +55,8 @@ object DependencyInjector { cache[qualify!!] = instance instance } else { - val childType = types[parameter.type] ?: throw IllegalArgumentException("의존성 주입 실패") + val childType = + typeMatches[parameter.type] ?: throw IllegalArgumentException("의존성 주입 실패") inject(childType.classifier as KClass<*>) } } @@ -64,7 +65,10 @@ object DependencyInjector { clazz.java.declaredFields.forEach { field -> if (field.isAnnotationPresent(Inject::class.java)) { field.isAccessible = true - field.set(instance, inject(types[field.type.kotlin.starProjectedType]!!.jvmErasure)) + field.set( + instance, + inject(typeMatches[field.type.kotlin.starProjectedType]!!.jvmErasure), + ) } } @@ -78,6 +82,10 @@ object DependencyInjector { private fun isSameType(type: KType): Boolean { return dependencies.keys.any { qualify -> qualify.type == type } } + + fun addTypeMatch(parentType: KType, childType: KType) { + typeMatches[parentType] = childType + } } fun modules( @@ -101,8 +109,11 @@ fun modules( } } -fun types(vararg types: Pair, KClass<*>>) { - types.forEach { (parentType, childType) -> - DependencyInjector.types[parentType.starProjectedType] = childType.starProjectedType +fun matchTypes(vararg classes: Pair, KClass<*>>) { + classes.forEach { (parentClass, childClass) -> + val parentType = parentClass.starProjectedType + val childType = childClass.starProjectedType + + DependencyInjector.addTypeMatch(parentType, childType) } } From 1c72fcfd34e4ad1c458320732718d449f10db0bf Mon Sep 17 00:00:00 2001 From: buna Date: Fri, 8 Sep 2023 16:10:06 +0900 Subject: [PATCH 10/37] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=A0=9C=EA=B3=B5=ED=95=98=EB=8A=94=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/buna/di/DependencyInjector.kt | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/di/src/main/java/com/buna/di/DependencyInjector.kt b/di/src/main/java/com/buna/di/DependencyInjector.kt index d03686c82..e9346dba2 100644 --- a/di/src/main/java/com/buna/di/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/DependencyInjector.kt @@ -1,23 +1,23 @@ package com.buna.di import com.buna.di.annotation.Inject +import com.buna.di.module.Module import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KType import kotlin.reflect.full.declaredMemberFunctions -import kotlin.reflect.full.hasAnnotation import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.starProjectedType import kotlin.reflect.jvm.jvmErasure data class Qualify( - val module: com.buna.di.module.Module, + val module: Module, val type: KType, val annotation: Annotation? = null, ) object DependencyInjector { - val dependencies = mutableMapOf>() + private val providers = mutableMapOf>() private val typeMatches = mutableMapOf() private val cache = mutableMapOf() @@ -31,7 +31,7 @@ object DependencyInjector { val isSameType = isSameType(parameter.type) if (hasQualifiedAnnotation && isSameType) { - val qualify = dependencies.keys.find { qualify -> + val qualify = providers.keys.find { qualify -> qualify.annotation == parameter.annotations.first() && qualify.type == parameter.type } @@ -39,18 +39,18 @@ object DependencyInjector { if (cached != null) { return@map cached } - val instance = dependencies[qualify]?.call(qualify!!.module) + val instance = providers[qualify]?.call(qualify!!.module) cache[qualify!!] = instance!! instance } else if (isSameType) { - val qualify = dependencies.keys.find { qualify -> + val qualify = providers.keys.find { qualify -> qualify.type == parameter.type } val cached = cache[qualify] if (cached != null) { return@map cached } - val instance = dependencies[qualify]?.call(qualify!!.module) + val instance = providers[qualify]?.call(qualify!!.module) ?: throw IllegalArgumentException("의존성 주입 실패") cache[qualify!!] = instance instance @@ -76,36 +76,27 @@ object DependencyInjector { } private fun isAnnotationPresent(annotation: Annotation): Boolean { - return dependencies.keys.any { qualify -> qualify.annotation == annotation } + return providers.keys.any { qualify -> qualify.annotation == annotation } } private fun isSameType(type: KType): Boolean { - return dependencies.keys.any { qualify -> qualify.type == type } + return providers.keys.any { qualify -> qualify.type == type } } fun addTypeMatch(parentType: KType, childType: KType) { typeMatches[parentType] = childType } + + fun addProvider(module: Module, function: KFunction<*>) { + val qualify = Qualify(module, function.returnType, function.annotations.firstOrNull()) + providers[qualify] = function + } } -fun modules( - vararg modules: com.buna.di.module.Module, -) { +fun modules(vararg modules: Module) { modules.forEach { module -> - val providerFunctions = module::class.declaredMemberFunctions - providerFunctions.forEach { function -> - if (function.hasAnnotation()) { - val qualify = Qualify( - module, - function.returnType, - function.annotations.first(), - ) - DependencyInjector.dependencies[qualify] = function - } else { - val qualify = Qualify(module, function.returnType) - DependencyInjector.dependencies[qualify] = function - } - } + val providers = module::class.declaredMemberFunctions + providers.forEach { provider -> DependencyInjector.addProvider(module, provider) } } } From ff6f067bc70275c130e36e6bf47580be59bea71d Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 14:34:55 +0900 Subject: [PATCH 11/37] =?UTF-8?q?refactor:=20ViewModel=EC=97=90=EC=84=9C?= =?UTF-8?q?=20Repository=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98?= =?UTF-8?q?=EC=9D=84=20=EC=A7=80=EC=A0=95=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/ShoppingApplication.kt | 47 +++----- .../data/repository/DefaultCartRepository.kt | 25 +++- .../repository/DefaultProductRepository.kt | 2 + .../shopping/ui/cart/CartActivity.kt | 2 +- .../shopping/ui/cart/CartViewModel.kt | 4 +- .../shopping/ui/common/di/Modules.kt | 1 - .../ui/common/di/module/RepositoryModule.kt | 15 +++ .../ui/common/di/qualifier/Qualifier.kt | 8 +- .../shopping/ui/main/MainActivity.kt | 2 +- .../shopping/ui/main/MainViewModel.kt | 8 +- .../java/com/buna/di/DependencyInjector.kt | 110 ------------------ .../java/com/buna/di/annotation/Inject.kt | 2 +- .../java/com/buna/di/annotation/Qualifier.kt | 5 - .../com/buna/di/dsl/DependencyModuleDsl.kt | 14 +++ .../java/com/buna/di/dsl/DependencyTypeDsl.kt | 16 +++ .../buna/di/injector/DependencyInjector.kt | 96 +++++++++++++++ .../com/buna/di/injector/DependencyKey.kt | 27 +++++ .../main/java/com/buna/di/injector/Module.kt | 3 + di/src/main/java/com/buna/di/module/Module.kt | 3 - .../java/com/buna/di/util/FunctionUtil.kt | 4 +- .../ActivityViewModelLazy.kt | 5 +- .../{util => viewModel}/ViewModelFactory.kt | 2 +- .../di/injector/DependencyInjectorTest.kt | 11 +- 23 files changed, 233 insertions(+), 179 deletions(-) delete mode 100644 app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt create mode 100644 app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt delete mode 100644 di/src/main/java/com/buna/di/DependencyInjector.kt delete mode 100644 di/src/main/java/com/buna/di/annotation/Qualifier.kt create mode 100644 di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt create mode 100644 di/src/main/java/com/buna/di/dsl/DependencyTypeDsl.kt create mode 100644 di/src/main/java/com/buna/di/injector/DependencyInjector.kt create mode 100644 di/src/main/java/com/buna/di/injector/DependencyKey.kt create mode 100644 di/src/main/java/com/buna/di/injector/Module.kt delete mode 100644 di/src/main/java/com/buna/di/module/Module.kt rename di/src/main/java/com/buna/di/{lazy => viewModel}/ActivityViewModelLazy.kt (81%) rename di/src/main/java/com/buna/di/{util => viewModel}/ViewModelFactory.kt (92%) diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index 615e1eea9..1fb044e3d 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -1,46 +1,25 @@ package woowacourse.shopping import android.app.Application -import android.content.Context -import androidx.room.Room -import com.buna.di.matchTypes -import com.buna.di.module.Module -import com.buna.di.modules -import woowacourse.shopping.data.CartProductDao -import woowacourse.shopping.data.ShoppingDatabase -import woowacourse.shopping.data.repository.DefaultCartRepository +import com.buna.di.dsl.modules +import com.buna.di.dsl.types +import woowacourse.shopping.data.repository.DatabaseCartRepository import woowacourse.shopping.data.repository.DefaultProductRepository +import woowacourse.shopping.data.repository.InMemoryCartRepository import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository -import woowacourse.shopping.ui.common.di.qualifier.DatabaseDao -import woowacourse.shopping.ui.common.di.qualifier.InMemoryDao +import woowacourse.shopping.ui.common.di.module.RepositoryModule class ShoppingApplication : Application() { override fun onCreate() { super.onCreate() - - modules( - DaoModule(this@ShoppingApplication), - ) - - matchTypes( - ProductRepository::class to DefaultProductRepository::class, - CartRepository::class to DefaultCartRepository::class, - ) + types { + type(ProductRepository::class to DefaultProductRepository::class) + type(CartRepository::class to InMemoryCartRepository::class) + type(CartRepository::class to DatabaseCartRepository::class) + } + modules { + module(RepositoryModule(this@ShoppingApplication)) + } } } - -class DaoModule(private val context: Context) : Module { - @DatabaseDao - fun provideDatabaseCartProductDao(): CartProductDao = Room.databaseBuilder( - context, - ShoppingDatabase::class.java, - ShoppingDatabase.DATABASE_NAME, - ).build().cartProductDao() - - @InMemoryDao - fun provideInMemoryCartProductDao(): CartProductDao = Room.inMemoryDatabaseBuilder( - context, - ShoppingDatabase::class.java, - ).build().cartProductDao() -} diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt index 8b93fc604..6653b823d 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt @@ -1,15 +1,17 @@ package woowacourse.shopping.data.repository import woowacourse.shopping.data.CartProductDao +import woowacourse.shopping.data.CartProductEntity import woowacourse.shopping.data.mapper.toDomain import woowacourse.shopping.data.mapper.toEntity import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository -import woowacourse.shopping.ui.common.di.qualifier.InMemoryDao +import woowacourse.shopping.ui.common.di.qualifier.DatabaseCartRepositoryQualifier +import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier -class DefaultCartRepository( - @InMemoryDao +@DatabaseCartRepositoryQualifier +class DatabaseCartRepository( private val dao: CartProductDao, ) : CartRepository { override suspend fun addCartProduct(product: Product) { @@ -24,3 +26,20 @@ class DefaultCartRepository( dao.delete(id) } } + +@InMemoryCartRepositoryQualifier +class InMemoryCartRepository : CartRepository { + private val cartProducts = mutableListOf() + + override suspend fun addCartProduct(product: Product) { + cartProducts.add(product.toEntity()) + } + + override suspend fun getAllCartProducts(): List { + return cartProducts.toDomain() + } + + override suspend fun deleteCartProduct(id: Long) { + cartProducts.removeIf { it.id == id } + } +} diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt index 5fc51701b..3f414332e 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt @@ -2,7 +2,9 @@ package woowacourse.shopping.data.repository import woowacourse.shopping.model.Product import woowacourse.shopping.repository.ProductRepository +import woowacourse.shopping.ui.common.di.qualifier.InMemoryProductRepositoryQualifier +@InMemoryProductRepositoryQualifier class DefaultProductRepository : ProductRepository { private val products: List = listOf( Product( diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt index 16d90a577..ec9740e6a 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt @@ -3,7 +3,7 @@ package woowacourse.shopping.ui.cart import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.buna.di.lazy.viewModel +import com.buna.di.viewModel.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityCartBinding diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt index ea4b34467..8cd7dda7f 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt @@ -7,9 +7,11 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch import woowacourse.shopping.model.CartProduct import woowacourse.shopping.repository.CartRepository +import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier class CartViewModel( - private val cartRepository: CartRepository, + @InMemoryCartRepositoryQualifier + val cartRepository: CartRepository, ) : ViewModel() { private val _cartProducts: MutableLiveData> = diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt deleted file mode 100644 index 0c52d7331..000000000 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/Modules.kt +++ /dev/null @@ -1 +0,0 @@ -package woowacourse.shopping.ui.common.di diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt new file mode 100644 index 000000000..5e533b988 --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt @@ -0,0 +1,15 @@ +package woowacourse.shopping.ui.common.di.module + +import android.content.Context +import androidx.room.Room +import com.buna.di.injector.Module +import woowacourse.shopping.data.CartProductDao +import woowacourse.shopping.data.ShoppingDatabase + +class RepositoryModule(private val context: Context) : Module { + fun provideCartProductDao(): CartProductDao = Room.databaseBuilder( + context, + ShoppingDatabase::class.java, + ShoppingDatabase.DATABASE_NAME, + ).build().cartProductDao() +} diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt index ca3173423..b598855de 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt @@ -1,9 +1,7 @@ package woowacourse.shopping.ui.common.di.qualifier -import com.buna.di.annotation.Qualifier +annotation class InMemoryCartRepositoryQualifier -@Qualifier -annotation class DatabaseDao +annotation class DatabaseCartRepositoryQualifier -@Qualifier -annotation class InMemoryDao +annotation class InMemoryProductRepositoryQualifier diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt index 971abc88e..cdbe01cad 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt @@ -5,7 +5,7 @@ import android.os.Bundle import android.view.Menu import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.buna.di.lazy.viewModel +import com.buna.di.viewModel.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityMainBinding import woowacourse.shopping.ui.cart.CartActivity diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index 5ea3dde65..212de1c43 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -4,16 +4,20 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.buna.di.annotation.Inject import kotlinx.coroutines.launch import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository +import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier +import woowacourse.shopping.ui.common.di.qualifier.InMemoryProductRepositoryQualifier class MainViewModel( + @InMemoryProductRepositoryQualifier private val productRepository: ProductRepository, ) : ViewModel() { - - @com.buna.di.annotation.Inject + @Inject + @InMemoryCartRepositoryQualifier private lateinit var cartRepository: CartRepository private val _products: MutableLiveData> = MutableLiveData(emptyList()) diff --git a/di/src/main/java/com/buna/di/DependencyInjector.kt b/di/src/main/java/com/buna/di/DependencyInjector.kt deleted file mode 100644 index e9346dba2..000000000 --- a/di/src/main/java/com/buna/di/DependencyInjector.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.buna.di - -import com.buna.di.annotation.Inject -import com.buna.di.module.Module -import kotlin.reflect.KClass -import kotlin.reflect.KFunction -import kotlin.reflect.KType -import kotlin.reflect.full.declaredMemberFunctions -import kotlin.reflect.full.primaryConstructor -import kotlin.reflect.full.starProjectedType -import kotlin.reflect.jvm.jvmErasure - -data class Qualify( - val module: Module, - val type: KType, - val annotation: Annotation? = null, -) - -object DependencyInjector { - private val providers = mutableMapOf>() - private val typeMatches = mutableMapOf() - private val cache = mutableMapOf() - - fun inject(clazz: KClass): T { - val primaryConstructor = - clazz.primaryConstructor ?: throw IllegalArgumentException("주생성자 없음") - val parameters = primaryConstructor.parameters - val arguments = parameters.map { parameter -> - val hasQualifiedAnnotation = - parameter.annotations.any(DependencyInjector::isAnnotationPresent) - val isSameType = isSameType(parameter.type) - - if (hasQualifiedAnnotation && isSameType) { - val qualify = providers.keys.find { qualify -> - qualify.annotation == parameter.annotations.first() && - qualify.type == parameter.type - } - val cached = cache[qualify] - if (cached != null) { - return@map cached - } - val instance = providers[qualify]?.call(qualify!!.module) - cache[qualify!!] = instance!! - instance - } else if (isSameType) { - val qualify = providers.keys.find { qualify -> - qualify.type == parameter.type - } - val cached = cache[qualify] - if (cached != null) { - return@map cached - } - val instance = providers[qualify]?.call(qualify!!.module) - ?: throw IllegalArgumentException("의존성 주입 실패") - cache[qualify!!] = instance - instance - } else { - val childType = - typeMatches[parameter.type] ?: throw IllegalArgumentException("의존성 주입 실패") - inject(childType.classifier as KClass<*>) - } - } - - val instance = primaryConstructor.call(*arguments.toTypedArray()) - clazz.java.declaredFields.forEach { field -> - if (field.isAnnotationPresent(Inject::class.java)) { - field.isAccessible = true - field.set( - instance, - inject(typeMatches[field.type.kotlin.starProjectedType]!!.jvmErasure), - ) - } - } - - return instance - } - - private fun isAnnotationPresent(annotation: Annotation): Boolean { - return providers.keys.any { qualify -> qualify.annotation == annotation } - } - - private fun isSameType(type: KType): Boolean { - return providers.keys.any { qualify -> qualify.type == type } - } - - fun addTypeMatch(parentType: KType, childType: KType) { - typeMatches[parentType] = childType - } - - fun addProvider(module: Module, function: KFunction<*>) { - val qualify = Qualify(module, function.returnType, function.annotations.firstOrNull()) - providers[qualify] = function - } -} - -fun modules(vararg modules: Module) { - modules.forEach { module -> - val providers = module::class.declaredMemberFunctions - providers.forEach { provider -> DependencyInjector.addProvider(module, provider) } - } -} - -fun matchTypes(vararg classes: Pair, KClass<*>>) { - classes.forEach { (parentClass, childClass) -> - val parentType = parentClass.starProjectedType - val childType = childClass.starProjectedType - - DependencyInjector.addTypeMatch(parentType, childType) - } -} diff --git a/di/src/main/java/com/buna/di/annotation/Inject.kt b/di/src/main/java/com/buna/di/annotation/Inject.kt index 0f5d19595..f71aba90d 100644 --- a/di/src/main/java/com/buna/di/annotation/Inject.kt +++ b/di/src/main/java/com/buna/di/annotation/Inject.kt @@ -1,5 +1,5 @@ package com.buna.di.annotation @Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY_SETTER) +@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) annotation class Inject diff --git a/di/src/main/java/com/buna/di/annotation/Qualifier.kt b/di/src/main/java/com/buna/di/annotation/Qualifier.kt deleted file mode 100644 index ecdbba9bc..000000000 --- a/di/src/main/java/com/buna/di/annotation/Qualifier.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.buna.di.annotation - -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) -annotation class Qualifier diff --git a/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt b/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt new file mode 100644 index 000000000..d3ce699a1 --- /dev/null +++ b/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt @@ -0,0 +1,14 @@ +package com.buna.di.dsl + +import com.buna.di.injector.DependencyInjector +import com.buna.di.injector.Module + +class DependencyModuleDsl { + fun module(module: Module) { + DependencyInjector.module(module) + } +} + +fun modules(block: DependencyModuleDsl.() -> Unit) { + DependencyModuleDsl().block() +} \ No newline at end of file diff --git a/di/src/main/java/com/buna/di/dsl/DependencyTypeDsl.kt b/di/src/main/java/com/buna/di/dsl/DependencyTypeDsl.kt new file mode 100644 index 000000000..e80f04617 --- /dev/null +++ b/di/src/main/java/com/buna/di/dsl/DependencyTypeDsl.kt @@ -0,0 +1,16 @@ +package com.buna.di.dsl + +import com.buna.di.injector.DependencyInjector +import kotlin.reflect.KClass + +class DependencyTypeDsl { + fun type(pairClass: Pair, KClass<*>>) { + val superClass = pairClass.first + val subClass = pairClass.second + DependencyInjector.type(superClass, subClass) + } +} + +fun types(block: DependencyTypeDsl.() -> Unit) { + DependencyTypeDsl().block() +} diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt new file mode 100644 index 000000000..6fc225ac5 --- /dev/null +++ b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt @@ -0,0 +1,96 @@ +package com.buna.di.injector + +import com.buna.di.annotation.Inject +import com.buna.di.util.validateHasPrimaryConstructor +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KMutableProperty +import kotlin.reflect.KProperty +import kotlin.reflect.KType +import kotlin.reflect.full.declaredMemberFunctions +import kotlin.reflect.full.hasAnnotation +import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.starProjectedType +import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.jvmErasure + +object DependencyInjector { + private val typeConverter = mutableMapOf() + private val cache = mutableMapOf() + + // 모듈로 주입 (모듈 내 Provider 함수로 객체를 생성하여 저장) + fun module(module: Module) { + val providers = module::class.declaredMemberFunctions + providers.forEach { provider -> caching(module, provider) } + } + + // 함수로 객체 만들어서 주입 + private fun caching(module: Module, provider: KFunction<*>) { + val dependencyKey = DependencyKey.createDependencyKey(provider) + val dependency = provider.call(module) + + cache[dependencyKey] = dependency + } + + // value == null 이면 가져올 때 생성할 예정 + fun type(superClass: KClass<*>, subClass: KClass<*>) { + val superType = superClass.starProjectedType + val annotation = subClass.annotations.firstOrNull() + val dependencyKey = DependencyKey(superType, annotation) + + val subType = subClass.starProjectedType + typeConverter[dependencyKey] = subType + cache[dependencyKey] = null + } + + // 외부로 종속 항목 제공 + fun inject(clazz: KClass): T { + // 1. container에 있으면 바로 제공 + val type = clazz.supertypes.getOrElse(0) { clazz.starProjectedType } + val annotation = clazz.annotations.firstOrNull() + val dependencyKey = DependencyKey(type, annotation) + + if (cache[dependencyKey] != null) { + return cache[dependencyKey] as T + } + + // 없으면 클래스의 주생성자 파라미터 순회 + val primaryConstructor = clazz.validateHasPrimaryConstructor() + val parameters = primaryConstructor.parameters + + // 1. 파라미터를 순회하며 inject() 재귀 호출 + val arguments = parameters.map { parameter -> + // 현재 문제 : 인터페이스 타입을 넘기는 경우 인터페이스 타입이 아닌 구현체 타입을 넘겨야 함 + val paramAnnotation = parameter.annotations.firstOrNull() + val paramType = parameter.type // 인터페이스 타입일 수도 있음 + val paramDependencyKey = DependencyKey(paramType, paramAnnotation) + val subType = typeConverter[paramDependencyKey] ?: paramType + val paramInstance = inject(subType.jvmErasure) + + paramInstance + } + + // 2. 파라미터를 인자 목록으로 만들어서 주생성자 호출 + val instance = primaryConstructor.call(*arguments.toTypedArray()) + + // 3. 필드 주입 (필드 클래스에 대해 inject() 재귀 호출) + clazz.memberProperties.forEach { property: KProperty<*> -> + if (!property.hasAnnotation()) return@forEach + if (property !is KMutableProperty<*>) return@forEach + property.isAccessible = true + + val propertyType = property.returnType + val propertyAnnotation = + property.annotations.first { it.annotationClass != Inject::class } + val propertyDependencyKey = DependencyKey(propertyType, propertyAnnotation) + + val subType = typeConverter[propertyDependencyKey] ?: propertyType + val propertyInstance = inject(subType.jvmErasure) + + cache[propertyDependencyKey] = propertyInstance + property.setter.call(instance, propertyInstance) + } + + return instance + } +} diff --git a/di/src/main/java/com/buna/di/injector/DependencyKey.kt b/di/src/main/java/com/buna/di/injector/DependencyKey.kt new file mode 100644 index 000000000..dc1c566b0 --- /dev/null +++ b/di/src/main/java/com/buna/di/injector/DependencyKey.kt @@ -0,0 +1,27 @@ +package com.buna.di.injector + +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KType +import kotlin.reflect.full.starProjectedType + +data class DependencyKey( + val type: KType, + val annotation: Annotation? = null, +) { + companion object { + fun createDependencyKey(clazz: KClass<*>): DependencyKey { + val annotation = clazz.annotations.firstOrNull() + val type = clazz.starProjectedType + + return DependencyKey(type, annotation) + } + + fun createDependencyKey(provider: KFunction<*>): DependencyKey { + val returnType = provider.returnType + val annotation = provider.annotations.firstOrNull() + + return DependencyKey(returnType, annotation) + } + } +} diff --git a/di/src/main/java/com/buna/di/injector/Module.kt b/di/src/main/java/com/buna/di/injector/Module.kt new file mode 100644 index 000000000..cd6dee086 --- /dev/null +++ b/di/src/main/java/com/buna/di/injector/Module.kt @@ -0,0 +1,3 @@ +package com.buna.di.injector + +interface Module diff --git a/di/src/main/java/com/buna/di/module/Module.kt b/di/src/main/java/com/buna/di/module/Module.kt deleted file mode 100644 index c6d5df305..000000000 --- a/di/src/main/java/com/buna/di/module/Module.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.buna.di.module - -interface Module diff --git a/di/src/main/java/com/buna/di/util/FunctionUtil.kt b/di/src/main/java/com/buna/di/util/FunctionUtil.kt index 515d3ec4c..0b09ed927 100644 --- a/di/src/main/java/com/buna/di/util/FunctionUtil.kt +++ b/di/src/main/java/com/buna/di/util/FunctionUtil.kt @@ -1,9 +1,9 @@ package com.buna.di.util +import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.full.primaryConstructor -inline fun validateHasPrimaryConstructor(): KFunction { - val primaryConstructor = T::class.primaryConstructor +fun KClass.validateHasPrimaryConstructor(): KFunction { return requireNotNull(primaryConstructor) { "[ERROR] 주생성자가 존재하지 않습니다." } } diff --git a/di/src/main/java/com/buna/di/lazy/ActivityViewModelLazy.kt b/di/src/main/java/com/buna/di/viewModel/ActivityViewModelLazy.kt similarity index 81% rename from di/src/main/java/com/buna/di/lazy/ActivityViewModelLazy.kt rename to di/src/main/java/com/buna/di/viewModel/ActivityViewModelLazy.kt index 284c9cbbd..58ce879a1 100644 --- a/di/src/main/java/com/buna/di/lazy/ActivityViewModelLazy.kt +++ b/di/src/main/java/com/buna/di/viewModel/ActivityViewModelLazy.kt @@ -1,10 +1,9 @@ -package com.buna.di.lazy +package com.buna.di.viewModel import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelLazy -import com.buna.di.DependencyInjector -import com.buna.di.util.viewModelFactory +import com.buna.di.injector.DependencyInjector inline fun ComponentActivity.viewModel(): Lazy { return ViewModelLazy( diff --git a/di/src/main/java/com/buna/di/util/ViewModelFactory.kt b/di/src/main/java/com/buna/di/viewModel/ViewModelFactory.kt similarity index 92% rename from di/src/main/java/com/buna/di/util/ViewModelFactory.kt rename to di/src/main/java/com/buna/di/viewModel/ViewModelFactory.kt index e779ed1ff..e9a6f1a2e 100644 --- a/di/src/main/java/com/buna/di/util/ViewModelFactory.kt +++ b/di/src/main/java/com/buna/di/viewModel/ViewModelFactory.kt @@ -1,4 +1,4 @@ -package com.buna.di.util +package com.buna.di.viewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider diff --git a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt index d75c5adfc..cdfbef2a5 100644 --- a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt +++ b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt @@ -2,9 +2,8 @@ package com.buna.di.injector import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel -import com.buna.di.DependencyInjector import com.buna.di.annotation.Inject -import com.buna.di.lazy.viewModel +import com.buna.di.viewModel.viewModel import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotNull @@ -27,7 +26,7 @@ class FieldTestActivity : AppCompatActivity() { } class FieldTestViewModel : ViewModel() { - @com.buna.di.annotation.Inject + @Inject lateinit var fieldWithInjectAnnotation: FieldDependency lateinit var fieldWithoutInjectAnnotation: FieldDependency @@ -49,7 +48,7 @@ class DependencyInjectorTest { fun 뷰모델의_생성자_종속_항목을_자동_주입해준다() { // given: 뷰모델 생성에 필요한 종속 항목을 주입한다. val constructorDependency = ConstructorDependency() - DependencyInjector.inject(constructorDependency) + DependencyInjector.module(constructorDependency) // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(ConstructorTestActivity::class.java) @@ -67,7 +66,7 @@ class DependencyInjectorTest { fun 뷰모델에서_Inject_애노테이션이_붙은_필드에_종속_항목을_자동_주입해준다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. val fieldDependency = FieldDependency() - DependencyInjector.inject(fieldDependency) + DependencyInjector.module(fieldDependency) // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(FieldTestActivity::class.java) @@ -85,7 +84,7 @@ class DependencyInjectorTest { fun 뷰모델에서_Inject_애노테이션이_없는_필드에는_종속_항목을_주입하지_않는다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. val fieldDependency = FieldDependency() - DependencyInjector.inject(fieldDependency) + DependencyInjector.module(fieldDependency) // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(FieldTestActivity::class.java) From 0bb35a3f771fe7461d735bb137b63644e849665b Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 15:22:48 +0900 Subject: [PATCH 12/37] =?UTF-8?q?refactor:=20=EC=BA=90=EC=8B=9C=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=ED=95=9C=20=EB=B2=88=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=ED=95=9C=20=EA=B0=9D=EC=B2=B4=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/buna/di/injector/Cache.kt | 22 +++++++ .../buna/di/injector/DependencyInjector.kt | 59 +++++-------------- .../com/buna/di/injector/DependencyKey.kt | 21 ++++++- .../java/com/buna/di/util/ExceptionUtil.kt | 5 -- 4 files changed, 57 insertions(+), 50 deletions(-) create mode 100644 di/src/main/java/com/buna/di/injector/Cache.kt delete mode 100644 di/src/main/java/com/buna/di/util/ExceptionUtil.kt diff --git a/di/src/main/java/com/buna/di/injector/Cache.kt b/di/src/main/java/com/buna/di/injector/Cache.kt new file mode 100644 index 000000000..deaf096bd --- /dev/null +++ b/di/src/main/java/com/buna/di/injector/Cache.kt @@ -0,0 +1,22 @@ +package com.buna.di.injector + +import kotlin.reflect.KFunction + +class Cache( + private val cache: MutableMap = mutableMapOf(), +) { + fun caching(module: Module, provider: KFunction<*>) { + val dependencyKey = DependencyKey.createDependencyKey(provider) + val dependency = provider.call(module) + + cache[dependencyKey] = dependency + } + + fun caching(dependencyKey: DependencyKey, dependency: Any? = null) { + cache[dependencyKey] = dependency + } + + fun get(dependencyKey: DependencyKey): Any? { + return cache[dependencyKey] + } +} diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt index 6fc225ac5..ce80826d7 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt @@ -3,7 +3,6 @@ package com.buna.di.injector import com.buna.di.annotation.Inject import com.buna.di.util.validateHasPrimaryConstructor import kotlin.reflect.KClass -import kotlin.reflect.KFunction import kotlin.reflect.KMutableProperty import kotlin.reflect.KProperty import kotlin.reflect.KType @@ -16,81 +15,55 @@ import kotlin.reflect.jvm.jvmErasure object DependencyInjector { private val typeConverter = mutableMapOf() - private val cache = mutableMapOf() + private val cache = Cache() - // 모듈로 주입 (모듈 내 Provider 함수로 객체를 생성하여 저장) fun module(module: Module) { val providers = module::class.declaredMemberFunctions - providers.forEach { provider -> caching(module, provider) } + providers.forEach { provider -> cache.caching(module, provider) } } - // 함수로 객체 만들어서 주입 - private fun caching(module: Module, provider: KFunction<*>) { - val dependencyKey = DependencyKey.createDependencyKey(provider) - val dependency = provider.call(module) - - cache[dependencyKey] = dependency - } - - // value == null 이면 가져올 때 생성할 예정 fun type(superClass: KClass<*>, subClass: KClass<*>) { val superType = superClass.starProjectedType val annotation = subClass.annotations.firstOrNull() - val dependencyKey = DependencyKey(superType, annotation) + val dependencyKey = DependencyKey(superType, annotation) val subType = subClass.starProjectedType + typeConverter[dependencyKey] = subType - cache[dependencyKey] = null + cache.caching(dependencyKey) } - // 외부로 종속 항목 제공 fun inject(clazz: KClass): T { - // 1. container에 있으면 바로 제공 - val type = clazz.supertypes.getOrElse(0) { clazz.starProjectedType } - val annotation = clazz.annotations.firstOrNull() - val dependencyKey = DependencyKey(type, annotation) - - if (cache[dependencyKey] != null) { - return cache[dependencyKey] as T - } + val dependencyKey = DependencyKey.createDependencyKey(clazz) + val cached = cache.get(dependencyKey) + if (cached != null) return cached as T - // 없으면 클래스의 주생성자 파라미터 순회 val primaryConstructor = clazz.validateHasPrimaryConstructor() val parameters = primaryConstructor.parameters - // 1. 파라미터를 순회하며 inject() 재귀 호출 val arguments = parameters.map { parameter -> - // 현재 문제 : 인터페이스 타입을 넘기는 경우 인터페이스 타입이 아닌 구현체 타입을 넘겨야 함 - val paramAnnotation = parameter.annotations.firstOrNull() - val paramType = parameter.type // 인터페이스 타입일 수도 있음 - val paramDependencyKey = DependencyKey(paramType, paramAnnotation) + val paramDependencyKey = DependencyKey.createDependencyKey(parameter) + val paramType = parameter.type val subType = typeConverter[paramDependencyKey] ?: paramType - val paramInstance = inject(subType.jvmErasure) - - paramInstance + inject(subType.jvmErasure) } - // 2. 파라미터를 인자 목록으로 만들어서 주생성자 호출 - val instance = primaryConstructor.call(*arguments.toTypedArray()) + val dependency = primaryConstructor.call(*arguments.toTypedArray()) - // 3. 필드 주입 (필드 클래스에 대해 inject() 재귀 호출) clazz.memberProperties.forEach { property: KProperty<*> -> if (!property.hasAnnotation()) return@forEach if (property !is KMutableProperty<*>) return@forEach property.isAccessible = true val propertyType = property.returnType - val propertyAnnotation = - property.annotations.first { it.annotationClass != Inject::class } - val propertyDependencyKey = DependencyKey(propertyType, propertyAnnotation) - + val propertyDependencyKey = DependencyKey.createDependencyKey(property) val subType = typeConverter[propertyDependencyKey] ?: propertyType val propertyInstance = inject(subType.jvmErasure) + cache.caching(propertyDependencyKey, propertyInstance) - cache[propertyDependencyKey] = propertyInstance - property.setter.call(instance, propertyInstance) + property.setter.call(dependency, propertyInstance) } - return instance + return dependency } } diff --git a/di/src/main/java/com/buna/di/injector/DependencyKey.kt b/di/src/main/java/com/buna/di/injector/DependencyKey.kt index dc1c566b0..4066fa30f 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyKey.kt +++ b/di/src/main/java/com/buna/di/injector/DependencyKey.kt @@ -1,7 +1,10 @@ package com.buna.di.injector +import com.buna.di.annotation.Inject import kotlin.reflect.KClass import kotlin.reflect.KFunction +import kotlin.reflect.KParameter +import kotlin.reflect.KProperty import kotlin.reflect.KType import kotlin.reflect.full.starProjectedType @@ -11,10 +14,10 @@ data class DependencyKey( ) { companion object { fun createDependencyKey(clazz: KClass<*>): DependencyKey { + val returnType = clazz.supertypes.getOrElse(0) { clazz.starProjectedType } val annotation = clazz.annotations.firstOrNull() - val type = clazz.starProjectedType - return DependencyKey(type, annotation) + return DependencyKey(returnType, annotation) } fun createDependencyKey(provider: KFunction<*>): DependencyKey { @@ -23,5 +26,19 @@ data class DependencyKey( return DependencyKey(returnType, annotation) } + + fun createDependencyKey(parameter: KParameter): DependencyKey { + val returnType = parameter.type + val annotation = parameter.annotations.firstOrNull() + + return DependencyKey(returnType, annotation) + } + + fun createDependencyKey(property: KProperty<*>): DependencyKey { + val returnType = property.returnType + val annotation = property.annotations.first { it.annotationClass != Inject::class } + + return DependencyKey(returnType, annotation) + } } } diff --git a/di/src/main/java/com/buna/di/util/ExceptionUtil.kt b/di/src/main/java/com/buna/di/util/ExceptionUtil.kt deleted file mode 100644 index 7ceb3b19a..000000000 --- a/di/src/main/java/com/buna/di/util/ExceptionUtil.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.buna.di.util - -fun throwNotExistDependency(clazz: Class<*>) { - throw IllegalArgumentException("[ERROR] 주입할 의존성이 존재하지 않습니다. parameterType: $clazz") -} From 7770cf7270b93af058b142c06d22c7a7077f5ac6 Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 15:53:42 +0900 Subject: [PATCH 13/37] =?UTF-8?q?refactor:=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=ED=95=98=EB=8A=94=20Map=EC=9D=84=20SubTypeCo?= =?UTF-8?q?nverter=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/common/di/module/RepositoryModule.kt | 2 +- .../com/buna/di/dsl/DependencyModuleDsl.kt | 2 +- .../main/java/com/buna/di/injector/Cache.kt | 1 + .../buna/di/injector/DependencyInjector.kt | 65 +++++++++---------- .../main/java/com/buna/di/injector/Module.kt | 3 - .../com/buna/di/injector/SubTypeConverter.kt | 15 +++++ di/src/main/java/com/buna/di/module/Module.kt | 3 + .../java/com/buna/di/util/FunctionUtil.kt | 9 --- .../java/com/buna/di/util/ReflectionUtil.kt | 25 +++++++ 9 files changed, 75 insertions(+), 50 deletions(-) delete mode 100644 di/src/main/java/com/buna/di/injector/Module.kt create mode 100644 di/src/main/java/com/buna/di/injector/SubTypeConverter.kt create mode 100644 di/src/main/java/com/buna/di/module/Module.kt delete mode 100644 di/src/main/java/com/buna/di/util/FunctionUtil.kt create mode 100644 di/src/main/java/com/buna/di/util/ReflectionUtil.kt diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt index 5e533b988..9f430408f 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt @@ -2,7 +2,7 @@ package woowacourse.shopping.ui.common.di.module import android.content.Context import androidx.room.Room -import com.buna.di.injector.Module +import com.buna.di.module.Module import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.ShoppingDatabase diff --git a/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt b/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt index d3ce699a1..4bdeb5954 100644 --- a/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt +++ b/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt @@ -1,7 +1,7 @@ package com.buna.di.dsl import com.buna.di.injector.DependencyInjector -import com.buna.di.injector.Module +import com.buna.di.module.Module class DependencyModuleDsl { fun module(module: Module) { diff --git a/di/src/main/java/com/buna/di/injector/Cache.kt b/di/src/main/java/com/buna/di/injector/Cache.kt index deaf096bd..896318e00 100644 --- a/di/src/main/java/com/buna/di/injector/Cache.kt +++ b/di/src/main/java/com/buna/di/injector/Cache.kt @@ -1,5 +1,6 @@ package com.buna.di.injector +import com.buna.di.module.Module import kotlin.reflect.KFunction class Cache( diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt index ce80826d7..fd6929b93 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt @@ -1,11 +1,11 @@ package com.buna.di.injector import com.buna.di.annotation.Inject +import com.buna.di.module.Module +import com.buna.di.util.createInstance import com.buna.di.util.validateHasPrimaryConstructor import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty -import kotlin.reflect.KProperty -import kotlin.reflect.KType import kotlin.reflect.full.declaredMemberFunctions import kotlin.reflect.full.hasAnnotation import kotlin.reflect.full.memberProperties @@ -14,56 +14,49 @@ import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.jvmErasure object DependencyInjector { - private val typeConverter = mutableMapOf() + private val subTypeConverter = SubTypeConverter() private val cache = Cache() - fun module(module: Module) { - val providers = module::class.declaredMemberFunctions - providers.forEach { provider -> cache.caching(module, provider) } - } - - fun type(superClass: KClass<*>, subClass: KClass<*>) { - val superType = superClass.starProjectedType - val annotation = subClass.annotations.firstOrNull() - - val dependencyKey = DependencyKey(superType, annotation) - val subType = subClass.starProjectedType - - typeConverter[dependencyKey] = subType - cache.caching(dependencyKey) - } - fun inject(clazz: KClass): T { - val dependencyKey = DependencyKey.createDependencyKey(clazz) - val cached = cache.get(dependencyKey) + val cached = cache.get(DependencyKey.createDependencyKey(clazz)) if (cached != null) return cached as T val primaryConstructor = clazz.validateHasPrimaryConstructor() - val parameters = primaryConstructor.parameters - - val arguments = parameters.map { parameter -> - val paramDependencyKey = DependencyKey.createDependencyKey(parameter) - val paramType = parameter.type - val subType = typeConverter[paramDependencyKey] ?: paramType - inject(subType.jvmErasure) - } + val dependency = primaryConstructor.createInstance(subTypeConverter) - val dependency = primaryConstructor.call(*arguments.toTypedArray()) + injectMemberProperties(clazz, dependency) + return dependency + } - clazz.memberProperties.forEach { property: KProperty<*> -> + private fun injectMemberProperties(clazz: KClass, instance: T) { + clazz.memberProperties.forEach { property -> if (!property.hasAnnotation()) return@forEach if (property !is KMutableProperty<*>) return@forEach property.isAccessible = true + val dependencyKey = DependencyKey.createDependencyKey(property) val propertyType = property.returnType - val propertyDependencyKey = DependencyKey.createDependencyKey(property) - val subType = typeConverter[propertyDependencyKey] ?: propertyType + val subType = subTypeConverter.convertType(dependencyKey, propertyType) val propertyInstance = inject(subType.jvmErasure) - cache.caching(propertyDependencyKey, propertyInstance) - property.setter.call(dependency, propertyInstance) + cache.caching(dependencyKey, propertyInstance) + property.setter.call(instance, propertyInstance) } + } - return dependency + fun module(module: Module) { + val providers = module::class.declaredMemberFunctions + providers.forEach { provider -> cache.caching(module, provider) } + } + + fun type(superClass: KClass<*>, subClass: KClass<*>) { + val superType = superClass.starProjectedType + val annotation = subClass.annotations.firstOrNull() + + val dependencyKey = DependencyKey(superType, annotation) + val subType = subClass.starProjectedType + + subTypeConverter.saveType(dependencyKey, subType) + cache.caching(dependencyKey) } } diff --git a/di/src/main/java/com/buna/di/injector/Module.kt b/di/src/main/java/com/buna/di/injector/Module.kt deleted file mode 100644 index cd6dee086..000000000 --- a/di/src/main/java/com/buna/di/injector/Module.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.buna.di.injector - -interface Module diff --git a/di/src/main/java/com/buna/di/injector/SubTypeConverter.kt b/di/src/main/java/com/buna/di/injector/SubTypeConverter.kt new file mode 100644 index 000000000..b4ce5021c --- /dev/null +++ b/di/src/main/java/com/buna/di/injector/SubTypeConverter.kt @@ -0,0 +1,15 @@ +package com.buna.di.injector + +import kotlin.reflect.KType + +class SubTypeConverter( + private val converter: MutableMap = mutableMapOf(), +) { + fun saveType(parentTypeKey: DependencyKey, childType: KType) { + converter[parentTypeKey] = childType + } + + fun convertType(parentTypeKey: DependencyKey, defaultType: KType): KType { + return converter[parentTypeKey] ?: defaultType + } +} diff --git a/di/src/main/java/com/buna/di/module/Module.kt b/di/src/main/java/com/buna/di/module/Module.kt new file mode 100644 index 000000000..c6d5df305 --- /dev/null +++ b/di/src/main/java/com/buna/di/module/Module.kt @@ -0,0 +1,3 @@ +package com.buna.di.module + +interface Module diff --git a/di/src/main/java/com/buna/di/util/FunctionUtil.kt b/di/src/main/java/com/buna/di/util/FunctionUtil.kt deleted file mode 100644 index 0b09ed927..000000000 --- a/di/src/main/java/com/buna/di/util/FunctionUtil.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.buna.di.util - -import kotlin.reflect.KClass -import kotlin.reflect.KFunction -import kotlin.reflect.full.primaryConstructor - -fun KClass.validateHasPrimaryConstructor(): KFunction { - return requireNotNull(primaryConstructor) { "[ERROR] 주생성자가 존재하지 않습니다." } -} diff --git a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt new file mode 100644 index 000000000..90a6b03fa --- /dev/null +++ b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt @@ -0,0 +1,25 @@ +package com.buna.di.util + +import com.buna.di.injector.DependencyInjector +import com.buna.di.injector.DependencyKey +import com.buna.di.injector.SubTypeConverter +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KParameter +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.jvm.jvmErasure + +fun KClass.validateHasPrimaryConstructor(): KFunction { + return requireNotNull(primaryConstructor) { "[ERROR] 주생성자가 존재하지 않습니다." } +} + +fun KFunction.createInstance(converter: SubTypeConverter): T { + return call(*parameters.createInstances(converter)) +} + +fun List.createInstances(converter: SubTypeConverter): Array = map { parameter -> + val paramDependencyKey = DependencyKey.createDependencyKey(parameter) + val paramType = parameter.type + val subType = converter.convertType(paramDependencyKey, paramType) + DependencyInjector.inject(subType.jvmErasure) +}.toTypedArray() From bffeafc2a096c11d42db7454a6b92425f5ca82bf Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 15:57:36 +0900 Subject: [PATCH 14/37] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt | 2 +- app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt | 2 +- di/src/main/java/com/buna/di/injector/DependencyInjector.kt | 2 ++ di/src/main/java/com/buna/di/util/ReflectionUtil.kt | 2 +- .../java/com/buna/di/{injector => util/diAssistant}/Cache.kt | 3 ++- .../buna/di/{injector => util/diAssistant}/SubTypeConverter.kt | 3 ++- .../com/buna/di/{ => util}/viewModel/ActivityViewModelLazy.kt | 2 +- .../java/com/buna/di/{ => util}/viewModel/ViewModelFactory.kt | 2 +- .../test/java/com/buna/di/injector/DependencyInjectorTest.kt | 2 +- 9 files changed, 12 insertions(+), 8 deletions(-) rename di/src/main/java/com/buna/di/{injector => util/diAssistant}/Cache.kt (88%) rename di/src/main/java/com/buna/di/{injector => util/diAssistant}/SubTypeConverter.kt (83%) rename di/src/main/java/com/buna/di/{ => util}/viewModel/ActivityViewModelLazy.kt (93%) rename di/src/main/java/com/buna/di/{ => util}/viewModel/ViewModelFactory.kt (91%) diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt index ec9740e6a..225abb97b 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt @@ -3,7 +3,7 @@ package woowacourse.shopping.ui.cart import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.buna.di.viewModel.viewModel +import com.buna.di.util.viewModel.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityCartBinding diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt index cdbe01cad..77b77bfb4 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt @@ -5,7 +5,7 @@ import android.os.Bundle import android.view.Menu import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.buna.di.viewModel.viewModel +import com.buna.di.util.viewModel.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityMainBinding import woowacourse.shopping.ui.cart.CartActivity diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt index fd6929b93..4eddfd5f9 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt @@ -1,6 +1,8 @@ package com.buna.di.injector import com.buna.di.annotation.Inject +import com.buna.di.util.diAssistant.Cache +import com.buna.di.util.diAssistant.SubTypeConverter import com.buna.di.module.Module import com.buna.di.util.createInstance import com.buna.di.util.validateHasPrimaryConstructor diff --git a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt index 90a6b03fa..3bc813908 100644 --- a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt +++ b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt @@ -1,8 +1,8 @@ package com.buna.di.util +import com.buna.di.util.diAssistant.SubTypeConverter import com.buna.di.injector.DependencyInjector import com.buna.di.injector.DependencyKey -import com.buna.di.injector.SubTypeConverter import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter diff --git a/di/src/main/java/com/buna/di/injector/Cache.kt b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt similarity index 88% rename from di/src/main/java/com/buna/di/injector/Cache.kt rename to di/src/main/java/com/buna/di/util/diAssistant/Cache.kt index 896318e00..dd8399718 100644 --- a/di/src/main/java/com/buna/di/injector/Cache.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt @@ -1,5 +1,6 @@ -package com.buna.di.injector +package com.buna.di.util.diAssistant +import com.buna.di.injector.DependencyKey import com.buna.di.module.Module import kotlin.reflect.KFunction diff --git a/di/src/main/java/com/buna/di/injector/SubTypeConverter.kt b/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt similarity index 83% rename from di/src/main/java/com/buna/di/injector/SubTypeConverter.kt rename to di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt index b4ce5021c..d858a7ba5 100644 --- a/di/src/main/java/com/buna/di/injector/SubTypeConverter.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt @@ -1,5 +1,6 @@ -package com.buna.di.injector +package com.buna.di.util.diAssistant +import com.buna.di.injector.DependencyKey import kotlin.reflect.KType class SubTypeConverter( diff --git a/di/src/main/java/com/buna/di/viewModel/ActivityViewModelLazy.kt b/di/src/main/java/com/buna/di/util/viewModel/ActivityViewModelLazy.kt similarity index 93% rename from di/src/main/java/com/buna/di/viewModel/ActivityViewModelLazy.kt rename to di/src/main/java/com/buna/di/util/viewModel/ActivityViewModelLazy.kt index 58ce879a1..e520aaf87 100644 --- a/di/src/main/java/com/buna/di/viewModel/ActivityViewModelLazy.kt +++ b/di/src/main/java/com/buna/di/util/viewModel/ActivityViewModelLazy.kt @@ -1,4 +1,4 @@ -package com.buna.di.viewModel +package com.buna.di.util.viewModel import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel diff --git a/di/src/main/java/com/buna/di/viewModel/ViewModelFactory.kt b/di/src/main/java/com/buna/di/util/viewModel/ViewModelFactory.kt similarity index 91% rename from di/src/main/java/com/buna/di/viewModel/ViewModelFactory.kt rename to di/src/main/java/com/buna/di/util/viewModel/ViewModelFactory.kt index e9a6f1a2e..422a03674 100644 --- a/di/src/main/java/com/buna/di/viewModel/ViewModelFactory.kt +++ b/di/src/main/java/com/buna/di/util/viewModel/ViewModelFactory.kt @@ -1,4 +1,4 @@ -package com.buna.di.viewModel +package com.buna.di.util.viewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider diff --git a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt index cdfbef2a5..e5ee77394 100644 --- a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt +++ b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt @@ -3,7 +3,7 @@ package com.buna.di.injector import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel import com.buna.di.annotation.Inject -import com.buna.di.viewModel.viewModel +import com.buna.di.util.viewModel.viewModel import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotNull From 0c4c466f8fc38855d99178b2d15ce59fc303b733 Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 16:00:33 +0900 Subject: [PATCH 15/37] =?UTF-8?q?feat:=20DependencyInjector=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/buna/di/injector/DependencyInjector.kt | 9 +++++++-- di/src/main/java/com/buna/di/util/diAssistant/Cache.kt | 6 +++++- .../com/buna/di/util/diAssistant/SubTypeConverter.kt | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt index 4eddfd5f9..226f8bf5f 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt @@ -1,10 +1,10 @@ package com.buna.di.injector import com.buna.di.annotation.Inject -import com.buna.di.util.diAssistant.Cache -import com.buna.di.util.diAssistant.SubTypeConverter import com.buna.di.module.Module import com.buna.di.util.createInstance +import com.buna.di.util.diAssistant.Cache +import com.buna.di.util.diAssistant.SubTypeConverter import com.buna.di.util.validateHasPrimaryConstructor import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty @@ -61,4 +61,9 @@ object DependencyInjector { subTypeConverter.saveType(dependencyKey, subType) cache.caching(dependencyKey) } + + fun clear() { + subTypeConverter.clear() + cache.clear() + } } diff --git a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt index dd8399718..fc36635aa 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt @@ -4,7 +4,7 @@ import com.buna.di.injector.DependencyKey import com.buna.di.module.Module import kotlin.reflect.KFunction -class Cache( +data class Cache( private val cache: MutableMap = mutableMapOf(), ) { fun caching(module: Module, provider: KFunction<*>) { @@ -21,4 +21,8 @@ class Cache( fun get(dependencyKey: DependencyKey): Any? { return cache[dependencyKey] } + + fun clear(): Cache { + return copy(cache = mutableMapOf()) + } } diff --git a/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt b/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt index d858a7ba5..147c58ed5 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt @@ -3,7 +3,7 @@ package com.buna.di.util.diAssistant import com.buna.di.injector.DependencyKey import kotlin.reflect.KType -class SubTypeConverter( +data class SubTypeConverter( private val converter: MutableMap = mutableMapOf(), ) { fun saveType(parentTypeKey: DependencyKey, childType: KType) { @@ -13,4 +13,8 @@ class SubTypeConverter( fun convertType(parentTypeKey: DependencyKey, defaultType: KType): KType { return converter[parentTypeKey] ?: defaultType } + + fun clear(): SubTypeConverter { + return copy(converter = mutableMapOf()) + } } From eb091ed7d414ebdfe01a1b484fbcfeb4cbb35dfc Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 16:52:50 +0900 Subject: [PATCH 16/37] =?UTF-8?q?test:=20=EA=B8=B0=EB=8A=A5=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=AC=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/ui/main/MainViewModel.kt | 6 +- .../woowacourse/shopping/MainCoroutineRule.kt | 25 ++++++++ .../shopping/ui/cart/CartViewModelTest.kt | 20 +++--- .../shopping/ui/main/MainViewModelTest.kt | 7 ++- di/build.gradle.kts | 3 + .../com/buna/di/ExampleInstrumentedTest.kt | 8 +-- di/src/main/AndroidManifest.xml | 2 +- .../com/buna/di/dsl/DependencyModuleDsl.kt | 2 +- .../java/com/buna/di/util/ReflectionUtil.kt | 2 +- .../di/injector/DependencyInjectorTest.kt | 62 ++++++++----------- .../fakeClasses/FakeConstructorDependency.kt | 21 +++++++ .../fakeClasses/FakeFieldDependency.kt | 26 ++++++++ 12 files changed, 127 insertions(+), 57 deletions(-) create mode 100644 app/src/test/java/woowacourse/shopping/MainCoroutineRule.kt create mode 100644 di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt create mode 100644 di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index 212de1c43..6aa612b31 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.buna.di.annotation.Inject import kotlinx.coroutines.launch import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository @@ -15,10 +14,9 @@ import woowacourse.shopping.ui.common.di.qualifier.InMemoryProductRepositoryQual class MainViewModel( @InMemoryProductRepositoryQualifier private val productRepository: ProductRepository, -) : ViewModel() { - @Inject @InMemoryCartRepositoryQualifier - private lateinit var cartRepository: CartRepository + private val cartRepository: CartRepository, +) : ViewModel() { private val _products: MutableLiveData> = MutableLiveData(emptyList()) val products: LiveData> get() = _products diff --git a/app/src/test/java/woowacourse/shopping/MainCoroutineRule.kt b/app/src/test/java/woowacourse/shopping/MainCoroutineRule.kt new file mode 100644 index 000000000..e291316ef --- /dev/null +++ b/app/src/test/java/woowacourse/shopping/MainCoroutineRule.kt @@ -0,0 +1,25 @@ +package woowacourse.shopping + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.rules.TestWatcher +import org.junit.runner.Description + +@OptIn(ExperimentalCoroutinesApi::class) +class MainCoroutineRule constructor( + private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(), +) : TestWatcher() { + override fun starting(description: Description) { + super.starting(description) + Dispatchers.setMain(testDispatcher) + } + + override fun finished(description: Description) { + super.finished(description) + Dispatchers.resetMain() + } +} diff --git a/app/src/test/java/woowacourse/shopping/ui/cart/CartViewModelTest.kt b/app/src/test/java/woowacourse/shopping/ui/cart/CartViewModelTest.kt index e7601c26b..85cc05da7 100644 --- a/app/src/test/java/woowacourse/shopping/ui/cart/CartViewModelTest.kt +++ b/app/src/test/java/woowacourse/shopping/ui/cart/CartViewModelTest.kt @@ -2,24 +2,28 @@ package woowacourse.shopping.ui.cart import androidx.arch.core.executor.testing.InstantTaskExecutorRule import io.mockk.Runs -import io.mockk.every +import io.mockk.coEvery import io.mockk.just import io.mockk.mockk import org.junit.Before import org.junit.Rule import org.junit.Test +import woowacourse.shopping.MainCoroutineRule import woowacourse.shopping.getOrAwaitValue +import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository class CartViewModelTest { - private lateinit var viewModel: CartViewModel private lateinit var cartRepository: CartRepository @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() + @get:Rule + val mainDispatcher = MainCoroutineRule() + @Before fun setup() { cartRepository = mockk() @@ -30,11 +34,11 @@ class CartViewModelTest { fun 모든_장바구니_목록을_불러오면_기존_장바구니_목록을_갱신한다() { // given: 불러올 장바구니 목록이 존재한다. val expected = listOf( - Product("우테코 과자", 1000, "snackimage"), - Product("우테코 쥬스", 2000, "juiceimage"), - Product("우테코 아이스크림", 3000, "icecreamimage"), + CartProduct(0, 0L, Product("우테코 과자", 1000, "snack")), + CartProduct(1, 0L, Product("우테코 쥬스", 2000, "juice")), + CartProduct(2, 2L, Product("우테코 아이스크림", 3000, "icecream")), ) - every { cartRepository.getAllCartProducts() } returns expected + coEvery { cartRepository.getAllCartProducts() } returns expected // when: 장바구니 목록을 불러온다. viewModel.fetchAllCartProducts() @@ -47,8 +51,8 @@ class CartViewModelTest { @Test fun 장바구니에서_상품을_제거하면_제거_상태가_참이_된다() { // given: 제거할 상품 ID와 상품 목록이 존재한다. - val productIdForDeleting = 1 - every { cartRepository.deleteCartProduct(productIdForDeleting) } just Runs + val productIdForDeleting = 1L + coEvery { cartRepository.deleteCartProduct(productIdForDeleting) } just Runs // when: 장바구니에서 상품을 제거한다. viewModel.deleteCartProduct(productIdForDeleting) diff --git a/app/src/test/java/woowacourse/shopping/ui/main/MainViewModelTest.kt b/app/src/test/java/woowacourse/shopping/ui/main/MainViewModelTest.kt index afaadd8d4..086e4bb15 100644 --- a/app/src/test/java/woowacourse/shopping/ui/main/MainViewModelTest.kt +++ b/app/src/test/java/woowacourse/shopping/ui/main/MainViewModelTest.kt @@ -2,12 +2,14 @@ package woowacourse.shopping.ui.main import androidx.arch.core.executor.testing.InstantTaskExecutorRule import io.mockk.Runs +import io.mockk.coEvery import io.mockk.every import io.mockk.just import io.mockk.mockk import org.junit.Before import org.junit.Rule import org.junit.Test +import woowacourse.shopping.MainCoroutineRule import woowacourse.shopping.getOrAwaitValue import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository @@ -22,6 +24,9 @@ class MainViewModelTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() + @get:Rule + val mainDispatcher = MainCoroutineRule() + @Before fun setUp() { productRepository = mockk() @@ -37,7 +42,7 @@ class MainViewModelTest { fun 장바구니_목록에_제품을_추가하면_상품_추가_상태가_참이_된다() { // given: 장바구니에 추가할 상품이 존재한다. val product = mockk() - every { cartRepository.addCartProduct(product) } just Runs + coEvery { cartRepository.addCartProduct(product) } just Runs // when: 장바구니에 상품을 추가한다. viewModel.addCartProduct(product) diff --git a/di/build.gradle.kts b/di/build.gradle.kts index eb46920a8..93d9be6fd 100644 --- a/di/build.gradle.kts +++ b/di/build.gradle.kts @@ -30,6 +30,9 @@ android { kotlinOptions { jvmTarget = "1.8" } + testOptions { + unitTests.isIncludeAndroidResources = true + } } dependencies { diff --git a/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt b/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt index 82727a48d..ed03249e7 100644 --- a/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt +++ b/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.buna.di -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * @@ -21,4 +19,4 @@ class ExampleInstrumentedTest { val appContext = InstrumentationRegistry.getInstrumentation().targetContext assertEquals("com.buna.di.test", appContext.packageName) } -} \ No newline at end of file +} diff --git a/di/src/main/AndroidManifest.xml b/di/src/main/AndroidManifest.xml index a5918e68a..44008a433 100644 --- a/di/src/main/AndroidManifest.xml +++ b/di/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt b/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt index 4bdeb5954..d749f1f07 100644 --- a/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt +++ b/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt @@ -11,4 +11,4 @@ class DependencyModuleDsl { fun modules(block: DependencyModuleDsl.() -> Unit) { DependencyModuleDsl().block() -} \ No newline at end of file +} diff --git a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt index 3bc813908..328968a5d 100644 --- a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt +++ b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt @@ -1,8 +1,8 @@ package com.buna.di.util -import com.buna.di.util.diAssistant.SubTypeConverter import com.buna.di.injector.DependencyInjector import com.buna.di.injector.DependencyKey +import com.buna.di.util.diAssistant.SubTypeConverter import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter diff --git a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt index e5ee77394..05e866485 100644 --- a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt +++ b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt @@ -1,41 +1,22 @@ package com.buna.di.injector -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.ViewModel -import com.buna.di.annotation.Inject -import com.buna.di.util.viewModel.viewModel -import junit.framework.TestCase.assertEquals +import com.buna.di.dsl.modules +import com.buna.di.dsl.types +import com.buna.di.injector.fakeClasses.AConstructorDependency +import com.buna.di.injector.fakeClasses.AFieldDependency +import com.buna.di.injector.fakeClasses.ConstructorDependency +import com.buna.di.injector.fakeClasses.ConstructorTestActivity +import com.buna.di.injector.fakeClasses.FieldDependency +import com.buna.di.injector.fakeClasses.FieldTestActivity import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotNull +import junit.framework.TestCase.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner -class ConstructorTestActivity : AppCompatActivity() { - val viewModel: ConstructorTestViewModel by viewModel() -} - -class ConstructorTestViewModel( - val constructorDependency: ConstructorDependency, -) : ViewModel() - -class FieldTestActivity : AppCompatActivity() { - val viewModel: FieldTestViewModel by viewModel() -} - -class FieldTestViewModel : ViewModel() { - @Inject - lateinit var fieldWithInjectAnnotation: FieldDependency - lateinit var fieldWithoutInjectAnnotation: FieldDependency - - fun isFieldWithoutInjectAnnotationInitialized() = ::fieldWithoutInjectAnnotation.isInitialized -} - -class ConstructorDependency -class FieldDependency - @RunWith(RobolectricTestRunner::class) class DependencyInjectorTest { @@ -47,8 +28,11 @@ class DependencyInjectorTest { @Test fun 뷰모델의_생성자_종속_항목을_자동_주입해준다() { // given: 뷰모델 생성에 필요한 종속 항목을 주입한다. - val constructorDependency = ConstructorDependency() - DependencyInjector.module(constructorDependency) + modules { + types { + type(ConstructorDependency::class to AConstructorDependency::class) + } + } // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(ConstructorTestActivity::class.java) @@ -58,15 +42,18 @@ class DependencyInjectorTest { // then: 액티비티의 뷰모델에 종속 항목이 주입되어 있다. assertNotNull(activity.viewModel) activity.viewModel.run { - assertEquals(constructorDependency, this.constructorDependency) + assertTrue(constructorDependency is AConstructorDependency) } } @Test fun 뷰모델에서_Inject_애노테이션이_붙은_필드에_종속_항목을_자동_주입해준다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. - val fieldDependency = FieldDependency() - DependencyInjector.module(fieldDependency) + modules { + types { + type(FieldDependency::class to AFieldDependency::class) + } + } // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(FieldTestActivity::class.java) @@ -76,15 +63,18 @@ class DependencyInjectorTest { // then: Inject 애노테이션이 포함된 뷰모델의 필드에 종속 항목이 주입되어 있다. assertNotNull(activity.viewModel) activity.viewModel.run { - assertEquals(fieldDependency, this.fieldWithInjectAnnotation) + assertTrue(fieldWithInjectAnnotation is AFieldDependency) } } @Test fun 뷰모델에서_Inject_애노테이션이_없는_필드에는_종속_항목을_주입하지_않는다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. - val fieldDependency = FieldDependency() - DependencyInjector.module(fieldDependency) + modules { + types { + type(FieldDependency::class to AFieldDependency::class) + } + } // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(FieldTestActivity::class.java) diff --git a/di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt b/di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt new file mode 100644 index 000000000..3afdb5d8a --- /dev/null +++ b/di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt @@ -0,0 +1,21 @@ +package com.buna.di.injector.fakeClasses + +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import com.buna.di.util.viewModel.viewModel + +annotation class AConstructorDependencyQualifier + +interface ConstructorDependency + +@AConstructorDependencyQualifier +class AConstructorDependency : ConstructorDependency + +class ConstructorTestActivity : AppCompatActivity() { + val viewModel: ConstructorTestViewModel by viewModel() +} + +class ConstructorTestViewModel( + @AConstructorDependencyQualifier + val constructorDependency: ConstructorDependency, +) : ViewModel() diff --git a/di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt b/di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt new file mode 100644 index 000000000..ad80a43ec --- /dev/null +++ b/di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt @@ -0,0 +1,26 @@ +package com.buna.di.injector.fakeClasses + +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import com.buna.di.annotation.Inject +import com.buna.di.util.viewModel.viewModel + +annotation class AFieldDependencyQualifier + +interface FieldDependency + +@AFieldDependencyQualifier +class AFieldDependency : FieldDependency + +class FieldTestActivity : AppCompatActivity() { + val viewModel: FieldTestViewModel by viewModel() +} + +class FieldTestViewModel : ViewModel() { + @Inject + @AFieldDependencyQualifier + lateinit var fieldWithInjectAnnotation: FieldDependency + private lateinit var fieldWithoutInjectAnnotation: FieldDependency + + fun isFieldWithoutInjectAnnotationInitialized() = ::fieldWithoutInjectAnnotation.isInitialized +} From 5aff10cabed72b4c80cde52eee0acdf698b07f87 Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 16:59:12 +0900 Subject: [PATCH 17/37] =?UTF-8?q?refactor:=20CartRepository=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/DatabaseCartRepository.kt | 26 +++++++++++++++++++ ...epository.kt => InMemoryCartRepository.kt} | 19 -------------- 2 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt rename app/src/main/java/woowacourse/shopping/data/repository/{DefaultCartRepository.kt => InMemoryCartRepository.kt} (61%) diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt new file mode 100644 index 000000000..029435df0 --- /dev/null +++ b/app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt @@ -0,0 +1,26 @@ +package woowacourse.shopping.data.repository + +import woowacourse.shopping.data.CartProductDao +import woowacourse.shopping.data.mapper.toDomain +import woowacourse.shopping.data.mapper.toEntity +import woowacourse.shopping.model.CartProduct +import woowacourse.shopping.model.Product +import woowacourse.shopping.repository.CartRepository +import woowacourse.shopping.ui.common.di.qualifier.DatabaseCartRepositoryQualifier + +@DatabaseCartRepositoryQualifier +class DatabaseCartRepository( + private val dao: CartProductDao, +) : CartRepository { + override suspend fun addCartProduct(product: Product) { + dao.insert(product.toEntity()) + } + + override suspend fun getAllCartProducts(): List { + return dao.getAll().toDomain() + } + + override suspend fun deleteCartProduct(id: Long) { + dao.delete(id) + } +} diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt similarity index 61% rename from app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt rename to app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt index 6653b823d..5f6b34906 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt @@ -1,32 +1,13 @@ package woowacourse.shopping.data.repository -import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.CartProductEntity import woowacourse.shopping.data.mapper.toDomain import woowacourse.shopping.data.mapper.toEntity import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository -import woowacourse.shopping.ui.common.di.qualifier.DatabaseCartRepositoryQualifier import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier -@DatabaseCartRepositoryQualifier -class DatabaseCartRepository( - private val dao: CartProductDao, -) : CartRepository { - override suspend fun addCartProduct(product: Product) { - dao.insert(product.toEntity()) - } - - override suspend fun getAllCartProducts(): List { - return dao.getAll().toDomain() - } - - override suspend fun deleteCartProduct(id: Long) { - dao.delete(id) - } -} - @InMemoryCartRepositoryQualifier class InMemoryCartRepository : CartRepository { private val cartProducts = mutableListOf() From c56d2116f85ca127ba5c4c1071f3c14fab104e3c Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 18:43:24 +0900 Subject: [PATCH 18/37] =?UTF-8?q?refactor:=20=EC=BA=90=EC=8B=9C=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A2=85=EC=86=8D=ED=95=AD=EB=AA=A9=EC=9D=84=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=A5=BC=20operator=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- di/src/main/java/com/buna/di/injector/DependencyInjector.kt | 2 +- di/src/main/java/com/buna/di/util/diAssistant/Cache.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt index 226f8bf5f..be7fbc4a0 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt @@ -20,7 +20,7 @@ object DependencyInjector { private val cache = Cache() fun inject(clazz: KClass): T { - val cached = cache.get(DependencyKey.createDependencyKey(clazz)) + val cached = cache[DependencyKey.createDependencyKey(clazz)] if (cached != null) return cached as T val primaryConstructor = clazz.validateHasPrimaryConstructor() diff --git a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt index fc36635aa..f89d9d9d8 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt @@ -18,7 +18,7 @@ data class Cache( cache[dependencyKey] = dependency } - fun get(dependencyKey: DependencyKey): Any? { + operator fun get(dependencyKey: DependencyKey): Any? { return cache[dependencyKey] } From 95f32b32880c5ac575362d25163a1387d578663e Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 18:49:28 +0900 Subject: [PATCH 19/37] =?UTF-8?q?fix:=20inject()=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=83=9D=EC=84=B1=ED=95=9C=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=A5=BC=20=EC=BA=90=EC=8B=B1=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- di/src/main/java/com/buna/di/injector/DependencyInjector.kt | 4 ++++ di/src/main/java/com/buna/di/util/ReflectionUtil.kt | 5 ++++- di/src/main/java/com/buna/di/util/diAssistant/Cache.kt | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt index be7fbc4a0..450710b75 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt +++ b/di/src/main/java/com/buna/di/injector/DependencyInjector.kt @@ -62,6 +62,10 @@ object DependencyInjector { cache.caching(dependencyKey) } + fun caching(dependencyKey: DependencyKey, dependency: Any? = null) { + cache.caching(dependencyKey, dependency) + } + fun clear() { subTypeConverter.clear() cache.clear() diff --git a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt index 328968a5d..8cee53ef5 100644 --- a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt +++ b/di/src/main/java/com/buna/di/util/ReflectionUtil.kt @@ -21,5 +21,8 @@ fun List.createInstances(converter: SubTypeConverter): Array = val paramDependencyKey = DependencyKey.createDependencyKey(parameter) val paramType = parameter.type val subType = converter.convertType(paramDependencyKey, paramType) - DependencyInjector.inject(subType.jvmErasure) + val instance = DependencyInjector.inject(subType.jvmErasure) + + DependencyInjector.caching(paramDependencyKey, instance) + instance }.toTypedArray() diff --git a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt index f89d9d9d8..ca6411589 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt @@ -1,5 +1,6 @@ package com.buna.di.util.diAssistant +import android.util.Log import com.buna.di.injector.DependencyKey import com.buna.di.module.Module import kotlin.reflect.KFunction @@ -16,6 +17,7 @@ data class Cache( fun caching(dependencyKey: DependencyKey, dependency: Any? = null) { cache[dependencyKey] = dependency + Log.d("buna", "캐시: $cache") } operator fun get(dependencyKey: DependencyKey): Any? { From d5b56e3a27d7ac69a2d7538bac631f9d8f0775ab Mon Sep 17 00:00:00 2001 From: buna Date: Sat, 9 Sep 2023 18:56:53 +0900 Subject: [PATCH 20/37] =?UTF-8?q?fix:=20InMemoryCartRepository=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=83=81=ED=92=88=20=ED=95=98=EB=82=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=ED=95=98=EB=A9=B4=20=EB=AA=A8=EB=91=90=20=EC=82=AC?= =?UTF-8?q?=EB=9D=BC=EC=A7=80=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/data/repository/InMemoryCartRepository.kt | 3 ++- di/src/main/java/com/buna/di/util/diAssistant/Cache.kt | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt index 5f6b34906..e15ce44d0 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt @@ -11,9 +11,10 @@ import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifi @InMemoryCartRepositoryQualifier class InMemoryCartRepository : CartRepository { private val cartProducts = mutableListOf() + private var lastId: Long = 0 override suspend fun addCartProduct(product: Product) { - cartProducts.add(product.toEntity()) + cartProducts.add(product.toEntity().apply { id = ++lastId }) } override suspend fun getAllCartProducts(): List { diff --git a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt index ca6411589..f89d9d9d8 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt @@ -1,6 +1,5 @@ package com.buna.di.util.diAssistant -import android.util.Log import com.buna.di.injector.DependencyKey import com.buna.di.module.Module import kotlin.reflect.KFunction @@ -17,7 +16,6 @@ data class Cache( fun caching(dependencyKey: DependencyKey, dependency: Any? = null) { cache[dependencyKey] = dependency - Log.d("buna", "캐시: $cache") } operator fun get(dependencyKey: DependencyKey): Any? { From 8c2b8aa1edfad80932a3a9d3fd5d4325930cda37 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 10:00:36 +0900 Subject: [PATCH 21/37] =?UTF-8?q?fix:=20SubTypeConverter,=20Cache=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=95=88=20=EB=90=98=EB=8A=94=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- di/src/main/java/com/buna/di/util/diAssistant/Cache.kt | 1 + .../main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt index f89d9d9d8..da0024664 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt @@ -23,6 +23,7 @@ data class Cache( } fun clear(): Cache { + cache.clear() return copy(cache = mutableMapOf()) } } diff --git a/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt b/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt index 147c58ed5..55fc563cd 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt +++ b/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt @@ -15,6 +15,7 @@ data class SubTypeConverter( } fun clear(): SubTypeConverter { + converter.clear() return copy(converter = mutableMapOf()) } } From 28b27990aaef172effd1acb2a02c44e04b514d32 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 10:08:32 +0900 Subject: [PATCH 22/37] =?UTF-8?q?refactor:=20@InMemoryProductRepositoryQua?= =?UTF-8?q?lifier=20=EC=95=A0=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/data/repository/DefaultProductRepository.kt | 2 -- .../woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt | 2 -- .../main/java/woowacourse/shopping/ui/main/MainViewModel.kt | 5 +---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt index 3f414332e..5fc51701b 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt @@ -2,9 +2,7 @@ package woowacourse.shopping.data.repository import woowacourse.shopping.model.Product import woowacourse.shopping.repository.ProductRepository -import woowacourse.shopping.ui.common.di.qualifier.InMemoryProductRepositoryQualifier -@InMemoryProductRepositoryQualifier class DefaultProductRepository : ProductRepository { private val products: List = listOf( Product( diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt index b598855de..8ce76c7cf 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt @@ -3,5 +3,3 @@ package woowacourse.shopping.ui.common.di.qualifier annotation class InMemoryCartRepositoryQualifier annotation class DatabaseCartRepositoryQualifier - -annotation class InMemoryProductRepositoryQualifier diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index 6aa612b31..6d2035b4b 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -9,13 +9,10 @@ import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier -import woowacourse.shopping.ui.common.di.qualifier.InMemoryProductRepositoryQualifier class MainViewModel( - @InMemoryProductRepositoryQualifier private val productRepository: ProductRepository, - @InMemoryCartRepositoryQualifier - private val cartRepository: CartRepository, + @InMemoryCartRepositoryQualifier private val cartRepository: CartRepository, ) : ViewModel() { private val _products: MutableLiveData> = MutableLiveData(emptyList()) From f53467465e785e446d2ae2e06393ca6cbf18dba9 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 10:17:03 +0900 Subject: [PATCH 23/37] =?UTF-8?q?chore:=20di=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=9E=90=EB=B0=94=20=EB=B2=84=EC=A0=84=2011=EB=A1=9C=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- di/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/di/build.gradle.kts b/di/build.gradle.kts index 93d9be6fd..a15f81b7a 100644 --- a/di/build.gradle.kts +++ b/di/build.gradle.kts @@ -24,11 +24,11 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "11" } testOptions { unitTests.isIncludeAndroidResources = true From c61014ceeca90a21f20d9b754e90c8f8eb7c853d Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 10:41:23 +0900 Subject: [PATCH 24/37] =?UTF-8?q?refactor:=20di=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=95=88=EB=93=9C=EB=A1=9C=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 +- .../shopping/ShoppingApplication.kt | 4 +- .../shopping/ui/cart/CartActivity.kt | 2 +- .../ui/common/di/module/RepositoryModule.kt | 3 +- .../shopping/ui/main/MainActivity.kt | 2 +- .../util/viewModel/ActivityViewModelLazy.kt | 4 +- .../ui}/util/viewModel/ViewModelFactory.kt | 2 +- bunadi/build.gradle.kts | 1 - .../woowacourse/bunadi}/annotation/Inject.kt | 2 +- .../bunadi}/dsl/DependencyModuleDsl.kt | 6 +- .../bunadi}/dsl/DependencyTypeDsl.kt | 4 +- .../bunadi}/injector/DependencyInjector.kt | 14 +-- .../bunadi}/injector/DependencyKey.kt | 4 +- .../com/woowacourse/bunadi/module/Module.kt | 3 + .../bunadi}/util/ReflectionUtil.kt | 8 +- .../woowacourse/bunadi/util/core}/Cache.kt | 6 +- .../bunadi/util/core}/SubTypeConverter.kt | 4 +- di/.gitignore | 1 - di/build.gradle.kts | 50 ----------- di/consumer-rules.pro | 0 di/proguard-rules.pro | 21 ----- .../com/buna/di/ExampleInstrumentedTest.kt | 22 ----- di/src/main/AndroidManifest.xml | 4 - di/src/main/java/com/buna/di/module/Module.kt | 3 - .../di/injector/DependencyInjectorTest.kt | 90 ------------------- .../fakeClasses/FakeConstructorDependency.kt | 21 ----- .../fakeClasses/FakeFieldDependency.kt | 26 ------ settings.gradle.kts | 2 +- 28 files changed, 37 insertions(+), 274 deletions(-) rename {di/src/main/java/com/buna/di => app/src/main/java/woowacourse/shopping/ui}/util/viewModel/ActivityViewModelLazy.kt (80%) rename {di/src/main/java/com/buna/di => app/src/main/java/woowacourse/shopping/ui}/util/viewModel/ViewModelFactory.kt (88%) rename {di/src/main/java/com/buna/di => bunadi/src/main/java/com/woowacourse/bunadi}/annotation/Inject.kt (74%) rename {di/src/main/java/com/buna/di => bunadi/src/main/java/com/woowacourse/bunadi}/dsl/DependencyModuleDsl.kt (59%) rename {di/src/main/java/com/buna/di => bunadi/src/main/java/com/woowacourse/bunadi}/dsl/DependencyTypeDsl.kt (78%) rename {di/src/main/java/com/buna/di => bunadi/src/main/java/com/woowacourse/bunadi}/injector/DependencyInjector.kt (86%) rename {di/src/main/java/com/buna/di => bunadi/src/main/java/com/woowacourse/bunadi}/injector/DependencyKey.kt (94%) create mode 100644 bunadi/src/main/java/com/woowacourse/bunadi/module/Module.kt rename {di/src/main/java/com/buna/di => bunadi/src/main/java/com/woowacourse/bunadi}/util/ReflectionUtil.kt (81%) rename {di/src/main/java/com/buna/di/util/diAssistant => bunadi/src/main/java/com/woowacourse/bunadi/util/core}/Cache.kt (83%) rename {di/src/main/java/com/buna/di/util/diAssistant => bunadi/src/main/java/com/woowacourse/bunadi/util/core}/SubTypeConverter.kt (84%) delete mode 100644 di/.gitignore delete mode 100644 di/build.gradle.kts delete mode 100644 di/consumer-rules.pro delete mode 100644 di/proguard-rules.pro delete mode 100644 di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt delete mode 100644 di/src/main/AndroidManifest.xml delete mode 100644 di/src/main/java/com/buna/di/module/Module.kt delete mode 100644 di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt delete mode 100644 di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt delete mode 100644 di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e7961d5ef..aa30b51f6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -73,7 +73,7 @@ dependencies { testImplementation("io.mockk:mockk:1.13.5") androidTestImplementation("io.mockk:mockk-android:1.13.5") // DI Library - implementation(project(":di")) + implementation(project(":bunadi")) } kapt { diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index 1fb044e3d..fe6faf461 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -1,8 +1,8 @@ package woowacourse.shopping import android.app.Application -import com.buna.di.dsl.modules -import com.buna.di.dsl.types +import com.woowacourse.bunadi.dsl.modules +import com.woowacourse.bunadi.dsl.types import woowacourse.shopping.data.repository.DatabaseCartRepository import woowacourse.shopping.data.repository.DefaultProductRepository import woowacourse.shopping.data.repository.InMemoryCartRepository diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt index 225abb97b..3b5f992de 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartActivity.kt @@ -3,9 +3,9 @@ package woowacourse.shopping.ui.cart import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.buna.di.util.viewModel.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityCartBinding +import woowacourse.shopping.ui.util.viewModel.viewModel class CartActivity : AppCompatActivity() { private val binding by lazy { ActivityCartBinding.inflate(layoutInflater) } diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt index 9f430408f..03a880b19 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt @@ -2,11 +2,10 @@ package woowacourse.shopping.ui.common.di.module import android.content.Context import androidx.room.Room -import com.buna.di.module.Module import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.ShoppingDatabase -class RepositoryModule(private val context: Context) : Module { +class RepositoryModule(private val context: Context) : com.woowacourse.bunadi.module.Module { fun provideCartProductDao(): CartProductDao = Room.databaseBuilder( context, ShoppingDatabase::class.java, diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt index 77b77bfb4..f7085c5f3 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainActivity.kt @@ -5,10 +5,10 @@ import android.os.Bundle import android.view.Menu import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.buna.di.util.viewModel.viewModel import woowacourse.shopping.R import woowacourse.shopping.databinding.ActivityMainBinding import woowacourse.shopping.ui.cart.CartActivity +import woowacourse.shopping.ui.util.viewModel.viewModel class MainActivity : AppCompatActivity() { private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) } diff --git a/di/src/main/java/com/buna/di/util/viewModel/ActivityViewModelLazy.kt b/app/src/main/java/woowacourse/shopping/ui/util/viewModel/ActivityViewModelLazy.kt similarity index 80% rename from di/src/main/java/com/buna/di/util/viewModel/ActivityViewModelLazy.kt rename to app/src/main/java/woowacourse/shopping/ui/util/viewModel/ActivityViewModelLazy.kt index e520aaf87..39c5c724c 100644 --- a/di/src/main/java/com/buna/di/util/viewModel/ActivityViewModelLazy.kt +++ b/app/src/main/java/woowacourse/shopping/ui/util/viewModel/ActivityViewModelLazy.kt @@ -1,9 +1,9 @@ -package com.buna.di.util.viewModel +package woowacourse.shopping.ui.util.viewModel import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelLazy -import com.buna.di.injector.DependencyInjector +import com.woowacourse.bunadi.injector.DependencyInjector inline fun ComponentActivity.viewModel(): Lazy { return ViewModelLazy( diff --git a/di/src/main/java/com/buna/di/util/viewModel/ViewModelFactory.kt b/app/src/main/java/woowacourse/shopping/ui/util/viewModel/ViewModelFactory.kt similarity index 88% rename from di/src/main/java/com/buna/di/util/viewModel/ViewModelFactory.kt rename to app/src/main/java/woowacourse/shopping/ui/util/viewModel/ViewModelFactory.kt index 422a03674..93ef11153 100644 --- a/di/src/main/java/com/buna/di/util/viewModel/ViewModelFactory.kt +++ b/app/src/main/java/woowacourse/shopping/ui/util/viewModel/ViewModelFactory.kt @@ -1,4 +1,4 @@ -package com.buna.di.util.viewModel +package woowacourse.shopping.ui.util.viewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider diff --git a/bunadi/build.gradle.kts b/bunadi/build.gradle.kts index 1883501dc..68f3cf5e6 100644 --- a/bunadi/build.gradle.kts +++ b/bunadi/build.gradle.kts @@ -10,5 +10,4 @@ java { dependencies { implementation("org.jetbrains.kotlin", "kotlin-reflect", "1.8.21") - implementation("androidx.activity:activity-ktx:1.7.2") } diff --git a/di/src/main/java/com/buna/di/annotation/Inject.kt b/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Inject.kt similarity index 74% rename from di/src/main/java/com/buna/di/annotation/Inject.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/annotation/Inject.kt index f71aba90d..08a1c9e22 100644 --- a/di/src/main/java/com/buna/di/annotation/Inject.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Inject.kt @@ -1,4 +1,4 @@ -package com.buna.di.annotation +package com.woowacourse.bunadi.annotation @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) diff --git a/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt b/bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyModuleDsl.kt similarity index 59% rename from di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyModuleDsl.kt index d749f1f07..8f0cfe04f 100644 --- a/di/src/main/java/com/buna/di/dsl/DependencyModuleDsl.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyModuleDsl.kt @@ -1,7 +1,7 @@ -package com.buna.di.dsl +package com.woowacourse.bunadi.dsl -import com.buna.di.injector.DependencyInjector -import com.buna.di.module.Module +import com.woowacourse.bunadi.injector.DependencyInjector +import com.woowacourse.bunadi.module.Module class DependencyModuleDsl { fun module(module: Module) { diff --git a/di/src/main/java/com/buna/di/dsl/DependencyTypeDsl.kt b/bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyTypeDsl.kt similarity index 78% rename from di/src/main/java/com/buna/di/dsl/DependencyTypeDsl.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyTypeDsl.kt index e80f04617..1ea900075 100644 --- a/di/src/main/java/com/buna/di/dsl/DependencyTypeDsl.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyTypeDsl.kt @@ -1,6 +1,6 @@ -package com.buna.di.dsl +package com.woowacourse.bunadi.dsl -import com.buna.di.injector.DependencyInjector +import com.woowacourse.bunadi.injector.DependencyInjector import kotlin.reflect.KClass class DependencyTypeDsl { diff --git a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt similarity index 86% rename from di/src/main/java/com/buna/di/injector/DependencyInjector.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt index 450710b75..0c551e612 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyInjector.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt @@ -1,11 +1,11 @@ -package com.buna.di.injector +package com.woowacourse.bunadi.injector -import com.buna.di.annotation.Inject -import com.buna.di.module.Module -import com.buna.di.util.createInstance -import com.buna.di.util.diAssistant.Cache -import com.buna.di.util.diAssistant.SubTypeConverter -import com.buna.di.util.validateHasPrimaryConstructor +import com.woowacourse.bunadi.annotation.Inject +import com.woowacourse.bunadi.module.Module +import com.woowacourse.bunadi.util.createInstance +import com.woowacourse.bunadi.util.core.Cache +import com.woowacourse.bunadi.util.core.SubTypeConverter +import com.woowacourse.bunadi.util.validateHasPrimaryConstructor import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty import kotlin.reflect.full.declaredMemberFunctions diff --git a/di/src/main/java/com/buna/di/injector/DependencyKey.kt b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyKey.kt similarity index 94% rename from di/src/main/java/com/buna/di/injector/DependencyKey.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyKey.kt index 4066fa30f..e1761f0d0 100644 --- a/di/src/main/java/com/buna/di/injector/DependencyKey.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyKey.kt @@ -1,6 +1,6 @@ -package com.buna.di.injector +package com.woowacourse.bunadi.injector -import com.buna.di.annotation.Inject +import com.woowacourse.bunadi.annotation.Inject import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/module/Module.kt b/bunadi/src/main/java/com/woowacourse/bunadi/module/Module.kt new file mode 100644 index 000000000..3fedd0609 --- /dev/null +++ b/bunadi/src/main/java/com/woowacourse/bunadi/module/Module.kt @@ -0,0 +1,3 @@ +package com.woowacourse.bunadi.module + +interface Module diff --git a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt similarity index 81% rename from di/src/main/java/com/buna/di/util/ReflectionUtil.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt index 8cee53ef5..542d5fb11 100644 --- a/di/src/main/java/com/buna/di/util/ReflectionUtil.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt @@ -1,8 +1,8 @@ -package com.buna.di.util +package com.woowacourse.bunadi.util -import com.buna.di.injector.DependencyInjector -import com.buna.di.injector.DependencyKey -import com.buna.di.util.diAssistant.SubTypeConverter +import com.woowacourse.bunadi.injector.DependencyInjector +import com.woowacourse.bunadi.injector.DependencyKey +import com.woowacourse.bunadi.util.core.SubTypeConverter import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter diff --git a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt b/bunadi/src/main/java/com/woowacourse/bunadi/util/core/Cache.kt similarity index 83% rename from di/src/main/java/com/buna/di/util/diAssistant/Cache.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/util/core/Cache.kt index da0024664..eecd5c550 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/Cache.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/util/core/Cache.kt @@ -1,7 +1,7 @@ -package com.buna.di.util.diAssistant +package com.woowacourse.bunadi.util.core -import com.buna.di.injector.DependencyKey -import com.buna.di.module.Module +import com.woowacourse.bunadi.injector.DependencyKey +import com.woowacourse.bunadi.module.Module import kotlin.reflect.KFunction data class Cache( diff --git a/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt b/bunadi/src/main/java/com/woowacourse/bunadi/util/core/SubTypeConverter.kt similarity index 84% rename from di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt rename to bunadi/src/main/java/com/woowacourse/bunadi/util/core/SubTypeConverter.kt index 55fc563cd..ed605de04 100644 --- a/di/src/main/java/com/buna/di/util/diAssistant/SubTypeConverter.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/util/core/SubTypeConverter.kt @@ -1,6 +1,6 @@ -package com.buna.di.util.diAssistant +package com.woowacourse.bunadi.util.core -import com.buna.di.injector.DependencyKey +import com.woowacourse.bunadi.injector.DependencyKey import kotlin.reflect.KType data class SubTypeConverter( diff --git a/di/.gitignore b/di/.gitignore deleted file mode 100644 index 42afabfd2..000000000 --- a/di/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/di/build.gradle.kts b/di/build.gradle.kts deleted file mode 100644 index a15f81b7a..000000000 --- a/di/build.gradle.kts +++ /dev/null @@ -1,50 +0,0 @@ -plugins { - id("com.android.library") - id("org.jetbrains.kotlin.android") -} - -android { - namespace = "com.buna.di" - compileSdk = 33 - - defaultConfig { - minSdk = 26 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = "11" - } - testOptions { - unitTests.isIncludeAndroidResources = true - } -} - -dependencies { - - implementation("androidx.core:core-ktx:1.9.0") - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("com.google.android.material:material:1.9.0") - testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") - implementation("org.jetbrains.kotlin", "kotlin-reflect", "1.8.21") - implementation("androidx.activity:activity-ktx:1.7.2") - // Robolectric - testImplementation("org.robolectric:robolectric:4.9") -} diff --git a/di/consumer-rules.pro b/di/consumer-rules.pro deleted file mode 100644 index e69de29bb..000000000 diff --git a/di/proguard-rules.pro b/di/proguard-rules.pro deleted file mode 100644 index 481bb4348..000000000 --- a/di/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt b/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt deleted file mode 100644 index ed03249e7..000000000 --- a/di/src/androidTest/java/com/buna/di/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.buna.di - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.* -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.buna.di.test", appContext.packageName) - } -} diff --git a/di/src/main/AndroidManifest.xml b/di/src/main/AndroidManifest.xml deleted file mode 100644 index 44008a433..000000000 --- a/di/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/di/src/main/java/com/buna/di/module/Module.kt b/di/src/main/java/com/buna/di/module/Module.kt deleted file mode 100644 index c6d5df305..000000000 --- a/di/src/main/java/com/buna/di/module/Module.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.buna.di.module - -interface Module diff --git a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt b/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt deleted file mode 100644 index 05e866485..000000000 --- a/di/src/test/java/com/buna/di/injector/DependencyInjectorTest.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.buna.di.injector - -import com.buna.di.dsl.modules -import com.buna.di.dsl.types -import com.buna.di.injector.fakeClasses.AConstructorDependency -import com.buna.di.injector.fakeClasses.AFieldDependency -import com.buna.di.injector.fakeClasses.ConstructorDependency -import com.buna.di.injector.fakeClasses.ConstructorTestActivity -import com.buna.di.injector.fakeClasses.FieldDependency -import com.buna.di.injector.fakeClasses.FieldTestActivity -import junit.framework.TestCase.assertFalse -import junit.framework.TestCase.assertNotNull -import junit.framework.TestCase.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.Robolectric -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class DependencyInjectorTest { - - @Before - fun setup() { - DependencyInjector.clear() - } - - @Test - fun 뷰모델의_생성자_종속_항목을_자동_주입해준다() { - // given: 뷰모델 생성에 필요한 종속 항목을 주입한다. - modules { - types { - type(ConstructorDependency::class to AConstructorDependency::class) - } - } - - // when: 액티비티를 생성한다. - val activity = Robolectric.buildActivity(ConstructorTestActivity::class.java) - .create() - .get() - - // then: 액티비티의 뷰모델에 종속 항목이 주입되어 있다. - assertNotNull(activity.viewModel) - activity.viewModel.run { - assertTrue(constructorDependency is AConstructorDependency) - } - } - - @Test - fun 뷰모델에서_Inject_애노테이션이_붙은_필드에_종속_항목을_자동_주입해준다() { - // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. - modules { - types { - type(FieldDependency::class to AFieldDependency::class) - } - } - - // when: 액티비티를 생성한다. - val activity = Robolectric.buildActivity(FieldTestActivity::class.java) - .create() - .get() - - // then: Inject 애노테이션이 포함된 뷰모델의 필드에 종속 항목이 주입되어 있다. - assertNotNull(activity.viewModel) - activity.viewModel.run { - assertTrue(fieldWithInjectAnnotation is AFieldDependency) - } - } - - @Test - fun 뷰모델에서_Inject_애노테이션이_없는_필드에는_종속_항목을_주입하지_않는다() { - // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. - modules { - types { - type(FieldDependency::class to AFieldDependency::class) - } - } - - // when: 액티비티를 생성한다. - val activity = Robolectric.buildActivity(FieldTestActivity::class.java) - .create() - .get() - - // then: Inject 애노테이션이 포함된 뷰모델의 필드에 종속 항목이 주입되어 있다. - assertNotNull(activity.viewModel) - activity.viewModel.run { - assertFalse(isFieldWithoutInjectAnnotationInitialized()) - } - } -} diff --git a/di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt b/di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt deleted file mode 100644 index 3afdb5d8a..000000000 --- a/di/src/test/java/com/buna/di/injector/fakeClasses/FakeConstructorDependency.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.buna.di.injector.fakeClasses - -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.ViewModel -import com.buna.di.util.viewModel.viewModel - -annotation class AConstructorDependencyQualifier - -interface ConstructorDependency - -@AConstructorDependencyQualifier -class AConstructorDependency : ConstructorDependency - -class ConstructorTestActivity : AppCompatActivity() { - val viewModel: ConstructorTestViewModel by viewModel() -} - -class ConstructorTestViewModel( - @AConstructorDependencyQualifier - val constructorDependency: ConstructorDependency, -) : ViewModel() diff --git a/di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt b/di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt deleted file mode 100644 index ad80a43ec..000000000 --- a/di/src/test/java/com/buna/di/injector/fakeClasses/FakeFieldDependency.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.buna.di.injector.fakeClasses - -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.ViewModel -import com.buna.di.annotation.Inject -import com.buna.di.util.viewModel.viewModel - -annotation class AFieldDependencyQualifier - -interface FieldDependency - -@AFieldDependencyQualifier -class AFieldDependency : FieldDependency - -class FieldTestActivity : AppCompatActivity() { - val viewModel: FieldTestViewModel by viewModel() -} - -class FieldTestViewModel : ViewModel() { - @Inject - @AFieldDependencyQualifier - lateinit var fieldWithInjectAnnotation: FieldDependency - private lateinit var fieldWithoutInjectAnnotation: FieldDependency - - fun isFieldWithoutInjectAnnotationInitialized() = ::fieldWithoutInjectAnnotation.isInitialized -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 0ce4a5d38..baf59f811 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,4 +15,4 @@ dependencyResolutionManagement { rootProject.name = "android-di" include(":app") include(":domain") -include(":di") +include(":bunadi") From 0c51aaa574b342e526816496dc0130b04f6f38ce Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 12:07:35 +0900 Subject: [PATCH 25/37] =?UTF-8?q?test:=20DependencyInjectorTest=EB=A5=BC?= =?UTF-8?q?=20app=EB=AA=A8=EB=93=88=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/DependencyInjectorTest.kt | 88 +++++++++++++++++++ .../fakeClasses/FakeConstructorDependency.kt | 21 +++++ .../fakeClasses/FakeFieldDependency.kt | 26 ++++++ .../bunadi/injector/DependencyInjector.kt | 2 +- 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 app/src/test/java/woowacourse/DependencyInjectorTest.kt create mode 100644 app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt create mode 100644 app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt new file mode 100644 index 000000000..f99b7a7a1 --- /dev/null +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -0,0 +1,88 @@ +package woowacourse + +import com.buna.di.injector.fakeClasses.AConstructorDependency +import com.buna.di.injector.fakeClasses.AFieldDependency +import com.buna.di.injector.fakeClasses.ConstructorDependency +import com.buna.di.injector.fakeClasses.ConstructorTestActivity +import com.buna.di.injector.fakeClasses.FieldDependency +import com.buna.di.injector.fakeClasses.FieldTestActivity +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertNotNull +import junit.framework.TestCase.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class DependencyInjectorTest { + + @Before + fun setup() { + com.woowacourse.bunadi.injector.DependencyInjector.clear() + } + + @Test + fun 뷰모델의_생성자_종속_항목을_자동_주입해준다() { + // given: 뷰모델 생성에 필요한 종속 항목을 주입한다. + com.woowacourse.bunadi.dsl.modules { + com.woowacourse.bunadi.dsl.types { + type(ConstructorDependency::class to AConstructorDependency::class) + } + } + + // when: 액티비티를 생성한다. + val activity = Robolectric.buildActivity(ConstructorTestActivity::class.java) + .create() + .get() + + // then: 액티비티의 뷰모델에 종속 항목이 주입되어 있다. + assertNotNull(activity.viewModel) + activity.viewModel.run { + assertTrue(constructorDependency is AConstructorDependency) + } + } + + @Test + fun 뷰모델에서_Inject_애노테이션이_붙은_필드에_종속_항목을_자동_주입해준다() { + // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. + com.woowacourse.bunadi.dsl.modules { + com.woowacourse.bunadi.dsl.types { + type(FieldDependency::class to AFieldDependency::class) + } + } + + // when: 액티비티를 생성한다. + val activity = Robolectric.buildActivity(FieldTestActivity::class.java) + .create() + .get() + + // then: Inject 애노테이션이 포함된 뷰모델의 필드에 종속 항목이 주입되어 있다. + assertNotNull(activity.viewModel) + activity.viewModel.run { + assertTrue(fieldWithInjectAnnotation is AFieldDependency) + } + } + + @Test + fun 뷰모델에서_Inject_애노테이션이_없는_필드에는_종속_항목을_주입하지_않는다() { + // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. + com.woowacourse.bunadi.dsl.modules { + com.woowacourse.bunadi.dsl.types { + type(FieldDependency::class to AFieldDependency::class) + } + } + + // when: 액티비티를 생성한다. + val activity = Robolectric.buildActivity(FieldTestActivity::class.java) + .create() + .get() + + // then: Inject 애노테이션이 포함된 뷰모델의 필드에 종속 항목이 주입되어 있다. + assertNotNull(activity.viewModel) + activity.viewModel.run { + assertFalse(isFieldWithoutInjectAnnotationInitialized()) + } + } +} diff --git a/app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt b/app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt new file mode 100644 index 000000000..d2a5dbdb1 --- /dev/null +++ b/app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt @@ -0,0 +1,21 @@ +package com.buna.di.injector.fakeClasses + +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import woowacourse.shopping.ui.util.viewModel.viewModel + +annotation class AConstructorDependencyQualifier + +interface ConstructorDependency + +@AConstructorDependencyQualifier +class AConstructorDependency : ConstructorDependency + +class ConstructorTestActivity : AppCompatActivity() { + val viewModel: ConstructorTestViewModel by viewModel() +} + +class ConstructorTestViewModel( + @AConstructorDependencyQualifier + val constructorDependency: ConstructorDependency, +) : ViewModel() diff --git a/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt b/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt new file mode 100644 index 000000000..36af94f6b --- /dev/null +++ b/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt @@ -0,0 +1,26 @@ +package com.buna.di.injector.fakeClasses + +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import com.woowacourse.bunadi.annotation.Inject +import woowacourse.shopping.ui.util.viewModel.viewModel + +annotation class AFieldDependencyQualifier + +interface FieldDependency + +@AFieldDependencyQualifier +class AFieldDependency : FieldDependency + +class FieldTestActivity : AppCompatActivity() { + val viewModel: FieldTestViewModel by viewModel() +} + +class FieldTestViewModel : ViewModel() { + @com.woowacourse.bunadi.annotation.Inject + @AFieldDependencyQualifier + lateinit var fieldWithInjectAnnotation: FieldDependency + private lateinit var fieldWithoutInjectAnnotation: FieldDependency + + fun isFieldWithoutInjectAnnotationInitialized() = ::fieldWithoutInjectAnnotation.isInitialized +} diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt index 0c551e612..1569e23db 100644 --- a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt @@ -2,9 +2,9 @@ package com.woowacourse.bunadi.injector import com.woowacourse.bunadi.annotation.Inject import com.woowacourse.bunadi.module.Module -import com.woowacourse.bunadi.util.createInstance import com.woowacourse.bunadi.util.core.Cache import com.woowacourse.bunadi.util.core.SubTypeConverter +import com.woowacourse.bunadi.util.createInstance import com.woowacourse.bunadi.util.validateHasPrimaryConstructor import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty From c9176b57a4212237b2c44886c23a7cc4aefe0810 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 13:37:51 +0900 Subject: [PATCH 26/37] =?UTF-8?q?refactor:=20=EC=9D=BC=EB=8B=A8=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/InMemoryCartRepository.kt | 2 -- .../ui/common/di/qualifier/Qualifier.kt | 6 ++++++ .../shopping/ui/main/MainViewModel.kt | 4 ++-- .../bunadi/annotation/Qualifier.kt | 5 +++++ .../woowacourse/bunadi/util/ReflectionUtil.kt | 21 ++++++++++++++----- 5 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 bunadi/src/main/java/com/woowacourse/bunadi/annotation/Qualifier.kt diff --git a/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt index e15ce44d0..0e41ba6ed 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt @@ -6,9 +6,7 @@ import woowacourse.shopping.data.mapper.toEntity import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository -import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier -@InMemoryCartRepositoryQualifier class InMemoryCartRepository : CartRepository { private val cartProducts = mutableListOf() private var lastId: Long = 0 diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt index 8ce76c7cf..55d212eac 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt @@ -1,5 +1,11 @@ package woowacourse.shopping.ui.common.di.qualifier +import com.woowacourse.bunadi.annotation.Qualifier +import woowacourse.shopping.data.repository.InMemoryCartRepository + annotation class InMemoryCartRepositoryQualifier annotation class DatabaseCartRepositoryQualifier + +@Qualifier(InMemoryCartRepository::class) +annotation class InMemoryCartRepositoryQualifier2 diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index 6d2035b4b..0614e401a 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -8,11 +8,11 @@ import kotlinx.coroutines.launch import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository -import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier +import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier2 class MainViewModel( private val productRepository: ProductRepository, - @InMemoryCartRepositoryQualifier private val cartRepository: CartRepository, + @InMemoryCartRepositoryQualifier2 private val cartRepository: CartRepository, ) : ViewModel() { private val _products: MutableLiveData> = MutableLiveData(emptyList()) diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Qualifier.kt b/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Qualifier.kt new file mode 100644 index 000000000..285225f58 --- /dev/null +++ b/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Qualifier.kt @@ -0,0 +1,5 @@ +package com.woowacourse.bunadi.annotation + +import kotlin.reflect.KClass + +annotation class Qualifier(val clazz: KClass<*>) diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt index 542d5fb11..d3e3aba7f 100644 --- a/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt @@ -1,5 +1,6 @@ package com.woowacourse.bunadi.util +import com.woowacourse.bunadi.annotation.Qualifier import com.woowacourse.bunadi.injector.DependencyInjector import com.woowacourse.bunadi.injector.DependencyKey import com.woowacourse.bunadi.util.core.SubTypeConverter @@ -18,11 +19,21 @@ fun KFunction.createInstance(converter: SubTypeConverter): T { } fun List.createInstances(converter: SubTypeConverter): Array = map { parameter -> - val paramDependencyKey = DependencyKey.createDependencyKey(parameter) - val paramType = parameter.type - val subType = converter.convertType(paramDependencyKey, paramType) - val instance = DependencyInjector.inject(subType.jvmErasure) + val annotation = parameter.annotations.firstOrNull() + val qualifier = annotation?.annotationClass + val qualifier2 = qualifier?.annotations?.find { it is Qualifier } as? Qualifier + val realType = qualifier2?.clazz - DependencyInjector.caching(paramDependencyKey, instance) + val instance: Any? + if (realType != null) { + instance = DependencyInjector.inject(realType) + } else { + val paramDependencyKey = DependencyKey.createDependencyKey(parameter) + val paramType = parameter.type + val subType = converter.convertType(paramDependencyKey, paramType) + instance = DependencyInjector.inject(subType.jvmErasure) + + DependencyInjector.caching(paramDependencyKey, instance) + } instance }.toTypedArray() From 7f016fd86ed1f4587eb89880c1563c111072bf57 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 14:11:25 +0900 Subject: [PATCH 27/37] =?UTF-8?q?refactor:=20=EC=9D=BC=EB=8B=A8=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/ShoppingApplication.kt | 11 ---------- .../data/repository/DatabaseCartRepository.kt | 4 ++-- .../repository/DefaultProductRepository.kt | 2 ++ .../data/repository/InMemoryCartRepository.kt | 2 ++ .../shopping/ui/cart/CartViewModel.kt | 5 ++--- .../ui/common/di/qualifier/Qualifier.kt | 10 ++++++--- .../shopping/ui/main/MainViewModel.kt | 8 +++---- .../woowacourse/DependencyInjectorTest.kt | 12 +++++------ .../fakeClasses/FakeConstructorDependency.kt | 5 +++-- .../fakeClasses/FakeFieldDependency.kt | 11 +++++----- .../woowacourse/bunadi/annotation/Scope.kt | 3 +++ .../bunadi/injector/DependencyInjector.kt | 21 +++++++++++++++---- .../bunadi/injector/DependencyKey.kt | 2 +- .../woowacourse/bunadi/util/ReflectionUtil.kt | 12 +++++------ 14 files changed, 61 insertions(+), 47 deletions(-) create mode 100644 bunadi/src/main/java/com/woowacourse/bunadi/annotation/Scope.kt diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index fe6faf461..d03a6fe21 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -2,22 +2,11 @@ package woowacourse.shopping import android.app.Application import com.woowacourse.bunadi.dsl.modules -import com.woowacourse.bunadi.dsl.types -import woowacourse.shopping.data.repository.DatabaseCartRepository -import woowacourse.shopping.data.repository.DefaultProductRepository -import woowacourse.shopping.data.repository.InMemoryCartRepository -import woowacourse.shopping.repository.CartRepository -import woowacourse.shopping.repository.ProductRepository import woowacourse.shopping.ui.common.di.module.RepositoryModule class ShoppingApplication : Application() { override fun onCreate() { super.onCreate() - types { - type(ProductRepository::class to DefaultProductRepository::class) - type(CartRepository::class to InMemoryCartRepository::class) - type(CartRepository::class to DatabaseCartRepository::class) - } modules { module(RepositoryModule(this@ShoppingApplication)) } diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt index 029435df0..07875bb14 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DatabaseCartRepository.kt @@ -1,14 +1,14 @@ package woowacourse.shopping.data.repository +import com.woowacourse.bunadi.annotation.Singleton import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.mapper.toDomain import woowacourse.shopping.data.mapper.toEntity import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository -import woowacourse.shopping.ui.common.di.qualifier.DatabaseCartRepositoryQualifier -@DatabaseCartRepositoryQualifier +@Singleton class DatabaseCartRepository( private val dao: CartProductDao, ) : CartRepository { diff --git a/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt index 5fc51701b..1c5a7bdcc 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/DefaultProductRepository.kt @@ -1,8 +1,10 @@ package woowacourse.shopping.data.repository +import com.woowacourse.bunadi.annotation.Singleton import woowacourse.shopping.model.Product import woowacourse.shopping.repository.ProductRepository +@Singleton class DefaultProductRepository : ProductRepository { private val products: List = listOf( Product( diff --git a/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt index 0e41ba6ed..2bcd158fd 100644 --- a/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt +++ b/app/src/main/java/woowacourse/shopping/data/repository/InMemoryCartRepository.kt @@ -1,5 +1,6 @@ package woowacourse.shopping.data.repository +import com.woowacourse.bunadi.annotation.Singleton import woowacourse.shopping.data.CartProductEntity import woowacourse.shopping.data.mapper.toDomain import woowacourse.shopping.data.mapper.toEntity @@ -7,6 +8,7 @@ import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository +@Singleton class InMemoryCartRepository : CartRepository { private val cartProducts = mutableListOf() private var lastId: Long = 0 diff --git a/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt index 8cd7dda7f..31b165323 100644 --- a/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt @@ -7,11 +7,10 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch import woowacourse.shopping.model.CartProduct import woowacourse.shopping.repository.CartRepository -import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier +import woowacourse.shopping.ui.common.di.qualifier.DatabaseCartRepositoryQualifier class CartViewModel( - @InMemoryCartRepositoryQualifier - val cartRepository: CartRepository, + @DatabaseCartRepositoryQualifier val cartRepository: CartRepository, ) : ViewModel() { private val _cartProducts: MutableLiveData> = diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt index 55d212eac..bb57cae19 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/qualifier/Qualifier.kt @@ -1,11 +1,15 @@ package woowacourse.shopping.ui.common.di.qualifier import com.woowacourse.bunadi.annotation.Qualifier +import woowacourse.shopping.data.repository.DatabaseCartRepository +import woowacourse.shopping.data.repository.DefaultProductRepository import woowacourse.shopping.data.repository.InMemoryCartRepository +@Qualifier(DefaultProductRepository::class) +annotation class DefaultProductRepositoryQualifier + +@Qualifier(InMemoryCartRepository::class) annotation class InMemoryCartRepositoryQualifier +@Qualifier(DatabaseCartRepository::class) annotation class DatabaseCartRepositoryQualifier - -@Qualifier(InMemoryCartRepository::class) -annotation class InMemoryCartRepositoryQualifier2 diff --git a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt index 0614e401a..8b154ee0a 100644 --- a/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt +++ b/app/src/main/java/woowacourse/shopping/ui/main/MainViewModel.kt @@ -8,13 +8,13 @@ import kotlinx.coroutines.launch import woowacourse.shopping.model.Product import woowacourse.shopping.repository.CartRepository import woowacourse.shopping.repository.ProductRepository -import woowacourse.shopping.ui.common.di.qualifier.InMemoryCartRepositoryQualifier2 +import woowacourse.shopping.ui.common.di.qualifier.DatabaseCartRepositoryQualifier +import woowacourse.shopping.ui.common.di.qualifier.DefaultProductRepositoryQualifier class MainViewModel( - private val productRepository: ProductRepository, - @InMemoryCartRepositoryQualifier2 private val cartRepository: CartRepository, + @DefaultProductRepositoryQualifier private val productRepository: ProductRepository, + @DatabaseCartRepositoryQualifier private val cartRepository: CartRepository, ) : ViewModel() { - private val _products: MutableLiveData> = MutableLiveData(emptyList()) val products: LiveData> get() = _products diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index f99b7a7a1..4e42fd046 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -1,11 +1,11 @@ package woowacourse -import com.buna.di.injector.fakeClasses.AConstructorDependency -import com.buna.di.injector.fakeClasses.AFieldDependency -import com.buna.di.injector.fakeClasses.ConstructorDependency -import com.buna.di.injector.fakeClasses.ConstructorTestActivity -import com.buna.di.injector.fakeClasses.FieldDependency -import com.buna.di.injector.fakeClasses.FieldTestActivity +import woowacourse.fakeClasses.AConstructorDependency +import woowacourse.fakeClasses.AFieldDependency +import woowacourse.fakeClasses.ConstructorDependency +import woowacourse.fakeClasses.ConstructorTestActivity +import woowacourse.fakeClasses.FieldDependency +import woowacourse.fakeClasses.FieldTestActivity import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertTrue diff --git a/app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt b/app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt index d2a5dbdb1..53b41e33b 100644 --- a/app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt +++ b/app/src/test/java/woowacourse/fakeClasses/FakeConstructorDependency.kt @@ -1,14 +1,15 @@ -package com.buna.di.injector.fakeClasses +package woowacourse.fakeClasses import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel +import com.woowacourse.bunadi.annotation.Qualifier import woowacourse.shopping.ui.util.viewModel.viewModel +@Qualifier(AConstructorDependency::class) annotation class AConstructorDependencyQualifier interface ConstructorDependency -@AConstructorDependencyQualifier class AConstructorDependency : ConstructorDependency class ConstructorTestActivity : AppCompatActivity() { diff --git a/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt b/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt index 36af94f6b..585d985b2 100644 --- a/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt +++ b/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt @@ -1,23 +1,24 @@ -package com.buna.di.injector.fakeClasses +package woowacourse.fakeClasses import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel import com.woowacourse.bunadi.annotation.Inject +import com.woowacourse.bunadi.annotation.Qualifier import woowacourse.shopping.ui.util.viewModel.viewModel -annotation class AFieldDependencyQualifier - interface FieldDependency -@AFieldDependencyQualifier class AFieldDependency : FieldDependency +@Qualifier(AFieldDependency::class) +annotation class AFieldDependencyQualifier + class FieldTestActivity : AppCompatActivity() { val viewModel: FieldTestViewModel by viewModel() } class FieldTestViewModel : ViewModel() { - @com.woowacourse.bunadi.annotation.Inject + @Inject @AFieldDependencyQualifier lateinit var fieldWithInjectAnnotation: FieldDependency private lateinit var fieldWithoutInjectAnnotation: FieldDependency diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Scope.kt b/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Scope.kt new file mode 100644 index 000000000..70f037450 --- /dev/null +++ b/bunadi/src/main/java/com/woowacourse/bunadi/annotation/Scope.kt @@ -0,0 +1,3 @@ +package com.woowacourse.bunadi.annotation + +annotation class Singleton diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt index 1569e23db..ec68385c3 100644 --- a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt @@ -1,6 +1,8 @@ package com.woowacourse.bunadi.injector import com.woowacourse.bunadi.annotation.Inject +import com.woowacourse.bunadi.annotation.Qualifier +import com.woowacourse.bunadi.annotation.Singleton import com.woowacourse.bunadi.module.Module import com.woowacourse.bunadi.util.core.Cache import com.woowacourse.bunadi.util.core.SubTypeConverter @@ -20,13 +22,18 @@ object DependencyInjector { private val cache = Cache() fun inject(clazz: KClass): T { - val cached = cache[DependencyKey.createDependencyKey(clazz)] + val dependencyKey = DependencyKey.createDependencyKey(clazz) + val cached = cache[dependencyKey] if (cached != null) return cached as T val primaryConstructor = clazz.validateHasPrimaryConstructor() val dependency = primaryConstructor.createInstance(subTypeConverter) injectMemberProperties(clazz, dependency) + + if (clazz.hasAnnotation()) { + caching(dependencyKey, dependency) + } return dependency } @@ -38,10 +45,16 @@ object DependencyInjector { val dependencyKey = DependencyKey.createDependencyKey(property) val propertyType = property.returnType - val subType = subTypeConverter.convertType(dependencyKey, propertyType) - val propertyInstance = inject(subType.jvmErasure) - cache.caching(dependencyKey, propertyInstance) + val annotation = property.annotations.find { it !is Inject } + val qualifier = annotation?.annotationClass + val qualifier2 = qualifier?.annotations?.find { it is Qualifier } as? Qualifier + val realType = qualifier2?.clazz + val propertyInstance = inject(realType ?: propertyType.jvmErasure) + + if (clazz.hasAnnotation()) { + caching(dependencyKey, propertyInstance) + } property.setter.call(instance, propertyInstance) } } diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyKey.kt b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyKey.kt index e1761f0d0..0a9168dac 100644 --- a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyKey.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyKey.kt @@ -14,7 +14,7 @@ data class DependencyKey( ) { companion object { fun createDependencyKey(clazz: KClass<*>): DependencyKey { - val returnType = clazz.supertypes.getOrElse(0) { clazz.starProjectedType } + val returnType = clazz.starProjectedType val annotation = clazz.annotations.firstOrNull() return DependencyKey(returnType, annotation) diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt index d3e3aba7f..576b15e08 100644 --- a/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt @@ -25,15 +25,15 @@ fun List.createInstances(converter: SubTypeConverter): Array = val realType = qualifier2?.clazz val instance: Any? - if (realType != null) { - instance = DependencyInjector.inject(realType) + val paramDependencyKey = DependencyKey.createDependencyKey(parameter) + + instance = if (realType != null) { + DependencyInjector.inject(realType) } else { - val paramDependencyKey = DependencyKey.createDependencyKey(parameter) val paramType = parameter.type val subType = converter.convertType(paramDependencyKey, paramType) - instance = DependencyInjector.inject(subType.jvmErasure) - - DependencyInjector.caching(paramDependencyKey, instance) + DependencyInjector.inject(subType.jvmErasure) } + DependencyInjector.caching(paramDependencyKey, instance) instance }.toTypedArray() From 9a8ee710cdb92e5dc613a6b26ac0e89dca4fe18f Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 15:28:54 +0900 Subject: [PATCH 28/37] =?UTF-8?q?refactor:=20SubTypeProvider=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/ShoppingApplication.kt | 4 +- .../{RepositoryModule.kt => DaoModule.kt} | 3 +- .../bunadi/dsl/DependencyTypeDsl.kt | 16 -------- .../bunadi/injector/DependencyInjector.kt | 29 ++------------ .../woowacourse/bunadi/util/ReflectionUtil.kt | 38 ++++++++++++------- .../bunadi/util/core/SubTypeConverter.kt | 21 ---------- 6 files changed, 32 insertions(+), 79 deletions(-) rename app/src/main/java/woowacourse/shopping/ui/common/di/module/{RepositoryModule.kt => DaoModule.kt} (80%) delete mode 100644 bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyTypeDsl.kt delete mode 100644 bunadi/src/main/java/com/woowacourse/bunadi/util/core/SubTypeConverter.kt diff --git a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt index d03a6fe21..ed7faf4fd 100644 --- a/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt +++ b/app/src/main/java/woowacourse/shopping/ShoppingApplication.kt @@ -2,13 +2,13 @@ package woowacourse.shopping import android.app.Application import com.woowacourse.bunadi.dsl.modules -import woowacourse.shopping.ui.common.di.module.RepositoryModule +import woowacourse.shopping.ui.common.di.module.DaoModule class ShoppingApplication : Application() { override fun onCreate() { super.onCreate() modules { - module(RepositoryModule(this@ShoppingApplication)) + module(DaoModule(this@ShoppingApplication)) } } } diff --git a/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt b/app/src/main/java/woowacourse/shopping/ui/common/di/module/DaoModule.kt similarity index 80% rename from app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt rename to app/src/main/java/woowacourse/shopping/ui/common/di/module/DaoModule.kt index 03a880b19..be36e560e 100644 --- a/app/src/main/java/woowacourse/shopping/ui/common/di/module/RepositoryModule.kt +++ b/app/src/main/java/woowacourse/shopping/ui/common/di/module/DaoModule.kt @@ -2,10 +2,11 @@ package woowacourse.shopping.ui.common.di.module import android.content.Context import androidx.room.Room +import com.woowacourse.bunadi.module.Module import woowacourse.shopping.data.CartProductDao import woowacourse.shopping.data.ShoppingDatabase -class RepositoryModule(private val context: Context) : com.woowacourse.bunadi.module.Module { +class DaoModule(private val context: Context) : Module { fun provideCartProductDao(): CartProductDao = Room.databaseBuilder( context, ShoppingDatabase::class.java, diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyTypeDsl.kt b/bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyTypeDsl.kt deleted file mode 100644 index 1ea900075..000000000 --- a/bunadi/src/main/java/com/woowacourse/bunadi/dsl/DependencyTypeDsl.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.woowacourse.bunadi.dsl - -import com.woowacourse.bunadi.injector.DependencyInjector -import kotlin.reflect.KClass - -class DependencyTypeDsl { - fun type(pairClass: Pair, KClass<*>>) { - val superClass = pairClass.first - val subClass = pairClass.second - DependencyInjector.type(superClass, subClass) - } -} - -fun types(block: DependencyTypeDsl.() -> Unit) { - DependencyTypeDsl().block() -} diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt index ec68385c3..4088629c0 100644 --- a/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/injector/DependencyInjector.kt @@ -1,24 +1,21 @@ package com.woowacourse.bunadi.injector import com.woowacourse.bunadi.annotation.Inject -import com.woowacourse.bunadi.annotation.Qualifier import com.woowacourse.bunadi.annotation.Singleton import com.woowacourse.bunadi.module.Module import com.woowacourse.bunadi.util.core.Cache -import com.woowacourse.bunadi.util.core.SubTypeConverter import com.woowacourse.bunadi.util.createInstance +import com.woowacourse.bunadi.util.parseFromQualifier import com.woowacourse.bunadi.util.validateHasPrimaryConstructor import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty import kotlin.reflect.full.declaredMemberFunctions import kotlin.reflect.full.hasAnnotation import kotlin.reflect.full.memberProperties -import kotlin.reflect.full.starProjectedType import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.jvmErasure object DependencyInjector { - private val subTypeConverter = SubTypeConverter() private val cache = Cache() fun inject(clazz: KClass): T { @@ -27,8 +24,7 @@ object DependencyInjector { if (cached != null) return cached as T val primaryConstructor = clazz.validateHasPrimaryConstructor() - val dependency = primaryConstructor.createInstance(subTypeConverter) - + val dependency = primaryConstructor.createInstance() injectMemberProperties(clazz, dependency) if (clazz.hasAnnotation()) { @@ -44,13 +40,8 @@ object DependencyInjector { property.isAccessible = true val dependencyKey = DependencyKey.createDependencyKey(property) - val propertyType = property.returnType - - val annotation = property.annotations.find { it !is Inject } - val qualifier = annotation?.annotationClass - val qualifier2 = qualifier?.annotations?.find { it is Qualifier } as? Qualifier - val realType = qualifier2?.clazz - val propertyInstance = inject(realType ?: propertyType.jvmErasure) + val realType = property.parseFromQualifier() + val propertyInstance = inject(realType ?: property.returnType.jvmErasure) if (clazz.hasAnnotation()) { caching(dependencyKey, propertyInstance) @@ -64,23 +55,11 @@ object DependencyInjector { providers.forEach { provider -> cache.caching(module, provider) } } - fun type(superClass: KClass<*>, subClass: KClass<*>) { - val superType = superClass.starProjectedType - val annotation = subClass.annotations.firstOrNull() - - val dependencyKey = DependencyKey(superType, annotation) - val subType = subClass.starProjectedType - - subTypeConverter.saveType(dependencyKey, subType) - cache.caching(dependencyKey) - } - fun caching(dependencyKey: DependencyKey, dependency: Any? = null) { cache.caching(dependencyKey, dependency) } fun clear() { - subTypeConverter.clear() cache.clear() } } diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt index 576b15e08..fcefff56a 100644 --- a/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt +++ b/bunadi/src/main/java/com/woowacourse/bunadi/util/ReflectionUtil.kt @@ -1,12 +1,13 @@ package com.woowacourse.bunadi.util +import com.woowacourse.bunadi.annotation.Inject import com.woowacourse.bunadi.annotation.Qualifier import com.woowacourse.bunadi.injector.DependencyInjector import com.woowacourse.bunadi.injector.DependencyKey -import com.woowacourse.bunadi.util.core.SubTypeConverter import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter +import kotlin.reflect.KProperty import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.jvmErasure @@ -14,26 +15,35 @@ fun KClass.validateHasPrimaryConstructor(): KFunction { return requireNotNull(primaryConstructor) { "[ERROR] 주생성자가 존재하지 않습니다." } } -fun KFunction.createInstance(converter: SubTypeConverter): T { - return call(*parameters.createInstances(converter)) +fun KFunction.createInstance(): T { + return call(*parameters.createInstances()) } -fun List.createInstances(converter: SubTypeConverter): Array = map { parameter -> - val annotation = parameter.annotations.firstOrNull() - val qualifier = annotation?.annotationClass - val qualifier2 = qualifier?.annotations?.find { it is Qualifier } as? Qualifier - val realType = qualifier2?.clazz - - val instance: Any? +fun List.createInstances(): Array = map { parameter -> + val realType = parameter.parseFromQualifier() val paramDependencyKey = DependencyKey.createDependencyKey(parameter) - instance = if (realType != null) { + val instance = if (realType != null) { DependencyInjector.inject(realType) } else { - val paramType = parameter.type - val subType = converter.convertType(paramDependencyKey, paramType) - DependencyInjector.inject(subType.jvmErasure) + DependencyInjector.inject(parameter.type.jvmErasure) } DependencyInjector.caching(paramDependencyKey, instance) instance }.toTypedArray() + +fun KParameter.parseFromQualifier(): KClass<*>? { + val annotation = annotations.firstOrNull() + return annotation?.parseClassInQualifier() +} + +fun KProperty<*>.parseFromQualifier(): KClass<*>? { + val annotation = annotations.find { it !is Inject } + return annotation?.parseClassInQualifier() +} + +private fun Annotation.parseClassInQualifier(): KClass<*>? { + val annotation = annotationClass + val qualifier = annotation.annotations.find { it is Qualifier } as? Qualifier + return qualifier?.clazz +} diff --git a/bunadi/src/main/java/com/woowacourse/bunadi/util/core/SubTypeConverter.kt b/bunadi/src/main/java/com/woowacourse/bunadi/util/core/SubTypeConverter.kt deleted file mode 100644 index ed605de04..000000000 --- a/bunadi/src/main/java/com/woowacourse/bunadi/util/core/SubTypeConverter.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.woowacourse.bunadi.util.core - -import com.woowacourse.bunadi.injector.DependencyKey -import kotlin.reflect.KType - -data class SubTypeConverter( - private val converter: MutableMap = mutableMapOf(), -) { - fun saveType(parentTypeKey: DependencyKey, childType: KType) { - converter[parentTypeKey] = childType - } - - fun convertType(parentTypeKey: DependencyKey, defaultType: KType): KType { - return converter[parentTypeKey] ?: defaultType - } - - fun clear(): SubTypeConverter { - converter.clear() - return copy(converter = mutableMapOf()) - } -} From c28d68fa69803cec01bfa3d30eccf8616f91001f Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 15:34:42 +0900 Subject: [PATCH 29/37] =?UTF-8?q?test:=20DependencyInjector=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/DependencyInjectorTest.kt | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index 4e42fd046..2c6171368 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -1,11 +1,6 @@ package woowacourse -import woowacourse.fakeClasses.AConstructorDependency -import woowacourse.fakeClasses.AFieldDependency -import woowacourse.fakeClasses.ConstructorDependency -import woowacourse.fakeClasses.ConstructorTestActivity -import woowacourse.fakeClasses.FieldDependency -import woowacourse.fakeClasses.FieldTestActivity +import com.woowacourse.bunadi.injector.DependencyInjector import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertTrue @@ -14,23 +9,22 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner +import woowacourse.fakeClasses.AConstructorDependency +import woowacourse.fakeClasses.AFieldDependency +import woowacourse.fakeClasses.ConstructorTestActivity +import woowacourse.fakeClasses.FieldTestActivity @RunWith(RobolectricTestRunner::class) class DependencyInjectorTest { @Before fun setup() { - com.woowacourse.bunadi.injector.DependencyInjector.clear() + DependencyInjector.clear() } @Test fun 뷰모델의_생성자_종속_항목을_자동_주입해준다() { // given: 뷰모델 생성에 필요한 종속 항목을 주입한다. - com.woowacourse.bunadi.dsl.modules { - com.woowacourse.bunadi.dsl.types { - type(ConstructorDependency::class to AConstructorDependency::class) - } - } // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(ConstructorTestActivity::class.java) @@ -47,11 +41,6 @@ class DependencyInjectorTest { @Test fun 뷰모델에서_Inject_애노테이션이_붙은_필드에_종속_항목을_자동_주입해준다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. - com.woowacourse.bunadi.dsl.modules { - com.woowacourse.bunadi.dsl.types { - type(FieldDependency::class to AFieldDependency::class) - } - } // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(FieldTestActivity::class.java) @@ -68,11 +57,6 @@ class DependencyInjectorTest { @Test fun 뷰모델에서_Inject_애노테이션이_없는_필드에는_종속_항목을_주입하지_않는다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. - com.woowacourse.bunadi.dsl.modules { - com.woowacourse.bunadi.dsl.types { - type(FieldDependency::class to AFieldDependency::class) - } - } // when: 액티비티를 생성한다. val activity = Robolectric.buildActivity(FieldTestActivity::class.java) From 0b05e6eaed2b5404478a3c0926e86a23cc0281af Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 16:44:06 +0900 Subject: [PATCH 30/37] =?UTF-8?q?test:=20DependencyInjector=20=EC=9E=AC?= =?UTF-8?q?=EA=B7=80=20=EC=A3=BC=EC=9E=85=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 + .../woowacourse/DependencyInjectorTest.kt | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index aa30b51f6..4155a3b7b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -74,6 +74,8 @@ dependencies { androidTestImplementation("io.mockk:mockk-android:1.13.5") // DI Library implementation(project(":bunadi")) + // Reflection + implementation(kotlin("reflect")) } kapt { diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index 2c6171368..fef32e12d 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -1,6 +1,10 @@ package woowacourse +import com.woowacourse.bunadi.annotation.Qualifier +import com.woowacourse.bunadi.annotation.Singleton import com.woowacourse.bunadi.injector.DependencyInjector +import com.woowacourse.bunadi.injector.DependencyKey +import com.woowacourse.bunadi.util.core.Cache import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertTrue @@ -13,6 +17,9 @@ import woowacourse.fakeClasses.AConstructorDependency import woowacourse.fakeClasses.AFieldDependency import woowacourse.fakeClasses.ConstructorTestActivity import woowacourse.fakeClasses.FieldTestActivity +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.full.starProjectedType +import kotlin.reflect.jvm.isAccessible @RunWith(RobolectricTestRunner::class) class DependencyInjectorTest { @@ -69,4 +76,41 @@ class DependencyInjectorTest { assertFalse(isFieldWithoutInjectAnnotationInitialized()) } } + + // given: 클래스의 프로퍼티도 프로퍼티가 필요하다. + @Qualifier(Inner::class) + annotation class InnerQualifier + + @Qualifier(NestedInner::class) + annotation class NestedInnerQualifier + + @Singleton + class Outer(@InnerQualifier val inner: Inner) + class Inner(@NestedInnerQualifier val realInner: NestedInner) + class NestedInner + + @Test + fun 클래스에_프로퍼티가_재귀적으로_존재하면_재귀적으로_주입하여_생성한다() { + // given: 클래스를 재귀적으로 생성할 수 있는 종속 항목을 주입한다. + val cache = getDependencyInjectorCache() + val outerKey = DependencyKey.createDependencyKey(Outer::class) + + // when: 주입했던 클래스를 받아온다. + val actual = DependencyInjector.inject(Outer::class) + val expected = cache[outerKey] + + // then: 주입했던 클래스를 재귀적으로 생성하여 반환한다. + assert(actual == expected) + } + + private fun getDependencyInjectorCache(): Cache { + val cacheProperty = DependencyInjector::class + .declaredMemberProperties + .find { it.returnType == Cache::class.starProjectedType } + ?.also { it.isAccessible = true } + ?: throw IllegalStateException("[ERROR] 캐시가 없습니다.") + + return cacheProperty.call() as? Cache + ?: throw IllegalStateException("[ERROR] 캐시를 생성할 수 없습니다.") + } } From a740717f807a684041164ded7dc23a88367ce48f Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 16:51:09 +0900 Subject: [PATCH 31/37] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=ED=95=A8=EC=88=98=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/test/java/woowacourse/DependencyInjectorTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index fef32e12d..12df3fcbb 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -46,7 +46,7 @@ class DependencyInjectorTest { } @Test - fun 뷰모델에서_Inject_애노테이션이_붙은_필드에_종속_항목을_자동_주입해준다() { + fun 클래스_멤버_프로퍼티에_Inject_애노테이션이_있으면_종송_항목을_주입한다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. // when: 액티비티를 생성한다. @@ -62,7 +62,7 @@ class DependencyInjectorTest { } @Test - fun 뷰모델에서_Inject_애노테이션이_없는_필드에는_종속_항목을_주입하지_않는다() { + fun 클래스_멤버_프로퍼티에_Inject_애노테이션이_없으면_종속_항목을_주입하지_않는다() { // given: 뷰모델 생성과 필드에 필요한 종속 항목을 주입한다. // when: 액티비티를 생성한다. From f24e3086a1afb3c73bde6ff904a78e23aae74b47 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 16:54:52 +0900 Subject: [PATCH 32/37] =?UTF-8?q?test:=20@Inject=EC=9D=B4=20=EB=B6=99?= =?UTF-8?q?=EC=9D=80=20=EB=A9=A4=EB=B2=84=EA=B0=80=20=EB=B3=B5=EC=88=98?= =?UTF-8?q?=EA=B0=9C=EC=97=AC=EB=8F=84=20=EC=A0=95=EC=83=81=20=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=EB=90=98=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/DependencyInjectorTest.kt | 28 +++++++++++++++++++ .../fakeClasses/FakeFieldDependency.kt | 4 +++ 2 files changed, 32 insertions(+) diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index 12df3fcbb..80d524cab 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -1,5 +1,6 @@ package woowacourse +import com.woowacourse.bunadi.annotation.Inject import com.woowacourse.bunadi.annotation.Qualifier import com.woowacourse.bunadi.annotation.Singleton import com.woowacourse.bunadi.injector.DependencyInjector @@ -15,6 +16,9 @@ import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import woowacourse.fakeClasses.AConstructorDependency import woowacourse.fakeClasses.AFieldDependency +import woowacourse.fakeClasses.AFieldDependencyQualifier +import woowacourse.fakeClasses.BFieldDependency +import woowacourse.fakeClasses.BFieldDependencyQualifier import woowacourse.fakeClasses.ConstructorTestActivity import woowacourse.fakeClasses.FieldTestActivity import kotlin.reflect.full.declaredMemberProperties @@ -77,6 +81,30 @@ class DependencyInjectorTest { } } + @Test + fun 클래스_멤버_프로퍼티가_복수개여도_종속_항목을_주입한다() { + // given: 멤버 프로퍼티가 2개인 클래스가 존재한다. + class TwoMemberPropertyClass { + @Inject + @AFieldDependencyQualifier + private lateinit var first: AFieldDependency + + @Inject + @BFieldDependencyQualifier + private lateinit var second: BFieldDependency + + fun isAllMemberPropertyInitialized(): Boolean { + return ::first.isInitialized && ::second.isInitialized + } + } + + // when: 클래스를 주입한다. + val actual = DependencyInjector.inject(TwoMemberPropertyClass::class) + + // then: 멤버 프로퍼티가 모두 주입되어 있다. + assertTrue(actual.isAllMemberPropertyInitialized()) + } + // given: 클래스의 프로퍼티도 프로퍼티가 필요하다. @Qualifier(Inner::class) annotation class InnerQualifier diff --git a/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt b/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt index 585d985b2..69d24f39c 100644 --- a/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt +++ b/app/src/test/java/woowacourse/fakeClasses/FakeFieldDependency.kt @@ -9,10 +9,14 @@ import woowacourse.shopping.ui.util.viewModel.viewModel interface FieldDependency class AFieldDependency : FieldDependency +class BFieldDependency : FieldDependency @Qualifier(AFieldDependency::class) annotation class AFieldDependencyQualifier +@Qualifier(BFieldDependency::class) +annotation class BFieldDependencyQualifier + class FieldTestActivity : AppCompatActivity() { val viewModel: FieldTestViewModel by viewModel() } From 5d2f67eba9fe7346811c26f683d0d56d7d9f535d Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 16:59:09 +0900 Subject: [PATCH 33/37] =?UTF-8?q?test:=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=20=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0?= =?UTF-8?q?=EA=B0=80=20=EB=B3=B5=EC=88=98=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?Inject=20=EC=95=A0=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=EC=9D=B4?= =?UTF-8?q?=20=EC=97=86=EB=8A=94=20=EB=A9=A4=EB=B2=84=EC=97=90=20=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EA=B2=83?= =?UTF-8?q?=EC=9D=84=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/DependencyInjectorTest.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index 80d524cab..f63d59dc3 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -11,6 +11,7 @@ import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertTrue import org.junit.Before import org.junit.Test +import org.junit.jupiter.api.assertThrows import org.junit.runner.RunWith import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner @@ -105,6 +106,34 @@ class DependencyInjectorTest { assertTrue(actual.isAllMemberPropertyInitialized()) } + @Test + fun 클래스_멤버_프로퍼티가_복수일_때_Inject_애노테이션이_없는_멤버는_주입하지_않는다() { + // given: 멤버 프로퍼티가 2개인 클래스가 존재한다. + class TwoMemberPropertyClass { + @Inject + @AFieldDependencyQualifier + private lateinit var first: AFieldDependency + + @BFieldDependencyQualifier + private lateinit var second: BFieldDependency + + fun isFirstInitialized(): Boolean { + return ::first.isInitialized + } + + fun isSecondInitialized(): Boolean { + return ::second.isInitialized + } + } + + // when: 클래스를 주입한다. + val twoMemberClass = DependencyInjector.inject(TwoMemberPropertyClass::class) + + // then: second 멤버는 초기화되어 있지 않다. + assertTrue(twoMemberClass.isFirstInitialized()) + assertFalse(twoMemberClass.isSecondInitialized()) + } + // given: 클래스의 프로퍼티도 프로퍼티가 필요하다. @Qualifier(Inner::class) annotation class InnerQualifier From 76b76931a3e272a68c5b02e3ea77da62c89350b4 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 17:02:20 +0900 Subject: [PATCH 34/37] =?UTF-8?q?test:=20=EC=83=9D=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EC=A3=BC=EC=9E=85=20=EC=8B=A4=ED=8C=A8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=9E=91=EC=84=B1(?= =?UTF-8?q?=EC=8B=9D=EB=B3=84=EC=9E=90=20=EC=95=A0=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/woowacourse/DependencyInjectorTest.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index f63d59dc3..50444d596 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -21,7 +21,9 @@ import woowacourse.fakeClasses.AFieldDependencyQualifier import woowacourse.fakeClasses.BFieldDependency import woowacourse.fakeClasses.BFieldDependencyQualifier import woowacourse.fakeClasses.ConstructorTestActivity +import woowacourse.fakeClasses.FieldDependency import woowacourse.fakeClasses.FieldTestActivity +import java.lang.IllegalArgumentException import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.starProjectedType import kotlin.reflect.jvm.isAccessible @@ -170,4 +172,20 @@ class DependencyInjectorTest { return cacheProperty.call() as? Cache ?: throw IllegalStateException("[ERROR] 캐시를 생성할 수 없습니다.") } + + interface Dependency + + @Test + fun 인터페이스_타입인_프로퍼티에_식별자_애노테이션을_추가하지_않으면_주입할_때_예외가_발생하다() { + // given: 인터페이스 타입을 생성자로 프로퍼티를 가진 클래스가 존재한다. + class InterfacePropertyClass( + private val dependency: Dependency, + ) + + // when: 클래스를 주입한다. + // then: 식별자 애노테이션이 없으므로 예외가 발생한다. + assertThrows { + DependencyInjector.inject(InterfacePropertyClass::class) + } + } } From 617ae3671014a1f8818cefa51f2e396652f90e6f Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 17:02:50 +0900 Subject: [PATCH 35/37] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=8F=AC=EB=A7=B7=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/test/java/woowacourse/DependencyInjectorTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index 50444d596..4b0ff67a2 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -21,7 +21,6 @@ import woowacourse.fakeClasses.AFieldDependencyQualifier import woowacourse.fakeClasses.BFieldDependency import woowacourse.fakeClasses.BFieldDependencyQualifier import woowacourse.fakeClasses.ConstructorTestActivity -import woowacourse.fakeClasses.FieldDependency import woowacourse.fakeClasses.FieldTestActivity import java.lang.IllegalArgumentException import kotlin.reflect.full.declaredMemberProperties From 29ededf3e59b4e716acfe29408e54d999126f128 Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 17:09:15 +0900 Subject: [PATCH 36/37] =?UTF-8?q?refactor:=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=ED=83=80=EC=9E=85=EC=9D=B8=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=EC=97=90=20=EC=8B=9D=EB=B3=84=EC=9E=90=20=EC=95=A0?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=EC=9D=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=A7=80=20=EC=95=8A=EC=95=98=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=EC=9D=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/woowacourse/DependencyInjectorTest.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/woowacourse/DependencyInjectorTest.kt b/app/src/test/java/woowacourse/DependencyInjectorTest.kt index 4b0ff67a2..63cacdcd0 100644 --- a/app/src/test/java/woowacourse/DependencyInjectorTest.kt +++ b/app/src/test/java/woowacourse/DependencyInjectorTest.kt @@ -22,7 +22,6 @@ import woowacourse.fakeClasses.BFieldDependency import woowacourse.fakeClasses.BFieldDependencyQualifier import woowacourse.fakeClasses.ConstructorTestActivity import woowacourse.fakeClasses.FieldTestActivity -import java.lang.IllegalArgumentException import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.starProjectedType import kotlin.reflect.jvm.isAccessible @@ -187,4 +186,19 @@ class DependencyInjectorTest { DependencyInjector.inject(InterfacePropertyClass::class) } } + + @Test + fun 인터페이스_타입인_필드에_식별자_애노테이션을_추가하지_않으면_주입할_성_예외가_발생한다() { + // given: 인터페이스 타입을 필드로 가진 클래스가 존재한다. + class InterfaceFieldClass { + @Inject + private lateinit var dependency: Dependency + } + + // when: 클래스를 주입한다. + // then: 인터페이스 타입인 필드에 식별자 애노테이션이 없으므로 예외가 발생한다. + assertThrows { + DependencyInjector.inject(InterfaceFieldClass::class) + } + } } From 87e85530fc122ca2c137a581072ea466d056c8fe Mon Sep 17 00:00:00 2001 From: buna Date: Wed, 13 Sep 2023 23:35:20 +0900 Subject: [PATCH 37/37] =?UTF-8?q?refactor:=20CartProductEntity=EB=A5=BC=20?= =?UTF-8?q?mapping=ED=95=A0=20=EB=95=8C=20=EC=9D=B8=EC=9E=90=EB=AA=85?= =?UTF-8?q?=EC=9D=84=20=EC=A7=80=EC=A0=95=ED=95=98=EC=97=AC=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shopping/data/mapper/CartMapper.kt | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt b/app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt index 40707be38..2ea07ef1a 100644 --- a/app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt +++ b/app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt @@ -4,24 +4,20 @@ import woowacourse.shopping.data.CartProductEntity import woowacourse.shopping.model.CartProduct import woowacourse.shopping.model.Product -fun Product.toEntity(): CartProductEntity { - return CartProductEntity( - name = name, - price = price, - imageUrl = imageUrl, - ) -} +fun Product.toEntity(): CartProductEntity = CartProductEntity( + name = name, + price = price, + imageUrl = imageUrl, +) -fun List.toDomain(): List { - return map { - CartProduct( - product = Product( - name = it.name, - price = it.price, - imageUrl = it.imageUrl, - ), - id = it.id, - createdAt = it.createdAt, - ) - } +fun List.toDomain(): List = map { cartProductEntity -> + CartProduct( + product = Product( + name = cartProductEntity.name, + price = cartProductEntity.price, + imageUrl = cartProductEntity.imageUrl, + ), + id = cartProductEntity.id, + createdAt = cartProductEntity.createdAt, + ) }