Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[둘리] 2, 3단계 자동 DI 미션 제출합니다 #28

Merged
merged 57 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
168589f
docs: 요구사항 업데이트
hyemdooly Sep 6, 2023
87d3c2a
docs: 요구사항 업데이트
hyemdooly Sep 7, 2023
657518e
feat: 요구사항에 맞게 CartRepository 변경
hyemdooly Sep 7, 2023
1bdceaf
feat: CartProduct 생성
hyemdooly Sep 7, 2023
5e94a5e
rename: Repository 네이밍 변경
hyemdooly Sep 7, 2023
7ab7e4e
feat: Room 사용 구현
hyemdooly Sep 7, 2023
04ac319
refactor: Inject 로직 분리
hyemdooly Sep 7, 2023
c75db5c
refactor: Inject 로직 분리
hyemdooly Sep 7, 2023
f483bbb
refactor: viewModelFactory 수정
hyemdooly Sep 8, 2023
d2744a7
feat: Field 주입에 사용할 Annotation 추가
hyemdooly Sep 8, 2023
c6393e6
refactor: getInstance Nullable 허용
hyemdooly Sep 8, 2023
8b64b40
refactor: Field 주입으로 변경
hyemdooly Sep 8, 2023
ffa5c7b
feat: Recursive DI 구현
hyemdooly Sep 8, 2023
2254b53
feat: Recursive DI, Field Injection 적용
hyemdooly Sep 8, 2023
8d57300
docs: 요구사항 적용 여부 업데이트
hyemdooly Sep 8, 2023
e2b9366
test: Injector Test 작성
hyemdooly Sep 8, 2023
62465b1
feat: 상품의 position과 CartRepository::deleteCartProduct의 id가 동일한 값임을 보장…
hyemdooly Sep 8, 2023
559b93f
feat: 구분을 위한 추가 Annotation 생성
hyemdooly Sep 8, 2023
f6c41a8
feat: ViewModel 프로퍼티에 annotations 지정
hyemdooly Sep 8, 2023
c0ee682
refactor: 필요없는 괄호 삭제
hyemdooly Sep 8, 2023
38725ee
feat: injector annotation 고려
hyemdooly Sep 8, 2023
55308d0
feat: InMemory, Database 레포지토리 구현
hyemdooly Sep 8, 2023
a80f744
feat: 바뀐 요구사항에 맞게 주입 수정
hyemdooly Sep 8, 2023
15b4fe6
feat: annotation 고려를 위한 instances 수정
hyemdooly Sep 8, 2023
37d31ed
test: 바뀐 코드에 맞게 test 수정
hyemdooly Sep 8, 2023
7f5c22d
docs: 요구사항 충족 여부 수정
hyemdooly Sep 8, 2023
368e9c9
rename: DI 모듈화
hyemdooly Sep 8, 2023
83f960e
docs: 모듈화 충족 여부 수정
hyemdooly Sep 8, 2023
6f0dcf2
test: ViewModel 주입 테스트 수정
hyemdooly Sep 8, 2023
d66108d
test: di module에 InjectorTest 구현
hyemdooly Sep 8, 2023
5abddb2
test: 액티비티 뷰모델 주입 테스트 보완
hyemdooly Sep 8, 2023
b9ebfdf
feat: DI 배포
hyemdooly Sep 8, 2023
0b4f3a1
feat: DI 배포
hyemdooly Sep 8, 2023
2498638
refactor: constructor 제거
hyemdooly Sep 10, 2023
9eecad2
deploy: DI 배포 수정
hyemdooly Sep 11, 2023
8a46ecd
deploy: DI 배포 수정
hyemdooly Sep 11, 2023
8fca93c
refactor: di 버전 수정
hyemdooly Sep 11, 2023
a636e8c
refactor: Qualifier 수정, DI 수정
hyemdooly Sep 13, 2023
7184501
refactor: println() 삭제
hyemdooly Sep 13, 2023
7443924
deploy: publishing 옵션 변경
hyemdooly Sep 13, 2023
353d48b
refactor: implementation 추가
hyemdooly Sep 13, 2023
bbb76a5
deploy: di release
hyemdooly Sep 13, 2023
a89cc44
deploy: di release
hyemdooly Sep 13, 2023
acaa5ef
test: 바뀐 di에 따른 테스트 변경
hyemdooly Sep 13, 2023
e6ee91f
refactor: 배포된 di 적용
hyemdooly Sep 13, 2023
1f27660
refactor: 전단계의 잔해 삭제
hyemdooly Sep 14, 2023
d53b175
refactor: instance return 수정
hyemdooly Sep 14, 2023
885ab4d
refactor: 파라미터명 명시
hyemdooly Sep 14, 2023
fa81116
refactor: 변수명 변경
hyemdooly Sep 14, 2023
77bc755
refactor: 파라미터명 변경
hyemdooly Sep 14, 2023
ad360bb
refactor: 중복 코드 제거
hyemdooly Sep 14, 2023
55bc13c
refactor: T 타입 non null로 변경
hyemdooly Sep 14, 2023
cc6cc2e
refactor: scope function 적용
hyemdooly Sep 14, 2023
6c59beb
refactor: id 계산법 수정
hyemdooly Sep 14, 2023
a7baa40
refactor: assertAll 적용
hyemdooly Sep 14, 2023
47600c6
refactor: kotlin class type 고려 제외
hyemdooly Sep 14, 2023
a91d38b
refactor: Module 개념 추가
hyemdooly Sep 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: Qualifier 수정, DI 수정
  • Loading branch information
hyemdooly committed Sep 13, 2023
commit a636e8c453caf82f4426dac7a0a60343cac38f07
5 changes: 2 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,15 @@ android {

dependencies {
implementation(project(":domain"))
implementation("com.github.hyemdooly:android-di:v2.0.0")
// implementation(project(":di"))
// implementation("com.github.hyemdooly:android-di:v2.0.0")
implementation(project(":di"))

implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
implementation(project(mapOf("path" to ":di")))
testImplementation("junit:junit:4.13.2")
testImplementation("com.google.truth:truth:1.1.3")
testImplementation("androidx.arch.core:core-testing:2.2.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package woowacourse.shopping.data

import io.hyemdooly.di.annotation.InDisk
import woowacourse.shopping.data.mapper.toCartProduct
import woowacourse.shopping.data.mapper.toEntity
import woowacourse.shopping.model.CartProduct
import woowacourse.shopping.model.Product

// TODO: Step2 - CartProductDao를 참조하도록 변경
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요없는 주석은 제거해주세요!

@InDisk
class InDiskCartRepository(
private val dao: CartProductDao,
) : CartRepository {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package woowacourse.shopping.data

import io.hyemdooly.di.annotation.InMemory
import woowacourse.shopping.model.CartProduct
import woowacourse.shopping.model.Product

// TODO: Step2 - CartProductDao를 참조하도록 변경
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요없는 주석이 InMemory 에도 따라왔네요!

@InMemory
class InMemoryCartRepository : CartRepository {
private val products = mutableListOf<CartProduct>()
override suspend fun addCartProduct(product: Product) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package woowacourse.shopping.data

import io.hyemdooly.di.annotation.InMemory
import io.hyemdooly.di.annotation.Inject
import woowacourse.shopping.model.Product

@InMemory
class InMemoryProductRepository : ProductRepository {
@Inject
private val products: List<Product> = emptyList()
Expand Down
9 changes: 5 additions & 4 deletions app/src/main/java/woowacourse/shopping/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.hyemdooly.di.annotation.InDisk
import io.hyemdooly.di.annotation.InMemory
import io.hyemdooly.di.annotation.Qualifier
import kotlinx.coroutines.launch
import woowacourse.shopping.data.CartRepository
import woowacourse.shopping.data.InDiskCartRepository
import woowacourse.shopping.data.InMemoryProductRepository
import woowacourse.shopping.data.ProductRepository
import woowacourse.shopping.model.Product

class MainViewModel(
@InMemory private val productRepository: ProductRepository,
@InDisk private val cartRepository: CartRepository,
@Qualifier(InMemoryProductRepository::class) private val productRepository: ProductRepository,
@Qualifier(InDiskCartRepository::class) private val cartRepository: CartRepository,
) : ViewModel() {

private val _products: MutableLiveData<List<Product>> = MutableLiveData(emptyList())
Expand Down
13 changes: 5 additions & 8 deletions app/src/main/java/woowacourse/shopping/ui/ShoppingApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import android.app.Application
import androidx.room.Room
import io.hyemdooly.di.Container
import io.hyemdooly.di.Injector
import woowacourse.shopping.data.CartProductDao
import woowacourse.shopping.data.CartRepository
import woowacourse.shopping.data.InDiskCartRepository
import woowacourse.shopping.data.InMemoryCartRepository
import woowacourse.shopping.data.InMemoryProductRepository
import woowacourse.shopping.data.ProductRepository
import woowacourse.shopping.data.ShoppingDatabase
import woowacourse.shopping.model.Product

Expand Down Expand Up @@ -44,11 +41,11 @@ class ShoppingApplication : Application() {
),
)

Container.addInstance(List::class, products)
Container.addInstance(CartProductDao::class, inDiskDb.cartProductDao())
Container.addInstance(products)
Container.addInstance(inDiskDb.cartProductDao())

Container.addInstance(ProductRepository::class, Injector.inject(InMemoryProductRepository::class))
Container.addInstance(CartRepository::class, Injector.inject(InDiskCartRepository::class))
Container.addInstance(CartRepository::class, Injector.inject(InMemoryCartRepository::class))
Container.addInstance(Injector.inject(InMemoryProductRepository::class))
Container.addInstance(Injector.inject(InDiskCartRepository::class))
Container.addInstance(Injector.inject(InMemoryCartRepository::class))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.hyemdooly.di.annotation.InDisk
import io.hyemdooly.di.annotation.Qualifier
import kotlinx.coroutines.launch
import woowacourse.shopping.data.CartRepository
import woowacourse.shopping.data.InDiskCartRepository
import woowacourse.shopping.model.CartProduct

class CartViewModel(
@InDisk private val cartRepository: CartRepository,
@Qualifier(InDiskCartRepository::class) private val cartRepository: CartRepository,
) : ViewModel() {

private val _cartProducts: MutableLiveData<List<CartProduct>> =
Expand Down
17 changes: 9 additions & 8 deletions di/src/main/java/io/hyemdooly/di/Container.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package io.hyemdooly.di

import kotlin.reflect.KClass

data class InstanceInfo(val clazz: KClass<*>, val annotations: List<Annotation>)
import kotlin.reflect.full.isSubclassOf

object Container {
private val instances = mutableMapOf<InstanceInfo, Any>()
fun addInstance(type: KClass<*>, instance: Any) {
val key = InstanceInfo(type, instance::class.annotations)
instances[key] = instance
private val instances = mutableMapOf<KClass<*>, Any>()

fun addInstance(instance: Any) {
println(instance::class)
instances[instance::class] = instance
}

fun getInstance(type: KClass<*>, annotations: List<Annotation> = emptyList()): Any? {
val key = InstanceInfo(type, annotations)
fun getInstance(type: KClass<*>): Any? {
if (instances[type] != null) instances[type]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instances 의 get 을 해주는 것 말고는 하는게 없는데 의도하신게 있나요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return이 빠졌네요^^...;;

val key = instances.keys.firstOrNull { it.isSubclassOf(type) }
return instances[key]
}

Expand Down
10 changes: 9 additions & 1 deletion di/src/main/java/io/hyemdooly/di/Injector.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.hyemdooly.di

import io.hyemdooly.di.annotation.Inject
import io.hyemdooly.di.annotation.Qualifier
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.jvm.isAccessible
Expand All @@ -30,7 +32,13 @@ object Injector {
private fun <T> getParamInstances(constructor: KFunction<T>): List<Any> {
val paramInstances = constructor.parameters.map { param ->
val type = param.type.jvmErasure
Container.getInstance(type, param.annotations) ?: inject(type)

val annotation = param.findAnnotation<Qualifier>()
if (annotation != null) {
Container.getInstance(annotation.clazz) ?: inject(annotation.clazz)
} else {
Container.getInstance(type) ?: inject(type)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

같은 함수에 대한 분기를 Container 외부에서 하고있네요.
Container 가 자신의 책임을 다할 수 있도록 능동적으로 협력할 수 있게 작성하는 것이 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annotation을 뜯어 타입을 확인하는 것을 Container 내부에서 하게 하려고 했으나,
내부에서 annotation.clazz or param.type.jvmErasure로 인스턴스를 찾을 때 없는 경우 다시 inject를 해주기 위해 null을 반환하게 되는데요!
이런 경우 annotation.clazz과 param.type.jvmErasure 중 어떤 타입으로 inject를 해주어야할지 알 방법이 없었습니다.
그리고 Annotation을 뜯는 것은 DI의 몫이라고 생각해서 대신에 중복 코드를 제거했습니다!

}
return paramInstances
}
Expand Down
4 changes: 0 additions & 4 deletions di/src/main/java/io/hyemdooly/di/annotation/InDisk.kt

This file was deleted.

4 changes: 0 additions & 4 deletions di/src/main/java/io/hyemdooly/di/annotation/InMemory.kt

This file was deleted.

5 changes: 3 additions & 2 deletions di/src/main/java/io/hyemdooly/di/annotation/Qualifier.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package io.hyemdooly.di.annotation

@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class Qualifier
import kotlin.reflect.KClass

annotation class Qualifier(val clazz: KClass<*>)
29 changes: 21 additions & 8 deletions di/src/test/kotlin/io/hyemdooly/di/InjectorTest.kt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

object 주입이나 인자가 여러개인 경우 등 다른 경우의 수를 추가해서 테스트 해봤는데 잘 작동하는 것 확인했습니다!

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.hyemdooly.di

import io.hyemdooly.di.annotation.InMemory
// import io.hyemdooly.di.annotation.InMemory
import io.hyemdooly.di.annotation.Inject
import io.hyemdooly.di.annotation.Qualifier
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.BeforeEach
Expand All @@ -13,18 +14,24 @@ class InjectorTest {
class FakeDatabase

interface FakeRepository {
@Inject
val name: String

@Inject
val items: List<String>
}

@InMemory
// @InMemory
class DefaultFakeRepository(private val database: FakeDatabase) : FakeRepository {
@Inject
override val name: String = ""

@Inject
override val items: List<String> = emptyList()
}

class FakeViewModel(
@InMemory val fakeRepository: FakeRepository,
@Qualifier(DefaultFakeRepository::class) val fakeRepository: FakeRepository,
)

@BeforeEach
Expand All @@ -35,33 +42,39 @@ class InjectorTest {
@Test
fun `Container에서 타입에 맞는 instance를 찾아 의존성을 주입한다`() {
// given
val name = "FakeRepository"
val items = listOf("item1", "item2", "item3")

// when
Container.addInstance(List::class, items)
Container.addInstance(FakeRepository::class, Injector.inject(FakeDatabase::class))
Container.addInstance(FakeRepository::class, Injector.inject(DefaultFakeRepository::class))
Container.addInstance(name)
Container.addInstance(items)
Container.addInstance(Injector.inject(FakeDatabase::class))
Container.addInstance(Injector.inject(DefaultFakeRepository::class))
val viewModel = Injector.inject<FakeViewModel>(FakeViewModel::class)

// then
assertNotNull(viewModel)
assertNotNull(viewModel.fakeRepository)
assertEquals(name, viewModel.fakeRepository.name)
assertEquals(items, viewModel.fakeRepository.items)
}

@Test
fun `Container에서 찾을 수 없는 instance는 재귀로 생성하여 주입한다`() {
// given
val name = "FakeRepository"
val items = listOf("item1", "item2", "item3")

// when
Container.addInstance(List::class, items)
Container.addInstance(FakeRepository::class, Injector.inject(DefaultFakeRepository::class))
Container.addInstance(name)
Container.addInstance(items)
Container.addInstance(Injector.inject(DefaultFakeRepository::class))
val viewModel = Injector.inject<FakeViewModel>(FakeViewModel::class)

// then
assertNotNull(viewModel)
assertNotNull(viewModel.fakeRepository)
assertEquals(name, viewModel.fakeRepository.name)
assertEquals(items, viewModel.fakeRepository.items)
}

Expand Down