Effect Java 제2판(Joshua Bloch / Addison Wesley) 을 공부하고 정리한 내용 입니다.

5. 불필요한 객체의 생성을 피하자.

기능적으로 동일한 객체를 필요할 때마다 매번 새로 생성하기보다는 하나의 객체를 재사용하는 것이 좋을 때가 많다. 재사용을 하면 객체 생성에 소요되는 비용(시간과 자원)이 절감되므로 실행 속도가 더 빨라지고 코드도 보기 좋게 작성할 수 있다. 불변(immutable) 객체는 항상 재사용 가능하다.

해서는 안될 극단적인 예로 다음 코드를 생각해 보자

 
// 이렇게 하지 말자
String s = new String("stringette");

// 위 문장의 개선된 버전
String s = "stringette";

여기서는 실행될 때마다 새로운 인스턴스를 생성하지 않고 하나의 String 인스턴스를 사용하며, 같은 JVM에서 실행되는 어떤 코드에서도 동일한 문자열 리터럴(literal)을 갖도록 재사용 될 것이다.

불변 클래스의 불필요한 객체 생성을 막으려면 생성자보다는 static 팩토리 메소드를 사용하는 것이 좋다. 불변 객체의 재사용에 덧붙여, 객체(의 상태)가 변경되지 않는다면 가변(mutable) 객체도 재사용 가능하다.

해서는 안될 다른 예

 
public class Person {
	private final Date birthDate;
	
	// 다른 필드와 메소드 및 생성자는 생략
	// 이렇게 하지 말자!
	public boolean isBabyBoomer() {
		// 불필요한 객체 생성
		Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
		gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
		Date boomStart = gmtCal.getTime();
		gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
		Date boomEnd = gmtCal.getTime();
		return birthDate.compareTo(boomStart) >= &&
				birthDate.compareTo(boomEnd) < 0;
	}
}

 

isBabyBoomer 메소드에서는 매번 호출될 때마다 하나의 Calendar 객체와 TimeZon 객체 및 두개의 Date 인스턴스를 불필요하게 생성한다. 아래 버전에서는 static 초기자(initializer)를 사용해서 그런 비효율적인 객체 생성을 막는다.

 
public Person {
	private final Date birthDate;
	// 다른 필드와 메소드 및 생성자는 생략
	/**
	 * 베이비 붐의 사작과 종료 일자.
	 */
	private static final Date BOOM_START;
	private static final Date BOOM_END;
	
	static {
		Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
		gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
		BOOM_START = gmtCal.getTime();
		gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
		BOOM_END = gmtCal.getTime();
	}
	
	public boolean isBabyBoomer() {
		return birthDate.compareTo(BOOM_START) >= &&
				birthDate.compareTo(BOOM_END) < 0;
	}
}

 

이 개선된 버전에서는 Person 클래스가 초기화될 때 Calendar, TimeZone, Date 인스턴스를 한번만 생성한다.
성능 향상은 물로 코드도 더 명확하다

자바 1.5 이상의 버전에서는 불필요한 객체를 생성하는 새로운 방법이 있다. 오토박싱(autoboxing)이라고 하는데, 기본형 데이터를 이에 대응되는 박스화 기본형(boxed primitive) 클래스 객체로 자동 변환해주는 기능이다. 이와는 반대로 박스화 기본형 클래스 객체를 기본형으로 자동 변환해주는 것을 오토언박싱(autounboxing)이라 한다.

 
// 무지하게 느린 프로그램! 어디서 쓸데없는 객체가 생성되는지 찾아보자.
public static void main(String[] args) {
	Long sum = 0L;
	for (long i = 0; i < Integer.MAX_VALUE; i++) {
		sum += i;
	}
	System.out.println(sum);
}

의도하지 않은 오토 박싱이 생기지 않도록 주의하자!!

그러나 객체 생성은 많은 비용이 드니 피해야 한다는 의미로만 이 항목을 오해해서는 안 된다. 도리어 생성자에서 일을 거의 하지 않는 작은 객체의 생성과 재상용은 비용이 적게 든다. 프로그램의 명확성과 단순성 및 능력을 향상시키기 위해서라면 객체를 추가로 만드는 것도 좋은 일이다. 이와는 반대로, 우리가 직접 객체 풀(object pool)을 만들고 유지하여 객체 생성을 피하려는 방법은 좋지 않다. 단, 풀에 유지할 객체들이 대단히 무거워서 생성 비용이 많이 드는 것이라면 고려해 볼만하다.(데이터베이스 연결)

Posted by outliers
,

Effect Java 제2판(Joshua Bloch / Addison Wesley) 을 공부하고 정리한 내용 입니다.

4. private 생성자를 사용해서 인스턴스 생성을 못하게 하자.

static 메소드와 static 필드만을 모아 놓은 클래스를 만들 필요가 종종 있을 것이다. 그런 유틸리티(Utility) 클래스들은 인스턴스를 생성하지 못하게 설계되었다. 인스턴스 생성이 무의미 하기 때문이다.(Math, Arrays 등) 그러나 그런 클래스일지라도 명시적으로 지정한 생성자가 없을 때는 컴파일러가 디폴트(default) 생성자를 만들어 준다.
클래스 사용자 입장에서는 이 생성자가 다른 것과 차이가 없으며, 인스턴스 생성이 가능한 클래스로 오인될 수 있다.

우리가 private 생성자를 정의하면 인스턴스 생성이 불가능한 클래스를 만들 수 있다.

 
// 인스턴스 생성이 불가능한 유틸리티 클래스
public class UtilityClass {
	// 디폴트 생성자가 자동으로 생기는 것을 방지한다.
	private UtilityClass() {
		throw new AssertionError();
	}
	... // 이하 생략
}

명시적으로 정의한 생성자가 private 이므로 이 클래스 외부에서는 생성자 호출이 불가능 하다.

Posted by outliers
,

Effect Java 제2판(Joshua Bloch / Addison Wesley) 을 공부하고 정리한 내용 입니다.

3. private 생성자나 enum 타입을 사용해서 싱글톤의 특성을 유지하자.

싱글톤(singleton)은 정확히 하나의 인스턴스만 생성되는 클래스이다.
자바 1.5 버전 이전에는 싱글톤을 구현하는 방법이 두 가지 있었으며, 두 방법 모두 생성자를 private으로 하고, 유일한 인스턴스에 접근할 수 있도록 public static 멤버를 외부에 제공한다.

1. public final 필드를 갖는 싱글톤

 
// public final 필드를 갖는 싱글톤
public class Elvis {
	public static final Elvis INSTANCE = new Elvis();
	private Elvis() { ... }
	
	public void leaveTheBuilding() { ... }
}

 

public이나 protected 생성자가 없으므로 private 생성자는 딱 한번만 호출되어 public static final 필드인 Elvis.INSTANCE를 초기화 한다. 클래스가 최초 한 번 초기화 될 때 정확히 하나의 인스턴스만 생길 것이다.

2. static 팩토리 메소드를 갖는 싱글톤

 
// static 팩토리 메소드를 갖는 싱글톤
public class Elvis {
	private static final Elvis INSTANCE = new Elvis();
	private Elvis() { ... }	
	public static Elvis getInstance() { return INSTANCE; }
	
	public void leaveTheBuilding() { ... }
}

 

1번 방법의 주된 장점은, 멤버 필드만 봐도 싱글톤 클래스인지 명쾌하게 알 수 있다는 것이다. 즉 public static 필드가 final 이므로 그 필드는 항상 같은 객체 참조를 갖는다.

2번 방법의 장점은, 클래스의 API를 변경하지 않고 클래스에서 반환하는 싱글톤 인스턴스의 형태를 바꿀 수 있는 유연성을 제공한다는 것이다. 예를 들어, 팩토리 메소드에서는 오직 하나의 인스턴스를 반환하지만, 이 메소드를 호출하는 각 스레드(thread)마다 하나씩의 인스턴스를 반환하도록 쉽게 수정할 수 있다.

1, 2번의 방법은 직렬화 하는데 불편함이 있다.

3. 자바 1.5 이후 버전에서는 싱글톤을 구현하는 다른 방법이 있다. 하나의 요소를 갖는 열거형 타입(enum)을 만들면 된다.

 
// 열거형 (Enum) 싱글톤 - 가장 좋은 방법임.
public enum Elvis {
	INSTANCE;
	public void leaveTheBuilding() { ... }
}

 

이 방법은 public 필드 방법과 기능적으로 동일하지만 더 간단하다. 그리고 복잡한 직렬화나 리플렉션(reflection) 상황에서도 직렬화가 자동으로 지원되고, 인스턴스기 여러 개 생기지 않도록 확실하게 보장해 준다. 싱글톤을 구현하는 가장 좋은 방법이다.

Posted by outliers
,

Effect Java 제2판(Joshua Bloch / Addison Wesley) 을 공부하고 정리한 내용 입니다.

2. 생성자의 매개 변수가 많을 때는 빌더(builder)를 고려하자.

Static 팩토리 메소드와 생성자는 공통적인 제약이 있다. 선택 가능한 매개변수가 많아질 경우 신축성 있게 처리하지 못한다는 것이다.

매개변수가 많은 생성자의 대안으로 텔리스코핑 생성자(telescoping constructor) 패턴과 자바빈즈(JavaBeans)패턴이 있다.
텔리스코핑 생성자 패턴은 매개변수의 수가 증가하면 클라이언트 코드 작서이 힘들고 코드의 가독성이 떨어지는 단점이 있고, 자바빈즈 패턴은객체가 일관된 상태를 유지하지 못할 수 있고, 스레드에서 안전성을 유지 하려면 프로그래머의 추가적인 노력이 필요하다는 단점이 있다.

2가지 패턴의 장점 결합한 텔리스코핑 생성자 패턴의 안전성과 자바빈즈 패턴의 가독성을 결합한 방법이 있다.

빌더(Builder) 패턴형태로써, 원하는 객체를 바로 생성하는 대신 클라이언트는 모든 필수 매개변수를 갖는 생성자(또는 static 팩토리 메소드)를 호출하여 빌더 객체를 얻는다. 그 다음에 빌더 객체의 세터 메소드를 호출하여 필요한 선택 매개변수들의 값을 설정한다. 마지막으로, 클라이언트는 매개변수가 없는 builder 메소드를 호출하여 불변 객체를 생성한다. 빌더는 자신이 생성하는 객체의 클래스에 포함된 static 멤버 클래스이다.

빌더패턴 예제

 
// 빌더(Builder) 패턴
public class NutritionFacts {
	private final int servingSize;
	private final int servings;
	private final int calories;
	private final int fat;
	private final int sodium;
	private final int carbohydrate;
	
	public static class Builder {
		// 필수 매개변수들
		private final int servingSize;
		private final int servings;
		
		// 선택 매개변수들 - 디폴트 값으로 초기화 한다.
		private int calories = 0;
		private int fat = 0;
		private int carbohydrate = 0;
		private int sodium = 0;
		
		public Builder(int servingSize, int servings) {
			this.servingSize = servingSize;
			this.servings = servings;
		}
		
		public Builder calories(int val) {
			calories = val;
			return this;
		}
		
		public Builder fat(int val) {
			fat = val;
			return this;
		}
		
		public Builder carbohydrate(int val) {
			carbohydrate = val;
			return this;
		}
		
		public Builder sodium(int val) {
			sodium = val;
			return this;
		}
		
		public NutritionFacts build() {
			return new NutritionFacts(this);
		}
	}
	
	private NutritionFacts(Builder builder) {
		servingSize = builder.servingSize;
		servings = builder.servings;
		calories = builder.calories;
		fat = builder.fat;
		sodium = builder.sodium;
		carbohydrate = builder.carbohydrate;
	}
}

NutritionFacts는 불변 클래스이며, 모든 매개변수의 디폴트 값을 한군데에 모아 둔다는 것에 주목하자. 빌더의 세터 메소드들은 연속적으로 여러 번 호출될 수 있도록 빌더 자신의 객체를 반환한다. 

 
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).
				sodium(35).carbohydrate(27).build();

빌더 패턴의 장점

  1. 클라이언트 코드는 작성이 쉽고, 가독성이 좋다.
  2. 생성자처럼 빌더는 자신의 매개변수에 불변 규칙(invariants)을 적용할 수 있고 build 메소드는 그런 불변 규칙을 검사할 수 있다.
  3. 여러 개의 가변인자(varargs) 매개변수를 가질 수 있다.
  4. 유연성이 좋다. 하나의 빌더는 여러 개의 객체를 생성하는데 사용될 수 있으며, 이러한 과정 중에 빌더의 매개변수는 다양하게 조정될 수 있다.
  5. 매개변수들의 값이 설정된 빌더는 훌륭한 추상 팩토리를 만든다. 즉, 클라이언트 코드에서는 그런 빌더를 메소드로 전달하여 그 메소드에서 하나 이상의 객체를 생성하게 할 수 있다.
 
public interface Builder<T> { 
	public T build();	
}

 

단점

  1. 어떤 객체를 생성하려면 우선 그것의 빌더를 생성해야 한다. 성능이 매우 중요한 상황에서는 문제가 될 수 있다.
  2. 빌더 패턴은 텔리스코핑 패턴보다 코드가 길어지므로, 매개변수가 많을 때(4개 이상)만 사용하는 것이 좋다.
    그러나 향후에 매개 변수가 추가될 수 있다는 것을 염두에 두자

정리
생성자나 static 팩토리 메소드에서 많은 매개변수를 갖게 될 클래스를 설계할 때는 빌더 패턴이 좋은 선택이다. 특히 선택 매개변수가 대부분인 경우가 그렇다. 그리고 그간 사용하던 텔리스코핑 생성자 패턴보다 빌더를 사용하면 클라이언트 코드의 가독성이 좋고 작성도 쉽다. 또한 빌더는 자바빈즈 패턴보다 훨씬 더 안전하다.

Posted by outliers
,

Effect Java 제2판(Joshua Bloch / Addison Wesley) 을 공부하고 정리한 내용 입니다.

1. 생성자 대신 static 팩토리(factory) 메소드 사용을 고려하자.

장점

  1. 생성자와 다릴 자기 나름의 이름을 가질 수 있다는 것이다. 만일 생성자에 전달되는 매개변수가 반환 객체를 잘 나타내지 못한다면, 이름을 잘 지은 static 팩토리 메소드가 더 사용하기 쉬우며, 이 메소드를 호출하는 클라이언트 코드도 이해하기 쉽다.

    자바 클래스는 동일한 시그니처를 갖는 생성자를 하나만 가질 수 있다. 이런 제약을 해결하기 위해 타입이 다른 매개변수의 순서만을 바꾸어 두 개의 생성자로 만든다. 이 방법은 좋지 않다. 필요한 생성자가 어떤 것인지 기억하기도 어렵고, 실수로 다른 생성자를 호출 할 수도 있기 때문이다. 또한 그런 생성자를 사용하는 코드를 파악하려는 사람들은 문서를 봐야만 이해할 수 있다.

    하나의 클래스에 동일한 시그니처를 갖는 여러 개의 생성자가 필요한 경우에는 생성자 대신 static 팩토리 메소드를 사용하되, 메소드 간의 차이점을 부각시키도록 신중하게 이름을 선정한다.

  2. 생성자와 달리 호출될 때마다 매번 새로운 객체를 생성할 필요가 없다는 것이다. 따라서 불변(immutable)클래스의 경우 이미 생성된 인스턴스를 다시 사용할 수 있으며, 불필요하게 중복된 인스턴스들이 생성되는 것을 방지하기 위해 이미 생성된 인스턴스들을 저장했다가 반복 사용할 수 있다.

    동일한 객체가 자주 사용될 때 특히 객체 생성 시 자원이나 시간이 많이 든다면 프로그램 성능을 크게 향상시킬 수 있다.
    static 팩토리 메소드는 여러 번 호출되더라도 이미 생성된 동일 객체를 반환할 수 있으므로, 클래스에서는 언제든지 인스턴스들의 존재를 직접 제어 할 수 있다. 이런 일을 하는 클래스를 인서튼스 제어(instance-coltrolled) 클래스라고 한다. 인스턴스를 제어하면 싱글톤, 인스턴스 생성 불가 클래스로 만들 수 있다. 불변 클래스에서 두 개의 동일한 인스턴스가 생기지 않도록 해준다. 클래스에서 이것을 보장해 준다면 인스턴스의 동일 여부를 확인할 때 equals(Object) 메소드 대신 == 연산자를 사용할 수 있으므로 프로그램 성능도 향상될 수 있다. 열거형(enum)에서는 이것을 보장하고 있다.

  3. 자신의 클래스 인스턴스만 반환하는 생성자와 달리, static 팩토리 메소드는 자신이 반환하는 타입의 어떤 서브타입(subtype) 객체도 반환할 수 있다. 따라서 반환되는 객체의 클래스를 선택해야 할 때 뛰어난 유연성을 제공한다.

  4. 매개변수화 타입(parameterized type)의 인스턴스를 생성하는 코드를 간결하게 해준다는 것이다.

 

단점

  1. 인스턴스 생성을 위해 static 팩토리 메소드만 갖고 있으면서 public이나 protected 생성자가 없는 클래스의 경우는 서브 클래스를 가질 수 없다는 것이다.

    이런 단점으로 인해 상속 대신 컴포지션(composition)을 사용하게 끔 해주기 때문에 장점이 될 수도 있다.

  2. 다른 static 메소드와 쉽게 구별할 수 없다는 것이다.

    클래스나 인터페이스 주석으로 표시를 하거나, 공통적인 작명 규칙을 만들고 지킴으로써 이런 단점을 줄일 수 있다.
    작명 규칙 예) valueOf, of, getInstance, newInstance, getType, newType

 

static 팩토리 메소드와 public 생성자는 모두 나름의 용도가 있으므로 상호간의 장점을 아는 것이 중요하다.

프로그래밍 기술의 습득 또한 규칙을 먼저 배운 다음 그 규칙을 언제 깰 것인지 아는 것이 중요하다.

Posted by outliers
,

 

 

  • 특징리스트는 모두 소프트웨어가 무엇을 해야 하는지 이해하는 것에 대한 것입니다.
  • 유스케이스 다이어그램은 불필요한 세부 항목들 속으로 빠져들지 않고, 소프트퉤어가 어떻게 사용될 것인지에 대해 생각할 수 있도록 합니다.
  • 유스케이스는 사용법을 반영하고, 특징은 기능을 반영합니다.
  • 시스템의 특징은 시스템의 기능을 나타냅니다. 비록 기능이 항상 어떤 특정 유스케이스의 눈에 드러난 일부는 아니지만, 시스템은 유스케이스가 실제로 작동하기 위해서는 이러한 것들을 해야만 합니다.
  • 유스케이스는 어떻게 사람이나 사물(액터)이 시스템과 상호작용하는지에 대한 요구사항이고, 특징은 시스템이 작업해야 할 것들에 대한 요구 사항입니다.
  • 디자인 결정은 좋은 객체지향 원리뿐만 아니라 여러분의 시스템이 어떻게 사용될 것인가에 근거하고 있어야함 합니다.
  • 고객에게 그들이 원하는 기능을 얻도록 하는 것과 코드가 유연하고 잘 설계되도록 유지하는 것 사이의 균형을 잡는 것이 여러분의 일입니다.
  • 때로는 특정 문제의 가장 좋은 코드가 이미 작성되어 있습니다. 만약 누군가가 이미 동작하고 있는 해결책을 가지고 있다면, 여러분 스스로 코드를 작성하는 데 시간을 지체하지 마십시오.

 

예)

  1. 특징 리스트 작성
    주어진 요구사항을 보고 특징 리스트를 작성한다.
  2. 유스케이스 다이어 그램
    특징 리스트를 가지고 유스케이스 다이어그램을 작성한다.
  3. 문제점 분해하기
    유스케이스 다이어 그램의 각 항목들을 개별 기능별로 크게 분할을 한다.
  4. 문제점 이해하기
    해당 기능에 대한 이해가 부족하면 문제 점을 이해하기 위한 분석을 한다.
  5. 요구사항 (요구 사항, 2장, 3장 요구 사항 참조)
    큰 문제를 작게 기능별로 나누어 기능 리스트를 작성한다.
    각 기능별로 유스케이스 작성 및 시나리오를 작성한다.
  6. 도메인 분석
    본문 분석 및 도메인 분석을 한다.
  7. 사전 설계
    본문 분석을 한 내용을 가지고 클래스 다이어그램 및 클래스 간의 관계를 정의 한다.
  8. 구현
    객체지향 원리를 사용하여 좋은 디자인으로 코드를 작성한다.
  9. 반복
    2번에서 나온 유스케이스 다이어 그램의 모든 기능을 완료 할 때까지 3 ~ 8번 항목을 반복하고 테스트 한다.

 

'Programs > OOAD' 카테고리의 다른 글

9. 반복하기, 테스팅하기  (1) 2012.09.28
8. 디자인 원리들  (5) 2012.09.25
7. 아키텍처  (0) 2012.09.25
6. 큰 문제들 해결하기  (3) 2012.09.22
5-2. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.22
Posted by outliers
,

여러분은 반복 작업을 통해 위대한 소프트웨어를 작성합니다. 큰 그림을 그리고, 그 다음 애플리에키션이 완성될 때까지 조각조각들에 대해 반복적으로 작업하세요.

반복 심화 작업: 두 가지 기본적인 선택
반복 작업의 두 가지 접근 방식은 잘 작성된 요구 사항에 의해 유도됩니다.

1. 특징 주도 개발(Feature driven development)
애플리케이션의 특정한 특징을 선택하고, 완성될 때까지 그 특징을 계획, 분석, 개발하는 것입니다.
애플리케이션의 특정한 특징들에 집중하는 것을 선택할 수있습니다. 이 접근 방법은 고객이 원하는 기능의 한 조각을 선택하고, 그것이 완성될 때까지 작업하는 것입니다.

한 번에 하나의 특징을 작업하고 그리고 나서 반복하여, 애플리케이션의 기능을 완성할 때까지 한 번에 하니씩 특징들을 구현합니다.

 2. 유스케이스 주도 개발(Use case driven development)
유스케이스의 시나리오를 선택하여 시나리오가 완성될 때까지 코드를 작성하는 것입니다.
애플리케이션의 특정한 흐름에 집중하는 것을 선택할 수 있습니다. 이 접근 방법은 애플리케이션에서 하나의 완전한 경로를, 명확한 시작에서 끝까지 선택하여 코드로 구현하는 것입니다.

유스케이스 내에서 하나의 시나리오를 완성하는 작업을 합니다. 그 다음 또 다른 시나리오를 선택하여 작업하며, 유스케이스의 모든 시나리오가 완성될 때까지 반복합니다. 모든 유스케이스가 작동할 때까지 반복합니다.

특징 주도 개발과 유스케이스 주도 개발이의 차이점 

특징 주도 개발

유스케이스 주도 개발

특징 주도 개발은 더 세분화된 모양 입니다. 

 유스케이스 주도 개발은 더 "큰 그림"의 모양 입니다.
서로 별로 연결되어 있지 않은 특징들이 많을 때 잘 동작 합니다.   독립적인 기능보다는 많은 프로세스와 시나리오를 가지고 있는 애플리케이션의 경우 잘 동작합니다.

고객에게 동작하는 코드를 빨리 보여줄 수 있습니다. 

고객에게 각 개발 단계마다, 더 큰 단위의 기능 조각을 보여줄 수 있습니다.

매우 기능 주도적입니다. 특징 주도 개발을 사용하면 어떠한 특징도 빠트리는 일이 없을 겁니다. 

 매우 사용자 중심적입니다. 유스케이스 주도 개발을 사용하면 사용자가 시스템을 사용하는 여러 방법들을 모두 고려하여 코딩할 것입니다.

 연결되어 있지 않은 기능의 조각들이 많은 시스템에서 특히 잘 동작합니다.

길고 복잡한 프로세스를 가진 트랜잭션 시스템에서 특히 잘 동작합니다. 

* 실제 대부분의 좋은 소프트웨어 분석 설계는 다양한 접근 방식을 혼용하여 사용합니다. 여러분은 유스케이스로 시작할지도 모릅니다. 그 다음 작업을 시작하기 위해 유스케이스에서 작은 특징 하나를 선택할 수 있습니다. 마지막으로 이 특징을 어떻게 구현할지 이해하기 위해 테스트를 사용할 수도 있습니다.(테스트 주도개발)

특징 주도 개발 방법 사용 예

  1. 특징 리스트 중 하나를 선택하여 그것을 끝낼 때까지 작업할 수 있습니다.
  2. 하나의 특징을 결정했다면, 본문 분석을 그 특징에 대해서 다시 하여 특징에 대한 새로운 특징 리스트를 작성한다.
  3. 특징들에 대한 자세한 클래스 다이어그램을 작성한다.
  4. 테스트 시나리오를 작성한다.
    1. 테스트는 간단하고 한 번에 기능의 작은 부분 하나만을 테스트 해야 합니다.
    2. 테스트 주도 개발은 클래스의 기능을 올바르게 만드는데 집중합니다.
  5. 클래스들에 대해 다시 분석하기(공통점 분석 정제하기)
    좋은 소프트웨어는 반복 작업을 통해 완성됩니다. 애플리케이션의 매우 작은 부분을 작업하면서, 분석과 설계를 하고, 다시 이를 되풀이 하세요. 반복해서 작업할 때마다, 설계 결정들을 재평가하고, 여러분의 설계에 도움이 되면 무언가 변경하는 것을 두려우워하지 마세요.
  6. 클래스 작성

 

좋은 테스트를 만드는 방법

  1. 각 테스트 케이스는 아이디와 이름을 가지고 있어야 합니다.
    테스트 케이스의 이름에 무엇이 테스트되는지 나타나야 합니다.
  2. 각 테스트 케이스는 그것이 테스트하는 특정한 것 하나를 테스트 해야 합니다.
    각 테스트 케이스는 한 번에 한 가지 기능만을 테스트해야 합니다.
  3. 각 테스트 케이스는 여러분이 제공하는 입력 값을 가지고 있어야 합니다.
  4. 각 테스트 케이스는 예상되는 출력 값을 가지고 있어야 합니다.
  5. 대부분의 테스트 케이스는 초기 상태를 가지고 있습니다.

 

OOA&D 도구 상자

요구사항

  • 좋은 요구 사항은 시스템이 고객이 기대한 대로 동작하도록 합니다.
  • 여러분의 요구 사항이 시스템의 유스케이스의 모든 단계들을 담고 있는지 확인하세요.
  • 유스케이스를 사용해서 고객이 잊고 얘기하지 않았던 것들을 찾아내세요.
  • 유스케이스는 시스템에 추가되어야 할 불완전하거나 빠진 요구 사항들을 분명하게 합니다.
  • 요구 사항은 항상 끊임없이 변화합니다.(그리고 성장합니다.).

분석과 설계

  • 잘 디자인된 소프트웨어는 변경과 확장이 쉽다.
  • 기본적인 캡슐화와 상속 같은 객체지향 원리를 사용하여 소프트웨어를 좀 더 유연하게 만드세요.
  • 디자인이 유연하지 않으면, 변경하세요. 변경해야 하는 것이 여러분의 디자인이더라도, 결코 나쁜 디자인을 고수하지는 마세요.
  • 여러분의 각 클래스의 응집도를 높게 하세요. 여러분의 클래스 각각은 하나의 일을 정말 잘하는 것에 중점을 두어야 합니다.
  • 소프트웨어 디자인을 진행하면서, 항상 높은 응집도를 위해 노력하세요.

큰 문제들 해결하기 정리

  1. 고객의 말에 귀 기울여, 여러분이 만들어 주길 바라는 것이 무엇인지 알아낸다.
  2. 특징 리스트를 고객이 이해하는 용어를 사용하여 작성한다.
  3. 작성한 특징 리스트가 고객이 실제로 원하는 것인지 확인한다.
  4. 유스케이스 다이어그램을 사용하여 시스템의 청사진을 만든다.
  5. 큰 시스템을 여러 개의 작은 섹션으로 나눈다.
  6. 디자인 패턴을 시스템의 작은 섹션에 적용한다.

객체지향 원리

  • 변하는 것을 캡슐화 하라.
  • 구현에 의존하기 보다는 인터페이스에 의존하도록 코딩하라.
  • 각 클래스는 변경 요인이 오직 하나이어야 한다.
  • 클래스는 행동과 기능에 관한 것입니다.
  • 클래스는 확장에는 열려있고, 수정에는 닫현 있어야 한다.(OCP)
  • 공통되는 부분을 추출하여 추상화하고 한 곳에 두어 중복 코드를 피하라(DRY원리).
  • 시스템의 모든 객체는 하나의 책임만을 가지며, 객체가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다.(SRP)
  • 서브 클래스들은 부모 클래스들이 사용되는 곳에 대체될 수 있어야 한다.(LSP)

개발 접근 방식

  • 유스케이스 주도 개발은 시스템에서 하나의 유스케이스를 선정하여, 유스케이스의 모든 시나리오를 포함해서 그 전체를 구현하고 코드 작업을 완성하는데 중점을 두고 있습니다. 그 다음에 애플리케이션의 다른 것으로 넘어갑니다.
  • 특징 주도 개발은 하나의 특징에 집중하여 그 특징의 모든 행위 모두를 코드 작업합니다. 그 다음에 애플리케이션의 다른 것으로 넘어갑니다.
  • 테스트 주도 개발은 한 가지 기능의 테스트 시나리오를 작성하고, 그 다음에 그 기능에 대한 코드를 작성합니다. 그리고 모든 테스트를 통과할 때까지 소프트웨어를 작성합니다.
  • 좋은 소프트웨어 개발 방법이란 대체로 개발 주기의 여러 단계에서 이러한 개발 모델 모두를 혼용합니다.

프로그래밍 기술

  • 약정에 의한 프로그래밍은 여러분과 소프트웨어 사용자가 지키기로 합의한, 소프트웨어가 어떻게 동작해야 하는지에 관한 계약을 설정합니다.
  • 방어적 프로그래밍은 다른 소프트웨어를 신뢰하지 않고, 다른 소프트웨어가 나쁜거나 안전하지 않은 정보를 제공하는지 확인하기 위해, 광범위 하게 오류와 데이터를 점검합니다.

 

핵심 정리

  • 좋은 소프트웨어를 작성하는 첫 번째 단계는 여러분의 소프트웨어가 고객이 기대하고 원하는대로 동작하도록 하는 것입니다.
  • 고객은 다이어그램이나 목록에는 관심이 없습니다. 단지 여러분의 소프트웨어가 실제 무엇인가 하는 것을 보고 싶어합니다.
  • 유스케이스 주도 개발은 애플리케이션의 유스케이스에서 한 번에 하나의 시나리오에 집중합니다.
  • 유스케이스 주도 개발에서, 한 번에 하나의 시나리오에 집중합니다. 그러나 대부분 하나의 유스케이스의 모든 시나리오를 코딩 작업한 이후에 또 다른 유스케이스의 시나리오로 이동합니다.
  • 특징 주도 개발은 하나의 특징을 완전히 코딩 작업한 후에 그 밖의 다른 것으로 이동합니다.
  • 한 번에 하나의 특징을 선택하는 한, 특징 주도 개발에서는 크거나 작은 특징 중에서 한 가지로 작업할 수 있습니다.
  • 소프트웨어 개발은 항상 반복 작업입니다. 큰 그림을 살펴보고, 그 다음 작은 단위의 기능을 반복 작업합니다.
  • 하나의 새로운 특징이나 유스케이스를 가지고 작업을 시작하는 것을 포함해서, 개발 주기의 모든 단계마다 분석과 설계를 해야 합니다.
  • 테스트를 통해서 소프트웨어가 어떠한 버그도 가지고 있지 않다는 것을 확인하고, 고객에게 소프트웨어가 작동하고 있다는 것을 입증할 수 있습니다.
  • 좋은 테스트 케이스는 오직 기능의 특정 부분 하나만 테스트 합니다.
  • 테스트 케이스는 하나의 클래스에 하나 혹은 서너개의 메소드를 포함할 수 있고 또는 여러 개의 클래스를 포함할 수 있습니다.
  • 테스트 주도 개발은 테스트를 먼저 작성하고 이 테스트를 통과할 수 있도록 소프트웨어를 개발한다는 생각에 기반하고 있습니다. 결과는 모든 기능을 갖춘 동작하는 소프트웨어 입니다.
  • 약정에 의한 프로그래밍은 트랜잭션의 양쪽 대상이 무슨 행동이 무슨 행위를 발생시키는지 알고 있다는 가정하에 약정을 지키는 것입니다.
  • 약정에 의한 프로그래밍 환경에서, 메소드는 대부분 오류가 발생할 때 null이나 점검이 필요 없는 예외 상황을 반환합니다.
  • 방어적 프로그래밍은 잘못될 것들을 찾아서, 문제 상황을 회피할 수 있도록 폭넓게 테스트 합니다.
  • 방어적 프로그래밍 환경에서, 메소드는 대부분 "빈"객체나 혹은 점검이 필요한 예외 상황을 반환합니다.

 

'Programs > OOAD' 카테고리의 다른 글

10. OOA&D 생명주기  (2) 2012.09.30
8. 디자인 원리들  (5) 2012.09.25
7. 아키텍처  (0) 2012.09.25
6. 큰 문제들 해결하기  (3) 2012.09.22
5-2. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.22
Posted by outliers
,

디자인 원리 정리

디자인 원리는 코드를 좀 더 유지보수하기 쉽고, 유연하고 확장히기 쉽게 만들기 위해, 코드의 작성이나 디자인에 적용되는 기본 도구 또는 기법입니다.

지금까지 배운 디자인 원리(객체지향 원리)

  • 변하는 것을 캡슐화하라.
  • 구현에 의존하기보다는 인터페이스에 의존하도록 코딩하라.
  • 각 클래스는 변경 요인이 오직 하나이어야 한다.
  • 클래스는 행동과 기능에 관한 것이다.

 

원리1. 개방 - 폐쇄의 원리(Open - Closed Principle, OCP)

클래스의 확장에는 열려있고, 수정에는 닫혀 있어야 한다. OCP는 기존 코드를 변경하지 않으면서 코드의 수정을 혀용하는 것에 관한 원리 입니다.

새라는 클래스에 fly()라는 함수를 정의 한다. 참새, 독수리, 매 등의 서브 클래스에서는 fly() 라는 함수를 Overriding 해서 각각의 나는(fly) 방식을 따로 구현한다.

여기서 fly()메소드는 부모 클래스에 정의되어 변경되지 않습니다. 하지만 모든 서브 클래스들이 fly() 메소드의 행동을 변경할 수 있기 때문에 새 클래스는 확장에는 열려 있습니다.

상속만이 유일한 방법은 아닙니다. 구성(Composition)을 사용 할때도 좋은 방법이 있습니다. 클래스에 private 메소드가 여러개 있고, 그 private 메소드를 여러 방법으로 호출하는 public 메소드를 추가 할 수도 있습니다.

원리2. 반복 금지의 원리(Don't Repeat Yourself, DRY)

공통되는 부분을 추출하여 추상화하고 한 곳에 두어 중복 코드를 피하라. DRY는 하나의 요구 사항은 한 곳에 두어야 한다는 원리 입니다. 시스템의 각 정보와 기능을 말이 되는 하나의 장소에 두는 것을 의미합니다.

원리3. 단일 책임의 원리(Single Responsibility Principle, SRP)

시스템의 모든 객체는 하나의 책임만을 가지며, 객체가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다. 각 객체가 변화의 요인이 하나뿐이라면, 여러분은 단일 책임의 원리를 정확히 구현한 것입니다.

응집도는 사실 SRP의 다른 이름이라 할 수 있습니다. 여러분이 응집도가 높은 프로그램을 작성한다면, 이는 곧 SRP를 잘 적용하고 있는 것을 의미합니다.

여러개의 책임 찾아내기
대부분의 경우 간단한 테스트를 통해 SRP를 사용하지 않은 클래스들을 찾을 수 있습니다.

  1. 아래의 ___ 에 class 이름을 기입합니다.
    _____________ 클래스에 대한 SRP 분석
  2. 아래의 ____1___에 클래스를 기입하고 ___2___ 에 해당 메소드를 기입하세요, 메소드의 인자값도 같이 넣어서 읽어보세요.
    ____1___ (이)가 자신을 ___2___ 한다.
  3. 모든 메소드를 확인해보고 말이 되지 않으면, 그 메소드에서 SRP 원리를 위반하고 있을 것입니다.

 

원리4. 리스코프 치환원리(Liskov Substitution Principle, LSP)

자식 타입들은 부모 타입들이 사용되는 곳에 대체될 수 있어야 한다. 부모 클래스를 상속할 때, 부모 클래스가 사용되는 곳은 아무 문제없이 자식 클래스도 사용할 수 있어야 합니다. 그렇지 않으면, 상속을 잘못 사용하고 있는 것입니다.

상속을 오용하는 코드들은 이해하기 어렵습니다. 그러면 상속 외에 어떠한 방법들이 있을까요?

위임(Delegation)
특정 일의 책임을 다른 클래스나 메소드에 맡길 때를 지칭 합니다. 클래스의 행동을 변경하고 싶지 않고 그 행동을 스스로 구현하는 것이 그 클래스의 책임이 아닌 경우에는 그 행동을 다른 클래스에 위임하세요.

언제 위임을 하나?
다른 클래스의 기능을 그 클래스 행동의 변경없이 그대로 사용하고 싶을 때, 가장 잘 사용되는 것입니다.
다른 클래스의 기능을 사용해야 하지만 그 기능을 변경하고 싶지 않다면, 상속 대신 위임의 사용을 고려하세요.

구성(Composition)을 사용하여 다른 클래스들의 행동을 조합하기.
구성을 통해 하나의 인터페이스를 구현한 여러 클래스들의 기능을 사용할 수 있고 실행 중에 그 클래스를 바꾸어 기능을 변경할 수 있습니다. 구성을 사용하여 하나 또는 여러개의 클래스, 특히 비슷한 종류의 여러 클래스들로부터 행동을 재사용할 수 있습니다. 여러분의 객체가 다른 객체를 완전히 소유하고 있는 형태이며, 구성관계로 연결된 객체는 여러분 객체의 외부에 독립적으로 존재할 수 없습니다.

언제 구성을 사용할까요?
구성은 우리가 인터페이스에 정의된 기능을 사용하고, 그래서 컴파일 중이나 실행 중에 그 인터페이스의 여러 구현 클래스 중에 선택하기를 원할 때 위력을 발휘합니다.

집합(Aggregation)
한 클래스가 다른 클래스의 부분으로 사용되지만 다른 클래스의 외부에서도 여전히 존재하는 경우를 지칭합니다.

언제 구성을 사용하고, 언제 집합을 사용하나요?
내가 사용하고 싶은 행동을 가지고 있는 객체가 그 행동을 사용하는 객체의 외부에서도 존재하나?
만약 객체가 독립적으로 존재하는 것도 의미가 있다면, 집합(Aggregation)을 그렇지 않다면 구성(Composition)을 사용하세요.

위임(Delegation)과 구성(Composition), 그리고 집합(Aggregation)을 상속보다 선호하면, 대개의 경우 여러분의 소프트웨어는 더 유연하고, 유지보수성, 확장성, 그리고 재사용성이 좀 더 좋아집니다.

이 세 가지(위임, 구성, 집합) 객체 지향 기법들을 이용해 LSP를 위반하지 않으면서 행동의 재사용이 가능해 집니다.

 

 

OOA&D 도구 상자

요구사항

  • 좋은 요구 사항은 시스템이 고객이 기대한 대로 동작하도록 합니다.
  • 여러분의 요구 사항이 시스템의 유스케이스의 모든 단계들을 담고 있는지 확인하세요.
  • 유스케이스를 사용해서 고객이 잊고 얘기하지 않았던 것들을 찾아내세요.
  • 유스케이스는 시스템에 추가되어야 할 불완전하거나 빠진 요구 사항들을 분명하게 합니다.
  • 요구 사항은 항상 끊임없이 변화합니다.(그리고 성장합니다.).

분석과 설계

  • 잘 디자인된 소프트웨어는 변경과 확장이 쉽다.
  • 기본적인 캡슐화와 상속 같은 객체지향 원리를 사용하여 소프트웨어를 좀 더 유연하게 만드세요.
  • 디자인이 유연하지 않으면, 변경하세요. 변경해야 하는 것이 여러분의 디자인이더라도, 결코 나쁜 디자인을 고수하지는 마세요.
  • 여러분의 각 클래스의 응집도를 높게 하세요. 여러분의 클래스 각각은 하나의 일을 정말 잘하는 것에 중점을 두어야 합니다.
  • 소프트웨어 디자인을 진행하면서, 항상 높은 응집도를 위해 노력하세요.

큰 문제들 해결하기 정리

  1. 고객의 말에 귀 기울여, 여러분이 만들어 주길 바라는 것이 무엇인지 알아낸다.
  2. 특징 리스트를 고객이 이해하는 용어를 사용하여 작성한다.
  3. 작성한 특징 리스트가 고객이 실제로 원하는 것인지 확인한다.
  4. 유스케이스 다이어그램을 사용하여 시스템의 청사진을 만든다.
  5. 큰 시스템을 여러 개의 작은 섹션으로 나눈다.
  6. 디자인 패턴을 시스템의 작은 섹션에 적용한다.

객체지향 원리

  • 변하는 것을 캡슐화 하라.
  • 구현에 의존하기 보다는 인터페이스에 의존하도록 코딩하라.
  • 각 클래스는 변경 요인이 오직 하나이어야 한다.
  • 클래스는 행동과 기능에 관한 것입니다.
  • 클래스는 확장에는 열려있고, 수정에는 닫현 있어야 한다.(OCP)
  • 공통되는 부분을 추출하여 추상화하고 한 곳에 두어 중복 코드를 피하라(DRY원리).
  • 시스템의 모든 객체는 하나의 책임만을 가지며, 객체가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다.(SRP)
  • 서브 클래스들은 부모 클래스들이 사용되는 곳에 대체될 수 있어야 한다.(LSP)

 

핵심 정리

  • 개방-폐쇄 원리는 클래스들을 확장에는 열려 있고 변경에는 닫혀 있게 함으로써, 여러분의 소프트웨어를 재사용 가능하지만, 여전히 유연하게 유지합니다.
  • 클래스가 단일 책임의 원리를 통해 한 가지 일만 하게 함으로써, OCP를 여러분의 코드에 적용하는 것을 훨씬 더 쉽게 합니다.
  • 여러분은 어떤 메소드가 클래스의 책임이 맞는지 결정하려면, 이 특정한 일을 하는 것이 이 클래스의 일인지 자문해 보세요. 그렇지 않다면, 그 메소드를 다른 클래스로 옮겨야 합니다.
  • 객체지향 코드를 거의 환성한 후에, DRY를 사용하세요. 중복 코드를 피할 수 있고, 코드 안의 각 기능이 한 곳에 있도록 할 수 있습니다.
  • DRY는 코드뿐만 아니라 요구 사항에도 적용됩니다.: 각 특징과 요구 사항을 한 곳에서 구현되게 해야 합니다.
  • 리스코프 치환 원리는 서브 클래스가 부모 클래스 대신 사용될 수 있게 함으로써 상속을 올바르게 사용할 수 있게 합니다.
  • LSP를 위반하는 코드를 발견하면, 위임, 구성, 또는 집합을 사용하여 상속을 이용하지 않고 다른 클래스의 행동을 사용하는 것을 고려해 보세요.
  • 다른 클래스의 행동이 필요한데 그 행동을 바꿀 필요가 없으면, 원하는 행동을 사용하기 위해 그 클래스에게 위임할 수 있습니다.
  • 구성은 인터페이스의 여러 구현을 통해 비슷한 여러 행동으로부터 하나의 행동을 선택할 수 있게 합니다.
  • 구성을 사용하면 구성하는 객체가 자신이 사용하는 행동을 소유하고, 구성하는 객체가 사라지면 구성에 참여한 객체도 사라집니다.
  • 집합은 다른 클래스의 생존에 영향을 미치지 않으면서 그 행동을 사용할 수 있게 합니다.
  • 집합에 참여한 클래스의 행동은 자신을 사용했던 집합 클래스가 사라진 후에도 계속 존재합니다.

 

 

 

 

 

 

 

 

 

'Programs > OOAD' 카테고리의 다른 글

10. OOA&D 생명주기  (2) 2012.09.30
9. 반복하기, 테스팅하기  (1) 2012.09.28
7. 아키텍처  (0) 2012.09.25
6. 큰 문제들 해결하기  (3) 2012.09.22
5-2. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.22
Posted by outliers
,

7. 아키텍처

Programs/OOAD 2012. 9. 25. 11:58

어디서부터 어떻게 무엇을 먼저 시작해야 하나요?
아키텍처는 디자인의 구조이고, 프로그램의 가장 중요한 부분들과 그들 사이의 관계를 명확히 보여줍니다.

* 아키텍처
아키텍처는 시스템의 분할, 나뉜 부분들 사이의 연결과 상호 작용 메커니즘, 그리고 시스템의 디자인에 사용된 원리와 결정 사항들을 담고있는 시스테므이 구조입니다.

  1. 첫 번째 단계는 항상 프로그램이 고객이 원하는 일을 하게 하는 것입니다.(기능부터 시작합시다.)
    어떤 것이 아키텍처적인 의미에서 중요한지 알아내려고 할 때, 다음의 세가지 질문을 할 수있습니다.
    1. 시스템의 본질이 되는 부분인가?
      정말 그 특징이 시스템의 핵심인가? 그 특징이 없는 시스템을 생각할 수 있는가? 아니라면 아마도 시스템의 본질이 되는 특징을 찾은 것입니다.
    2. 이것은 도대체 무슨 의미죠? 어떤 일인지 확실치 않을 때, 그 일은 시간이 오래 걸리거나 시스템의 나머지 부분에 문제를 야기시킬수 있습니다. 그러한 특징들은 일찌감치 알아봐야 합니다.

    3. 도대체 어떻게 해야 하죠?
      구현하기 정말 어려워 보이거나 여러분에게 완전히 새로운 프로그래밍 작업인 경우 입니다. 특정 문제를 어떻게 해결할지 잘 모르겠으면, 처음에 그 특징에 시간을 들여 문제의 발생을 미리 막아야 합니다.

  2. 1번의 질문에 대한 핵심 특징 리스트를 작성 합니다. 프로젝트 성공의 위험 요소를 줄이는 방향으로 진행된다면, 어느 것을 먼저 하는지는 상관없습니다. 시나리오들이 위험 요소를 줄이는 데 도움이 됩니다. 간단한 기능을 할 수 있는 시나리오를 작성 합니다.
  3. 몇 개의 핵심 특징이 되는 부분의 기능을 간단히 구현 합니다. 프로젝트에서 위험을 줄일려면, 한 번에 하나의 특징에 집중하세요. 위험을 줄이는 데 도움이 되지 않는 특징들에 너무 신경쓰지 마세요. 기능 구현을 하면서 필요한 클래스들과의 관계를 정리하면서 클래스들의 기본 설계를 합니다. 위험 요소를 줄이는 일에만 집중을 하세요.
  4. 어떤 특징이 무엇을 의미하는지 확실치 않을 때, 가장 좋은 방법은 고객에게 질문하는 것입니다.
    고객에게 문의 -> 공통점 분석 -> 구현 계획
    모든 내용은 프로젝트의 위험 요소를 확인 후 해결 하는것에 집중을 합니다.

 

핵심 정리

  • 아키텍처는 모든 다이어그램, 계획, 특징 리스트들을 잘 정돈된 애플리케이션으로 만드는 데 도움을 줍니다.
  • 프로젝트에 매우 중요한 시스템의 특징들은 아키텍처적으로 중요합니다.
  • 시스템의 본질인 특징, 의미가 명확하지 않은 특징, 또는 처음에 어떻게 구현해야 할지 명확하지 않은 특징에 초점을 맞추세요.
  • 프로젝트의 아키텍처 설계 단계에서 하는 모든 일은 프로젝트 실패의 위험을 줄여야 합니다.
  • 유스케이스의 세부 사항이 필요하지 않을 경우, 소프트웨어가 어떻게 이용될 수 있는지를 설명하는 시나리오를 작성하면 요구 사항을 빠르게 수집하는데 도움이 됩니다.
  • 특징이 무엇인지 확실히 모를 때, 고객에게 묻고, 그런 후 얻ㄴ은 답을 일반화하여 특징을 잘 이해하도록 합니다.
  • 공통점 분석을 사용해서 유연한 소프트웨어 솔루션을 만드세요
  • 고객은 여러분 생각에 정말 멋지게 짜여진 코드에 관심이 있기보다는 그들이 워ㅓㅜㄴ하는 일을 하고, 시간에 맞게 만들어지는 소프트웨어에 관심이 있습니다.

 

'Programs > OOAD' 카테고리의 다른 글

9. 반복하기, 테스팅하기  (1) 2012.09.28
8. 디자인 원리들  (5) 2012.09.25
6. 큰 문제들 해결하기  (3) 2012.09.22
5-2. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.22
5-1. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.21
Posted by outliers
,

큰 문제 해결 하기 위한 방법

  • 큰 문제는 여러 개의 기능별 조각들로 나누고 각 조각들을 개별적으로 풀어감으로써 해결할 수 있습니다.
  • 큰 문제를 바라보는 가장 좋은 방법은 여러 기능의 조각들로 나누어 보는 것입니다.
  • 각 조각들을 풀어야 할 개별 문제로 다룰 수 있고, 크기가 작은 개별 문제에는 이미 알고 있는 도구들을 적용할 수 있습니다.

* 도메인 분석
기존 시스템과 개발 이력, 도메인 전문가들로부터 얻은 지식, 기반이론, 그리고 도메인에서 새로 등장하는 기술을 기반으로 도메인 관련 정보를 찾아내고, 모으고, 구조화하고, 나타내는 프로세스.

방법 설명

  • 요구 사항과 유스케이스부터 시작하는 것도 좋습니다.
     그런데 우리가 지금까지 시스템에 대해 정말 무엇을 알죠? 시스템에 대해 좀 더 알아낼 수 있는 방법 중의 하나는 비슷한 시스템을 알아내는 것입니다. 또 다른 방법은 시스템과 상관없는 것을 알아내는 것입니다. 이렇게 하면 시스템에서 신경쓰지 않아도 되는 부분을 결정하는데 도움이 됩니다.

  • 특징들을 찾아 내세요.
     특징은 시스템이 해야 할 일에 대해 추상적으로 설명한 것입니다. 고객과 대화를 통해 시스템의 특징들을 얻어냅니다. 대개의 경우, 하나의 특징을 얻고 그 특징을 만족시키기 위한 여러 개의 요구 사항들이 나옵니다. 그래서 시스템의 특징들을 알아내는 것은 요구 사항을 얻는 좋은 방법입니다.
    개발 시스템에 대해 정보가 많지 않고, 어디서부터 시작해야 할지 감이 잡히지 않는 경우, 시스템의 규모가 커다란 프로젝트인 경우에는 시스템의 특징부터 시작하는 것이 정말 도움이 됩니다.
    주의 : 특징과 요구사항의 차이점에 너무 얽매이지 마세요.

  • 유스케이스 다이어 그램을 그리세요.
     가끔 시스템이 하는 일을 알아야 하지만 유스케이스 작성에 필요한 세세한 내용까지 다루고 싶지 않을 때가 있습니다. 이러한 상황에서는 유스케이스 다이어그램을 사용할 수 있습니다. 유스케이스 다이어그램은 여러분의 시스템에 대한 청사진 입니다. 항상 세부 내용은 늦출 수 있을 때까지 최대한 늦추세요.
    특징 리스트를 사용해서 유스케이스 다이어그램에 빠진 것이 없게 만드세요.
    특징 또는 요구 사항 리스트로 시스템이 해야 할 큰 일을 알아내세요.

  • 도메인 분석을 하세요.
     도메인 분석을 통해 디자인을 확인할 수 있고, 고객이 사용하는 용어를 사용할 수 있습니다.
    시스템에 대해 알아 낸 모든 내용을, 우리의 고객이 실제 이해하는 방식으로 모아보고 봅시다. 이것은 도메인 분석이라고 하는 프로세스이고, 고객이 이해하는 용어로 문제를 설명하는 것입니다.
    도메인 분석을 하면 여러분이 만들 필요가 없는 시스템의 부분을 만들지 않는 데 도움이 됩니다.

  • 큰 문제를 기능의 작은 조각들로 나누기.
     이제 큰 문제를 쪼개어 기능의 작은 조각들로 나눌 때입니다. 청사진과 기능 리스트를 가지고 큰 프로그램을 각각의 기능을 가진 여러 개의 작은 조각들로 나눌 수 있습니다.

  • 작은 문제들의 해결에 디자인 패턴을 적용합니다.

 

OOA&D 도구 상자

요구사항

  • 좋은 요구 사항은 시스템이 고객이 기대한 대로 동작하도록 합니다.
  • 여러분의 요구 사항이 시스템의 유스케이스의 모든 단계들을 담고 있는지 확인하세요.
  • 유스케이스를 사용해서 고객이 잊고 얘기하지 않았던 것들을 찾아내세요.
  • 유스케이스는 시스템에 추가되어야 할 불완전하거나 빠진 요구 사항들을 분명하게 합니다.
  • 요구 사항은 항상 끊임없이 변화합니다.(그리고 성장합니다.).

분석과 설계

  • 잘 디자인된 소프트웨어는 변경과 확장이 쉽다.
  • 기본적인 캡슐화와 상속 같은 객체지향 원리를 사용하여 소프트웨어를 좀 더 유연하게 만드세요.
  • 디자인이 유연하지 않으면, 변경하세요. 변경해야 하는 것이 여러분의 디자인이더라도, 결코 나쁜 디자인을 고수하지는 마세요.
  • 여러분의 각 클래스의 응집도를 높게 하세요. 여러분의 클래스 각각은 하나의 일을 정말 잘하는 것에 중점을 두어야 합니다.
  • 소프트웨어 디자인을 진행하면서, 항상 높은 응집도를 위해 노력하세요.

객체지향 원리

  • 변하는 것을 캡슐화하라.
  • 구현에 의존하기보다는 인터페이스에 의존하도록 코딩하라.
  • 각 클래스는 변경 요인이 오직 하나이어야 한다.
  • 클래스는 행동과 기능에 관한 것이다.

큰 문제들 해결하기 정리

  1. 고객의 말에 귀 기울여, 여러분이 만들어 주길 바라는 것이 무엇인지 알아낸다.
  2. 특징 리스트를 고객이 이해하는 용어를 사용하여 작성한다.
  3. 작성한 특징 리스트가 고객이 실제로 원하는 것인지 확인한다.
  4. 유스케이스 다이어그램을 사용하여 시스템의 청사진을 만든다.
  5. 큰 시스템을 여러 개의 작은 섹션으로 나눈다.
  6. 디자인 패턴을 시스템의 작은 섹션에 적용한다.

 

핵심 정리

  • 큰 문제를 바라보는 가장 좋은 방법은 작은 문제들의 집합으로 보는 것입니다.
  • 작은 프로젝트들에서처럼 큰 프로젝트에서도 특징과 요구 사항들을 모으는 것부터 시작합니다.
  • 특징은 보통 시스템이 하는 "큰"일이지만 "요구 사항"과 같은 뜻으로도 사용될 수 있습니다.
  • 공통점과 차이점은 새로운 시스템을 여러분이 이미 알고 있는 것과 비교하여 바라보는 시각을 제공합니다.
  • 유스케이스는 세부 사항에 중점을 두고 있습니다. 유스케이스 다이어그램은 큰 그림에 보다 중점을 두고 잇습니다.
  • 유스케이스 다이어그램이 시스템의 모든 특징들을 설명해야 합니다.
  • 도메인 분석은 시스템을 고객이 이해하는 용어를 사용하여 표현하는 것입니다.
  • 액터는 시스템과 상호작용하지만 시스템의 일부는 아닌 모든 것입니다.

 

'Programs > OOAD' 카테고리의 다른 글

8. 디자인 원리들  (5) 2012.09.25
7. 아키텍처  (0) 2012.09.25
5-2. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.22
5-1. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.21
4. 분석  (0) 2012.09.19
Posted by outliers
,