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 미션 제출합니다 #29

Merged
merged 37 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
961523a
refactor: 메서드명 getParameterValues()를 getArgs()로 변경
tmdgh1592 Sep 7, 2023
5ac37cf
feat: 필드 주입 기능 구현
tmdgh1592 Sep 7, 2023
f3db239
feat: 종속 항목 맵 초기화 기능 구현
tmdgh1592 Sep 7, 2023
c95b7c0
test: 뷰모델 생성자/필드 주입 테스트 작성
tmdgh1592 Sep 7, 2023
af93fd7
refactor: CartRepository에서 Dao를 주입받도록 변경
tmdgh1592 Sep 7, 2023
a2b640c
refactor: 장바구니 화면에서 CartProduct 목록을 가지고 있도록 변경
tmdgh1592 Sep 7, 2023
ed8e750
feat: Qualifier를 사용하여 인터페이스 구분하는 기능 구현
tmdgh1592 Sep 8, 2023
970dc8b
chore: di 모듈화
tmdgh1592 Sep 8, 2023
8175c12
refactor: 부모 자식 타입을 매칭하는 코드 리팩토링
tmdgh1592 Sep 8, 2023
1c72fcf
refactor: 모듈 제공하는 함수 리팩토링
tmdgh1592 Sep 8, 2023
ff6f067
refactor: ViewModel에서 Repository 어노테이션을 지정하도록 변경
tmdgh1592 Sep 9, 2023
0bb35a3
refactor: 캐시 클래스로 한 번 생성한 객체 관리하도록 변경
tmdgh1592 Sep 9, 2023
7770cf7
refactor: 타입 변환하는 Map을 SubTypeConverter로 분리
tmdgh1592 Sep 9, 2023
bffeafc
refactor: 패키지 분리
tmdgh1592 Sep 9, 2023
0c4c466
feat: DependencyInjector 초기화 기능 구현
tmdgh1592 Sep 9, 2023
eb091ed
test: 기능 변경에 따른 테스트 코드 재작성
tmdgh1592 Sep 9, 2023
5aff10c
refactor: CartRepository 파일 분리
tmdgh1592 Sep 9, 2023
c56d211
refactor: 캐시에서 종속항목을 가져오는 함수를 operator로 변경
tmdgh1592 Sep 9, 2023
95f32b3
fix: inject() 함수에서 생성한 객체를 캐싱하지 않는 버그 수정
tmdgh1592 Sep 9, 2023
d5b56e3
fix: InMemoryCartRepository에서 상품 하나 제거하면 모두 사라지는 버그 수정
tmdgh1592 Sep 9, 2023
8c2b8aa
fix: SubTypeConverter, Cache 삭제 안 되는 버그 수정
tmdgh1592 Sep 13, 2023
28b2799
refactor: @InMemoryProductRepositoryQualifier 애노테이션 제거
tmdgh1592 Sep 13, 2023
f534674
chore: di 모듈 자바 버전 11로 통일
tmdgh1592 Sep 13, 2023
c61014c
refactor: di 모듈에서 안드로이드 의존성 제거
tmdgh1592 Sep 13, 2023
0c51aaa
test: DependencyInjectorTest를 app모듈로 이동
tmdgh1592 Sep 13, 2023
c9176b5
refactor: 일단 커밋
tmdgh1592 Sep 13, 2023
7f016fd
refactor: 일단 커밋
tmdgh1592 Sep 13, 2023
9a8ee71
refactor: SubTypeProvider 제거
tmdgh1592 Sep 13, 2023
c28d68f
test: DependencyInjector 코드 변경에 따른 테스트 코드 수정
tmdgh1592 Sep 13, 2023
0b05e6e
test: DependencyInjector 재귀 주입 테스트 작성
tmdgh1592 Sep 13, 2023
a740717
test: 테스트 코드 함수명 변경
tmdgh1592 Sep 13, 2023
f24e308
test: @Inject이 붙은 멤버가 복수개여도 정상 주입되는지 확인하는 테스트 코드 작성
tmdgh1592 Sep 13, 2023
5d2f67e
test: 클래스 멤버 프로퍼티가 복수인 경우 Inject 애노테이션이 없는 멤버에 주입하지 않는 것을 확인하는 테스트 코드 작성
tmdgh1592 Sep 13, 2023
76b7693
test: 생성자 주입 실패 테스트 케이스 작성(식별자 애노테이션이 없는 경우)
tmdgh1592 Sep 13, 2023
617ae36
refactor: 코드 포맷팅
tmdgh1592 Sep 13, 2023
29ededf
refactor: 인터페이스 타입인 필드에 식별자 애노테이션을 추가하지 않았을 때 예외 발생을 검증하는 테스트 코드 작성
tmdgh1592 Sep 13, 2023
87e8553
refactor: CartProductEntity를 mapping할 때 인자명을 지정하여 명시적으로 변경
tmdgh1592 Sep 13, 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
4 changes: 3 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ dependencies {
// Mockk
testImplementation("io.mockk:mockk:1.13.5")
androidTestImplementation("io.mockk:mockk-android:1.13.5")
// DI Library
implementation(project(":bunadi"))
// Reflection
implementation("org.jetbrains.kotlin", "kotlin-reflect", "1.8.21")
implementation(kotlin("reflect"))
}

kapt {
Expand Down
11 changes: 3 additions & 8 deletions app/src/main/java/woowacourse/shopping/ShoppingApplication.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package woowacourse.shopping

import android.app.Application
import woowacourse.shopping.data.repository.DefaultCartRepository
import woowacourse.shopping.data.repository.DefaultProductRepository
import woowacourse.shopping.di.injector.modules
import woowacourse.shopping.repository.CartRepository
import woowacourse.shopping.repository.ProductRepository
import com.woowacourse.bunadi.dsl.modules
import woowacourse.shopping.ui.common.di.module.DaoModule

class ShoppingApplication : Application() {
override fun onCreate() {
super.onCreate()

modules {
inject<ProductRepository>(DefaultProductRepository())
inject<CartRepository>(DefaultCartRepository())
module(DaoModule(this@ShoppingApplication))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Comment on lines +10 to +12
Copy link

Choose a reason for hiding this comment

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

세심한 상수화 😎

}
23 changes: 23 additions & 0 deletions app/src/main/java/woowacourse/shopping/data/mapper/CartMapper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package woowacourse.shopping.data.mapper

import woowacourse.shopping.data.CartProductEntity
import woowacourse.shopping.model.CartProduct
import woowacourse.shopping.model.Product

fun Product.toEntity(): CartProductEntity = CartProductEntity(
name = name,
price = price,
imageUrl = imageUrl,
)

fun List<CartProductEntity>.toDomain(): List<CartProduct> = map { cartProductEntity ->
CartProduct(
product = Product(
name = cartProductEntity.name,
price = cartProductEntity.price,
imageUrl = cartProductEntity.imageUrl,
),
id = cartProductEntity.id,
createdAt = cartProductEntity.createdAt,
)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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

@Singleton
class DatabaseCartRepository(
private val dao: CartProductDao,
) : CartRepository {
override suspend fun addCartProduct(product: Product) {
dao.insert(product.toEntity())
}

override suspend fun getAllCartProducts(): List<CartProduct> {
return dao.getAll().toDomain()
}

override suspend fun deleteCartProduct(id: Long) {
dao.delete(id)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<Product> = listOf(
Product(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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
import woowacourse.shopping.model.CartProduct
import woowacourse.shopping.model.Product
import woowacourse.shopping.repository.CartRepository

@Singleton
class InMemoryCartRepository : CartRepository {
private val cartProducts = mutableListOf<CartProductEntity>()
private var lastId: Long = 0

override suspend fun addCartProduct(product: Product) {
cartProducts.add(product.toEntity().apply { id = ++lastId })
}

override suspend fun getAllCartProducts(): List<CartProduct> {
return cartProducts.toDomain()
}

override suspend fun deleteCartProduct(id: Long) {
cartProducts.removeIf { it.id == id }
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import woowacourse.shopping.R
import woowacourse.shopping.databinding.ActivityCartBinding
import woowacourse.shopping.di.lazy.viewModel
import woowacourse.shopping.ui.util.viewModel.viewModel

class CartActivity : AppCompatActivity() {
private val binding by lazy { ActivityCartBinding.inflate(layoutInflater) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Product>,
onClickDelete: (position: Int) -> Unit,
items: List<CartProduct>,
onClickDelete: (id: Long) -> Unit,
private val dateFormatter: DateFormatter,
) : RecyclerView.Adapter<CartProductViewHolder>() {

private val items: MutableList<Product> = items.toMutableList()
private val items: MutableList<CartProduct> = items.toMutableList()

private val onClickDelete = { position: Int ->
onClickDelete(position)
private val onClickDelete = { id: Long, position: Int ->
onClickDelete(id)
removeItem(position)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
23 changes: 15 additions & 8 deletions app/src/main/java/woowacourse/shopping/ui/cart/CartViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,33 @@ 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
import woowacourse.shopping.ui.common.di.qualifier.DatabaseCartRepositoryQualifier

class CartViewModel(
private val cartRepository: CartRepository,
@DatabaseCartRepositoryQualifier val cartRepository: CartRepository,
) : ViewModel() {
Comment on lines 12 to 14
Copy link

Choose a reason for hiding this comment

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

뷰모델은 레포지토리를 통해 그저 데이터를 관리하는데에만 관심사가 있습니다.

다르게 말하면 우리가 레포지토리 패턴을 사용한다는 가정하에 레포지토리의 데이터 소스로 Local이 오냐 Remote가 오냐는 레포지토리 내부 사정이라고 생각하는데 부나의 어노테이션은 데이터베이스, 즉 데이터 소스의 성격을 표시하고 있습니다.

부나가 생각하시기에 어떤가요?? 상관없을 것 같다면 반영하시지 않으셔도 좋습니다. 다만 부나의 의견은 궁금하니 코멘트 남겨주세요!

Copy link
Member Author

Choose a reason for hiding this comment

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

저도 반달과 동일한 생각이예요!
하지만 이 외에는 별다른 아이디어가 떠오르지 않아 위와 같은 구조로 설계하였습니다.

제가 아는 선에서 사실 Hilt도 위와 같은 형식으로 애노테이션을 지정해주고 있는 것으로 알고 있어요!
저도 이 부분에 영향을 많이 받아서 위처럼 작성하였습니다. 😅

참고 자료를 위해 링크를 함께 첨부해드려요 : )
Hilt 식별자


private val _cartProducts: MutableLiveData<List<Product>> =
private val _cartProducts: MutableLiveData<List<CartProduct>> =
MutableLiveData(emptyList())
val cartProducts: LiveData<List<Product>> get() = _cartProducts
val cartProducts: LiveData<List<CartProduct>> get() = _cartProducts

private val _onCartProductDeleted: MutableLiveData<Boolean> = MutableLiveData(false)
val onCartProductDeleted: LiveData<Boolean> 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
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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 DaoModule(private val context: Context) : Module {
fun provideCartProductDao(): CartProductDao = Room.databaseBuilder(
context,
ShoppingDatabase::class.java,
ShoppingDatabase.DATABASE_NAME,
).build().cartProductDao()
}
Original file line number Diff line number Diff line change
@@ -0,0 +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
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import woowacourse.shopping.R
import woowacourse.shopping.databinding.ActivityMainBinding
import woowacourse.shopping.di.lazy.viewModel
import woowacourse.shopping.ui.cart.CartActivity
import woowacourse.shopping.ui.util.viewModel.viewModel

class MainActivity : AppCompatActivity() {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
Expand Down
Loading