JAVA/Java

객체지향 생활 체조

dodop 2021. 12. 12. 17:05

 

 

객체지향 생활 체조는 절차지향적인 생각에서 벗어나 객체지향 코드를 작성하기 위한 가이드 라인이다. 

기존에 작성한 코드는 절차지향중심으로 작성된 부분이 매우 많았기에 공부하면서 수정하고자 한다. 

 

 

객체지향 생활 체조 

객체지향 생활 체조는 9가지의 지침으로 나뉜다.

1. 메서드 당 들여쓰기 한 번 유지

2. else 예약어 사용 금지

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

4. 한 줄에 점을 하나만 찍기

5. 축약 금지(줄여쓰기 금지)

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

7. 2개 이상의 인스턴스 변수를 가진 클래스 사용 금지

8. 일급 컬렉션의 사용

9. getter / setter / property의 사용 금지

 

 

① 메서드 당 들여쓰기 한 번 

메서드의 응집력을 높이기 위해서 거대한 메소드를 사용하는 것을 지양한다. 즉, 하나의 메서드는 한가지 일에 집중할 수 있도록 설정한다. (1단계로 일에 집중하도록 설정하기)

class Board {
   String board() {
      StringBuffer buf = new StringBuffer();
      for (int i = 0; i < 10; i++) {
         for (int j = 0; j < 10; j++)
            buf.append(data[i][j]);
         buf.append("\n");
      }
      return buf.toString();
   }
}
class 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");
   }
}

여러목적을 가지고 있던 메소드 하나는 아래와 같이 하나의 메소드당 한가지 목적만을 가질 수 있도록 바꿔 주어야 한다. 

Intellij에서 메소드 추출의 단축키는 command + option + m 버튼을 누르면 된다. 

 

 

 

② else 예약어의 사용 금지

if-else 구문에서 return 값을 잘 설정한다면 불필요한 중첩 부분을 삭제할 수 있다. 

public static Node head() {
   if (isAdvancing()) { return first; }
   else { return last; }
}
public static Node head() {
   return isAdvancing() ? first : last;
}

 

 

 

③ 모든 원시값과 문자열을 포장

의미를 알수 없는 원시값과 문자열은 모두 분명한 의미를 알 수 있도록 클래스등의 방법을 사용하여 포장해 주어야 한다. 

int number = 13;
public class ReservationNumber{
	int number;
}

 

 

 

④ 한 줄에 점은 하나만 찍기

코드 한 줄에 점이 두개 이상 존재한다면 다른 두 개의 객체를 동시에 다루어 대상 객체가 다른 객체에 깊이 관여하게 될 수 있다. 이는 객체지향관점에서 캡슐화를 어기고 있다는 뜻이 된다. 

class Board { 

   class Piece {
      String representation;
   }
   
   class Location {
      Piece current;
   }
 
   String boardRepresentation() {
      StringBuffer buf = new StringBuffer();
      for (Location l : squares())
         buf.append(l.current.representation.substring(0, 1)); //두 개의 객체를 다루고자 한다.
      return buf.toString();
   }
}
class Board {
 
   class Piece {
      private String representation;
      String character() {
         return representation.substring(0, 1);
      }
 
      void addTo(StringBuffer buf) {
         buf.append(character());
      }
   }
   class Location {
      private Piece current;
 
      void addTo(StringBuffer buf) {
         current.addTo(buf);
      }
   }
 
   String boardRepresentation() {
      StringBuffer buf = new StringBuffer();
      for (Location l : squares())
         l.addTo(buf);
      return buf.toString();
   }
}

 

 

⑤ 축약 금지

클래스, 메서드, 변수 등의 이름을 줄여 혼동하는 일이 발생하지 않도록 하자. 

int checkNumber; 

int cn; //잘못된 표기

 

 

⑥ 모든 엔티티를 작게 유지

50줄 이상의 클래스는 모두 한 가지 일 이상의 일을 하게 되므로 코드의 재사용이 어렵게 만든다. 이를 방지 하기 위해서 50줄 이상의 클래스나 10개 이상의 파일을 지닌 패키지는 없애도록 해주자. 

 

 

 

⑦ 2개 이상의 인스턴스 변수를 가진 클래스 사용 금지

기존 클래스에 새로운 인스턴스를 하나 더 추가하는 일은 응집도를 떨어뜨리게 되므로 2개 이상의 인스턴스를 가지지 않도록 하자. 

{
   String first;
   String middle;
   String last;
}
class Name {
   Surname family;
   GivenNames given;
}
 
class Surname {
   String family;
}
 
class GivenNames {
   List<String> names;
}

 

 

⑧ 일급 컬렉션 사용

일급 컬렉션은 클래스를 wrapping하면서 다른 필드를 지니지 않는 클래스를 뜻한다. 

public class User{
	private String name;
    private List<Car> Cars;
}

public class Car{
	private String name;
    private String number;
}
public class User{
	private String name;
    private Cars cars;
}

public class Cars{		// Wrapper class (래핑한 클래스 외에 존재하지 않는다)
	private List<Car> cars;
}

public class Car{
	private String name;
    private String number;
}

이렇게 생성된 래퍼클래스는 클래스의 상태와 행위각각 관리 할 수 있다. 즉, 만약 차의 갯수가 10개 이상 존재하지 않아야 하고 이름을 가지고 차를 찾으려고 할 때, 래퍼클래스가 존재하지 않는다면 List<Car>를 사용하는 모든 클래스에서 위와 같은 검사 로직을 넣어주어야 한다. 하지만 일급클래스를 가지고 있다면 이 클래스에서 로직을 넣어주고 다른 클래스는 이 로직을 계속해서 사용하기만 하면 된다. 

 

 

public class Cars{
	private List<Car> cars;
    
    public Cars (List<Car> cars){
    	validateSize(cars);
        this.cars = cars;
    }
    
    private void validateSize(List<Car> cars){
    	if(cars.size() > 10){
        	return throw new IllegalArgumentException("차의 종류는 10개를 초과할 수 없습니다.");
        }
    }
    
    public find(String name){
    	return cars.stream()
            .filter(car::isSameName)
            .findFirst()
            .orElseThrow(RuntimeException::new)
    }
}

 

 

 

⑨ getter / setter / property 사용 금지

getter와 setter의 사용을 아주 금지하는 것은 아니지만, 남용하면 값의 변화를 예측할 수 없어지기 때문에 상태노출을 줄이기 위해 사용을 지양해야 한다. 직접적으로 setter, getter를 사용하기보다 엔티티 내에 각각의 역할을 수행할 수 있는 메소드를 만들어 기능을 수행하도록 하자. 

public class Card{
  private int number;

  public void setNumber(int number) {
      this.number = number;
  }

  public int getNumber() {
      return number;
  }
}

// 사용
card.setNumber(card.getNumber() + 1);
public class Card{
  private int number;
  
  public void addNumber(int plus) {
      number += plus;
  }
}

// 사용
card.addNumber(1);

 

 

 

 

 

(참고한 사이트)

https://developerfarm.wordpress.com/2012/02/03/object_calisthenics_summary/

 

객체지향 생활 체조 총정리

소트웍스 앤솔러지에서 발췌한 “객체지향 생활 체조”라는 글을 10차례에 이어 연재했습니다. 지금까지 연재한 9가지 객체지향 훈련 지침을 정리하면 다음과 같습니다. 규칙 1: 한 메서드에 오

developerfarm.wordpress.com

https://velog.io/@tigger/%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98

 

일급 컬렉션

본 글은 일급 컬렉션 (First Class Collection)의 소개와 써야할 이유를 참고 했다.일급 컬렉션이란 단어는 소트웍스 앤솔로지의 객체지향 생활체조 규칙 8. 일급 콜렉션 사용에서 언급되었다.Collection

velog.io

https://devwooks.tistory.com/59

 

객체지향 생활체조 원칙 (Object Calisthenics)

박재성 님의 <자바 플레이그라운드 with TDD> 강의를 들으면서 좋은 코드를 작성할 수 있는 여러 팁을 배우고 있다. 그 중 수업 초기부터 등장하는 <객체지향 생활체조 원칙>이 재미있다. 이 용어

devwooks.tistory.com

https://bethejustice.tistory.com/12

 

객체지향 생활 체조

<!doctype html> 객체지향 생활 체조 객체지향 생활 체조 The ThoughtWorks Anthology 챕터중 객체지향생활체조부분 절차지향적인 코딩이 아닌 객체지향적인 코딩을 연습할 목적으로 만들어진 가이드 라인

bethejustice.tistory.com