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

[둘리] 1단계 자동 DI 미션 제출합니다 #5

Merged
merged 18 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
# android-di
# 0.5단계
## 기능 요구 사항
### 생성자 주입 - 수동
다음 문제점을 해결한다.

- [x] DB 없이 테스트하기 어렵다.
- [x] DB 객체를 교체하기 위해 또다른 객체를 만들어 바꿔줘야 한다. 즉, ViewModel에 직접적인 변경사항이 발생한다.

# 1단계
## 기능 요구 사항
### 생성자 주입 - 자동
다음 문제점을 해결한다.

- [x] ViewModel에서 참조하는 Repository가 정상적으로 주입되지 않는다.
- [x] Repository를 참조하는 다른 객체가 생기면 주입 코드를 매번 만들어줘야 한다.
- [x] ViewModel에 수동으로 주입되고 있는 의존성들을 자동으로 주입되도록 바꿔본다.
- [x] 특정 ViewModel에서만이 아닌, 범용적으로 활용될 수 있는 자동 주입 로직을 작성한다. (MainViewModel, CartViewModel 모두 하나의 로직만 참조한다)
- [x] 100개의 ViewModel이 생긴다고 가정했을 때, 자동 주입 로직 100개가 생기는 것이 아니다. 하나의 자동 주입 로직을 재사용할 수 있어야 한다.
- [x] 장바구니에 접근할 때마다 매번 CartRepository 인스턴스를 새로 만들고 있다.
- [x] 여러 번 인스턴스화할 필요 없는 객체는 최초 한 번만 인스턴스화한다. (이 단계에서는 너무 깊게 생각하지 말고 싱글 오브젝트로 구현해도 된다.)

## 선택 요구 사항
- [ ] TDD로 DI 구현
- [ ] Robolectric으로 기능 테스트
- [ ] ViewModel 테스트
- [ ] 모든 도메인 로직, Repository 단위 테스트

## 프로그래밍 요구 사항
사전에 주어진 테스트 코드가 모두 성공해야 한다.
Annotation은 이 단계에서 활용하지 않는다.
4 changes: 3 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ android {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
"proguard-rules.pro",
)
}
}
Expand Down Expand Up @@ -68,4 +68,6 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.15.1")
// Robolectric
testImplementation("org.robolectric:robolectric:4.9")

implementation("androidx.fragment:fragment-ktx:1.5.7")
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ package woowacourse.shopping.data
import woowacourse.shopping.model.Product

// TODO: Step2 - CartProductDao를 참조하도록 변경
class CartRepository {

class CartRepositoryImpl : CartRepository {
private val cartProducts: MutableList<Product> = mutableListOf()
fun addCartProduct(product: Product) {
override fun addCartProduct(product: Product) {
cartProducts.add(product)
}

fun getAllCartProducts(): List<Product> {
override fun getAllCartProducts(): List<Product> {
return cartProducts.toList()
}

fun deleteCartProduct(id: Int) {
override fun deleteCartProduct(id: Int) {
cartProducts.removeAt(id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,26 @@ package woowacourse.shopping.data

import woowacourse.shopping.model.Product

class ProductRepository {

class ProductRepositoryImpl : ProductRepository {
private val products: List<Product> = listOf(
Product(
name = "우테코 과자",
price = 10_000,
imageUrl = "https://cdn-mart.baemin.com/sellergoods/api/main/df6d76fb-925b-40f8-9d1c-f0920c3c697a.jpg?h=700&w=700"
imageUrl = "https://cdn-mart.baemin.com/sellergoods/api/main/df6d76fb-925b-40f8-9d1c-f0920c3c697a.jpg?h=700&w=700",
),
Product(
name = "우테코 쥬스",
price = 8_000,
imageUrl = "https://cdn-mart.baemin.com/sellergoods/main/52dca718-31c5-4f80-bafa-7e300d8c876a.jpg?h=700&w=700"
imageUrl = "https://cdn-mart.baemin.com/sellergoods/main/52dca718-31c5-4f80-bafa-7e300d8c876a.jpg?h=700&w=700",
),
Product(
name = "우테코 아이스크림",
price = 20_000,
imageUrl = "https://cdn-mart.baemin.com/sellergoods/main/e703c53e-5d01-4b20-bd33-85b5e778e73f.jpg?h=700&w=700"
imageUrl = "https://cdn-mart.baemin.com/sellergoods/main/e703c53e-5d01-4b20-bd33-85b5e778e73f.jpg?h=700&w=700",
),
)

fun getAllProducts(): List<Product> {
override fun getAllProducts(): List<Product> {
return products
}
}
10 changes: 4 additions & 6 deletions app/src/main/java/woowacourse/shopping/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import woowacourse.shopping.R
import woowacourse.shopping.databinding.ActivityMainBinding
import woowacourse.shopping.ui.cart.CartActivity
import woowacourse.shopping.ui.common.ViewModelFactory

class MainActivity : AppCompatActivity() {

private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

private val viewModel by lazy {
ViewModelProvider(this)[MainViewModel::class.java]
}
private val viewModel by viewModels<MainViewModel>(factoryProducer = { ViewModelFactory() })

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -27,7 +26,6 @@ class MainActivity : AppCompatActivity() {
setupView()
}


override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.cart_menu, menu)
menu?.findItem(R.id.cart)?.actionView?.let { view ->
Expand Down Expand Up @@ -58,7 +56,7 @@ class MainActivity : AppCompatActivity() {
viewModel.products.observe(this) {
val adapter = ProductAdapter(
items = it,
onClickProduct = viewModel::addCartProduct
onClickProduct = viewModel::addCartProduct,
)
binding.rvProducts.adapter = adapter
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/woowacourse/shopping/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ class MainViewModel(
private val _onProductAdded: MutableLiveData<Boolean> = MutableLiveData(false)
val onProductAdded: LiveData<Boolean> get() = _onProductAdded


fun addCartProduct(product: Product) {
cartRepository.addCartProduct(product)
_onProductAdded.value = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ package woowacourse.shopping.ui.cart

import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import woowacourse.shopping.R
import woowacourse.shopping.databinding.ActivityCartBinding
import woowacourse.shopping.ui.common.ViewModelFactory

class CartActivity : AppCompatActivity() {

private val binding by lazy { ActivityCartBinding.inflate(layoutInflater) }

private val viewModel by lazy {
ViewModelProvider(this)[CartViewModel::class.java]
}
private val viewModel by viewModels<CartViewModel>(factoryProducer = { ViewModelFactory() })

private lateinit var dateFormatter: DateFormatter

Expand Down Expand Up @@ -60,7 +59,7 @@ class CartActivity : AppCompatActivity() {
val adapter = CartProductAdapter(
items = it,
dateFormatter = dateFormatter,
onClickDelete = viewModel::deleteCartProduct
onClickDelete = viewModel::deleteCartProduct,
)
binding.rvCartProducts.adapter = adapter
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package woowacourse.shopping.ui.common

import woowacourse.shopping.data.CartRepository
import woowacourse.shopping.data.CartRepositoryImpl
import woowacourse.shopping.data.ProductRepository
import woowacourse.shopping.data.ProductRepositoryImpl

object RepositoryModule {
val productRepository: ProductRepository = ProductRepositoryImpl()
val cartRepository: CartRepository = CartRepositoryImpl()
}
Copy link
Member

Choose a reason for hiding this comment

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

Repository 가 추가될 때 재사용 가능하게 만들어 보면 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

Container로 변경하고, 프로퍼티로 map을 추가해 instance를 추가하고 가져올 수 있게 했습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package woowacourse.shopping.ui.common

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class ViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val constructor = modelClass.declaredConstructors.first()
require(constructor != null) { IllegalArgumentException("Unknown ViewModel Class $modelClass") }
Copy link
Member

Choose a reason for hiding this comment

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

requireNotNull 을 활용해 보는게 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

오 좋아용 수정했습니다~!


val types = constructor.parameterTypes
val params = mutableListOf<Any?>()

val properties = RepositoryModule::class.java.declaredFields
for (type in types) {
val field = properties.first { it.type == type }
?: throw IllegalArgumentException("Can't find Property $type")
field.isAccessible = true
params.add(field.get(null))
}
Copy link
Member

Choose a reason for hiding this comment

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

가변 리스트와 for 문이 아닌 아닌 map 을 활용하는 것이 더 코틀린스럽지 않을까요?

Copy link
Member

Choose a reason for hiding this comment

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

추가로 함수 분리가 필요할 것 같아요ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

알고리즘 문제 푸는 것에 뇌가 절여져서 map 쓸 생각을 못했네요...

Copy link
Author

Choose a reason for hiding this comment

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

수정했습니다..!!! 제가 kotlin reflection이 안됐던 이유가 reflect를 import안해줘서임을 깨닫고 자바 reflection을 걷어내고 kotlin으로 바꿔줬습니다!
수업시간에 배운 내용들이긴 하지만 비슷해서 싹 바꿨어요!


return constructor.newInstance(*params.toTypedArray()) as T
}
}
3 changes: 1 addition & 2 deletions app/src/test/java/woowacourse/shopping/MainActivityTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import org.robolectric.RobolectricTestRunner
import woowacourse.shopping.ui.MainActivity
import woowacourse.shopping.ui.MainViewModel


@RunWith(RobolectricTestRunner::class)
class MainActivityTest {

@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
var instantTaskExecutorRule = InstantTaskExecutorRule() // AAC 컴포넌트들을 한 스레드에서 실행되도록 함

@Test
fun `Activity 실행 테스트`() {
Expand Down
11 changes: 11 additions & 0 deletions domain/src/main/java/woowacourse/shopping/data/CartRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package woowacourse.shopping.data

import woowacourse.shopping.model.Product

interface CartRepository {
fun addCartProduct(product: Product)

fun getAllCartProducts(): List<Product>

fun deleteCartProduct(id: Int)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package woowacourse.shopping.data

import woowacourse.shopping.model.Product

interface ProductRepository {
fun getAllProducts(): List<Product>
}