back_Java_06

객체지향 생활 체조 원칙 9가지

  1. 한 메서드에 오직 한 단계의 들여쓰기만 한다.

  2. else 예약어를 쓰지 않는다.

  3. 모든 원시 값과 문자열을 포장한다.

  4. 한 줄에 점을 하나만을 찍는다.

  5. 줄여쓰지 않는다(축약금지).

  6. 모든 엔티티를 작게 유지한다.

  7. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.

  8. 일급 컬렉션을 쓴다

  9. getter / setter / 프로퍼티를 쓰지 않는다.


1. 한 메서드에 오직 한 단계의 들여 쓰기만 한다.

WORST

class Board {
	public String board() {
		StringBuilder buf = new StringBuilder();

		for (int i = 0l i < 10; i++) {
			for (int j = 0; j < 10; j++) {
				buf.append(data[i][j]);
			}
			buf.append("\n");
		}

		return buf.toString();
	}
}

BETTER

class Board {
	public String board() {
		StringBuilder buf = new StringBuilder();

		collectRows(buf);

		return buf.toString();
	}

	private void collectRows(StringBuilder buf) {
		for (int  i = 0; i < 10; i++) {
			collectRow(buf, i);
		}
	}

	private void collectRaw(StringBuilder buf, int row) {
		for (int i = 0; i  10; i++) {
			buf.append(data[row][i]);
		}

		buf.append("\n");
	}
}

2. else 예약어를 쓰지 않는다.

WORST

public void login(String username, String password) {
	if (userRepository.isVald(username, password)) {
		redirect("homepage");
	} else {
		addFlash("error" , "Bad credentials");

		redirect("login");
	}

}

BETTER

public void login(String username, String password) {
	if (userRepository.isVald(username, password)) {
		return redirect("homepage");
	}
	
	addFlash("error" , "Bad credentials");

	return redirect("login");
}
  • 자주 가능한 것은 아니지만, 반환문을 매개변수화 할 수 있도록 변수 도입도 가능하다.

public void login(String username, String password) {
	String redirectRoute = "homepage";
	
	if (!userRepository.isVald(username, password)) {
		addFlash("error" , "Bad credentials");
		redirectRoute = "login";
	}

	redirect(redirectRoute); // 최종적으로 두 로직 모두 여기로 전달됨
}
  • 조건문을 정리하기 위해 다형성을 사용한다거나, Null Object, State 패턴, Strategy 패턴이 도움 될 수 있다.


3. 모든 원시값과 문자열을 포장한다.

  • 안티 패턴 중에 하나인 Primitive Obsession 을 피하기 위해서다.

  • 만약 당신이 사용하는 변수에 행동이 포함된다면, 캡슐화 하기를 적극 추천한다.

  • 이를 DDD(도메인 주도 개발) 에서는 특히 VO(Value Object)라고 부른다.


4. 한줄에 점 하나만 찍는다.

  • 보통 "메서드 호출을 연쇄적으로 연결해서는 안된다"라는 원칙이 존재한다.

    • 하지만 Fluent Interface 를 만들 때는 적용되지 않으며, 메소드 체인 패턴(Builder 패턴 같은 경우) 를 구현하는 경우 적용되진 않는다.

  • 단, 위의 경우를 제외하곤 보통 이 규칙을 준하는게 좋다.


5. 줄여 쓰지 않는다.

  • 축약하지 말자 이름은...!


6. 모든 Entity를 작게 유지하라!

  • 하나의 클래스는 50줄을 넘기지 않기를 권장하며, 하나의 Package는 10개 파일 담지를 않기로 권장한다.

  • 이는 class 가 길면 읽기 어렵다는 생각도 담겨 있다.


7. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.

  • 지키기 어려운 규칙이지만, 응집도를 높이고 캡슐화 정도를 높혀준다.

  • 왜 2개를 해야 하는가? 에서 원 글의 저자는 이러한 결론을 언급한다.

    • 단일 인스턴스 변수만을 사용해 상태를 유지하는 class

    • 2개의 개별 변수를 조화롭게 협력 시키는 class


8. 일급 컬렉션을 쓴다.

  • 일급 컬렉션이란, 컬렉션을 포함하는 해당 class 컬렉션을 제외한 다른 멤버 변수를 포함하지 말아야 한다.

  • 즉 예를 들면 Unit이라는 클래스가 존재하고, 이를 여러개 묶어 List<Unit> 단위로 사용하는 Units는 1급 컬렉션이 될 수 있다.

  • 각 컬렉션은 자체 클래스로 감싸지기 때문에, 이제 컬렉션과 관련된 행동은 1급 컬렉션을 통해 사용할 수 있다.

  • 컬렉션의 모든 API 또한 open 하지 않게 되는 장점도 있으며, 이는 컬렉션이 add, delete, peek, sort 라는 API 를 지원하는데, 1급 컬렉션의 publi API 가 add, delete 뿐이라면 외부의 사용자는 오직 2가지 메서드를 사용하게 된다.


9. getter/setter/프로퍼티 를 쓰지 않는다.

  • 핵심은 외부에서 어떤 행위를 결정하는게 아니라, 객체의 상태를 가져오는 정도라면 불가피하지만, 그렇지 않다면 한 객체의 상태에 기반한 모든 행동은 객체가 스스로 결정하도록 해야한다.

WORST

// Game

private int score;

public void setScore(int score) {
	this.score = score;
}

public int getScore() {
	return score;
}

//Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

BETTER

// Game
public void addScore (int delta) {
	score += delta;
}

//Usage
game.addScore(ENEMY_DESTROYED_SCORE);
  • 절대 Setter 는 쓰지 말것

Last updated