오늘도 삽질중

클린코드 책을 읽고서 Chapter. 클래스 본문

기타

클린코드 책을 읽고서 Chapter. 클래스

Choi3950 2021. 1. 7. 12:17
반응형


본 포스팅은 개인정리 목적으로 작성된 글입니다.

잘못된 정보가 있을 수 있으며, 피드백은 겸허히 받겠습니다.




해당 챕터를 읽고 인지하면 좋을만한 내용을 간단하게 정리해 봤다.




클래스는 작아야 한다.

클래스를 만들 때 첫번 째 규칙은 크기다. 클래스는 작아야 한다.

함수는 물리적인 행 수로 크기를 측정하지만 클래스는 다른 척도를 사용한다.

클래스가 맡은 책임을 센다

(여기서 책임이란 각자 생각하는 관점 마다 다를것으로 생각한다. 하나의 기능일수도 있고, 큰 카테고리 범주에서 생각하는 것일수도 있고...)

* 클래스 내 함수의 갯수가 작다고 해서 만족하는 것이 아니라, 해당 함수들은 하나의 책임을 가지고 그에 해당하는 기능만 구현하는 것이 바람직 하다.





단일 책임 원칙(SRP) 을 준수한다.

클래스나 모듈을 변경할 이유가 단 하나뿐이어야 한다는 원칙이다.

즉 하나의 클래스가 여러 책임을 가지고 있다면, 클래스를 수정 또는 변경할 이유가 많아진다.

ex) 하나의 클래스가 여러 기능을 책임지고 있다.

class Utils {

companion object {

/**
* 해당 클래스는 화면사이즈, 앱 버전, 뷰 사이즈 , 이미지의 uri를 가져오는등 여러가지 기능을 포함하고 있다.
*/

fun getScreenWidth() {}

fun getScreenHeight() {}

fun getAppVersion(){}

fun getViewWidth(){}

fun getViewHeight(){}

fun getImageUri(){}

//....
//....
//....

}

}


책을 읽고 개인적인 생각으로 단일 책임 원칙을 준수했을 땐 아래처럼 나오지 않을까 생각해본다.

class DeviceScreenUtils {
/**
* 디바이스 사이즈와 관련된 책임만 담당하는 클래스
*/
companion object {
fun getScreenWidth() {}

fun getScreenHeight() {}
}
}

class AppVersion {
/**
* 앱 버전과 관련된 책임만 담당하는 클래스
*/
companion object {
fun getAppVersion(){}
}
}


class ViewUtils {
/**
* 뷰와 관련된 책임만 담당하는 클래스
*
* 여기선 의문점이 존재한다.
* 1. 뷰 자체의 모든것을 책임지는 클래스를 만드는게 나은것인가?
* 2. 뷰의 사이즈만 책임지는 클래스는 만드는게 나은것인가?
*
* 만약 2번이 맞다면 클래스의 이름도 변경이 되어야 한다고 개인적으로 생각한다.
*/
companion object {
fun getViewWidth(){}

fun getViewHeight(){}
}
}





응집도 

소프트웨어 공학에선 응집도는 높을 수록 이상적이라 한다.

클래스는 인스턴스 변수가 작아야 하며,

클래스내 함수는 클래스 인스턴스 변수를 하나 이상 사용해야 한다.

함수에서 클래스의 인스턴스 변수를 많이 사용 할수록 응집도가 높다고 표현한다.

클래스에서 함수를 작게 만들다 보면 몇몇 함수만 사용하는 인스턴스 변수가 많아진다.

이러한 경우엔 새로운 클래스를 만드는 것이 바람직하다.

즉, 어느정도 응집도를 유지하면 작은 클래스가 여러개 나온다.

(개인적인 생각인데, 소프트웨어 공학에선 응집도가 높을수록 이상적이라는데 클린코드 책에선 응집도를 어느정도 유지를 해서 여러 클래스를 만드는 것을 권장한다고 말한다. 즉 응집도가 너무 높으면 좋지 않다는 것으로 인식되는데..... 무슨 차이점이 있는거지?)






변경하기 쉬운 클래스

클린코드 책의 예제가 설명이 잘 되있어 해당 예제를 간단하게 사용해봤다.

ex) Sql 책임을 가진 클래스

class Sql constructor(val table : String){
fun create(){}
fun insert(data : String){}
fun selectAll(){}
fun select(key : String){}
fun select(index : Int){}
fun insertAll(list : ArrayList<String>){}
}



현재 시점에선 삭제나 업데이트가 필요하지 않았는데 어느날 업데이트와 삭제 기능을 추가해야 한다면 클래스를 수정해야 한다.

클린코드 책에선 아래처럼 수정을 권장한다.

abstract class Sql constructor(table : String){
abstract fun generate()
}


class CreateSql(table: String) : Sql(table){
override fun generate() {
//Create Table
}
}


class InsertSql(table: String) : Sql(table) {
override fun generate() {

}

fun insert(data: String){}
fun insertAll(list : ArrayList<String>){}
}


class SelectSql(table: String) : Sql(table){
override fun generate() {

}
fun selectAll(){}
fun select(key : String){}
fun select(index: Int){}
}

/**
* 만약 업데이트나 삭제를 추가해야 한다면?
* InsertSql 이나 SelectSql 클래스를 수정하지 않아도 된다.
* 또한 insert 기능이 수정 및 변경이 이루어 지게 되면 InsertSql 클래스만 수정하면 된다.
*/

하나의 SQL 이라는 책임에서 세부적으로 또 다른 책임(검색,수정,추가,삭제 등등) 을 가진 클래스를 세분화 하여 2가지를 만족 할 수 있다.

1. 단일책임원칙(SRP) 을 준수한다.

2. OCP를 준수한다.

OCP란? 클래스는 확장에 개방적이고, 수정에 폐쇠적이어야 한다는 객체지향설계 핵심 원칙

반응형
Comments