1.1 문제 풀이
- 소프트웨어 솔루션은 풀어야할 문제가 존재함을 인식하는 데서 출발한다. 이러한 조직을 “고객”이라고 한다.
[그림 1.2] 소프트웨어 솔루션 생애의 단계1. 가능성 연구(feasilbility study) : 문제에 대한 명확한 문장과 그에 대한 솔루션의 잠재적인 이득에 대한 분석 자료를 만들어낸다. 이를 위해서는 문제를 풀기 위해 필요한 자원, 솔루션을 개발하기 위한 기간, 성공 시 예상되는 이윤을 추정하여야 한다.
2. 요구사항 분석(requirements analysis) : 최종 제품이 수행해야 할 사항을 명확하게 정의하는 문서 만들어 낸다. 이 문서는 나중에 소프트웨어가 정확하게 작동하는 지를 결정하기 위해 테스팅 팀이 사용하게 될 기준을 포함한다. 또한 사용자 매뉴얼의 초안이 작성됨.
3. 프로젝트 설계(project design) : 소프트웨어 시스템의 전체적인 구조를 결정한다. 개별 컴포넌트의 식별과 각 컴포넌트의 식별과 각 컴포넌트가 수행할 사항의 명세가 포함됨.
4. 구현(implementation) : 각 컴포넌트에 대해 실행 가능한 코드를 만들게 되며, 각각이 독립적으로 작동하는 지를 확인.
5. 테스팅(Testing) : 컴포넌트들은 프로젝트의 테스팅 단계에서 조립됨. 고객에게 전달하기 전에 요구사항 명세에 대비해 전체 시스템을 철저하게 테스트하게 됨.
6. 유지보수(maintenance) : 사용자 지원, 패치판(“서비스 팩”), 개정판을 포함. 소프트웨어를 처음으로 개발할 때는 없었던 다른 회사나 조직으로 넘어간다.
[예 1.1] 온도 단위 변환기
- 명령어 라인에 “java Convert <숫자> <단위>” 문자열 입력함(숫자 : 십진수, 단위 : 섭씨 “C”, 화씨 “F”)
- 출력 : 25.0 C = 77.0 F
1.2 객체-지향 설계
Java 표준 라이브러리 재사용의 장점
- 자기 자신의 소프트웨어를 구축하고 테스팅하는 비용을 절감시켜 준다.
- 상용 타입은 광고한 대로 작동할 것이라고 어느 정도 믿을 수 있다.
- 상용 소프트웨어는 시간과 공간 활용도 측면에서 거의 최적일 가능성이 높다.
- 다른 프로그래머들도 동일한 종류의 소프트웨어(클래스와 인터페이스)를 사용해 보아서 이미 친숙할 가능성이 높으므로 여러분의 시스템이 더 쉽게 이해할 수 있다.
* 온도 변환 문제를 풀기 위해 사용되는 클래스에 대한 설계 명세의 예
[리스팅 1.1] 온도 변황 문제를 위한 Java 인터페이스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * An interface for representing temperatures, with functionality * for converting their values between Celsius and Fahrenheit. * @author Ryu Seung Hyun * @see MyTemperature * */ public interface Temperature { /** @return the Celsius value for this temperature. */ public double getCelsius(); /** @return the Fahrenheit value for this temperature. */ public double getFahrenheit(); /** @param celsius the Celsius value for this temperature. */ public void setCelsius(double celsius); /** @param fahrenheit the Fahrenheit value for this temperature. */ public void setFahrenheit(double fahrenheit); } |
- 인터페이스이므로 온도를 어떻게 저장할 것인가에 대한 결정은 구현자에게 넘김.
- 인터페이스는 타입이 무엇인지만 정의 하며 그 일을 어떻게 수행할 것인가는 정의하지 않음.\
[ Javadoc]- /* 이것은 C 스타일 설명문*/
- // 이것은 C++ 스타일 설명문
- /** 이것은 Javadoc 설명문*/
- Java 소스 코드 파일을 Javadoc 유틸리티로 처리하면, 소스 코드안의 Javadoc 설명문을 기반으로 정보를 만들어 보여주는 HTML 파일 생성.
- @author, @param, @return, @see, @throws, @value와 같은 매개변수들을 식별함.
[온도 변환 문제에 대한 솔루션]- 공식
. F = 9C/5 + 32
. C = 5(F-32)/9
1. 2개의 문자열 s1과 s2를 명령어 라인에서 읽는다.
2. s1을 온도 값을 표현하는 value 라는 이름의 십진수로 변환한다.
3. s2를 섭씨나 화씨 단위를 표시하는 scale이라는 이름의 문자로 변환한다.
4. value와 scale로 부터 Temperature 객체를 생성한다.
5. 객체의 데이터를 다음 형태로 출력한다 : <섭씨-값> C = <화씨-값> F
1.3 소프트웨어 설계의 구현
* 설계 팀은 다음의 4개 문서를 구현 팀에게 전달함.
- 사용자 매뉴얼, 솔루션 개요, 변환 공식, Temperature 인터페이스
[리스팅 1.2] Temperature 인터페이스의 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class MyTemperature implements Temperature{ private double celsius; //stores temperature as a Celsius value public MyTemperature(double value, char scale) { if (scale == 'C') setCelsius(value); else setFahrenheit(value); } public double getCelsius() { return celsius; } public double getFahrenheit() { return 9*celsius/5 +32.0; } public void setCelsius(double celsius) { this.celsius = celsius; } public void setFahrenheit(double fahrenheit) { this.celsius = 5*(fahrenheit - 32)/9; } public String toString() { //Example : "25.0 C = 77.0F" return round(getCelsius()) + " C = " + round(getFahrenheit()) + " F"; } private double round(double x) { //returns x, rounded to one digit on the right of the decimal; return Math.round(10*x)/10.0; } } |
- 구현자는 Temperature 객체에 대한 데이터를 double 타입의 private 인스턴스 변수에 저장하였고 섭씨 값으로 표현하기로 하였음.
- 인터페이스 구현을 위해, 클래스는 인터페이스에 의해 명세된 각 메소드의 완벽한 정의를 포함해야한다.
(getClesius(), getFahrenheit(), setCelsius(double celsius), setFahrenheit(double fahrenheit))
- 이 클래스는 public 생성자(라인 4-7), public toString() 메소드(라인 25-28), private round() 메소드 (라인 30-33)를 정의함.
- toString() 메소드는 Object 클래스에서 자동적으로 상속한 toString() 메소드를 오버라이드(Override)하고 객체 프린트 가능한 표현으로 바꾸어 리턴.
(20.0 C = 68.0 F)
- round(double x) : 십진수를 소수점 아래 한자리까지 반올림.
* 리스팅 1.2의 구현의 기본적인 프로그래밍 원리를 어떻게 지키고 있는 지 유의
- 이 코드 앞에는 프로그램이 하는 일, 작성자, 작성 시점을 보여주는 식별용 설명문이 먼저 나오고 있다.
- 이 코드는 상이한 작업은 상이한 메소드에 의해 수행되어야 한다는 원칙에 따라서 모듈화되어 있다.
- 변수는 그 역할이 표현되도록 명명되어 있다. (temperature와 scale)
- 코드가 명확하지 않은 부분에는 내장 설명문이 추가되어 있다.
- 적용 가능한 부분에는 표준 라이브러리 메소드를 사용하고 있다. (Math)
- 정확성을 위해 입력을 검사하고 있다.
- 사용자의 편의를 위해 출력을 포맷팅(formatting)하고 있다.
1.4 클래스의 테스팅
테스팅 단계는 소프트웨어 개발 프로세스에서 가장 어렵고 중요한 부분이며 대부분의 경우에 극히 일부만 테스팅하는 오류를 범할 수 있음.
이 단계에서만 대부분의 에러를 찾아내고 제거할 수 있다.
[리스팅 1.3] Temperature 프로그램에 대한 솔루션의 테스팅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class Convert { public static void main(String[] args) { if(args.length != 2) exit(); double value = Double.parseDouble(args[0]); char scale = Character.toUpperCase(args[1].charAt(0)); if(scale != 'C' && scale != 'F') exit(); Temperature temperature = new MyTemperature(value, scale); System.out.println(temperature); } private static void exit() { //prints usage message and then terminates the program: System.out.println( "Usage: java Convert <temperature> <scale>" + "\nwhere:" + "\t<temperature> is the temperature that you want to convert" + "\n\t<scale> is either \"C\" or \"F\"." + "\nExample: java Convert 67 F" ); System.exit(0); } } |
- 라인 3 : 입력 배열 args[]의 길이가 정말로 2인지 검사
- 라인 4 : Double 클래스의 parseDouble() 메소드를 문자열 “25″에 적용해 double 값 25.0을 지역 변수 value 에 할당
- 라인 5 : 문자열 “C” 에서 첫 번째 문자 ‘C’를 추출해 지역 변수 value에 할당. toUpperCase() 메소드를 적용하여 scale 변수에 확실하게 대문자가 저장되게함.
- 라인 6 : scale 값이 ‘C’, ‘F’ 인지 검사하여 조건이 만족되지 않을 경우 프로그램 종료.
- 라인 7 : 2개 인자를 갖는 생성자에 scale 과 value의 값을 전달하면서 호출하여 Temperature 클래스 객체 생성.
- 라인 8 : temperature를 println() 메소드에 전달해서 클래스의 toString() 메소드 호출.
- 라인 11-21 : 별도의 exit() 메소드는 부적절한 입력 발생 시 프로그램의 통제된 종료를 지원한다.
그리고 사용자에게 입력의 형식을 알려주는 “Usage:” 메세지 프린트.
* 스텁 테스트
- 스텁(stub) : 본체가 빈 메소드로 컴파일에 필요한 정도의 코드만 가짐
- 사용 이유 : 점진적으로 메소드를 구축하고 테스팅하는 전체 테스팅 전략을 위해 사용함.
. 올바른 출력을 쉽게 결정할 수 있는 간단한 입력을 몇 개 선택한다.
. 경계 값을 별도로 검사한다.
. “전형적” 입력을 테스트하기 위해 임의로 생성된 값들을 사용한다.
1.5 알고리즘의 구현와 테스팅
[알고리즘 1.3] 소수 (1과 자기 자신만으로 나누어 떨어지는 1보다 큰 양의 정수)입력 : 정수 n
출력 : n이 소수이면 참을 리턴
1. n < 2 이면 거짓을 리턴
2. n < 4 이면 참을 리턴
3. n이 짝수이면 거짓을 리턴
4. 3부터 √n 까지의 모든 홀수 d에 대해 5를 반복
5. n을 d로 나눌 수 있으면 거짓을 리턴
6. 참을 리턴
[리스팅 1.5]소수 탐색
1 2 3 4 5 6 7 8 9 |
public static boolean isPrime(int n) { if(n < 2) return false; if(n < 4) return true; if(n%2 == 0) return false; for (int d=3; d*d <=n; d += 2) { if(n%d ==0) return false; } return true; } |
- UML(Unified Modeling Language)은 객체 지향 소프트웨어를 모델링하기 위한 시각적 명세 언어.
- 클래스 명세 다이어그램에 대한 표준 포맷
. “+” : public
. “-” : private
. “#” : protected
. 각 필드 타입과 리턴 타입 : “:” 다음에 표시
[그림 1.7] Person 클래스에 대한 UML 명세
- 클래스의 필드와 메소드를 분리하여 표시
- 필드 : 상태(데이터), 메소드 : 행위(연산)
- 객체의 특징 : 이름, 상태, 행위
1.7 계승, 집단, 합성
[그림 1.9] Person 클래스와 Car 클래스1) 계승(inheritance) : 다른 객체의 세분(specialization)을 나타낸다. 개는 포유동물의 특별한 타입이고, 트럭은 차량의 특별한 타입이며, 책은 인쇄물의 특별한 타입이고, 정렬된 집합은 집합의 특별한 타입이다. 이러한 종류의 관계를 is-a 관계라고도 한다.(즉, 개 is-a 포유동물)
2) 집단(aggregation) : 다른 독립적인 객체들의 그룹핑을 나타낸다. 집합은 그 원소들의 그룹이고, NATO 연방은 참가 국가의 그룹이다. 이러한 종류의 관계를 has-a 관계라고도 한다. 즉, 집합 has an 원소, 멤버 객체들은 집단 컨테이너와의 독립적으로 생성되고 제거된다. NATO 연방이 와해되더라도 국가들은 존속한다.
3) 합성(composition) : 다른 객체들은 포함하는 것을 나타낸다. 개는 심장을 포함하고, 트럭은 엔진을 포함하며, 책은 장들을 포함하고, 집합은 부분집합을 포함한다. 이러한 종류의 관계를 contains-a 관계라고도 한다. 즉, 집합 contains 부분집합. 포함하는 객체가 제거되면 그것이 포함한 객체들도 제거된다. [표 1.1] 클래스간의 관계에 대한 UML 기호
[예 1.2] 대학 등록 시스템을 위한 클래스- 과목, 과목의 섹션, 학생, 강사, 사람 클래스
- 과목은 섹션을 포함한다. 섹션의 존재를 그것을 포함하는 과목의 존재에 종속된다. 만약 과목이 취소되면 그에 속한 섹션들도 모두 취소된다.
- 하나의 섹션은 그 섹션에 등록한 학생들을 가지고, 그 센션을 가르치는 하나의 강사를 가진다. 이는 집단 관계다. 학생과 강사 객체는 섹션과 독립적으로 존재한다. 섹션이 취소되어도 이들은 계속 존재한다.
- 학생과 강사는 사람의 특별한 타입이다. 이는 계승관계로 표시되며 학생 객체와 강사 객체는 사람 객체의 모든 애트리뷰트(필드)와 연산(메소드)를 가지고, 그외의 추가적인 애트리뷰트나 연산을 더 가질수 있다. 이름 외에, 학생은 gpa(grade-point average)를 가지는 강사는 department를 가짐
- 합성이 집단 관계보다 강한 관계임.
[그림 1.10] 대학 등록 시스템을 위한 5개 클래스 [그림 1.11] 클래스들간의 관계 [리스팅 1.8] 계승과 집단의 구현
1 2 3 4 5 6 7 8 9 10 |
public class Student extends Person{ //Student inherits Person protected String country; protected int credits; protected double gpa; public Student(String name, boolean male, int yob, String country) { super(name, male, yob); this.country = country; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class Student extends Person{ //Student inherits Person protected String country; protected int credits; protected double gpa; private final Transcript transcript = new Transcript(); public Student(String name, boolean male, int yob, String country) { super(name, male, yob); this.country = country; } public void updateTranscript(Section section, Grade grade) { transcript.add(section, grade); } public void printTranscript() { System.out.println(transcript); } private class Transcript { //composition // internal fields void add(Section section, Grade grade) {} //... public String toString(){} } } |
1.8 가변 객체와 불변 객체
- 불변 객체 : 데이터를 변경할 수 없는 객체, 즉 상수 객체를 뜻함.
- 가변 객체 : 데이터를 변경할 수 있는 객체
- String : 불변 객체
. String greetings = “Hello”; //불변 객체
greetings += “, World!” //새 객체를 생성
- StringBuffer : 가변 객체
. StringBuffer greetings = new StringBuffer(“Hello”); //가변 객체
greetings.appent(“, World!”); //동일 객체
- Person 클래스를 불변으로 만들기 위한 방법
1) “설정자”(setter) 메소드 제거, 생성자 변경하여 객체의 phone 필드가 Person 객체 생성 시 설정되게함.
2) 모든 필드를 private 로 선언
Person |
#mail:boolean
#name:String #phone:Phone #yob:int |
+getName():String
+getPhone():Phone +getYob():int +isMale():boolean |
=> Student나 Instructor 클래스와 같은 확장된 클래스에서만 직접 접근 가능토록 protected로 정의.
=> 서브클래스가 해당 필드에 대해 설정자 메소드를 재정의 가능하므로 결과적으로 고객들이 이 필드를 변경 가능함.
[그림 1.13] 수정된 Person 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class Person { protected final boolean male; protected final String name; protected final Phone phone; protected final int yob; public Person(String name, boolean male, int yob, Phone phone) { this.name = name; this.male = male; this.yob = yob; this.phone = phone; } public String getName() { return name; } public Phone getPhone() { return phone; } public int getYob() { return yob; } public boolean isMale() { return male; } } |
=> 합성 대신 집단을 사용함으로써 Person 클래스는 이 클래스의 집단화된 클래스들처럼 가변적이 됨.
=>Phone 클래스에 전화 번호 지역 코드를 변경하기 위한 “설정자” 메소드를 포함하고 있서 가변적이됨.
[리스팅 1.11] 가변 Phone 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Phone { private String areaCode, number; public Phone(String areaCode, String number) { this.areaCode = areaCode; this.number = number; } public void setAreaCode(String areaCode) { this.areaCode = areaCode; } } |
Person gwb = new Person(“G. W. Bush”, true, 1946, tel);
=> gwb 객체의 4개 필드는 final로 선언되어 있어 변경 불가능
=> phone 필드는 가변 객체에 대한 참조이고, tel 은 동일객체에 대한 독립적인 참조가 됨.
=> tel 객체를 tel.setAreaCode(“808″)과 같은 문장으로 변경하면 Phone 객체가 변경되고, 따라서 gwb 객체가 변경됨.
=> Person 클래스가 Phone 클래스에 대한 집단 객체므로 tel 객체는 gwb 객체와 독립적으로 존재함.
=> 해법은 Phone 객체를 Person 객체 안에 완전히 캡슐화시켜, 그에 대한 모든 외부 접근을 차단함.
[리스팅 1.12] 불변 Person 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class Person { protected final boolean male; protected final String name; protected final Phone phone; protected final int yob; public Person(String name, boolean male, int yob, Phone phone) { this.name = name; this.male = male; this.yob = yob; this.phone = new Phone(phone); } public String getName() { return name; } public Phone getPhone() { return phone; } public int getYob() { return yob; } public boolean isMale() { return male; } } |
=> Phone 클래스가 다음과 같이 특정 Phone 객체의 사본을 생성하는 사본 생성자를 가지고 있는 것을 가정함.
1 2 3 4 |
public Phone(Phone that) { this.areaCode = that.areaCode; this.number = that.number } |
=> gwb 객체는 tel 객체에 대한 자신의 독립적인 사본을 소유하게되어 그 객체애 대한 완전한 통제가 가능함.
=> tel과 gwb.phone은 분리된 독립 객체가 되고, gwb는 후자에 대한 독점적인 접근을 하게됨.
[리스팅 1.13] 동일한 Phone 객체를 공유하는 불변 Person 객체
1 2 3 4 5 6 |
public Person(String name, boolean male, int yob, Person friend) { this.name = name; this.male = male; this.yob = yob; this.phone = friend.phone; } |
[리스팅 1.14] 수정된 Person 클래스에 대한 테스트 드라이버
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class TestPerson { public static void main(String[] args) { Phone p = new Phone("804", "3790550"); Person john = new Person("John Adams", true, 1980, p); System.out.println(john); Person jane = new Person("Jane Adams", false, 1981, john); System.out.println(jane); john = null; System.out.println(jane); } } |
About the Author
● 경력 5년(현 LG전자 WebOS Developer Site 운영업무)
댓글작성시 Code-Highlighter 삽입방법
Syntax [code title="..." theme="..."]coding...[/code]
Example [plsql title="현재시간 출력문" theme="classic"] select now() [/plsql]
Code-List plsql, mysql, java, objc, js, c#, c++, delphi, apache, php, css ...
Theme-List ado, arduino-ide, bncplusplus, cg-cookie, cisco-router, classic, eclipse, epicgeeks, familiar, feeldesign, github, idle, inlellij-idea, iris-vfx, mirc-dark, monokai, neon, secrets-of-rock, solarized-dark, solarized-light, son-of-obsidian, ssms2012, terminal, tomorrow, tomorrow-night, turnwall, twilight, vs2012 View Theme Demo
Alert 댓글에서 직접 코드 작성시 줄바꿈은 (Enter)값 대신 (Shift+Enter) 사용할 것