Skip to content

Commit

Permalink
[부나] 2, 3단계 자동 DI 미션 제출합니다 (#29)
Browse files Browse the repository at this point in the history
* refactor: 메서드명 getParameterValues()를 getArgs()로 변경

* feat: 필드 주입 기능 구현

* feat: 종속 항목 맵 초기화 기능 구현

* test: 뷰모델 생성자/필드 주입 테스트 작성

* refactor: CartRepository에서 Dao를 주입받도록 변경

* refactor: 장바구니 화면에서 CartProduct 목록을 가지고 있도록 변경

* feat: Qualifier를 사용하여 인터페이스 구분하는 기능 구현

* chore: di 모듈화

* refactor: 부모 자식 타입을 매칭하는 코드 리팩토링

* refactor: 모듈 제공하는 함수 리팩토링

* refactor: ViewModel에서 Repository 어노테이션을 지정하도록 변경

* refactor: 캐시 클래스로 한 번 생성한 객체 관리하도록 변경

* refactor: 타입 변환하는 Map을 SubTypeConverter로 분리

* refactor: 패키지 분리

* feat: DependencyInjector 초기화 기능 구현

* test: 기능 변경에 따른 테스트 코드 재작성

* refactor: CartRepository 파일 분리

* refactor: 캐시에서 종속항목을 가져오는 함수를 operator로 변경

* fix: inject() 함수에서 생성한 객체를 캐싱하지 않는 버그 수정

* fix: InMemoryCartRepository에서 상품 하나 제거하면 모두 사라지는 버그 수정

* fix: SubTypeConverter, Cache 삭제 안 되는 버그 수정

* refactor: @InMemoryProductRepositoryQualifier 애노테이션 제거

* chore: di 모듈 자바 버전 11로 통일

* refactor: di 모듈에서 안드로이드 의존성 제거

* test: DependencyInjectorTest를 app모듈로 이동

* refactor: 일단 커밋

* refactor: 일단 커밋

* refactor: SubTypeProvider 제거

* test: DependencyInjector 코드 변경에 따른 테스트 코드 수정

* test: DependencyInjector 재귀 주입 테스트 작성

* test: 테스트 코드 함수명 변경

* test: @Inject이 붙은 멤버가 복수개여도 정상 주입되는지 확인하는 테스트 코드 작성

* test: 클래스 멤버 프로퍼티가 복수인 경우 Inject 애노테이션이 없는 멤버에 주입하지 않는 것을 확인하는 테스트 코드 작성

* test: 생성자 주입 실패 테스트 케이스 작성(식별자 애노테이션이 없는 경우)

* refactor: 코드 포맷팅

* refactor: 인터페이스 타입인 필드에 식별자 애노테이션을 추가하지 않았을 때 예외 발생을 검증하는 테스트 코드 작성

* refactor: CartProductEntity를 mapping할 때 인자명을 지정하여 명시적으로 변경
  • Loading branch information
tmdgh1592 authored Sep 13, 2023
1 parent e4c9c8d commit 4a3fd54
Show file tree
Hide file tree
Showing 43 changed files with 721 additions and 143 deletions.
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"
}
}
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() {

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

0 comments on commit 4a3fd54

Please sign in to comment.