이 포스팅은 안드로이드 코드 개선을 위한 문제 파악 후의 코드 개선 작업을 다루고 있습니다. 문제 파악 부분은 안드로이드 코드 개선하기 (1) 문제 파악 포스팅을 확인해주세요!
우선 지난 포스팅에서 파악한 문제점들이다. 조바심 내지말고 차근차근 하나씩 수정해보도록 하자.
private val lineNumberModel = LineNumber("")
var statementEditView = MutableLiveData<EditText>()
val lineNumberView = MutableLiveData<String>()
가독성
객체지향
private val modelLineNumber = LineNumber()
private val _editTextStatement = MutableLiveData<EditText>()
val editTextStatement: MutableLiveData<EditText> = _editTextStatement
private val _textViewLineNumber = MutableLiveData<String>()
val textViewLineNumber: LiveData<String> = _textViewLineNumber
modelLineNumber
는 기본 매개변수를 명시해서 보기 싫은 ""를 제거해주었다._editTextStatement
와 _textViewLineNumber
는 ViewModel 내부에서만 수정하도록 Private으로 설정하고 textViewLineNumber
는 데이터바인딩한 컴포넌트를 read-only로 관찰하기 위해서 LiveData로 선언해주었다. editTextStatement
는 Activity에서 ViewBiding으로 넘겨받았기 때문에 MutableLiveData로 선언해주었다. 임의로 MutableLiveData의 값에 접근하지 않도록 주의해야한다. 권장할만한 방법은 아닌 것 같지만 곧 XML에서 Compose로 변경할 예정이기에 우선은 이렇게 변경해주었다. // Activity_editor.xml과 data binding #EditText의 afterTextChanged()
fun onStatementChanged() {
val editText = statementEditView.value
val layout = editText?.layout
layout?.let {
lineNumberModel.content = generateLineNumber(it.lineCount)
lineNumberView.value = lineNumberModel.content
}
}
자원 낭비
관심사 분리 부족
// Data binding with Activity_editor.xml #EditText afterTextChanged()
fun onStatementChanged() {
val editText = editTextStatement.value
editText?.layout?.let { updateLineNumbers(it) }
}
private fun updateLineNumber(it: Layout) {
if (it.lineCount != modelLineNumber.number) {
modelLineNumber.content = generateLineNumber(it.lineCount)
modelLineNumber.number = it.lineCount
_textViewLineNumber.value = modelLineNumber.content
}
}
우선은 Data Class인 LineNumber를 통해서 Line Count를 관리해주도록 바꿨다. 그리고 함수를 Null 검증과 Update로 분리해주면서 줄 수가 변했을 때만 generateLineNumber() 메소드를 호출하도록 바꿔주었다. 이렇게 해준 뒤 Log를 비교해보자.
Log.i("CALL_UPDATE", "${++count}")
좌측의 화면의 글자를 타이핑하는 동안 리펙터링 전에는 총 41번의 generateLineNumber() 함수 호출이 있었고 리펙터링 후에는 10번의 함수 호출이 있었다. 이전에는 한글자 입력할 때마다 함수를 호출했는데 지금은 줄 수의 변화에 따라 호출하는 것이라 상당히 많은 차이가 있었다.
// SQL문이 쿼리인지 검증. 즉 SELECT로 시작하는지 확인
private fun isQueryStatement(statement: String): Boolean {
return statement.startsWith("SELECT") or statement.startsWith("select")
}
의도 실패
가독성
// SQL문이 쿼리인지 검증. 즉 SELECT로 시작하는지 확인
private fun isSelectStatement(statement: String): Boolean {
return statement.startsWith("SELECT", ignoreCase = true)
}
ignoreCase를 적용해주고 함수명을 변경해주었다. seLect
나 SeleCT
처럼 대문자와 소문자를 혼합해서 사용하는 경우를 생각하지 못 해서 생긴 문제였다.
class EditorViewModel(private val databaseRepository: DatabaseRepository) : ViewModel() {
private val modelLineNumber = LineNumber()
private val _editTextStatement = MutableLiveData<EditText>()
val editTextStatement: MutableLiveData<EditText> = _editTextStatement
private val _textViewLineNumber = MutableLiveData<String>()
val textViewLineNumber: LiveData<String> = _textViewLineNumber
// Data binding with Activity_editor.xml #afterTextChanged()
fun onStatementChanged() {
val editText = editTextStatement.value
editText?.layout?.let { updateLineNumbers(it) }
}
// If line number changed, update model and render line number text view
private fun updateLineNumbers(it: Layout) {
if (it.lineCount != modelLineNumber.number) {
modelLineNumber.content = generateLineNumber(it.lineCount)
modelLineNumber.number = it.lineCount
_textViewLineNumber.value = modelLineNumber.content
}
}
// View binding with Activity_editor.xml, Check Editor Activity
fun execStatement(): Any? {
return _editTextStatement.value?.let {
if (isQueryStatement(editTextStatement.value.toString())) {
databaseRepository.execQuery(it.text.toString())
} else {
databaseRepository.execStatement(it.text.toString())
}
}
}
// Check if a statement starts with "SELECT" ignore case
private fun isSelectStatement(statement: String): Boolean {
return statement.startsWith("SELECT", ignoreCase = true)
}
// Generate line number like ""1/n2/n3/n ... count"
private fun generateLineNumber(count: Int): String {
val stringBuilder = StringBuilder()
for (i in 1..count) {
stringBuilder.append("$i\n")
}
return stringBuilder.toString()
}
}
리펙터링 후에 오히려 코드 수가 늘어났다. onStatementChanged()
의 관심사를 분리하기 위해서 별도의 함수를 구현한게 원인이다. 코드 수가 늘어난 점을 제외하면 많은 부분이 향상되었다. 정리해보자면 아래와 같다.
Layout(EditText)
가 변경될 때로 수정했다.이렇게 해서 리펙터링 1차를 마무리했다. 관심사 분리를 위해서 LineNumber 로직 전반을 별도의 클래스로 분리하려 했지만, 포스팅의 분량이 너무 길어질까봐 후에 다루도록 하겠다. 추천 노래를 남기면서 글을 마무리한다. 다들 안녕😉