위 책을 보면서 정리한 글입니다.
유일한 값 : 상수
개발 초기 : 각기 다른 클래스에 분산
개발 진행 : 하나의 클래스에서 관리
위의 방법은 상수가 적은 구조에서는 유용하지만, 기능이 점차 늘어날수록 상수도 많아지게 되어 문제가 발생한다.
결국은 기능이나 로직이 확장될수록, 의도치 않은 곳에서 상수를 사용하거나, 통합 관리 목적과는 다르게 진행된다.
상수의 통합 관리에 초점이 맞추어져, 유지보수 측면에서 클래스의 종속성, 의존성이 높아져 분류하기 어려운 상수 클래스가 생성된다.
문제는 상수 관리 클래스에 정의된 상수들이 찾기 어려운 만큼 많이 모여 있기 때문에 발생한다.
↪ 그렇다면, 의도된 상수들을 사용하도록, 상수 관리 클래스에 정의된 상수들을 목적에 맞게 분류하고, 필요에 따라 상수를 해당 객체로 이동시키자!
Q. 상수 관리 객체를 만드는 것이 좋을까
A. 상수 관리 객체를 만드는 것은 필요한 일.
서비스의 데이터베이스 접속 정보와 같은 자료는 관리 객체에서 공통으로 접속하여 사용하는 것이 좋으나, 관리하는 상수의 범위가 일정하지 않으면 유지보수성이 모호해진다.
↪ 특정 로직에 상수가 필요할 때마다 시스템 상수를 관리하는 곳에 추가하게 되면, 코드량이 늘어나면서
상수 관리 객체는 시스템 상수만을 취급하는 것이 아닌, 서비스의 부분에서만 사용하는 상수들과 섞이게 된다.
Q. 왜 상수를 목적에 맞게 객체로 이동해야 할까
A. 가장 큰 이유는 의존성. 모듈이 별도의 애플리케이션으로 이동할 경우, 모듈에서 사용하는 상수가 속한 클래스도 함께 이동하게 된다.
↪ 이럴 경우 의도하지 않았던 상수들도 같이 딸려오게 되고, 구조적으로도 좋지 않게 된다.
Q. 어떤 기준으로 상수를 이동해야 할까
A. 상수를 이용하는 객체의 기준으로 생각. 상수를 반환하기 위해 사용하는 객체, 반환받아 사용하는 객체가 있을 때 대개 반환받아 사용하는 객체가 좀 더 범용성이 있는 객체 (저자의 생각)
↪ 반환하기 위해 사용하는 로직은 여러 곳에 분산되지만, 반환받아 사용하는 곳은 한곳인 경우가 많아, 주로 상수를 사용하는 객체에 정의해서 사용
1. 상수를 사용하는 곳이 어느 패키지에 집중되어 있는지 확인
2. 상수를 사용하는 곳이 집중된 객체 중 제일 포괄적인 성격을 지닌 곳을 확인
public class ConstValue {
/** DataBase Info -- System **/
public static final String DATABASE_URL = "SMP1";
public static final String DATABASE_SCHEMA = "SMP2";
public static final String DATABASE_USER = "SMP3";
public static final String DATABASE_PASSWORD = "SMP4";
/** Req Info -- System **/
public static final int REQ_SUCCESS = 10;
public static final int REQ_FAIL = 19;
/** CheckInService Info **/
public static final int CHK_IN_RESULT_SUCCESS = 30;
public static final int CHK_IN_RESULT_FAIL = 39;
public static final int CHK_IN_FAIL_ALREADY_IN = 301;
public static final int CHK_IN_FAIL_NOT_REVERSED = 302;
public static final int CHK_IN_FAIL_DEBT_CHARHED = 303;
public static final int CHK_IN_FAIL_SYSTEM_ERR = 399;
/** CheckOutService Info **/
public static final int CHK_OUT_RESULT_SUCCESS = 40;
public static final int CHK_OUT_RESULT_FAIL = 49;
public static final int CHK_OUT_FAIL_ALREADY_IN = 401;
public static final int CHK_OUT_FAIL_NOT_REVERSED = 402;
public static final int CHK_OUT_FAIL_DEBT_CHARHED = 403;
public static final int CHK_OUT_FAIL_SYSTEM_ERR = 499;
/** PayService Info **/
public static final int PAY_RESULT_SUCCESS = 50;
public static final int PAY_RESULT_FAIL = 51;
public static final int PAY_FAIL_DEUBT_CHARGE = 501;
public static final int PAY_FAIL_DEUBT_RETURN = 502;
public static final int PAY_FAIL_DEUBT_CHARGE = 503;
/** RusultPageNumber Info -- System **/
public static final int RESULT_CODE_200 = 200;
public static final int RESULT_CODE_400 = 400;
public static final int RESULT_CODE_404 = 404;
public static final int RESULT_CODE_500 = 500;
}
위 상수 클래스의 문제점은, 시스템 상수와 일반 로직에서 사용하는 상수가 섞여 있다는 것이다.
일반 로직에서 사용할 상수를 추가할 때마다, 이 클래스에 추가될 가능성이 높고, 유지보수 할 대마다 이 객체의 코드량은 점점 늘어가게 되어, 어느 순간 내가 원하는 상수를 찾을 수 없는 상황이 발생하게 된다.
시스템 상수와 일반 로직 상수를 구분하고 분리해야 한다.
public class ConstValue {
/** DataBase Info -- System **/
public class Database {
public static final String DATABASE_URL = "SMP1";
public static final String DATABASE_SCHEMA = "SMP2";
public static final String DATABASE_USER = "SMP3";
public static final String DATABASE_PASSWORD = "SMP4";
}
/** Req Info -- System **/
public static final int REQ_SUCCESS = 10;
public static final int REQ_FAIL = 19;
/** CheckInService Info **/
public class CheckIn {
public static final int CHK_IN_RESULT_SUCCESS = 30;
public static final int CHK_IN_RESULT_FAIL = 39;
// 실패 원인
public class Fail {
public static final int CHK_IN_FAIL_ALREADY_IN = 301;
public static final int CHK_IN_FAIL_NOT_REVERSED = 302;
public static final int CHK_IN_FAIL_DEBT_CHARHED = 303;
public static final int CHK_IN_FAIL_SYSTEM_ERR = 399;
}
}
/** CheckOutService Info **/
public class CheckOut {
public static final int CHK_OUT_RESULT_SUCCESS = 40;
public static final int CHK_OUT_RESULT_FAIL = 49;
public class Fail {
public static final int CHK_OUT_FAIL_ALREADY_IN = 401;
public static final int CHK_OUT_FAIL_NOT_REVERSED = 402;
public static final int CHK_OUT_FAIL_DEBT_CHARHED = 403;
public static final int CHK_OUT_FAIL_SYSTEM_ERR = 499;
}
}
/** PayService Info **/
public class Pay {
public static final int PAY_RESULT_SUCCESS = 50;
public static final int PAY_RESULT_FAIL = 51;
public class Fail {
public static final int PAY_FAIL_DEUBT_CHARGE = 501;
public static final int PAY_FAIL_DEUBT_RETURN = 502;
public static final int PAY_FAIL_DEUBT_CHARGE = 503;
}
}
/** RusultPageNumber Info -- System **/
public class HttpStatus {
public static final int RESULT_CODE_200 = 200;
public static final int RESULT_CODE_400 = 400;
public static final int RESULT_CODE_404 = 404;
public static final int RESULT_CODE_500 = 500;
}
}
내부 클래스를 만든 이유는 외부 클래스의 상수들과 연관이 없고, 내부 클래스의 사용범위가 매우 한정적이기 때문이다. 이런 경우에는 사용 목적에 맞는 곳과 가깝게 배치하여 검색이나 수정이 쉬워지도록 분리한다.
public class ConstValue {
/** DataBase Info -- System **/
public class Database {
public static final String DATABASE_URL = "SMP1";
public static final String DATABASE_SCHEMA = "SMP2";
public static final String DATABASE_USER = "SMP3";
public static final String DATABASE_PASSWORD = "SMP4";
}
/** Req Info -- System **/
public static final int REQ_SUCCESS = 10;
public static final int REQ_FAIL = 19;
/** RusultPageNumber Info -- System **/
public class HttpStatus {
public static final int RESULT_CODE_200 = 200;
public static final int RESULT_CODE_400 = 400;
public static final int RESULT_CODE_404 = 404;
public static final int RESULT_CODE_500 = 500;
}
}
public class CheckInService {
public static final int CHK_OUT_RESULT_SUCCESS = 40;
public static final int CHK_OUT_RESULT_FAIL = 49;
public static final int CHK_OUT_FAIL_ALREADY_IN = 401;
public static final int CHK_OUT_FAIL_NOT_REVERSED = 402;
public static final int CHK_OUT_FAIL_DEBT_CHARHED = 403;
public static final int CHK_OUT_FAIL_SYSTEM_ERR = 499;
// ... 중략
public boolean checkInServ(UserInfo user) {
// ...
switch(result) {
case CHK_OUT_RESULT_SUCCESS:
// ...
break;
case CHK_OUT_RESULT_FAIL:
// ...
break
}
}
}
public class CheckOutService {
public static final int CHK_IN_RESULT_SUCCESS = 30;
public static final int CHK_IN_RESULT_FAIL = 39;
public static final int CHK_IN_FAIL_ALREADY_IN = 301;
public static final int CHK_IN_FAIL_NOT_REVERSED = 302;
public static final int CHK_IN_FAIL_DEBT_CHARHED = 303;
public static final int CHK_IN_FAIL_SYSTEM_ERR = 399;
// ... 중략
public boolean checOutServ(UserInfo user) {
// ...
switch(result) {
case CHK_IN_RESULT_SUCCESS:
// ...
break;
case CHK_IN_RESULT_FAIL:
// ...
break
}
}
}
public class PayService {
public static final int PAY_RESULT_SUCCESS = 50;
public static final int PAY_RESULT_FAIL = 51;
public static final int PAY_FAIL_DEUBT_CHARGE = 501;
public static final int PAY_FAIL_DEUBT_RETURN = 502;
public static final int PAY_FAIL_DEUBT_CHARGE = 503;
// ... 중략
public boolean payServ(UserInfo user) {
// ...
switch(result.getSuccess()) {
case PAY_RESULT_SUCCESS:
// ...
break;
case PAY_RESULT_FAIL:
case(failResult):
case PAY_FAIL_DEUBT_CHARGE:
// ..
break;
case PAY_FAIL_DEUBT_RETURN:
// ..
break;
case PAY_FAIL_DEUBT_CHARGE:
// ..
break;
break;
}
}
}
ConstValue에 있는 상수 중 일부가 이동되어 추가되었고, 각 로직에서만 사용되던 상수들은 그에 맞는 Service 클래스로 이동하여 ConstValue 클래스에 대한 의존성이 낮아지거나, 없어졌다.
또한, 각 로직에 필요한 상수는 로직과 관련된 클래스에서 찾고 추가할 수 있어 유지보수성도 높아졌다.
상수를 관리하는 클래스를 만드는 것은 설계 초기 매우 유용한 방법이지만, 상수가 많아지고, 상수들의 사용 범위가 모호해지면 관리를 위한 상수 지합이 원하는 코드를 찾는 순간부터 독이 된다.
따라서 초기 좋은 모습을 유지하려면 무조건적인 사용을 막고, 목적에 맞게 상수를 명확히 구분짓는 습관이 필요하다.
특히, 시스템 관련 상수는 로직 상수와 섞이지 않게하고, 로직 관련 상수는, 되도록 로직과 가까운곳에 있는것이 좋다.
내용 좋네요 감사합니다 ^^