24 May 2017

Implementation Patterns 02

State

  • 객체는 외부에 드러나는 행위 와 행위를 지원하기 위한 상태 를 묶어주는 편리한 단위
    • 객체 사용으로 프로그램에 있는 모든 상태를 잘게 쪼개서 적당한 곳에 저장할 수 있다.
      • 객체를 통해 변경할 수 있는 상태의 범위가 훨씬 줄어들므로, 코드 수정시 어떤 영향을 받을지 분석하기 쉽다.
  • 상태 관련 패턴
    • 상태: 시간에 따라 변화하는 값을 사용하여 연산
    • 접근: 상태에 대한 접근을 제한해서 유연성을 조절
    • 직접 접근: 객체 내의 상태를 직접 접근
    • 간접 접근: 좀 더 나은 유연성을 위해 메소드를 통해 상태 접근
    • 공용 상태: 클래스의 모든 인스턴스에 적용되는 상태는 필드에 저장
    • 가변 상태: 같은 클래스의 인스턴스마다 다른 상태를 유지해야할 경우, 상태를 맵에 저장
    • 외재 상태: 객체와 연동되는 특수 상태는 상태의 소유자가 소유하는 맵에 저장
    • 변수: 상태 접근에 필요한 네임스페이스 제공
    • 지역 변수: 지역 변수는 단일 범위내에서만 유효한 상태를 저장
    • 필드: 필드는 객체 생성될 때부터 소멸될 때까지 상태를 저장
    • 파라미터: 파라미터는 메소드가 활성화된 동안 상태를 전달
    • 수집 파라미터: 여러 개의 메소드를 통해 복잡한 결과를 얻기 위해 파라미터 전달
    • 파라미터 객체: 자주 사용하는 긴 파라미터 목록은 객체로 만들어서 통합
    • 상수: 변하지 않는 상태는 상수로 저장
    • 역할 제시형 작명: 변수 이름은 연산에서의 역할을 반영하여 짓는다.
    • 선언형 타입: 변수에 대한 일반적 타입을 선언한다.
    • 초기화: 변수 초기화는 가급적 선언적으로 한다.
    • 열성적 초기화: 인스턴스가 생성될 때 필드를 초기화한다.
    • 게으른 초기화: 초기화 비용이 높은 객체의 경우, 객체가 실제 사용되기 직전에 초기화한다.


상태

  • 상태는 프로그래밍에 있어서 유용한 도구
    • 변수나 할당을 사용하지 않는 프로그래밍에서는 수많은 효과적인 사고 기법들을 포기해야 함
  • 객체지향언어는 상태를 다루는데 적합한 전략
    • 전체 시스템을 매우 작은 단위로 쪼갠 후, 각각에 대해 엄격한 접근을 적용하여 알지 못하는 사이에 상태가 변해버리는 문제를 줄여준다.
    • 객체를 사용하면 빠르고 정확하게 변수에 대한 모든 접근을 확인할 수 있다.
  • 서로 유사한 상태를 묶어서 효과적으로 관리
    • 두 개의 상태가 동일한 연산에서 사용될 경우
    • 동일한 시점에 생성되고 소멸될 경우


직접 접근

  • 데이터를 가져오거나 저장하는 것을 나타내는 가장 간단한 방법은 다음과 같이 직접 변수 접근을 사용하는 것이다.
x = 10;
  • 위와 같이 직접 변수에 접근하는 것은 표현이 명확해진다. 그러나 이 명확성을 위해 유연성이 희생된다. 프로그램 여러 부분에서 어떤 변수에 직접 접근하면 수정 시, 모든 부분을 하나하나 바꾸어야 한다.

직접 접근이 많은 코드는 원활한 커뮤니케이션을 방해하는 요소이다.


간접 접근

  • 메소드 호출을 통해 외부에 상태 변화를 숨김으로써 데이터에 접근하는 코드가 유연성을 확보할 수 있다.
  • 간접 접근이 유용한 경우로 2개 이상의 데이터가 서로 의존관계가 있을 경우이다.
Rectangle void setWidth(int width) {
  this.width = width;
  area = width * height;
}


공용 상태

  • 프로그램 작성시, 여러 연산에서 같은 데이터 요소를 사용하는 경우가 있다.
    • 이런 경우는 다음과 같이 클래스로 각 데이터 요소들을 묶어 사용하는 것이 좋다.
class Point {
  int x;
  int y;
}
  • 위와 같이 공용 상태를 사용하면 코드만 봐도 이 객체를 만들기 위해 어떤 작업이 필요한지 쉽게 알 수 있다. 또한 이에 대한 정보를 명확하고 정확하게 전달할 수 있다.


변수

  • 변수를 범위에 따라 분류
    • 지역 변수
    • 필드 변수
      • public / default / protected / private
    • 정적 변수
  • 대부분의 경우에 지역 변수를 사용하고, 필요할 때 정적 변수와 필드 변수를 사용해서 객체 간의 의존성을 줄여야 한다.
    • 어떤 메소드가 수행 될 때만 필드 변수를 유효화할 수도 있지만, 이런 코드는 좋지 않다.
  • 변수의 생명은 가급적 변수의 범위에 가까워야 한다.
    • 같은 범위에서 정의된 변수는 모두 같은 생명 기간을 가져야 한다.


지역 변수

  • 지역 변수는 변수가 선언된 지점이 속한 범위에서만 접근할 수 있다.
    • 가급적 지역 변수는 사용되기 직전 에 선언하는 것이 좋다.


필드 변수

  • 필드 변수의 범위와 생명 기간은 필드를 갖고 있는 객체와 같다.

  • 도우미 필드: 객체의 여러 메소드에서 공통적으로 특정 객체를 파라미터로 받는다면, 파라미터를 도우미 필드로 설정하고, 생성자에서 필드를 설정하는 것도 고려
  • 플래그: boolean과 같은 플래그 설정을 할 수 있다는 것은 객체가 두 가지 다른 방식으로 동작함 이라고 표현하는 것이다. 플래그에 따라 결정을 내리는 코드가 중복되어 있다면 전략 필드를 사용하는 것이 좋다.
  • 전략: 객체의 연산을 하는데에 있어서 여러가지 다양한 방법이 있다면 그 부분을 수행하는 객체를 필드에 저장
    • 객체의 생명기간 동안 방법이 바뀌지 않는다면 생성자에서 전략 필드 설정
    • 바뀐다면 전략 필드를 수정하는 메소드를 제공


파라미터

  • 파라미터 사용으로 정적 변수나 필드 변수를 사용할 때마다 객체 간의 의존성을 줄일 수 있다.

  • 다음과 같이 결과로 여러 객체를 받아야 할 경우 파라미터를 전달해서 결과를 수집하는 것이 직관적이다.
    List<Data> asList() {
    List<Data> results = new ArrayList<>();
    addTo(results);
    return results;
    }
    void addTo(List<Data> elements) {
    elements.add(getValue());
    
    for (Node each: getChildren()) {
      each.addTo(elements);
    }
    }
    
  • 어떤 메소드가 특정 타입의 여러 파라미터를 취할 경우, 가변 인자를 사용할 수 있다.
    • 가변 인자는 반드시 마지막 파라미터여야 한다.
  • 여러 개의 파라미터가 함께 여러 메소드로 전달될 경우, 파라미터들을 묶어 하나의 객체로 표현
    • 코드가 짧아지고, 의도를 좀 더 명확히 전달할 수 있다.


상수

  • 변화하지 않는 데이터를 프로그램의 여러 부분에서 사용하는 경우 static final 로 선언
    • 상수 이름을 통해 그 값의 뜻을 명확하게 전달할 수 있다.


열성적 초기화

  • 변수가 선언되거나 생성되자마자 초기화
  • 가능하다면 선언문에서 초기화하는 것이 좋다.
    • 선언문에 필드를 초기화할 수 없다면 생성자에서 초기화
      class Library {
      List<Person> members = new ArrayList<>();
      ...
      }
      

Tags:
Stats:
0 comments