2019-04-04 [java-study] 객체지향 생활 체조 정리
04 Apr 2019 | java TDD객체지향 생활 체조 정리
규칙 1: 한 메서드에 오직 한 단계의 들여쓰기만 한다.
메서드의 크기가 크면 클수록 응집력이 떨어진다. 응집력이 강한 코드를 작성하기 위해서는 메서드가 한가지 일만하도록 즉 메서드당 하나의 제어 구조나 하나의 문장 단락으로 이루워지도록 코드 작성이 이루워져야한다.
한 메서드 안에 중첩된 제어구조가 있다면 다단계의 추상화를 코드로 짠 것이고 메서드가 한 가지 이상의 일을 하고있다는 뜻이다.
class Board {
...
String board() {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < 10; i++) { //collectRows();
for (int j = 0; j < 10; j++) //collectRow();
buf.append(data[i][j]);
buf.append("\n");
}
return buf.toString();
}
}
lass Board {
...
String board() {
StringBuffer buf = new StringBuffer();
collectRows(buf);
return buf.toString();
}
void collectRows(StringBuffer buf) {
for (int i = 0; i < 10; i++)
collectRow(buf, i);
}
void collectRow(StringBuffer buf, int row) {
for (int i = 0; i < 10; i++)
buf.append(data[row][i]);
buf.append("\n");
}
}
다음은 위의 코드를 한가지 일을 하는 메소드로 이루어지도록 리팩토링 한것이다.
리팩토링을 하므로써 여러가지 장점을 얻을 수 잇다.
- 메소드를 분리시키므로 메소드를 재사용할 수 있음.
- 각 메소드는 이름을 가지므로 코드의 가독성이 향샹됨(메소드명 잘 짜야함) => 주석을 작성하지 않아도 됨
- 유지보수나 버그를 찾을 때 좋음.
규칙 2 : else 예약어 금지
보통 if문을 사용 하면 else-if, else문을 자연스럽게 사용하게 된다. if문은 누구나 쉽게 이해 할 수 있는 논리 연산자이다. 하지만 else, else-if문은 가독성이 떨어지고 복잡성이 증가한다. 또한 if문을 조건을 봐야 else문을 이해 할 수 있고 else문이 다 실행되야만 if문이 끝난다.
public int calculate(int firstValue, char operator, int secondValue){
if(operator == '+'){
return add(firstValue, secondValue);
}else if(operator == '-'){
return subtract(firstValue, secondValue);
}else if(operator == '*'){
return multiply(firstValue, secondValue);
}else if(operator == '/'){
return divide(firstValue, secondValue);
}else{
throw new RuntimeException();
}
}
public int calculate(int firstValue, char operator, int secondValue) {
if (operator == '+')
return add(firstValue, secondValue);
if (operator == '-')
return subtract(firstValue, secondValue);
if (operator == '*')
return multiply(firstValue, secondValue);
if (operator == '/')
return divide(firstValue, secondValue);
throw new RuntimeException();
}
위의 코드는 if-else문 아래는 else문을 사용하지 않았다.
else문을 사용하지 않고 사용했을 시 여러가지 장점을 얻을 수 있다.
- 코드의 가독성이 좋아짐
return을 활용하여 특정 조건일때 반환하면 코드를 더욱 간단하게 만들수있음.- 유지보수나 버그를 찾을 때 좋음.
그 외 다형성을 활용한 방법
public void barkAnimal(Animal animal) {
if (animal instanceof Tiger) {
System.out.println("어흥");
} else if (animal instanceof Lion) {
System.out.println("으르렁");
}
}
public interface Barkable {
public void bark();
}
public class Tiger extends Animal {
public void bark() {
System.out.println("어흥");
}
}
public void barkAnimal(Barkable animal) {
animal.bark();
}
- 조건문을 사용하였을 때보다 유지보수가 좋아짐
- 코드의 의도가 분명하게 보임
규칙 3 : 원시값과 문자열의 포장
원시값을 파라미터로 사용하는 메서드를 작성할 경우, 컴파일러가 의미적으로 맞는 코드작성을 안내해 줄 수 있는 방법이 없을 뿐만 아니라, 원시값의 방어코드를 작성하면서 중복이 발생할 가능성이 많다.
int값은 그냥 아무 의미 없는 스칼라 값일 뿐 (주석, 변수명 등으로 정보 전달) -> 객체
- 유지보수에 좋음
- 왜 쓰고 있는지에 대한 정보를 전달
- 재사용성 증가
규칙 4 : 한 줄에 한 점만을 사용
한 줄에 여러개의 점이 있다면 어떤 객체가 동작을 맡고 있는지 찾기 힘들고 여러 개의 점이 들어 있는 코드 몇 줄을 들여다보기 시작하면 책임 소재의 오류를 많이 발견하기 시작한다. 한줄에 점들이 많다는 말은 다른 객체에 깊숙히 관여하고 있다고 말 할 수 있다. 즉 캡슐화를 어기고 있다는 증거이다. 객체가 자기 속을 들여다보려 하기보다는 뭔가 작업을 하도록 만들 필요가 있다.
buf.append(l.current.representation.substring(0, 1));
한 줄에 여러개의 점이 존재하는 코드이다
String character() {
return representation.substring(0, 1);
}
void addTo(StringBuffer buf) {
buf.append(character());
}
void addTo(StringBuffer buf) {
current.addTo(buf);
}
l.addTo(buf);
꼬리 물기식으로 기능별 메소드를 분리할 수 있다.
- 메소드를 재사용할 수 있음.
- 메소드 이름 짓기 중요
- 긴 코드를 이해하기 위해서 주석을 달 필요가 없음
규칙 5 : 축약 금지
클래스, 메서드, 변수 등 다양한 이름들이 존재한다. 많은 이름을 사용하면서 간단한 변수의 이름은 줄이려는 경향이 있다. 만약 단어 사용이 반복되어 축약하고 있다면 메소드를 활용할 기회를 놓치고 있는 것이다. 단어를 축약하면서 개발자들의 소통에 문제가 생기고 단어마다 주석을 달며 시간을 허비 할 수 있다. 주석은 필요한 내용만을 적고 클래스, 메서드, 변수명에 역할을 명확하게 작성하여 가독성을 높일 필요가 있다.
+) 클래스와 클래스 변수에 표현이 중복되지 않도록 해야함.
- class
Order이고 메소드shipOrder()Order 중복 -> 메소드명ship()(변수도 마찬가지)
규칙 6 : 모든 엔티티를 작게 유지
50줄 이상 되는 클래스와 파일이 10개 이상인 패키지가 없어야한다.
50줄 이상인 클래스는 한가지 이상의 일을 한다고 보고 코드의 이해나 재사용을 점점 어렵게 한다. (50줄짜리 클래스는 스크롤 하지 않아도 됨) 클래스가 점점 작아지고 하는 일이 줄어들며 패키지 크기를 제한함에 따라 패키지가 하나의 목적을 달성하기 위해 모인 연관 클래스들의 집합을 나타낸다는 사실을 알아차리게 된다. 패키지 또한 클래스처럼 응집력 있고 단일한 목표가 있어야한다.
- 50줄 이상으로 크기가 커지면 어쩔 수 없는 거임. (그 만큼 프로젝트 크기가 큼)
규칙 7 : 2개이상의 인스턴스 변수를 가진 클래스 사용금지
대부분의 클래스들이 간단하게 하나의 상태 변수를 처리하는 일을 맡아 마땅하지만, 몇몇 경우 둘이 필요할 때가 있습니다. 하지만 새로운 인스턴스 변수를 하나 더 추가하게 되면 클래스의 응집도는 떨어집니다.
class Name {
String first;
String middle;
String last;
}
새로운 인스턴스 변수를 하나 더 기존 클래스에 추가하면 클래스의 응집도는 떨어진다. 일반적으로 많은 인스턴스 변수를 지닌 클래스를 대상으로 응집력 있는 단일 작업을 설명할 수 있는 경우는 거의 없다.
class Name {
Surname family;
GivenNames given;
}
class Surname {
String family;
}
class GivenNames {
List<String> names;
}
규칙 8 : 일급 콜렉션 사용
Map<String, String> map = new HashMap<>();
map.put("1", "A");
map.put("2", "B");
map.put("3", "C");
public class GameRanking {
private Map<String, String> ranks;
public GameRanking(Map<String, String> ranks) {
this.ranks = ranks;
}
}
Collection을 Wrapping하면서, 그 외 다른 멤버 변수가 없는 상태를 말함.
규칙3과 같은 원리이다. 또한 일반적인 콜렉션은 범용적으로 사용되기 위해 다양한 메소드들을 제공 하는데 클래스로 감싸면서(Wrap) 외부에 노출할 메소드를 조절할 수 있다.
일급 컬렉션 (First Class Collection)의 소개와 써야할 이유 자세한 내용 참고
규칙 9 : 게터 /세터/ 속성 사용 금지
- 세터를 사용하지 않으면 객체가 생성된 후 임의로 값이 변하는 것을 방지할 수 있다. 외부에서 임의로 객체 내부의 값을 변경하지 않고 그 책임을 객체가 지도록 한다.
- 클라이언트에서 게터를 통해 값을 가져와 확인하고(if-else) 뭔가 처리를 하려고 하지 말고 그냥 그 객체에 책임을 넘기도록 한다.
Don't ask! Just Tell이라고 한다.
참고자료
https://developerfarm.wordpress.com/
https://github.com/iamkyu/TIL/blob/master/object-calisthenics/object-calisthenics.md
https://blog.one0.me/java
https://jojoldu.tistory.com/412
Comments