2009 01 15 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
2009.01.15 PowerPoint Presentation
Download Presentation
2009.01.15

Loading in 2 Seconds...

play fullscreen
1 / 35

2009.01.15 - PowerPoint PPT Presentation


  • 187 Views
  • Uploaded on

2009.01.15. Test Driven Development [Video Shop]. Version 0.1. Test Driven Development 접근하기. Test Driven Development 접근하기. 목표를 쏴라 ~. Test Driven Development 접근하기. 프로그래밍은 주어진 과제를 해결하는 것이다 . 최초 과제 혹은 요구사항은 대게 단순하다 . 하지만 , 점점 요구가 복잡해진다 . 물론 처음부터 조건이 까다로운 경우도 …

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '2009.01.15' - bianca-clark


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
2009 01 15
2009.01.15

Test Driven Development

[Video Shop]

Version 0.1

test driven development
Test Driven Development 접근하기

Test Driven Development

접근하기

test driven development1

목표를 쏴라~

Test Driven Development 접근하기
  • 프로그래밍은 주어진 과제를 해결하는 것이다.
  • 최초 과제 혹은 요구사항은 대게 단순하다.
  • 하지만, 점점 요구가 복잡해진다. 물론 처음부터 조건이 까다로운 경우도…
  • 일단 쏴서 맞추어 봐라. 그래서 시키는 대로 했다.
  • 점점 앉아쏴, 서서 쏴, 업드려 쏴… 심지어 덤블링 하면써 쏘라고 할지도?
test driven development2
Test Driven Development 접근하기

Non TDD

문제를 더 작은 문제로 쪼갠다.

각각의 작은 문제를 해결하기 위한 모듈들과 각 모듈간의 관계(상속, 합성)를 정의한다.

그리고, 문제를 해결하기 위한 코드를 만든다.

문제를 더 작은 문제로 쪼갠다.

하나의 컴포넌트를 선언하고, 작은 문제 하나를 푼다.

그리고, 또 다른 문제를 해결하기 위해 기존 컴포넌트에 코드를 계속 추가한다.

필요한 기능을 추가할 수록 점점 스파게티 코드가 되어간다.

리팩토링을 해서 코드를 개선하기도 하지만…

테스트가 점점 어려워진다. 하나의 컴포넌트가 여러가지 이상의 기능을 수행하면서

최초에 테스트 했던 기능이 동작하지 않는다.

그리고, 개발자는 애처롭게 말한다. “아니, 이거 원래 잘 동작하던 건데요? 미치겠네..”

PM도 미치고, 고객도 미치고, 개발자는 밤새고, 집에 가고 싶을 뿐이고~

사실은?

test driven development3
Test Driven Development 접근하기

TDD

작은 목표 혹은 문제를 선정한다.

문제에 대한 답을 예상한다. 그리고, 예상 결과를 검사하는 코드를 먼저 작성한다.

결과를 반환하는 컴포넌트를 선언하고, 간단히 결과를 생성하는 실행 코드를 작성한다.

위 과정을 반복하면서 필요하다면, 리팩토링을 하며 컴포넌트를 추가하거나 구조를 변경한다.

요구사항 혹은 조건이 추가되거나, 기능을 추가해도 이전에 동작하던 기능이 잘 동작할 것이라는 것을 테스트 코드가 보장한다.

대체로 잘 동작하는 상태로 업무가 진행된다. 자동화된 테스트를 통해 문제가 없다는 것을 빠르게 확인할 수 있어서 테스트 시간과 심리적 부담이 감소한다.

늘 동작하는 코드를 보면서 작업하게 되니. 한참 만들어 놓고, 동작 안하는 코드를 보고 절망할 일이 없다. 코드 분량이 늘어나도 적극적으로 코드를 수정할 수 있게 된다.

정시 퇴근이 가능할지도 모른다.

그래서?

문제의 범위를 크게 잡지 말고, 조금씩 나아지는 것을 느끼며, 리듬을 타야 한다.

videoshop
퀴즈 - VideoShop

Test Driven Development

퀴즈 풀기

videoshop1
퀴즈 - VideoShop

Video Shop

고객

  • 고객은 이름을 가지고 있다.
  • 고객은 한번에 여러개의 비디오 여러 개 대여할 수 있고, 각각의 대여 기간은 일정하지 않다.

비디오테이프

  • 비디오는 3가지 유형이 있다. (영화, 스포츠, 다큐멘터리)
  • 각각의 비디오는 독립적인 일일 대여요금이 부여되며, 유형에 따라 할인율이 틀리다.
  • 비디오 대여 시 유형에 따라 다른 포인트를 제공한다.

대여 서비스

  • 비디오 대여가 발생할 때, 포인트를 적립하고 누적 포인트를 조회할 수 있어야 한다.
  • 상세 대여 내역 (종류, 제목, 가격 등)을 조회할 수 있어야 한다.
  • 대여 기간에 따라 요금을 계산할 수 있어야 한다.

기타 조건

  • TDD의 순서를 따라서 개발할 것.
  • 테스트할 목록을 작성하라.
videoshop2
퀴즈 - VideoShop

문제가 너무 커 보이는데요?

어디서 부터 접근해야 할까요?

문제를 먼저 쪼개는 작업이 필요하지 않나요?

자칫 엉뚱한 곳부터 풀다가 헤매는 건 아닐까요?

저는 TDD를 처음 경험 해보는데 조언 해주실 건 없나요?

어디서 시작하나 상관 없는 게 TDD 입니다.

두려워하지 마시고 일단 시작해 보세요!

Just Do IT!

videoshop3
퀴즈 - VideoShop
  • 그래서, 그냥 순서대로 풀었습니다!
  • - 첫번째 문제 혹은 태스크 ‘고객은 이름을 가지고 있다.’
    • 이게 전부는 아니죠… 새로운 고객을 등록하고, 이름으로 검색할 수 있어야 합니다.
    • 그래서, 아래와 같이 작업했습니다.
  • 개발 환경 - 이클립스를 실행한 후워크스페이스를 만들고, VideoShop 프로젝트를 생성했습니다
  • 패키지 선언- 테스트 코드와 실행 코드는 다른 패키지에 위치 시켜야 한답니다. - 그래서, com.acme.videoshop 패키지와 test.acme.video.shop 으로 분리했습니다.
  • 테스트 프레임워크 설정- 다양한 도구가 있지만, 이클립스에 기본으로 포함되어 있는 Junit을 선택했습니다.
  • 테스트 클래스 선언- test.acme.videoshop. CustomerTest 클래스를 작성합니다.
videoshop4
퀴즈 - VideoShop

고객 관리를 테스트 하기 위한 CustomerTest 클래스.

package test.acme.videoshop;

import com.acme.videoshop.customer.Customer;

import com.acme.videoshop.customer.CustomerManager;

import junit.framework.TestCase;

public class CustomerTest extends TestCase {

private static final String CUSTOMER_NAME = "Sunny Kwak";

public void testCustomer() {

Customer customer = CustomerManager.register( CUSTOMER_NAME );

assertNotNull(customer);

customer = CustomerManager.lookup(CUSTOMER_NAME);

assertNotNull(customer);

customer = CustomerManager.lookup(CUSTOMER_NAME + ".");

assertNull(customer);

}

}

videoshop5
퀴즈 - VideoShop

Customer & CustomerManager

고객 객체를 직접 생성하지 않고, CustomerManager 클래스의 register() 메소드를 통해 간접 생성.

고객의 이름 검색 또한 lookup() 메소드를 통해 수행.

- 고객 관리에 대한 모든 책임을 CustomerManager에 위임.

- 고객 정보를 저장소 등으로 보내거나, 읽어오게끔 변경하더라도 CustomerManager 외부에서는 변경 사항을 알아차리지 못하게 됨.

- 이를 통해 정보 은폐(information hiding)을 시도함.

videoshop6
퀴즈 - VideoShop

Customer & CustomerManager

- Customer 객체를 CustomerManager의 register() 메소드 내부에서 생성하고 있습니다.

- 고객에 대한 정보(항목)가 늘어날 수도 있으니 Customer 객체를 생성한 후에 register() 메소드에 Customer 객체를 파라미터로 전달하는 건 어떨까요?

  • 만일 구조를 바꾸게 된다면, CustomerManager가 Customer 객체를 생성하는 경우와 그렇지 않을 경우 장단점은 무엇일까요?
  • 확실한 답을 얻기 위해 질문하는 것이 아닙니다. 어차피 정답이 없을 수도 없지만,
  • 토론하는 습관을 들이고, 남의 의견을 경청하기 위한 훈련입니다.
  • 멍청한 질문이 되지 않을까 두려워 하면 아무 것도 배우지 못합니다!
videoshop7
퀴즈 - VideoShop
  • 그리고, 두번째 태스크를 선택 했습니다.
  • - 내용은 거의 유사합니다. ‘비디오 상품 관리’
    • 비디오 유형에 따라 분리해 등록하고, 제목으로 검색할 수 있어야 합니다.
    • 비디오 마다 대여료가 다르다고 하는군요.
  • 테스트 케이스 클래스 생성- VideoTapeTest 클래스를 작성합니다.
  • 테스트 메소드 생성- testVideo() 메소드를 작성했습니다. - 비디오 테이프를 등록하고, 정상적으로 등록되었는지 등록된 내용을 검사합니다. - 비디오 테이프를 등록 한후 타이틀로 검색하고, 존재하는지 검사합니다.
  • 실행 코드 작성- VideoCatalog, Video 클래스를 생성합니다.
videoshop8
퀴즈 - VideoShop

비디오 목록 관리를 테스트 하기 위한 VideoTest 클래스.

package test.acme.videoshop;

import com.acme.videoshop.video.Video;

import com.acme.videoshop.video.VideoCatalog;

import junit.framework.TestCase;

public class VideoTapeTest extends TestCase {

public static String STAR_WARS = "StarWars";

public void testVideo() {

Video item = VideoCatalog.register(STAR_WARS, VideoCatalog.VIDEO_TYPE.MOVIE, 100);

assertNotNull(item);

assertEquals(item.getTitle(), STAR_WARS);

assertEquals(item.getType(), VideoCatalog.VIDEO_TYPE.MOVIE);

assertEquals(item.getRent(), 100);

item = VideoCatalog.lookup(STAR_WARS);

assertNotNull(item);

assertEquals(item.getTitle(), STAR_WARS);

assertEquals(item.getType(), VideoCatalog.VIDEO_TYPE.MOVIE);

assertEquals(item.getRent(), 100);

item = VideoCatalog.lookup(STAR_WARS + ".");

assertNull(item);

}

}

videoshop9
퀴즈 - VideoShop

Video & VideoCatalog

비디오 객체를 직접 생성하지 않고, VideoCatalog 클래스의 register() 메소드를 통해 간접 생성.

비디오 검색 또한 lookup() 메소드를 통해 수행.

  • 비디오 유형을 표현하기 위해서 상속하는 방법이 있지만, 불필요한 클래스 추가라고 판단되어 enum 선언
  • public enum VIDEO_TYPE {
  • MOVIE, SPORTS, DOCUMENTARY
  • };
videoshop10
퀴즈 - VideoShop

Video & VideoCatalog

  • VIDEO_TYPE enum 상수를 코드로 변경하는 것이 낫지 않을까요?
  • VIDEO_TYPE enum은 Video 클래스 혹은 VideoCatalog 클래스 어느 쪽에 포함하는 게 나을까?

- Video 클래스를 상속해서, 스포츠, 다큐멘터리, 영화 클래스를 따로 생성하는 경우와 그렇지 않은 경우의 차이는 무얼까요?

videoshop11
퀴즈 - VideoShop
  • 이제 좀 까다로운 문제입니다.
  • - 고객과 비디오를 등록하고 조회할 수 있으니까 다음 문제는...
    • 고객이 비디오를 대여하는 프로세스를 테스트합니다.
    • 가장 어려운 고비였습니다! 여러분은?
  • 대여는 누가 하나요?- 당연히 고객이 대여하죠. 그래서 Customer 클래스에 choose() 메소드를 추가 했습니다.
  • 포인트를 적립해야 할 것 같습니다. - 요금은 나중에 정산하지만, 포인트는 대여하는 시점에서 적립되겠죠. - 그런데, 적립된 누적 포인트와 대여 중인 비디오를 통해서 얻은 포인트를 구분해야 합니다. - 적립된 포인트를 어디선가 관리해야 하는 겁니다.
  • 그리고, 대여 중인 비디오 수를 알아야 합니다. - 그러니까 대여 중인 비디오 내역을 어디선가 관리를 해야 하는거죠. - 다행스럽게도 과거의 대역 내역은 조회할 필요가 없다고 합니다.
  • 계산은 언제? - 태스크를 너무 크게 만들지 말라고 해서, 일단 대여하고, 대여 결과를 확인하는 것 까지로 한정 했습니다.
videoshop12
퀴즈 - VideoShop

비디오 대여를 테스트 하기 위한 RentTest 클래스.

package test.acme.videoshop;

import java.util.List;

import com.acme.videoshop.counter.VideoRental;

import com.acme.videoshop.customer.Customer;

import com.acme.videoshop.customer.CustomerManager;

import com.acme.videoshop.util.DateUtils;

import com.acme.videoshop.video.Video;

import com.acme.videoshop.video.VideoCatalog;

import junit.framework.TestCase;

public class RentTest extends TestCase {

private static final String CUSTOMER_NAME = "Sunny Kwak";

public static String STAR_WARS = "StarWars";

public static String NBA_STARS = "NBA All Stars";

public static String THE_EARTH = "The Earth";

public void setUp() {

CustomerManager.register(CUSTOMER_NAME);

VideoCatalog.register(STAR_WARS, VideoCatalog.VIDEO_TYPE.MOVIE, 100);

VideoCatalog.register(THE_EARTH, VideoCatalog.VIDEO_TYPE.DOCUMENTARY, 200);

VideoCatalog.register(NBA_STARS, VideoCatalog.VIDEO_TYPE.SPORTS, 300);

}

(continued…)

videoshop13
퀴즈 - VideoShop

비디오 대여를 테스트 하기 위한 RentTest 클래스.

public void testRent() {

Video movie, sports, documentary;

// 영화 비디오 대여 (어제)

Customer customer = CustomerManager.lookup(CUSTOMER_NAME);

movie = VideoCatalog.lookup(STAR_WARS);

assertNotNull(movie);

customer.choose(movie, DateUtils.minusDays(1));

// 적립 포인트 1, 발생 포인트 1, 대여 수 1

assertEquals(customer.getResevePoint(), 1);

assertEquals(customer.getIssuePoint(), 1);

assertEquals(customer.rentCount(), 1);

// 스포츠 비디오 대여 (그저께)

sports = VideoCatalog.lookup(NBA_STARS);

assertNotNull(sports);

customer.choose(sports, DateUtils.minusDays(2));

// 적립 포인트 3, 발생 포인트 2, 대여 수 2

assertEquals(customer.getResevePoint(), 3);

assertEquals(customer.getIssuePoint(), 3);

assertEquals(customer.rentCount(), 2);

(continued…)

videoshop14
퀴즈 - VideoShop

비디오 대여를 테스트 하기 위한 RentTest 클래스.

// 다큐멘터리 비디오 대여 (오늘)

documentary = VideoCatalog.lookup(THE_EARTH);

assertNotNull(documentary);

customer.choose(documentary, DateUtils.getToday());

// 적립 포인트 4, 발생 포인트 4, 대여 수 3

assertEquals(customer.getResevePoint(), 4);

assertEquals(customer.getIssuePoint(), 4);

assertEquals(customer.rentCount(), 3);

// 대여정보: 비디오(종류 + 제목 + 가격), 대여기간 리스트

// int totalCharge = 0;

List<VideoRental> rentalList = customer.getRentalList();

for( VideoRental rental : rentalList ) {

Video video = rental.getVideo();

System.out.print( "Type : " + VideoCatalog.getTypeAsString(video.getType()) );

System.out.print( ", Title : " + video.getTitle() );

System.out.print( ", Rent : " + video.getRent() );

System.out.print( ", Rent Date : " + rental.getRentDate() );

System.out.println( ", Term : " + DateUtils.daysBetween(rental.getRentDate(),

DateUtils.getToday()) );

}

}

}

(end…)

videoshop15
퀴즈 - VideoShop

DateUtils, Customer, VideoRental & Video

현재 날짜, 며칠 전 날짜, 며칠 후 날짜 등을 얻기 위해 DateUtils 정적 클래스를 만들었습니다.

  • 날짜는 DateTime 타입을 사용하는 방안도 있지만, 문자열로 관리하는 formatting 하기 편합니다.
  • 기간 등을 계산하는 함수에서는 Joda Time API (http://joda-time.sourceforge.net)를 사용했습니다.
  • 만일, 음력을 계산해야 하는 경우가 있다면, IBM ICU 오픈소스 라이브러리를 사용하시면 좋습니다.

적립 포인트 속성은 Customer 클래스 내부에 포함 시켰습니다.

비디오 대여 목록을 표현하기 위해서 VideoRental 클래스를 만들고, List 인터페이스를 이용해 Customer 클래스 내에 동적 배열로 선언했습니다.

videoshop16
퀴즈 - VideoShop

Customer 클래스 : 비디오 대여

/**

* 비디오 대여.

*

* @param video 대여 비디오

* @param rentDate 대여 일자

*/

public void choose(Video video, String rentDate) {

prepareList();

VideoRental rental = new VideoRental(video, rentDate);

rentalList.add( rental );

reservePoint += rental.getPoint();

}

private void prepareList() {

if( rentalList == null ) {

rentalList = new ArrayList<VideoRental>();

}

}

videoshop17
퀴즈 - VideoShop

Customer 클래스 : 적립 포인트 반환 및 발생 포인트

/**

*

* @return 적립 포인트 반환

*/

public int getResevePoint() {

return reservePoint;

}

/**

*

* @return 현재 대여 중인 비디오로 인해 발생한 포인트

*/

public int getIssuePoint() {

int issuePoint = 0;

for( VideoRental rental : rentalList ) {

issuePoint += Counter.getPoint(rental.getVideo());

}

return issuePoint;

}

videoshop18
퀴즈 - VideoShop

VideoRental, Counter 클래스 : 비디오 별 포인트 반환

public class VideoRental {

public int getPoint() {

return Counter.getPoint(video);

}

}

public class Counter {

public static int getPoint(Video video) {

return video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS ? 2 : 1;

}

}

videoshop19
퀴즈 - VideoShop

DateUtils, Customer, VideoRental & Video

- Customer 클래스 내부에 대여 내역과 적립 포인트를 포함시켰는데, 별도 클래스로 분리하는 건 어떨까요?

videoshop20
퀴즈 - VideoShop
  • 거의 마지막에 도착한 것 같습니다.
  • - 마지막태스크는 고객이 비디오를 반납했을 때, 요금을 정산하는 프로세스입니다.
    • 대충하느라 테스트 클래스를 따로 만들지 않았습니다.
  • RentTest 클래스의 testRent() 메소드 변경 - 별도의 메소드를 작성하지 않고, testRent() 메소드에 코드를 추가했습니다.
  • 테스트는 꼼꼼하게… - 검사 조건을 따져 봤습니다. 비디오 유형이 최소 3가지, 각각 할인해주는 경우와 할인 없는 경우가 있습니다. - 게다가 오늘 오전에 빌리고, 오후에 반납하는 경우는 24시간을 넘지 않는데 이런 경우도 하루 요금을 받아야죠.
  • 그런데 반납하는 프로세스는 개발하지 않았습니다!
videoshop21
퀴즈 - VideoShop

비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.

public void testRent() {

// ---- 요금 계산을 해보자...! ----

// 스포츠는 장기대여 할인이 없다. (이게 제일 쉬우니 이것부터)

// 2일전 대여, 일일요금 300원

for( VideoRental rental : rentalList ) {

Video video = rental.getVideo();

if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS ) {

// 오늘 반납 시, 2 * 300 = 600원

assertEquals( 600, rental.getCharge(DateUtils.getToday()));

// 내일 반납 시, 3 * 300 = 900원

assertEquals( 900, rental.getCharge(DateUtils.plusDays(1)));

}

}

// 영화는 대여기간이 2일 이상되면 3일째 부터는 대여요금이 1/2로 할인된다.

// 어제 대여, 일일 요금 100원

for( VideoRental rental : rentalList ) {

Video video = rental.getVideo();

if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) {

// 오늘 반납 시, 1 * 100 = 100원

assertEquals( 100, rental.getCharge(DateUtils.getToday()));

// 내일 반납 시, 2 * 100 = 200원

assertEquals( 200, rental.getCharge(DateUtils.plusDays(1)));

// 모레 반납 시, (2 * 100) + (1 * 100 / 2) = 250

assertEquals( 250, rental.getCharge(DateUtils.plusDays(2)));

}

}

(continued…)

videoshop22
퀴즈 - VideoShop

비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.

// 다큐멘타리는 3일 이상 대여하면 4일째 부터는 1/3로 할인된다.

// 오늘 대여, 일일 요금 200원

for( VideoRental rental : rentalList ) {

Video video = rental.getVideo();

if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) {

// 오늘 반납 시, 1 * 200 = 200원

assertEquals( 200, rental.getCharge(DateUtils.getToday()));

// 내일 반납 시, 1 * 200 = 200원

assertEquals( 200, rental.getCharge(DateUtils.plusDays(1)));

// 2일 후 반납 시, (2 * 200) = 400

assertEquals( 400, rental.getCharge(DateUtils.plusDays(2)));

// 4일 후 반납 시, (3 * 200) + (1 * 200 / 3) = 667

assertEquals( 666, rental.getCharge(DateUtils.plusDays(4)));

}

}

// ---- 요금 계산을끝내자...! ----

(continued…)

videoshop23
퀴즈 - VideoShop

비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.

// 전체 요금 계산 (4일 후 반납 시, 6일간 대여)

String restoreDate = DateUtils.plusDays(4);

int totalCharge = 0;

for( VideoRental rental : rentalList ) {

int eachCharge = rental.getCharge(DateUtils.plusDays(4));

Video video = rental.getVideo();

// 다큐멘터리 오늘 대여, 일일요금 200원

// 4일 대여 요금 : (3 * 200) + (1 * 200 / 3) = 667

if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) {

assertEquals( eachCharge, 666 );

}

// 영화 어제 대여, 일일요금 100원

// 5일 대여 요금 : (2 * 100) + (3 * 100 / 2) = 350

else if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) {

assertEquals( eachCharge, 350 );

}

// 스포츠 2일전 대여, 일일요금 300원

// 6일 대여 요금 : 6 * 300 = 1800

else if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) {

assertEquals( eachCharge, 1800 );

}

totalCharge += eachCharge;

}

// 전체 요금 : 666 + 350 + 1800 = 2816

assertEquals(totalCharge, 2816);

(end…)

videoshop24
퀴즈 - VideoShop

비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.

// 전체 요금 계산 (4일 후 반납 시, 6일간 대여)

String restoreDate = DateUtils.plusDays(4);

int totalCharge = 0;

for( VideoRental rental : rentalList ) {

int eachCharge = rental.getCharge(DateUtils.plusDays(4));

Video video = rental.getVideo();

// 다큐멘터리 오늘 대여, 일일요금 200원

// 4일 대여 요금 : (3 * 200) + (1 * 200 / 3) = 667

if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) {

assertEquals( eachCharge, 666 );

}

// 영화 어제 대여, 일일요금 100원

// 5일 대여 요금 : (2 * 100) + (3 * 100 / 2) = 350

else if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) {

assertEquals( eachCharge, 350 );

}

// 스포츠 2일전 대여, 일일요금 300원

// 6일 대여 요금 : 6 * 300 = 1800

else if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) {

assertEquals( eachCharge, 1800 );

}

totalCharge += eachCharge;

}

// 전체 요금 : 666 + 350 + 1800 = 2816

assertEquals(totalCharge, 2816);

(end…)

videoshop25
퀴즈 - VideoShop

Counter 클래스

요금 및 포인트 계산을 Counter 클래스에 전담 시켰습니다.

기존의 경험 상 업무 규칙은 한 곳에 몰아 두는 것이 좋은 것 같습니다.

- 요금 할인/할증 규칙은 비디오 유형 별로 바뀌지 않고, 시기 (특정 계절에 따른 이벤트) 혹은 집합 조건 (2종 선택 시 하나 무료) 등 외적인 조건에 따라 바뀌는 경우가 많습니다.

videoshop26
퀴즈 - VideoShop

VideoRental 클래스 : 요금 반환

public class VideoRental {

public int getCharge(String restoreDate) {

return Counter.getCharge(video, rentDate, restoreDate);

}

}

public static int getCharge(Video video, String rentDate, String restoreDate) {

int days = DateUtils.daysBetween(rentDate, restoreDate);

if (days == 0)

days = 1;

int charge = 0;

// 다큐멘타리는 3일 이상 대여하면 4일째 부터는 1/3로 할인된다.

if (video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY) {

if( days < 4 ) {

charge = days * video.getRent();

}

else {

charge = (3 * video.getRent()) + ((days-3) * video.getRent() / 3);

}

}

(continued…)

videoshop27
퀴즈 - VideoShop

VideoRental 클래스 : 요금 반환

// 영화는 대여기간이 2일 이상되면 3일째 부터는 대여요금이 1/2로 할인된다.

else if (video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE) {

if( days < 3 ) {

charge = days * video.getRent();

}

else {

charge = (2 * video.getRent()) + ((days-2) * video.getRent() / 2);

}

}

// 스포츠는 장기대여 할인이 없다.

else if (video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) {

charge = video.getRent() * days;

}

return charge;

}

(end…)

videoshop28
퀴즈 - VideoShop

DateUtils, Customer, VideoRental & Video

- 규칙이 변경 되는 상황일 발생하는 것을 예상한다면, Counter와 VideoRental 사이에 Factory 패턴을 적용하면 어떨까요?

videoshop29
퀴즈 - VideoShop

Test Driven Development

[Video Shop]

봐주셔서 감사드립니다.

날카로운 지적 부탁 드립니다.