지뢰찾기: Use Case Model

Download Report

Transcript 지뢰찾기: Use Case Model

D. 지뢰찾기 분석 설계 예제
D1
0. 지뢰찾기 분석
D2
분석의 목적
• 시스템과 사용자 사이의 인터페이스를 정의
• 사용자 관점에서 인식할 수 있는 것만 정의한다
• 시스템 내부는 언급하지 않는다
• 사용자는 몰라도 되고 개발자만 알면 되는 것은 언급하지 않는다
• 여기서 언급한 사용자는
– 꼭 사람만을 의미하는 것은 아님
– 시스템과 명령이나 데이터를 주고 받는 것
– 시스템 내부가 아니고 외부 환경에 속하는 것
D3
분석 단계의 산출물
(중요도 순서로 나열됨)
1. Use-case 모델
(Use-case Diagram + Use-case Specification)
2. 보충 요구사항 명세서 (Supplementary Requirement Specification)
3. 개념 모델 (Conceptual Level Class Diagram)
4. 화면/메뉴 설계
5. 개발계획
6. 기타 : 용어사전, 사용자 메뉴얼
D4
0-1. Use-case Model
D5
Use-case Diagram
지뢰찾기
닫힌 칸을
열기
지뢰를 표시
플레이어
닫힌 칸들을
열기
D6
앞의 Use-case 모델은 적절한가?
• 무엇을 기준으로 판단할 것인가?
• 좋은 모델의 기준은 무엇인가?
D7
좋은 모델의 기준은?
• 좋은 모델은 정보의 전달이라는 목적에 충실해야 한다
– Use-case 모델이 고객에게 전달하는 정보 :
시스템을 이렇게 만들면 되나?
– Use-case 모델이 개발자에게 전달하는 정보 :
이러한 시스템을 만들어라
• 전달해야 하는 정보를 빠짐 없이 포함해야 한다
• 이해하기 쉽고 간결해야 한다
D8
빠진 Use-case 는?
• 새 게임 시작하기 시나리오
• 초급 중급 고급 선택
• 색깔 소리 같은 옵션 지정
• 최고 기록 보기
• 위와 같은 것도 포함해야 하나?
D9
Use-case Diagram 의 목적
• Use-case Diagram -> 조감도 (Bird’s Eye View)
– 시스템이 제공하는 기능을 큰 단위로 표현한 것
– 모든 세세한 기능을 Use-case Diagram 으로 표현하지 말라
• 세밀한 묘사는 다른 적당한 다이어그램에서 표현하면 된다
D10
지뢰찾기의 Use-case ?
• 지뢰찾기 게임이 사용자에게 제공하는 기능을 크게 분류하면?
D11
Use-case Diagram
지뢰찾기
닫힌 칸을
열기
지뢰를 표시
플레이어
닫힌 칸들을
열기
D12
Use-case Specification
• 자세한 내용은 Use-case Specification 에서 설명한다
D13
Use-case Specification
•
Name : 닫힌 칸을 열기
•
Description:
– 지뢰가 없을 것으로 짐작되는 칸을 연다
•
Actor: 플레이어
•
Precondition:
– 칸은 닫혀있어야 한다
– 지뢰 표시가 없는 칸이어야 한다
D14
닫힌 칸을 열기
• Main Flow
1. 지뢰가 없을 것으로 짐작되는 칸을 플레이어가 마우스 왼쪽 버튼
으로 클릭한다
2. 칸은 열려진 상태로 표시되며, 주변의 지뢰의 수가 칸 안에 표시
된다
3. 주변의 지뢰의 수가 0 이면
• 지뢰 수 0은 표시되지 않고
• 주변의 닫힌 칸들이 모두 열려지며
• 각 칸 안에 그 주변의 지뢰의 수가 표시된다.
• 이 과정은 재귀적으로 반복된다
D15
닫힌 칸을 열기
• Alternative Flow
A2. 지뢰가 있는 칸이었으면
지뢰가 폭발하여 게임은 실패로 종료된다
A2. 모든 칸이 열려졌으면
게임은 성공으로 종료된다
D16
Use-case Specification
• Name : 지뢰를 표시
• Description:
– 지뢰가 있을 것으로 짐작되는 칸에 지뢰 표시를 한다
• Actor: 플레이어
• Precondition:
– 칸은 닫혀있어야 한다
D17
지뢰를 표시
• Main Flow:
1. 지뢰가 있을 것으로 짐작되는 칸을 플레이어가 마우스 오른쪽 버
튼으로 클릭한다
2. 칸에 지뢰 표시가 되고, 남은 지뢰 수 표시가 1 감소한다.
3. 지뢰 표시가 된 칸을 마우스 오른쪽 버튼으로 클릭한다
4. 칸에서 지뢰 표시가 제거되고, 남은 지뢰 수 표시가 1 증가한다
D18
지뢰를 표시
• Notes:
– 지뢰 표시가 된 칸은 열 수가 없다
"닫힌 칸 열기" 의 Precondition 참고
D19
Use-case Specification
•
•
Name : 닫힌 칸들을 열기
Description:
– 인접한 지뢰가 모두 표시되었을 경우, 지뢰가 없는 인접한 빈 칸
을 모두 동시에 연다
•
Actor: 플레이어
•
Precondition:
– 클릭하는 칸은 열려 있어야 한다
– 열려진 칸에 표시된 주변 지뢰 수 만큼
주변 칸들에 지뢰 표시들이 있어야 한다
D20
닫힌 칸들을 열기
•
Main Flow
1. 열려진 칸에서 마우스 왼쪽과 오른쪽 버튼을 동시에 클릭한다
2. 인접한 8개의 칸들 중 지뢰 표시가 되어 있지 않은 칸들 이 열려
진다. 열려진 각 칸들에는 인접한 지뢰의 수가 표시된다.
3. 열려진 칸의 주변 지뢰의 수가 0이면
• 지뢰 수 0은 표시되지 않고
• 주변의 칸 모두 열려지며
• 각 칸 안에 그 주변의 지뢰의 수가 표시된다.
• 이 과정은 재귀적으로 반복된다
D21
닫힌 칸들을 열기
• Alternative Flow
A2. 지뢰가 있는 칸이었으면
지뢰가 폭발하여 게임은 실패로 종료된다
A2. 모든 칸이 열려졌으면
게임은 성공으로 종료된다
D22
0-2. 보충 요구사항
D23
보충 요구사항
• 모든 것을 Use-case 형태에 맞춰서 표현할 필요없다
• 사용자와 시스템 사이의 작업 시나리오 절차들은 Use-case 형태로
표현하는 것이 적당하다
• 기타 요구사항으로 표현하는 편이 더 단순한 것들도 있다
D24
보충 요구사항
• 난이도
– 초급: 9 행 9 열의 칸, 10개의 지뢰
– 중급: 16 행 16 열의 칸, 40개의 지뢰
– 고급: 16 행 30 열의 칸, 99개의 지뢰
• 화면에 남은 지뢰수가 표시된다
– 남은 지뢰수 = 전체 지뢰 수 – 지뢰 표시의 수
• 화면에 초시계가 표시된다
– 첫 칸을 연 때부터 초시계가 시작하고
– 성공하거나 실패한 순간 초시계가 정지한다
• 난이도 별로 최단시간 성공 기록을 유지한다
D25
0-3. 개념모델 (Conceptual Model)
D26
개념모델
• Use-case 모델의 이해를 돕기 위해 작성
• Use-case 모델에 등장하는 개념들의 관계를 표현한다
• 용어 사전과 비슷한 역할을 한다
D27
지뢰찾기의 개념모델
• 개념모델의 목적은 시스템 설계가 아니고 관련 개념의 이해이다
– 모듈 설계와 다르다
• 다음의 개념들을 이해하기 쉽게 표현하기 위한 모델이다
– 지뢰, 이웃칸, 지뢰표시, 칸의 상태 …
• 사용자와 개발자들이 위 개념들을 잘 알고 있다면, 개념모델은 생략
가능하다
– 지뢰 찾기의 경우 사실 개념 모델은 필요없다
• 금융 정보시스템을 개발하는 경우라면, 관련 개념들이 이해하기 매
우 어려울 것이므로 개념모델이 꼭 필요할 것이다.
D28
지뢰찾기의 개념모델
3,5,8
Minefield
+rowCount : int
+columnCount : int
1
*
1
<<enumeration>>
CellState
+CLOSED
+OPENED
3,5,8
Cell
+cellState : CellState
+/nearbyMineCount : int
0..1
Mine
+neighbor
1
0..1
Flag
D29
0-4. 화면/메뉴 설계
D30
화면설계
진행화면
성공
실패
D31
메뉴설계
D32
0-5. 개발 계획
D33
점진적 개발
• 크고 복잡한 시스템을 한 번에 완성하는 것은 매우 어렵다
– 작게 쪼개어 하나씩 만들어 가는 것이 좋다
• Use-case 단위로 만들어 나간다
– 중요한 Use-case 를 먼저 선택하여
– 설계, 구현, 테스트하여 완성하고
– 그 다음 Use-case 를 선택하여 만들어 나간다
D34
Use-case 단위로 점진적 개발
•
Use-case 의 우선순위 별로 점진적 개발
1. 닫힌 칸을 열기
2. 지뢰를 표시
3. 닫힌 칸들을 열기
•
1차 개발
– 닫힌 칸을 열기
•
2차 개발
– 지뢰를 표시
– 닫힌 칸들을 열기
D35
1-1. 1차개발 : 분석
D36
1차개발 개요
• 가장 중요한 use-case 인 "닫힌 칸을 열기" 를 구현한다
• 1차개발의 완료는 빠르면 빠를 수록 좋다
– 실행하고 테스트 해 볼 수 있는 1차 버전은, 고객으로부터 요구
사항 수집하는데, 개발자가 시스템을 이해하고 개발하는데, 아주
큰 도움이 된다
• 1차개발을 빠르게 완료하기 위하여, use-case 를 단순하게 수정하
고 구현하는 것도 좋다.
– 단순하게 수정된 내용은 2차 개발에서 보충 구현한다.
D37
1-1-1. Use-case 모델
D38
Use Case Diagram
지뢰찾기
닫힌 칸을
열기
지뢰를 표시
플레이어
닫힌 칸들을
열기
D39
Use Case Specification (단순화됨)
•
Name : 닫힌 칸을 열기
•
Description:
– 지뢰가 없을 것으로 짐작되는 칸을 연다
•
Actor: 플레이어
•
Precondition:
– 칸은 닫혀있어야 한다
D40
닫힌 칸을 열기
• Main Flow
1. 지뢰가 없을 것으로 짐작되는 칸을 플레이어가 마우스 왼쪽 버튼
으로 클릭한다
2. 칸은 열려진 상태로 표시되며, 주변의 지뢰의 수가 칸 안에 표시
된다
D41
닫힌 칸을 열기
• Alternative Flow
A2. 지뢰가 있는 칸이었으면
지뢰가 폭발하여 게임은 실패로 종료된다
A2. 모든 칸이 열려졌으면
게임은 성공으로 종료된다
D42
1-1-2. 보충 요구사항
D43
보충 요구사항
• 난이도
– 초급: 9 행 9 열의 칸, 10개의 지뢰
D44
1-1-3. 화면 설계
D45
화면설계
진행화면
성공하거나 실패했을 경우
위와 같은 대화상자가 출력된다
D46
메뉴설계
D47
1-2. 1차개발 : 아키텍처 설계
D48
아키텍처 설계의 목적
• 주요 클래스 사이의 인터페이스를 정의
– 시스템을 몇 개의 주요 클래스로 나누고
– 그들 사이의 인터페이스를 정의한다
• 주요 클래스 사이의 상호작용만 정의한다
• 주요 클래스 내부는 언급하지 않는다
– 주요 클래스 내부는 상세 설계 단계에서
• 설계에서 아키텍처 설계 단계가 가장 중요하다
D49
아키텍처 설계의 산출물
(중요도 순서로 나열됨)
1. Class Diagram
– 주요 클래스를 그린 클래스 다이어그램
– Specification Level Class Diagram
2. Sequence Diagram
– 주요 클래스 사이의 상호 작용을 설계
– Specification Level Sequence Diagram
– Use-case specification 에서 주요 이벤트 각각에 대하여
sequence diagram 이 그려져야 한다
3. Activity Diagram or Statechart Diagram
D50
작업1: 주요 클래스 식별
• 시스템을 주요 클래스로 나눈다
• 시스템을 주요 클래스로 잘 나누는 것은 설계자의 경험과 능력에 의
존한다
• 클래스는 단순하게 요약 설명될 수 있는 분명한 역할이 있어야 한다
• 클래스들 사이의 관계는 상호 의존성이 낮을 수록 좋다
• 개념 설계의 클래스가 아키텍처 설계의 주요 클래스가 되어야 하는
것은 아니다
– 그럴 수도 있고 아닐 수도 있다
D51
아키텍처
• MVC 구조
– 문서 중심 어플리케이션의 설계에 적합함
예: 워드프레세서, 그래픽 에디터
– Model : 어플리케이션의 주요 데이터를 담당
– View : 사용자 인터페이스를 담당
– Controller : 처리 절차를 담당
• 3계층 구조
– 데이터베이스 기반 정보 시스템 설계에 적합함
– Presentation 계층 : 사용자 인터페이스를 담당
– Business Logic 계층 : 업무 로직을 담당
– Data 계층 : 데이터를 담당
D52
주요 클래스와 모듈
• 주요 클래스는 모듈을 대표한다
• 모듈 내부에서만 접근되는 여러 클래스들이 있을 것이다
모듈
내부 클래스1
주요 클래스1
주요 클래스2
내부 클래스2
내부 클래스3
내부 클래스4
D53
모듈간 상호의존성
• 여러 모듈이 공통으로 접근하는 자료구조가 있으면 안됨
모듈2
주요 로직
자료구조 처리 로직
모듈1
모듈3
주요 로직
자료구조 처리 로직
주요 로직
자료구조 처리 로직
자료구조
D54
Abstract Data Type
• 바람직한 구조
모듈2
모듈1
주요 로직
모듈3
주요 로직
주요 로직
ADT
자료구조 처리로직
자료구조
D55
Abstract Data Type
• 자료구조 + 함수
• 함수는 자료구조에 직접 접근해야 하는 처리 로직을 구현 한 것
• 외부에서는 자료구조에 직접 접근할 수 없고 이 함수만 호출할 수 있
다
• 외부에 자료구조는 노출되지 않는다 (encapsulation)
• ADT 개념을 확장한 것이 객체지향 언어의 Class
D56
주요 모듈의 크기
• 주요 모듈이란 일종의 서브시스템(subsystem)을 말한다
• 주요 모듈의 크기는 한 사람이 상세 설계 및 구현을 담당하기에 적당
한 크기
• 너무 작고 세밀하게 나누지 말라
• 자세한 것은 상세설계 단계에서 설계한다
D57
작업1: 주요 클래스 식별
• 지뢰찾기는 비교적 단순한 로직이므로 주요 클래스도 단순하다
• Minesweeper : 게임 로직을 담당
• UI : 사용자 인터페이스를 담당
Minesweeper
UI
D58
작업2: 시스템 내부 절차 식별
• Use-case specification 의 절차를 수행하기 위해 시스템 내부에서
수행해야 하는 일을 순서대로 적는다
• 너무 자세하면 안되고 주요 아키텍처 수준의 절차이어야 한다
D59
닫힌 칸을 열기 use-case
• Main Flow
1. 지뢰가 없을 것으로 짐작되는 칸을 플레이어가 마우스 왼쪽 버튼
으로 클릭한다
2. 칸은 열려진 상태로 표시되며, 주변의 지뢰의 수가 칸 안에 표시
된다
D60
닫힌 칸을 열기 use-case
• Alternative Flow
A2. 지뢰가 있는 칸이었으면
지뢰가 폭발하여 게임은 실패로 종료된다
A2. 모든 칸이 열려졌으면
게임은 성공으로 종료된다
D61
닫힌 칸을 열기 절차
1. 지뢰가 없을 것으로 짐작되는 칸을 플레이어가 마우스 왼쪽 버튼으
로 클릭한다
a. 마우스 입력을 받는다
b. 눌려진 칸을 찾는다
2. 칸은 열려진 상태로 표시되며, 주변의 지뢰의 수가 칸 안에 표시된다
c. 칸을 열린 상태로 바꾼다
d. 주변 지뢰 수를 계산한다
e. 주변 지뢰수와 칸의 상태를 그린다
D62
닫힌 칸을 열기 절차
•
Alternative Flow
A2. 지뢰가 있는 칸이었으면
지뢰가 폭발하여 게임은 실패로 종료된다
c-1. 지뢰가 있는 칸이면 게임 실패
c-2. 게임실패 메시지를 출력하고 게임종료
A2. 모든 칸이 열려졌으면
게임은 성공으로 종료된다
e-1. 모든 칸이 열려졌으면 게임성공
e-2. 게임성공 메시지를 출력하고 게임종료
D63
작업3: 클래스에 역할 할당
•
UI
a. 마우스 입력을 받는다
b. 눌려진 칸을 찾는다
e. 주변 지뢰수와 칸의 상태를 그린다
c-2. 게임실패 메시지를 출력하고 게임종료
e-2. 게임성공 메시지를 출력하고 게임종료
•
Minesweeper
c. 칸을 열린 상태로 바꾼다
d. 주변 지뢰 수를 계산한다
c-1. 지뢰가 있는 칸이면 게임 실패
e-1. 모든 칸이 열려졌으면 게임성공
D64
작업4: 역할을 메소드에 할당
• UI
– OnLBottonDown
a. 마우스 입력을 받는다
b. 눌려진 칸을 찾는다
– DrawGame
e. 주변 지뢰수와 칸의 상태를 그린다
– GameFailure
c-2. 게임실패 메시지를 출력하고 게임종료
– GameSuccess
e-2. 게임성공 메시지를 출력하고 게임종료
D65
작업4: 역할을 메소드에 할당
• Minesweeper
– OpenCell
c. 칸을 열린 상태로 바꾼다
c-1. 지뢰가 있는 칸이면 게임 실패
e-1. 모든 칸이 열려졌으면 게임성공
– GetAdjacentMineCount
d. 주변 지뢰 수를 계산한다
D66
작업5: 메소드 보완
• 필요한 메소드를 추가한다
• Sequence diagram 을 그려보는 것이 바람직하다
• 상호작용을 가능한 단순하게 만든다
D67
:UI
:Minesweeper
OnLButtonDown(x,y)
OpenCell(row,col)
[지뢰가 있는 칸이면]
Player
GameFailure()
[모든 칸이 열려졌으면]
GameSuccess()
DrawGame()
GetAdjacentMineCount(row,col)
D68
작업5: 메소드 보완
• 꼭 필요한 절차가 빠졌다
• 무엇이 빠졌는가?
– 구현해 볼 때까지는 알 수 없나?
• 시퀀스 다이어그램으로 꼼꼼히 시뮬레이션 하면서 빠진 절차를 밝혀
내야 한다
D69
UI
+OnLButtonDown(in x : int, in y : int)
+DrawGame()
+GameFailure()
+GameSuccess()
Minesweeper
+OpenCell(in row : int, in col : int)
+GetAdjacentMineCount(in row : int, in col : int) : int
D70
작업5: 메소드 보완
• UI 가 칸의 상태를 그리려면 Minesweeper 로부터 상태값을 받아와
야 한다
• Minesweeper 가 UI 의 DrawGame() 을 호출할 필요가 없다
– 칸을 연 후 다시 그려야 하는 것은 당연하다
– UI 가 Minesweeper 의 OpenCell() 을 호출한 후
스스로 DrawGame() 을 호출하면 된다
• 성공과 실패 여부를 UI 가 Minesweeper 로부터 얻어가도록 수정
– 호출이 단방향일 수 있다면 구조가 더 단순해진다
D71
: Minesweeper
: UI
OnLButtonDown(x, y)
OpenCell(row, col)
IsSuccess()
IsFailure()
DrawGame()
GetCellState(row, col)
GetAdjacentMineCount(row, col)
D72
작업5: 메소드 보완
• 초기화/마무리 메소드를 추가한다
D73
: UI
: Minesweeper
OnNewGame()
InitGame(rowCnt, colCnt, mineCnt)
DrawGame()
D74
작업6: 클래스 다이어그램
<<enumeration>>
CellState
+OPENED
+CLOSED
UI
+OnNewGame()
+OnLButtonDown(in x : int, in y : int)
+DrawGame()
Minesweeper
+InitGame(in rowCnt : int, in colCnt : int, in mineCnt : int)
+OpenCell(in row : int, in col : int)
+IsSuccess()
+IsFailure()
+GetCellState(in row : int, in col : int) : CellState
+GetAdjacentMineCount(in row : int, in col : int) : int
D75
1-3. 1차개발 : 상세 설계
D76
1-3-1. 1차개발 : Minesweeper 상세설계
D77
상세 설계
• 주요 클래스 내부 로직을 설계한다
– 아키텍처 설계에서 주요 클래스의 메소드 목록이 정의되었다
– 주요 클래스의 메소드들의 내부 로직을 설계한다
• 주요 클래스 내부에서 사용될 내부 클래스들을 식별한다
• 내부 클래스에 역할을 할당한다
• 주요 클래스의 메소드 각각에 대하여 시퀀스 다이어그램이 그려져야
한다
D78
작업1: 내부 클래스 식별
• 개념 모델을 참고하여 내부 클래스를 만든다
• 개념 모델의 클래스가 주요 클래스나 내부 클래스가 아닐 수 있다
D79
Minesweeper
+InitGame(in rowCnt : int, in colCnt : int, in mineCnt : int)
+OpenCell(in row : int, in col : int)
+IsSuccess() : bool
+IsFailure() : bool
+GetCellState(in row : int, in col : int) : CellState
+GetAdjacentMineCount(in row : int, in col : int) : int
1
1
3,5,8
Minefield
Cell
+rowCount : int
+columnCount : int
+cellState : CellState
+/nearbyMineCount : int
1
*
3,5,8
+neighbor
1
<<enumeration>>
CellState
+CLOSED
+OPENED
0..1
Mine
D80
작업2: 클래스에 역할 할당
• Minesweeper : 지뢰찾기 게임의 로직을 담당
– 게임 성공과 실패를 판단
– 칸을 열 수 있는 지 없는 지를 판단
– 주변의 모든 칸도 저절로 열려야 하는 지 판단 (이 기능은 현재
반복에서 구현하지 않는다)
• Minefield : 자료구조를 담당
– 칸, 지뢰, 깃발의 목록 자료구조
– 게임의 로직과 자료구조를 분리
• Cell : 칸 ADT
– 자료구조는 가능한 노출되지 않는 편이 좋다
• Mine: 지뢰 ADT
D81
작업3: 상세로직 시퀀스 다이어그램
• 주요 클래스의 메소드 각각에 대하여 시퀀스 다이어그램을 그린다
– Minesweeper 모듈의 주요 클래스는 Minesweeper
– Minesweeper 클래스의 메소드 각각에 대하여
상세로직 시퀀스 다이어그램을 그린다
• 상세로직 시퀀스 다이어그램을 그리면서
– 내부 클래스에 할당된 역할에 따라
내부 클래스에 메소드를 만든다
D82
: UI
: Minesweeper
: Minefield
: Cel
mine : Mine
InitGame(rowCnt, colCnt, mineCnt)
InitMinefield(rowCnt, colCnt, mineCnt)
Cell()
Mine()
PutMine(mine)
D83
: UI
: Minesweeper
: Minefield
cell : Cell
GetCellState(row, col)
cell:=GetCell(row, col)
state:=GetState()
state
D84
: UI
: Minesweeper
: Minefield
cell : Cell
OpenCell(row, col)
cell:=GetCell(row, col)
GetState()
Open()
IsMined()
AreAllMinesFound()
D85
: UI
: Minesweeper
: Minefield
cell : Cell
GetAdjacentMineCount(row, col)
cell:=GetCell(row, col)
IsMined()
D86
작업4: 클래스 다이어그램
• 시퀀스 다이어그램을 그리며 만든 메소드를 클래스 다이어그램으로
정리한다
D87
Minesweeper
+InitGame(in rowCnt : int, in colCnt : int, in mineCnt : int)
+OpenCell(in row : int, in col : int)
+IsSuccess() : bool
+IsFailure() : bool
+GetCellState(in row : int, in col : int) : CellState
+GetAdjacentMineCount(in row : int, in col : int) : int
3,5,8
1
Cell
1
Minefield
*
+InitMinefield(in rowCnt : int, in colCnt : int, in mineCnt : int)
+GetCell(in row : int, in col : int) : Cell
+AreAllMinesFound() : bool
<<enumeration>>
CellState
+CLOSED
+OPENED
1
+Cell()
+PutMine(in mine : Mine)
+GetState() : CellState
+IsMined() : bool
+Open()
3,5,8
+neighbor
1
0..1
Mine
+Mine()
D88
작업5: 클래스 다이어그램 보완
• Mine 클래스는 메소드가 없다. 부여된 역할이 없다
• Cell 에 Mine 이 있는지 없는지 나타내는 bool 형의 멤버 변수 하나
면 족하다
• Minefield 자료구조를 2차원 배열 자료구조로 만들면 neighbor 연관
을 구현할 필요 없다
• GetAdjacentMineCount() 는 게임로직이라기 보다는 Minefield 자
료구조에 대한 검색에 가깝다
– Minefield 클래스에서 구현
D89
Minesweeper
+InitGame(in rowCnt : int, in colCnt : int, in mineCnt : int)
+OpenCell(in row : int, in col : int)
+IsSuccess() : bool
+IsFailure() : bool
+GetCellState(in row : int, in col : int) : CellState
+GetAdjacentMineCount(in row : int, in col : int) : int
1
Cell
1
Minefield
*
+InitMinefield(in rowCnt : int, in colCnt : int, in mineCnt : int)
+GetCell(in row : int, in col : int) : Cell
+AreAllMinesFound() : bool
+GetAdjacentMineCount(in row : int, in col : int) : int
1
+Cell()
+PutMine(in mine : Mine)
+GetState() : CellState
+IsMined() : bool
+Open()
<<enumeration>>
CellState
+CLOSED
+OPENED
D90
작업6: 시퀀스 다이어그램 보완
• 변경된 클래스 다이어그램에 따라 시퀀스 다이어그램을 보완한다
D91
: UI
: Minesweeper
: Minefield
: Cell
InitGame(rowCnt, colCnt, mineCnt)
InitMinefield(rowCnt, colCnt, mineCnt)
Cell()
PutMine(mine)
D92
: UI
: Minesweeper
: Minefield
cell : Cell
GetAdjacentMineCount(row, col)
GetAdjacentMineCount(row, col)
cell:=GetCell(row, col)
IsMined()
D93
1-3-2. 1차개발 : UI 상세 설계
D94
UI 상세 설계는 생략
• UI 상세 설계는 구현될 플랫폼의 UI 기술에 크게 의존한다
• 여기서는 Microsoft Visual C++ MFC 로 구현한다
D95
UI
-minesweeper : Minesweeper
+OnGameNew()
+DrawGame()
+OnLButtonDown()
Minesweeper
+InitGame(in rowCnt : int, in colCnt : int, in mineCnt : int)
+OpenCell(in row : int, in col : int)
+IsSuccess() : bool
+IsFailure() : bool
+GetCellState(in row : int, in col : int) : CellState
+GetAdjacentMineCount(in row : int, in col : int) : int
D96
1-4. 1차개발: 테스트 구현
D97
UI 테스트 구현
• 다음과 같은 Minefield 를 구현하여 UI 를 테스트한다
D98
UI 테스트 데이터 구현
• 지뢰
1
1
1
1
1
1
1
1
1
1
D99
UI 테스트 데이터 구현
• 주변 지뢰 수
1
2
1
1
0
0
0
0
0
2
1
0
0
0
0
0
2
2
1
0
0
0
1
1
1
1
3
1
1
1
1
2
0
0
2
0
0
2
0
0
1
1
1
0
1
1
1
0
1
1
1
0
0
0
0
0
1
2
1
0
1
2
2
1
1
0
1
1
2
1
1
D100
model.h
#ifndef MINESWEEPER_H
#define MINESWEEPER_H
enum CellState {
CLOSED = 0,
OPENED = 1
};
class Minesweeper {
public:
void InitGame(int rowCnt, int colCnt, int mineCnt);
void OpenCell(int row, int col);
bool IsSuccess();
bool IsFailure();
CellState GetCellState(int row, int col);
int GetAdjacentMineCount(int row, int col);
};
#endif
D101
model.cpp
#include <stdafx.h>
#include <assert.h>
#include "model.h"
static char mine[9][9]
0, 0, 1, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 1,
};
= {
0, 0,
0, 0,
0, 0,
0, 0,
1, 0,
0, 0,
0, 0,
0, 0,
1, 0
static char adjacentMineCnt[9][9] = {
1, 2, 0, 1, 0, 0, 0, 0, 0,
1, 0, 2, 1, 0, 0, 0, 0, 0,
1, 1, 2, 2, 2, 1, 0, 0, 0,
0, 0, 2, 0, 0, 1, 1, 1, 1,
0, 0, 2, 0, 3, 1, 1, 0, 1,
0, 0, 1, 1, 1, 0, 1, 1, 1,
0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 2, 0, 1, 0, 1, 2, 2, 1,
1, 0, 2, 1, 0, 1, 0, 0, 1
};
D102
static char opened[9][9] = {
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0
};
void Minesweeper::InitGame(int rowCnt, int colCnt, int mineCnt) {
memset(opened, 0, sizeof(char) * 9 * 9);
}
void Minesweeper::OpenCell(int row, int col) {
assert( 0 <= row && row < 9 );
assert( 0 <= row && col < 9 );
opened[row][col] = 1;
}
bool Minesweeper::IsSuccess() {
for (int r = 0; r < 9; ++r)
for (int c = 0; c < 9; ++c)
if (!opened[r][c] && !mine[r][c]) return false;
return true;
}
D103
bool Minesweeper::IsFailure() {
for (int r = 0; r < 9; ++r)
for (int c = 0; c < 9; ++c)
if (opened[r][c] && mine[r][c]) return true;
return false;
}
CellState Minesweeper::GetCellState(int row, int col) {
assert( 0 <= row && row < 9 );
assert( 0 <= row && col < 9 );
if (opened[row][col]) return OPENED;
return CLOSED;
}
int Minesweeper::GetAdjacentMineCount(int row, int col) {
assert( 0 <= row && row < 9 );
assert( 0 <= row && col < 9 );
return adjacentMineCnt[row][col];
}
D104
Model 테스트 구현
• UI 를 텍스트 모드로 구현한다
D105
UI.c
#include <stdio.h>
#include "model.h"
Minesweeper game;
void help() {
printf("o 6 3 -> 6 행 3 열의 사각형을 열기\n");
printf("h
-> 도움말\n");
printf("x
-> 게임 종료\n");
printf("\n\n");
}
void showMinefield() {
int row, col;
printf("
");
for (col = 0; col < 9; ++col)
printf(" %d ", col);
for (row = 0; row < 9; ++row) {
printf("\n %d ", row);
for (col = 0; col < 9; ++col)
if (game.GetCellState(row, col) == CLOSED) printf(" . ");
else {
int n = game.GetAdjacentMineCount(row, col);
if (n == 0) printf("
");
else printf(" %d ", n);
}
}
printf("\n\n>");
}
D106
void main() {
char s[80]; int r, c;
game.InitGame(9, 9, 10);
help();
for (;;) {
showMinefield();
gets(s);
switch(s[0]) {
case 'o': case 'O':
sscanf(s+1, "%d %d", &r, &c);
game.OpenCell(r, c);
if (game.IsSuccess()) { puts("You Win"); return; }
if (game.IsFailure()) { puts("Game Over"); return; }
break;
case 'h': case 'H': help(); break;
case 'q': case 'Q': return;
};
}
}
D107
2-1. 2차개발 분석
D108
개요
• "닫힌 칸을 열기" 구현 보완
• "지뢰를 표시" 구현
D109
2-1-1. Use-case Model
D110
Use Case Diagram
지뢰찾기
닫힌 칸을
열기
지뢰를 표시
플레이어
닫힌 칸들을
열기
D111
Use Case Specification
•
Name : 닫힌 칸을 열기
•
Description:
– 지뢰가 없을 것으로 짐작되는 칸을 연다
•
Actor: 플레이어
•
Precondition:
– 칸은 닫혀있어야 한다
– 지뢰 표시가 없는 칸이어야 한다
D112
닫힌 칸을 열기
• Main Flow
1. 지뢰가 없을 것으로 짐작되는 칸을 플레이어가 마우스 왼쪽 버튼
으로 클릭한다
2. 칸은 열려진 상태로 표시되며, 주변의 지뢰의 수가 칸 안에 표시
된다
3. 주변의 지뢰의 수가 0 이면
• 지뢰 수 0은 표시되지 않고
• 주변의 닫힌 칸들이 모두 열려지며
• 각 칸 안에 그 주변의 지뢰의 수가 표시된다.
• 이 과정은 재귀적으로 반복된다
D113
닫힌 칸을 열기
• Alternative Flow
A2. 지뢰가 있는 칸이었으면
지뢰가 폭발하여 게임은 실패로 종료된다
A2. 모든 칸이 열려졌으면
게임은 성공으로 종료된다
D114
Use Case Specification
• Name : 지뢰를 표시
• Description:
– 지뢰가 있을 것으로 짐작되는 칸에 지뢰 표시를 한다
• Actor: 플레이어
• Precondition:
– 칸은 닫혀있어야 한다
D115
지뢰를 표시
• Main Flow:
1. 지뢰가 있을 것으로 짐작되는 칸을 플레이어가 마우스 오른쪽 버
튼으로 클릭한다
2. 칸에 지뢰 표시가 되고, 남은 지뢰 수 표시가 1 감소한다.
3. 지뢰 표시가 된 칸을 마우스 오른쪽 버튼으로 클릭한다
4. 칸에서 지뢰 표시가 제거되고, 남은 지뢰 수 표시가 1 증가한다
D116
지뢰를 표시
• Notes:
– 지뢰 표시가 된 칸은 열 수가 없다
"닫힌 칸 열기" 의 Precondition 참고
D117
2-1-2. 보충 요구사항
D118
보충 요구사항
• 변화 없음
D119
2-1-3. 화면/메뉴 설계
D120
화면/메뉴 설계
D121
2-2. 2차개발 설계
D122
개요
• 2차 개발에서 추가된 기능을 설계 구현한다
• 1차 개발에서 설계 구현된 것을 수정한다
• 얼마나 수정되어야 하는가?
• 1차 개발의 설계가 우수했다면
– 변경은 적을 것이고
– 주로 기능 추가일 것이다
• 만약 크게 변경되어야 한다면
– 1차 개발의 설계가 무엇이 잘못되었는지 분석 반성하여야 한다
– 반성의 결과는 바람직한 설계 지침으로 정리되어야 함
D123
작업1: 시스템 내부 절차 식별
• 추가된 Use-case specification 의 절차를 수행하기 위해 시스템 내
부에서 수행해야 하는 일을 순서대로 적는다
D124
닫힌 칸을 열기 use-case
• Main Flow
1. 지뢰가 없을 것으로 짐작되는 칸을 플레이어가 마우스 왼쪽 버튼
으로 클릭한다
2. 칸은 열려진 상태로 표시되며, 주변의 지뢰의 수가 칸 안에 표시
된다
3. 주변의 지뢰의 수가 0 이면
• 지뢰 수 0은 표시되지 않고
• 주변의 닫힌 칸들이 모두 열려지며
• 각 칸 안에 그 주변의 지뢰의 수가 표시된다.
• 이 과정은 재귀적으로 반복된다
D125
주변 지뢰 수 0 일 때의 절차
•
주변의 닫힌 칸들을 모두 연다
•
이미 구현된 Minesweeper.OpenCell(int row, int col) 메소드를 호
출하여 주변의 닫힌 칸들을 모두 연다
•
재귀적으로 구현한다
D126
지뢰를 표시 use-case
• Main Flow:
1. 지뢰가 있을 것으로 짐작되는 칸을 플레이어가 마우스 오른쪽 버
튼으로 클릭한다
2. 칸에 지뢰 표시가 되고, 남은 지뢰 수 표시가 1 감소한다.
3. 지뢰 표시가 된 칸을 마우스 오른쪽 버튼으로 클릭한다
4. 칸에서 지뢰 표시가 제거되고, 남은 지뢰 수 표시가 1 증가한다
D127
지뢰를 표시 절차
a. 오른쪽 버튼의 입력을 받는다 (UI)
b. 눌려진 칸을 찾는다 (UI)
c. 칸에 지뢰 표시를 토글한다 (Minesweeper)
D128
칸의 상태
• Opened, Closed, Mined, Flaged
• Cell 의 상태 메소드 정리
– bool IsOpened();
– bool IsMined();
– bool IsFlaged();
• Minesweeper 의 상태 메소드 정리
– bool IsOpened();
– bool IsMined();
– bool IsFlaged();
D129
작업2: 시퀀스 다이어그램으로 설계
: UI
: Minesweeper
: Minefield
cell : Cell
OpenCell(row, col)
cell:=GetCell(row, col)
IsFlaged()
IsOpened()
Open()
IsMined()
AreAllMinesFound()
GetAdjacentMineCount(row, col)
OpenAdjacentCells(row, col)
D130
: UI
: Minesweeper
OpenAdjacentCells(row, col)
OpenCell(row, col)
D131
: UI
: Minesweeper
: Minefield
cell : Cell
OnRButtonDown(x, y)
FlagCell(row, col)
cell:=GetCell(row, col)
IsOpened()
IsFlaged()
PutFlag()
RemoveFlag()
D132
작업3: 클래스 다이어그램
UI
-minesweeper : Minesweeper
+OnGameNew()
+DrawGame()
+OnLButtonDown()
+OnRButtonDown(in x : int, in y : int)
Minesweeper
+InitGame(in rowCnt : int, in colCnt : int, in mineCnt : int)
+OpenCell(in row : int, in col : int)
+IsSuccess() : bool
+IsFailure() : bool
+GetAdjacentMineCount(in row : int, in col : int) : int
+FlagCell(in row : int, in col : int)
+IsOpened(in row : int, in col : int) : bool
+IsMined(in row : int, in col : int) : bool
+IsFlaged(in row : int, in col : int) : bool
+OpenAdjacentCells(in row : int, in col : int)
Cell
1
1
Minefield
+InitMinefield(in rowCnt : int, in colCnt : int, in mineCnt : int)
+GetCell(in row : int, in col : int) : Cell
+AreAllMinesFound() : bool
+GetAdjacentMineCount(in row : int, in col : int) : int
*
1
+Cell()
+PutMine(in mine : Mine)
+IsOpened() : bool
+IsMined() : bool
+Open()
+PutFlag()
+RemoveFlag()
+IsFlaged() : bool
D133
변경 사항
• 추가되는 메소드
– UI::OnRButtonDown(int x, int y);
– Minesweeper::FlagCell(int row, int col);
– Minesweeper::OpenAdjacentCells(int row, int col);
– Cell::PutFlag();
– Cell::RemoveFlag();
• 수정되는 메소드
– Minesweeper::OpenCell(int row, int col);
– 주변 지뢰 수가 0일 경우 OpenAdjacentCells() 호출을 하도록
수정
D134
변경 사항
• Cell 의 GetState() 메소드를 다음으로 대체한다
– Cell::IsOpened()
– Cell::IsFlaged()
– Cell::IsMined()
• Minesweeper 의 GetCellState() 메소드를 다음으로 대체한다
– Minesweeper::IsOpened()
– Minesweeper ::IsFlaged()
– Minesweeper ::IsMined()
D135
2-3. 2차개발 테스트 계획
D136
UI 테스트 구현
• 다음과 같은 Minefield 를 구현하여 UI 를 테스트한다
D137
UI 테스트 데이터 구현
• 지뢰
1
1
1
1
1
1
1
1
1
1
D138
UI 테스트 데이터 구현
• 주변 지뢰 수
1
2
1
1
0
0
0
0
0
2
1
0
0
0
0
0
2
2
1
0
0
0
1
1
1
1
3
1
1
1
1
2
0
0
2
0
0
2
0
0
1
1
1
0
1
1
1
0
1
1
1
0
0
0
0
0
1
2
1
0
1
2
2
1
1
0
1
1
2
1
1
D139
model.h
#ifndef MINESWEEPER_H
#define MINESWEEPER_H
class Minesweeper {
public:
void InitGame(int rowCnt, int colCnt, int mineCnt);
void OpenCell(int row, int col);
void FlagCell(int row, int col);
bool IsSuccess();
bool IsFailure();
int GetAdjacentMineCount(int row, int col);
bool IsFlaged(int row, int col);
bool IsOpened(int row, int col);
bool IsMined(int row, int col);
};
#endif
D140
model.cpp
#include <stdafx.h>
#include <assert.h>
#include "model.h"
static char mine[9][9]
0, 0, 1, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 1,
};
= {
0, 0,
0, 0,
0, 0,
0, 0,
1, 0,
0, 0,
0, 0,
0, 0,
1, 0
static char adjacentMineCnt[9][9] = {
1, 2, 0, 1, 0, 0, 0, 0, 0,
1, 0, 2, 1, 0, 0, 0, 0, 0,
1, 1, 2, 2, 2, 1, 0, 0, 0,
0, 0, 2, 0, 0, 1, 1, 1, 1,
0, 0, 2, 0, 3, 1, 1, 0, 1,
0, 0, 1, 1, 1, 0, 1, 1, 1,
0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 2, 0, 1, 0, 1, 2, 2, 1,
1, 0, 2, 1, 0, 1, 0, 0, 1
};
D141
static char opened[9][9] = {
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0
};
static char flaged[9][9] = {
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0
};
D142
void Minesweeper::InitGame(int rowCnt, int colCnt, int mineCnt) {
memset(opened, 0, sizeof(char) * 9 * 9);
memset(flaged, 0, sizeof(char) * 9 * 9);
}
void Minesweeper::OpenCell(int row, int col) {
assert( 0 <= row && row < 9 && 0 <= row && col < 9 );
if (flaged[row][col]) return;
opened[row][col] = 1;
}
bool Minesweeper::IsSuccess() {
for (int r = 0; r < 9; ++r)
for (int c = 0; c < 9; ++c)
if (!opened[r][c] && !mine[r][c]) return false;
return true;
}
bool Minesweeper::IsFailure() {
for (int r = 0; r < 9; ++r)
for (int c = 0; c < 9; ++c)
if (opened[r][c] && mine[r][c]) return true;
return false;
}
int Minesweeper::GetAdjacentMineCount(int row, int col) {
assert( 0 <= row && row < 9 && 0 <= row && col < 9 );
return adjacentMineCnt[row][col];
}
D143
bool Minesweeper::IsFlaged(int row, int col) {
assert( 0 <= row && row < 9 && 0 <= row && col < 9 );
return flaged[row][col] == 1;
}
bool Minesweeper::IsMined(int row, int col) {
assert( 0 <= row && row < 9 && 0 <= row && col < 9 );
return flaged[row][col] == 1;
}
bool Minesweeper::IsOpened(int row, int col) {
assert( 0 <= row && row < 9 && 0 <= row && col < 9 );
return opened[row][col] == 1;
}
void Minesweeper::FlagCell(int row, int col) {
assert( 0 <= row && row < 9 && 0 <= row && col < 9 );
flaged[row][col] = 1;
}
D144
Model 테스트
• Model 은 UI 와 통합하여 테스트한다
D145
3-1. 3차개발 분석
D146
개요
• "닫힌 칸들을 열기" 구현
D147
3-1-1. Use-case Model
D148
Use Case Diagram
지뢰찾기
닫힌 칸을
열기
지뢰를 표시
플레이어
닫힌 칸들을
열기
D149
Use-case Specification
•
•
Name : 닫힌 칸들을 열기
Description:
– 인접한 지뢰가 모두 표시되었을 경우, 지뢰가 없는 인접한 빈 칸
을 모두 동시에 연다
•
Actor: 플레이어
•
Precondition:
– 클릭하는 칸은 열려 있어야 한다
– 열려진 칸에 표시된 주변 지뢰 수 만큼
주변 칸들에 지뢰 표시들이 있어야 한다
D150
닫힌 칸들을 열기
•
Main Flow
1. 열려진 칸에서 마우스 왼쪽과 오른쪽 버튼을 동시에 누른다
2. 인접한 8개의 칸들 중 지뢰 표시가 되어 있지 않은 칸들 이 열려
진다. 열려진 각 칸들에는 인접한 지뢰의 수가 표시된다.
3. 열려진 칸의 주변 지뢰의 수가 0이면
• 지뢰 수 0은 표시되지 않고
• 주변의 칸 모두 열려지며
• 각 칸 안에 그 주변의 지뢰의 수가 표시된다.
• 이 과정은 재귀적으로 반복된다
D151
닫힌 칸들을 열기
• Alternative Flow
A2. 지뢰가 있는 칸이었으면
지뢰가 폭발하여 게임은 실패로 종료된다
A2. 모든 칸이 열려졌으면
게임은 성공으로 종료된다
D152
3-1-2. 보충 요구사항
D153
보충 요구사항
• 난이도
– 초급: 9 행 9 열의 칸, 10개의 지뢰
– 중급: 16 행 16 열의 칸, 40개의 지뢰
– 고급: 16 행 30 열의 칸, 99개의 지뢰
• 화면에 남은 지뢰수가 표시된다
– 남은 지뢰수 = 전체 지뢰 수 – 지뢰 표시의 수
D154
3-1-3. 화면/메뉴 설계
D155
화면설계
진행화면
성공
실패
D156
3-2. 3차개발 설계
D157
개요
• 3차 개발에서 추가된 기능을 설계 구현한다
• 2차 개발에서 설계 구현된 것을 수정한다
D158
작업1: 시스템 내부 절차 식별
• 추가된 Use-case specification 의 절차를 수행하기 위해 시스템 내
부에서 수행해야 하는 일을 순서대로 적는다
D159
닫힌 칸들을 열기 절차
1. 마우스 두 버튼이 동시에 눌려진 것을 감지한다
2. 열린 칸이 아니면 무시
3. 주변의 지뢰 수와 주변의 지뢰표시 수를 비교한다
3-1. 주변 지뢰 수 계산
3-2. 주변 지뢰표시 수 계산
4. 두 수가 같으면 주변의 지뢰표시가 없고 닫혀진 모든 칸을 연다.
5. 주변의 칸을 여는 절차는 이미 구현한 inesweeper.AdjacentCells()
를 이용한다
6. 두 수가 같지 않으면 무시
D160
메소드에 역할 할당
• UI::OnRButtonDown()  1
• Minesweeper::OpenCells()  2, 3, 4, 6
• Minefield::GetAdjacentMineCount()  3-1
• Minefield::GetAdjacentFlagCount()  3-2
• Minesweeper::OpenAdjacentCells()  5
D161
작업2: 시퀀스 다이어그램으로 설계
: UI
: Minesweeper
: Minefield
OnLButtonDown()
OnRButtonDown(x, y)
OpenCells(row, col)
GetAdjacentMineCount(row, col)
GetAdjacentFlagCount(row, col)
OpenAdjacentCells(row, col)
D162
: UI
: Minesweeper
: Minefield
cell : Cell
DrawGame()
GetFlagCount()
GetFlagCount()
IsFlaged()
IsSuccess()
IsFailure()
IsOpened(row, col)
cell:=GetCell(row, col)
IsFlaged(row, col)
IsFlaged()
IsExploded(row, col)
IsMined(row, col)
IsMined()
IsClosed(row, col)
D163
작업3: 클래스 다이어그램
UI
Minesweeper
-minesweeper : Minesweeper
+OnGameNew()
+DrawGame()
+OnLButtonDown()
+OnRButtonDown(in x : int, in y : int)
+OnGameBasic()
+OnGameIntermediate()
+OnGameExtended()
+InitGame(in rowCnt : int, in colCnt : int, in mineCnt : int)
+OpenCells(in row : int, in col : int)
+OpenAdjacentCells(in row : int, in col : int)
+OpenCell(in row : int, in col : int)
+FlagCell(in row : int, in col : int)
+GetAdjacentMineCount(in row : int, in col : int) : int
+GetFlagCount() : int
+IsSuccess() : bool
+IsFailure() : bool
+IsOpened(in row : int, in col : int) : bool
+IsMined(in row : int, in col : int) : bool
+IsFlaged(in row : int, in col : int) : bool
+IsClosed(in row : int, in col : int) : bool
+IsExploded(in row : int, in col : int) : bool
Cell
1
1
Minefield
+InitMinefield(in rowCnt : int, in colCnt : int, in mineCnt : int)
+GetCell(in row : int, in col : int) : Cell
+AreAllMinesFound() : bool
+GetAdjacentMineCount(in row : int, in col : int) : int
+GetAdjacentFlagCount(in row : int, in col : int) : int
+GetFlagCount() : int
+IsValidIndex(in row : int, in col : int) : bool
*
1
+Cell()
+PutMine(in mine : Mine)
+IsOpened() : bool
+IsMined() : bool
+Open()
+PutFlag()
+RemoveFlag()
+IsFlaged() : bool
D164
3-3. 3차개발 테스트 계획
D165
테스트 계획
• 추가되는 기능이 매우 단순한 편
• 두 모듈 각각의 테스트는 생략하고 통합하여 테스트한다
D166
4. 4차개발
D167
개요
• 화면 깜빡거림의 개선
• 마우스 왼쪽 버튼을 누를 때
– 누르고 있을 때는 칸이 눌린 상태로 그려짐
– 버튼이 놓여질 때 칸이 열리도록
• 두 버튼을 동시에 누를 때
– 어느 버튼이 먼저 눌려졌는지 상관 없도록
– 누르고 있을 때는 주변의 지뢰표시 없는 칸들이 눌린 상태로 그
려짐
– 버튼이 놓여질 때 칸들이 열리도록
D168
설계
OnLButtonUp
Or
OnRButtonUp
Init
OnLButtonUp
/ OpenCell()
OnRButtonUp
/ FlagCell()
OnLButtonDown
/ ShowCellPressed
OnRButtonDown
RDown
OnLButtonUp
Or
OnRButtonUp
/ OpenCells()
LDown
OnLButtonDown
OnRButtonDown
LRDown
Entry: ShowCellsPressed
D169