2015.03.14 23:38

오픈소스 문서관리 시스템의 첫 단계인 "파일 중복 검사 어플리케이션" 제작 과정에 대한 요약 및 안내 입니다.


문서(document) 정의와 구조

 : "문서"에 대한 용어 정의와 문서의 속성들에 대한 정리, 문서의 하위 객체와 문서의 관계 등을 정립 등.


파일(File) 클래스 설계 - 상태과 행위

  : "파일" 클래스의 역할, 행위와 속성들에 대한 설계


파일(File) 클래스 설계 - 버전 클래스 분리

  : "파일" 클래스에서 버전 클래스를 분리하게된 이유 설명


파일(File) 클래스 설계 - 인스턴스 생성

  : 인스턴스를 생성하는 2가지 기법에 대한 설명과 파일 인스턴스를 생성 방식을 결정하기 위한 기술 검토.


문서(document)의 색인 속성 설계 

  : 문서에 포함되는 속성들에 대한 자료형, 타입 등에 대한 검토


FileVersion 클래스 구현

  : FileVersion 클래스 소스 및 설명


File 클래스 구현 

  : File 클래스 소스 및 설명


Document 클래스 구현

  : Document 클래스 소스 및 설명


DocumentBuilder 클래스 구현

  : DocuemntBuilder 클래스 소스 및 설명


파일 중복 검사

  : 파일 중복 검사 어플리케이션 소스 및 설명


Posted by 善 곽중선
2015.03.14 22:59
  • 문서 관리 시스템의 로드맵(loadmap) 중에서 첫번째 어플리케이션은 파일 중복 검사 프로그램입니다.

  • 중복 방지 (preventing duplication) : 다양한 저장 매체(storage media)에서 문서를 수집할 때, 중복이 발생할 여지는 충분하다. 사용자가 다양한 환경에서 작업하면서 파일을 복사하는 경우가 빈번하기 때문이다. (USB 복사, 이메일 전송, SNS 공유 및 백업 등) 따라서, 다양한 매체에서  데이터를 수집할 때 이미 수집된 파일이 존재할 경우 새롭게 입력된 파일을 무시하는 기능은 필수적이다. 중복 방지 기능을 구현하기 위해서는 무결성 검사 혹은 check sum 기법을 적용해야 한다. 중복 방지 절차를 간단히 묘사하면 아래와 같다.

    • 파일을 저장소에 등록할 때 컨텐츠에 대한 체크섬을 생성한다.
    • 해당 체크섬에 해당하는 파일이 이미 존재하는지 검사한다. 만일 동일한 체크섬을 가진 파일이 존재한다면, checksum collision을 감안하여 동일한 파일 유무에 대한 정밀 확인 (파일 크기, 제목, 형식, 요약 등을 검사하고, 필요하다면 파일의 전반부 바이너리 데이터를 대조한다.)을 수행한다.
    • 동일한 파일이 존재하는 것으로 판단되면, 저장소에 등록하지 않는다.

  • 실행 결과 예시는 아래와 같습니다.


Posted by 善 곽중선
2015.03.14 22:52
  • Document 클래스의 객체를 생성하기 위해서는 File 및 FileVersion 객체를 함께 생성해야 합니다. Document 객체만 생성해서는 쓸모가 없기 때문입니다.
  • Document 클래스 내에 File 및 FileVersion 객체를 생성할 수 있으나, 소프트웨어 설계 시에 가급적 KISS (Keep It Simple Stupid!) 원칙을 기억해야 합니다. 한번에 많은 기능을 구현할 수도 있고, 때로는 그런 방식으로 작업하는 것이 생산성이 높다고 여겨질 수 있습니다. 그러나, 소프트웨어는 살아있는 생명이며, 최초에 모든 요구사항(기능명세)을 명확히 결정하고 코딩할 수 없습니다. 따라서, 각각의 클래스는 최소한의 기능을 담도록 하고 변경이 발생할 가능성이 줄어들게끔 적은 라인의 코드를 담는 것이 좋습니다. 작은 모듈은 더 이상 필요 없을 때 폐기하는데도 유리합니다.
  • 따라서, Document 클래스 자체는 문서를 표현하는 데이터를 담는 역할만 수행하도록 제한하고, 문서와 그에 따른 부가적인 객체를 생성하는 역할을 별도의 빌더(builder) 클래스로 분리했습니다.
  • 나아가, 빌더 자체도 Document, File, FileVersion 을 생성하는 역할로 제한했습니다. 문서를 분석하고, 키워드, 요약, 체크섬 등을 생성하는 작업은 ContentAnalyzer 클래스로 분리했습니다.

ContentAnalyzer.java


DocumentBuilder.java

public class DocumentBuilder {
...... 중략 .....
	public Document createDocument() {
		
		// ---- 필수 항목 점검 및 analyzer 준비 ----
		if(location == null) {
			throw new IllegalStateException("location property is not specified");
		}
		if(writer == null) {
			throw new IllegalStateException("writer property is not specified");
		}
		
		ContentAnalyzer contentAnalyzer = new ContentAnalyzer(location);
		contentAnalyzer.analyze();

		// ---- 입력 값이 없는 항목에 대한 대체 처리 -----
		if(createDate == null) {
			createDate = new Date(Calendar.getInstance().getTimeInMillis());
		}
		if(title == null) {
			title = contentAnalyzer.getFileName();
		}

		// -- FileVersion, File, Document 순서로 빌드 -----
		FileVersion fileVersion = new FileVersion(1, createDate, description);
		File file = new File(location, contentAnalyzer.getChecksum(), contentAnalyzer.getSize(), fileVersion);
		Document document = new Document(title, writer, keywords, 
                            contentAnalyzer.getAbstraction(), location, contentAnalyzer.getFormat());
		document.addFile(file);
		
		return document;
	}
}
package docar.archive.document;

/**
 * 문서 파일을 분석하는 클래스.
 * 
 * @author "Sunny Kwak"
 */
public class ContentAnalyzer {
	private static final String HTTP_PROTOCOL = "http";

	private URL location;
	private String fileName;
	private long fileSize;
	private String checksum;

	public ContentAnalyzer(URL location) {
		this.location = location;
	}

	public void analyze() {
		InputStream inputStream = null;

		try {
			String protocol = location.getProtocol();
			if (protocol == null
					|| !protocol.toLowerCase().equals(HTTP_PROTOCOL)) {
				inputStream = new FileInputStream(new java.io.File(location.toURI()));
			} else {
				inputStream = location.openConnection().getInputStream();
			}

			fileName = location.getFile();
			fileSize = 0L;
			checksum = getMD5Checksum(inputStream);

		} catch (IOException e) {
			// FIXME add logging
			e.printStackTrace();
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException ignored) {
					// do nothing
				}
			}
		}

	}


Posted by 善 곽중선
2015.03.14 22:35
  • Document 클래스의 속성은 제목 (title), 작성자 (wirter), 키워드 목록 (keywords), 요약(abstraction), 원본 링크 (original link), 파일 형식 (format), 버전 파일 목록 (files) 등입니다.
  • 작성자 정보는 이름, 이메일 주소, 홈페이지 등의 복합된 속성을 지녀야 하기 하기 때문에 별개의 클래스로 분리하였습니다. 또한, 한명의 작성자가 여러 문서를 작성하는 일대다(1:n) 관계이기 때문에 분리하는 것이 타당합니다.
  • 키워드는 복수의 명사 단어로 구성되며, 중복된 단어가 입력되어서는 안되기 때문에 Set<String> 타입을 선언합니다. 자료 구조 중에서 Set 타입은 포함된 데이터들의 중복을 허용하지 않는 특성을 가진다는 점을 활용하는 것입니다.
  • 파일 형식은 enum 타입을 사용했으나, 향후에 refactoring 가능성이 있습니다.
  • 하나의 문서가 여려 가지 버전으로 존재할 수 있고, 동일한 파일이 여러 위치에 존재할 수 있습니다. 버전을 고려했을 때 순서(order)가 중요하므로 List 타입을 사용했습니다.
package docar.archive.document;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import docar.user.Writer;

/**
 * '문서' 클래스.
 *
 * @author "Sunny Kwak"
 */
public class Document {
	private String title;
	private Writer writer;
	private Set<String> keywords;
	private String abstraction;
	private URL originalLink;
	private Format format;
	private List<File> files;

	public Document(String title, Writer writer, Set<String> keywords,
			String abstraction, URL originalLink, Format format) {
		if (title == null || title.isEmpty()) {
			throw new IllegalArgumentException("title argument is missing");
		}
		if (writer == null) {
			throw new IllegalArgumentException("writer argument is missing");
		}
		if (originalLink == null) {
			throw new IllegalArgumentException(
					"originalLink argument is missing");
		}
		if (format == null) {
			throw new IllegalArgumentException("format argument is missing");
		}

		this.title = title;
		this.writer = writer;
		this.keywords = keywords;
		this.abstraction = abstraction;
		this.originalLink = originalLink;
		this.format = format;

		files = new ArrayList<File>();
	}
Posted by 善 곽중선
2015.03.14 22:19
  • File 클래스는 문서(Document)의 "물리적인 상태 정보"를 나타내는 클래스입니다. 파일 위치(URL), 체크섬(cheksum), 크기(size), 파일 버전(file version) 등의 속성을 포함하고 있습니다.
  • 파일 위치를 경로(path)가 아니라, URL 클래스로 선언한 것은 PC 혹은 서버의 로컬 디스크에 존재하는 파일 뿐만 아니라 인터넷 상에 존재하는 파일 위치를 지정할 수 있도록 하기 위해서입니다. checksum 문자열은 파일의 무결성(integrity)와 중복 검사(duplication check) 목적으로 사용됩니다.
  • File 클래스 또한 FileVersion 클래스 처럼 방어적 코딩이 포함되어 있습니다.



File.java

package docar.archive.document;

import java.net.URL;

/**
 * "파일"은 "문서"의 구성요소이다.
 * 
 * - 문서는 하나 이상의 파일을 포함할 수 있다.
 * - 각각의 파일은 문서의 다른 버전을 표현한다.
 * - 파일은 문서의 물리적인 형태를 의미한다.
 *
 * @author "Sunny Kwak"
 */
public class File {
	private URL location;
	private String checksum;
	private long size;
	private FileVersion fileVersion;

	/**
	 * '파일' 생성자.
	 * 
	 * @param location 파일의 위치
	 * @param checksum 파일 식별을 위한 지문(finger print)
	 * @param size 파일 크기.
	 * @param fileVersion 파일 버전.
	 * @throws IllegalArgumentException 필수 항목이 누락된 경우, 예외 처리
	 */
	public File(URL location, String checksum, long size,
			FileVersion fileVersion) {
		
		if(location == null) {
			throw new IllegalArgumentException("location arguement is missing");
		}
		if(checksum == null || checksum.isEmpty()) {
			throw new IllegalArgumentException("checksum argument is missing");
		}
		if(size < 1) {
			throw new IllegalArgumentException("Invalid fie size. Input file = " + location);
		}
		if(fileVersion == null) {
			throw new IllegalArgumentException("fileVersion argument is missing");
		}
		
		this.location = location;
		this.checksum = checksum;
		this.size = size;
		this.fileVersion = fileVersion;
	}

Posted by 善 곽중선
2015.03.14 22:06
  • FileVersion 클래스 구현에서 눈여겨 봐야할 점은 생성자(constructor)에서 인자(parameter) 값을 검사하는 로직이다.
    객체지향 프로그래밍이 절차적(혹은 함수형) 프로그램과 다른 점은 데이터(혹은 상태)와 데이터를 조작하는 메소드(혹은 행위)를 결합시킨 구조를 가지고 있다는 점이다. 이러한 구조의 잇점을 최대한 활용하기 위해서는 객체에 값이 설정 혹은 변경될 때 해당 값이 적합한지 여부를 검사하는 로직을 구현해야 한다. 단지, 인터페이스와 클래스를 사용한다고 해서 훌륭한 객체가 되는 것이 아니다.
  • 전달되는 값 (인자, parameter)이 유효한 것인지 판정해 잘못된 값이 입력되었을 경우, 즉시 오류를 발생시켜 문제가 발생하더라도 그 원인이 외부에 있음을 알아내기 쉽고, 원인을 빠르고 명확하게 파악할 수 있도록 하는 기법을 방어적 프로그래밍이라 한다.


FileVersion.java


public class FileVersion {
	private int sequence;
	private Date createDate;
	private String description;

	/**
	 * 파일 버전 생성자.
	 * 
	 * @param sequence
	 *            버전 순번.
	 * @param createDate
	 *            버전 생성 일시
	 * @param description
	 *            버전 설명
	 * @throws IllegalArgumentException
	 *             1보다 작은 순번 혹은 날짜가 입력되지 않은 경우 예외 발생.
	 */
	public FileVersion(int sequence, Date createDate, String description) {
		if (sequence < 1) {
			throw new IllegalArgumentException("sequence must greater than 0");
		}

		if (createDate == null) {
			throw new IllegalArgumentException("Must specify valid date");
		}

		this.sequence = sequence;
		this.createDate = createDate;
		this.description = description;
	}
Posted by 善 곽중선
2015.03.12 02:48

DocAr 에서 수집하는 문서는 웹 페이지(HTML), MS-Office 문서(파워포인트, 워드, 엑셀), 텍스트(text), PDF 등 다양하며 이러한 문서들의 원본 파일 자체와 추출한 색인 정보를 함께 저장해야 한다. 문서의 물리적인 속성을 나타내는 파일(File) 클래스에 대한 설계를 마친 후, 문서 클래스의 색인 속성에 대한 상세 설계를 진행한다. 문서의 색인 속성은 다양하며, 개별 속성에 대한 관련 기능 및 데이터 구조에 대한 설계가 필요하다.

 속성 명칭

 설명 

 제목

 원본 파일의 명칭 혹은 문서의 첫 단락에 표시된 제목, HTML의 title tag 혹은 게시물의 제목

 작성자

 문서의 최초 작성자 정보. 이름, ID, e-mail, homepage (facebook 등) 정보 

 작성 일시

 문서를 최초 생성한 일시 혹은 최종 변경 일시

 키워드

 문서의 핵심 혹은 요약된 정보를 나타내는 복수의 명사 

 요약

 문서의 핵심 부분 혹은 축약된 내용

 원본 링크

 웹 상에서 다운로드한 문서일 경우 웹 문서에 대한 링크

 문서 형식

  XML, PDF, Word 등 파일 포맷(format) 에 따른 유형


  • 제목 : 문서의 제목은 파일 형식(format)에 따라 명시적으로 추출할 수 있는 경우가 있고, 명시적으로 추출할 수 없는 경우가 존재한다. HTML 형식에서는 title 태그를 이용해 문서의 제목을 추출할 수 있지만, 대다수의 문서 형식은 명시적인 필드(field)가 지정되어 있지 않다. 따라서, 파일 형태로 존재할 경우에는 파일의 명칭을 제목으로 사용할 수 있다.

  • 작성자 : 문서의 작성자 정보는 일반적으로 문서 자체에서 추출할 수 없는 정보이다. 따라서, DocAr 에 문서를 등록할 때 작성자 정보를 입력 받거나, DocAr 사용자 정보를 작성자 정보로 활용할 수 있다. 작성자 정보는 하나 이상의 필드 (성명, e-mail 주소, 홈페이지 주소 등)로 이루어져 있으므로 별개의 클래스로 분리하는 것도 고려할 필요가 있다.

  • 작성 일시 : 파일 형태로 존재하는 문서는 파일의 생성(변경) 일시를 작성일시로 설정할 수 있다. 만일 문서의 생성일시를 정확히 확인할 수 없을 경우, DocAr에 등록하는 시점을 생성일시로 사용해야 한다.

  • 키워드 : 문서의 검색 정확도 향상 및 분류에 활용하기 위해 핵심 단어들을 입력 받는다. 문서를 분석해서 키워드를 추출하는 방법과 사용자가 직접 입력하는 방법 등을 고려할 수 있다.

  • 요약 : 검색 결과에 문서의 요약된 정보를 출력할 경우, 사용자가 원하는 문서인지 여부를 빠르게 파악할 수 있다. 문서의 앞 문단을 추출해서 자동으로 요약을 생성하는 방법을 고려해 볼 수 있다.

  • 원본 링크 : 인터넷 등에서 다운로드 받은 문서인 경우, 해당 문서의 출처를 남겨두는 것은 향후에 문서가 갱신되었을 때 다시 다운로드 받을 수 있고, 관련 정보를 찾거나 유용한 웹 사이트 목록을 작성하는데 활용할 수 있다.

  • 문서 형식 : 문서의 포맷(format) 정보는 문서를 조회할 수 있는 프로그램을 선택 및 실행하기 위해 필요하다. 더불어 검색 시 특정 형식의 문서를 제한함으로써 검색의 정확도를 높일 수 있다. 문서 형식은 문자열 필드로 저장할 수도 있지만, 오류 혹은 부정확성를 방지하기 위해 - 예를 들어, html/htm/HTML 등 같은 타입을 다른 문자열로 입력할 수 있음 - 별개의 타입(enum 등)으로 분리하는 것이 낫다.

위와 같은 색인 속성 설계를 통해 확인할 수 있는 통찰(insight)은 다음과 같다.

  • 문서(Document) 클래스의 속성 중에서 일부 속성은 기본형(primitive type)으로 정의(선언)할 수 없고, 별개의 타입으로 분리해야 한다.
  • 물리적인 파일을 입력 받아 문서를 생성하기 위해서는 다양한 추출/분석/사용자 입력 등의 전처리(preprocessing) 및 가공 작업을 필요로 한다는 것을 알 수 있다. 따라서, 물리적 파일 정보를 인자로 입력 받아 문서 객체를 생성하는 생성자를 만들기 보다는 문서 객체 생성을 수행하는 클래스(빌더, 팩토리 등)를 분리하는 것이 유리하다.
  • 속성의 일부를 분리하고, 문서 생성에 필요한 전처리 작업 등 추가 요건이 발생한다 즉, 설계는 한번에 완료되는 것이 아니라, 반복(iteration)을 통해 정교화 시키는 과정을 거쳐야만 한다.

각 속성에 대한 구체적인 데이터 타입 설계를 진행한다.


  • 제목 : 문서의 제목은 문자열(String) 타입으로 충분하다, 자바의 문자열 집합(character-set)은 유니코드(Unicode)를 사용하기 때문에 다국어 지원 또한 문제는 없다. 다만, 직렬화(serialization) 처리 시에는 인코딩(encoding)에 유의해야 한다.

  • 작성자 : 작성자 정보는 성명, ID, 이메일 주소 등의 기본형 데이터 항목들을 구성된 복합(complex type)이므로 별도의 클래스를 선언하는 것이 타당하다. 타입 명칭은 Writer로 선언한다.

  • 작성 일시 : 자바에서 날짜 타입은 java.util.Date 와 java.sql.Date 2가지가 제공된다. util 패키지에 선언된 것은 오로지 날짜만 포함할 수 있고 시/분/초 정보는 담을 수 없다. 작성 시간까지 구분해야 한다면 java.sql.Date 클래스를 사용해야 한다. DocAr 에서는 java.sql 패키지에 포함된 타입을 사용한다.

  • 키워드 : 키워드는 복수의 단어로 구성된다. 하나의 문서에 설정된 키워드들은 중복되지 않아야 하므로, 중복을 허용하지 않고 복수의 문자열을 담을 수 있는 타입을 사용해야 한다. Set<String> 타입을 사용하면 된다.

  • 요약 : 문서의 핵심 내용을 짧게 정리한 요약은 긴 문자열이며, 태그(tag) 등을 포함하지 않는 문자열이어야 한다. String 타입으로 선언한다.

  • 원본 링크 : 외부의 웹 사이트 혹은 내부의 파일 시스템의 파일 주소(혹은 경로)를 모두 포함할 수 있는 주소 체계는 URL이다. java.net.URL 타입으로 선언한다.

  • 문서 형식 : 다양한 문서 타입이 존재하나 현실적으로 모든 유형의 문서 형식을 읽고 쓸 수는 없다. 일반적으로 공개된 문서 형식은 HTML, XML, PDF, MS-Word, MS-Powerpoint, MS-Excel 등이다. 시스템 기능 개선에 따라 향후에 지원할 수 있는 문서 형식이 늘어날 수 있지만, 개선이 빈번하게 발생하는 것은 아니기 때문에 지원 가능한 문서 형식이 고정되어 있다고 여겨도 무방하다. 따라서 문서 형식은 enum 타입으로 선언한다.




Posted by 善 곽중선
2015.03.12 02:04
  • 객체(혹은 인스턴스)를 생성하는 기법
    • 객체를 생성하는 기법은 여러가지가 있으나 크게 2가지로 분류할 수 있다. 생성자를 이용하는 것과 그외의 방법.
    • 생성자 메소드를 이용하는 방법
      : 다수의 클래스는 생성자 메소드를 제공하며, new 연산자와 생성자 메소드를 조합하여 객체를 생성할 수 있다. 이 때, 객체의 초기 값(initial value)은 생성자 메소드의 인자(parameter)로 전달한다.
    • 인스턴스를 생성하는 클래스를 제공하는 방법
      : 특정 클래스의 객체를 생성하고 초기 값을 설정하는 절차를 해당 클래스의 외부에서 수행한다. 팩토리 패턴(factory pattern), 빌더 패턴(builder pattern) 등 디자인 패턴(design pattern)에서 소개하는 객체 생성 기법을 사용하는 것이다. 객체를 임의로 생성하지 못하도록 제한해야 하거나, 객체를 생성하는 절차 혹은 계산 작업이 복잡한 경우에 사용된다.

  • File 객체 생성 기법 선택
    • File 객체 내에 포함되는 속성들은 '위치', '크기', '체크섬', '버전정보' 등이다.
    • '위치'와 '크기'는 물리적인 파일 정보를 통해 손쉽게 얻어낼 수 있다.
    • 그러나, 체크섬은 파일의 컨텐트(content)를 이용해 계산해야 한다. 
    • 버전 정보는 파일을 포함하고 있는 문서의 상태(state)를 바탕으로 계산해야 한다.
    • 따라서, File 클래스의 객체를 생성하는 기법은 'File 클래스의 인스턴스'를 생성하는 클래스를 별개로 만들거나, File 객체를 포함하는 Document 클래스에 기능을 부여하는 방법을 고려해야 한다. 둘 중에서 어떤 방식을 사용할 것인지는 Document 클래스 설계 단계에서 구체화하도록 한다.


Posted by 善 곽중선
2015.03.11 18:43
  • 파일(file) 클래스는 문서(document) 객체에 포함되는 객체이다. 그리고, 버전 정보는 파일에 포함되는 속성이다.
    '버전 정보'는 객체로 봐야 하는가? 혹은 기본형 데이터 타입(primitive type)의 집합으로 정의해야 하는가? 이에 대해 답을 하기 위해 몇가지 문제를 검토해 봐야 한다.

  • 기본형과 객체형의 차이
    • 객체지향 프로그래밍 언어에서 기본형 데이터는 정수 및 실수 값을 나타내는 숫자형(number type), 문자형(character type), 부울린형(boolean type), 바이트형(byte type) 등이 있다. 기본형과 객체형의 차이는 기본형은 데이터 만을 담을 수 있고, 메소드(기능)은 포함하지 못한다는 점이다.
    • 기본형 타입은 하나의 값(value) 만을 담을 수 있고, 객체형 혹은 클래스는 복수의 속성 혹은 값들을 담을 수 있다.

  • 동적인 측면에서의 설계
    버전 정보는 버전 순번(version sequence), 버전 생성일시(generation time), 버전 설명(description) 등 3가지 항목으로 구성된다. 각각의 항목은 버전 정보가 생성된 이후에 수정되어서는 안된다. 즉, 버전 정보는 읽기 전용 (read-only) 혹은 불변(immutable 타입)이다. 따라서, 필요한 기능은 생성자와 조회 메소드 (read-only access methods) 뿐이다. 객체 혹은 클래스를 소프트웨어의 동적인 부품(part)으로 보는 관점에서는 버전 정보를 굳이 클래스로 식별(혹은 분리)하는 것이 좋은 선택은 아니다. 파일 객체 내에 버전 객체를 포함시키면, 버전 숫자를 얻기 위해 파일 객체에서 버전 객체를 꺼낸 후에 다시 버전 객체에서 버전 숫자 조회 메소드를 호출해야 하는 번거로움이 있다.

  • 정적인 측면(구조적인 측면)에서의 설계
    객체지향 설계의 목적은 주어진 문제를 최대한 빠르게 풀어내는 프로그램을 제작하는 것이 아니다. 최대한 빠르거나, 최소한의 자원(메모리/네트워크 사용량 등)을 사용하는 프로그램을 작성하는 기법을 연구하는 것은 '알고리즘'의 고유 영역이다. 객체지향 설계의 목적은 '인간'의 사고 체계를 바탕으로(응용하여) 현실 세계의 문제를 가상 공간(컴퓨터)내에 이식하는 것이다.
    버전 정보를 앞서 제시한 3개의 항목으로 분리하여 표현하는 것보다, 버전 정보라는 하나의 객체로 표현하는 것이 인간이 소프트웨어 구조를 인지(recognition)하는데 있어서 유리하다. 달리 말해, 직관적인 구조라고 할 수 있다.

  • 위와 같은 이유 (컴퓨터보다는 인간 지향)로 파일 클래스를 '파일'과 '버전' 클래스로 분리한다.


Posted by 善 곽중선
2015.03.11 17:54
  • 파일 클래스의 역할(role)은 문서의 바이너리 형식 데이터(binary formatted data)를 표현(묘사)하는 것이다.
    표현(represent) 한다는 용어를 사용한 이유는 파일 객체는 물리적인 파일 그 자체가 아니기 때문이다. 달리 말해 파일 객체는 물리적 파일의 제한적인 특성만을 담고 있는 것이다.

  • 파일(file) 클래스는 문서(document) 객체에 포함되는 객체이다. (has-a relationship)
    객체는 상태(state)와 행위(behavior)로 구성된다. 따라서, 파일 클래스를 설계하기 위해 필요한 상태와 행위들을 정의한다.

  • 파일 클래스에 포함되어야 하는 '상태(state)' 혹은 속성(property)들은 다음과 같다.
    • 위치(location) : 물리적인 저장 매체(storage media) 내에서 실제 파일 데이터가 존재하는 주소(address). 주소 체계(addressing scheme)는 별도의 설계가 필요하다.
    • 크기 (size) : 바이너리 데이터의 길이, 64 bit 정수로 표현해야 함.
    • 체크섬 (check sum) : 바이너리 파일의 지문(finger print), 파일의 중복 검사에 이용된다. DocAr 내에서 동일한 내용을 가진 파일이 중복해서 존재하는 것을 막기 위해 모든 파일에 대한 디지털 지문을 생성하고, 새로운 파일 등록을 수행할 때마다 이미 등록되어 있는 파일들의 디지털 지문과 대조하여 이미 존재하는 파일인지 검사하고, 중복을 방지한다.
    • 버전 정보 (version info) :문서 내에서 파일 내용이 여러 번 변경되었을 경우, 현재 파일의 변경된 순서(혹은 순번), 최초 등록된 파일은 1번을 부여받는다. 또한 버전 정보에는 해당 버전에 대한 변경 일자와 간단한 설명(comment)이 포함될 수 있다.

  • 파일 클래스에 포함되어야 하는 '행위(behavior)' 혹은 메소드(method)들은 다음과 같다.
    • 파일 인스턴스를 생성하는 생성자(constructor)
    • 속성값을 반환하는 getter 메소드들 (location, size, checksum, version)

  •  '객체의 정의'를 적용하기(applying definition of object : self-contained entity)

    • 객체는 데이터와 데이터를 조작하는 프로시져로 구성된 필요한 모든 것을 자체적으로 담고 있는 독립체이다. 
      an object is a self-contained entity that consists of both data and procedures to manipulate the data.
      좋은 객체(혹은 잘 설계된 객체)는 자기 자신을 초기화하고, 데이터를 조작하기 위해서 외부의 도움(혹은 조작)을 필요로 하지 않아야 한다. 객체를 사용(혹은 호출)하는 프로그램이나 프로그래머는 특정 객체를 사용할 때, 다른 클래스를 사용하지 않고도 해당 객체를 제어할 수 있어야 한다는 말이다. (현실적으로 완벽히 지키기 어렵다. 설계자가 지향해야 하는 자세라고 생각하는 것이 타당하다.)

    • 파일 객체가 '바이너리 데이터'를 표현 한다는 것은 바이너리 데이터를 사용(조작)하는 관점에서 필요한 정보와 기능을 모두 제공해야 한다는 말이다. 파일 객체를 생성하는 수단 (엄밀히 말하자면 생성 시 외부에서 주어진 데이터를 이용해 스스로를 초기화하는)으로 생성자 메소드, 그리고 파일에 대한 각종 속성 정보를 조회할 수 있는 메소드와 바이너리 데이터를 제공하는 메소드를 선언한다.

  • 객체의 행위에 대한 설계 리뷰(review)

    • 파일 객체를 변경(수정)하는 메소드가 필요한가? DocAr 내에서 파일 객체는 물리적인 바이너리 데이터라는 실체에 대한 그림자이지, 그 자체를 나타내는 것이 아니다. 실체(물리적 파일)이 변경될 경우, 새로운 파일 객체를 생성해야 하며, 내용이 변경된 파일은 다른 버전의 파일이 된다. 따라서, setter 메소드는 선언하지 않는다. (설계자의 사상 혹은 의도를 반영하는 것이기에 모든 데이터를 다루는 객체가 변경 메소드를 제공하지 않는다고 이해해서는 안된다.) 달리 설명하자면, DocAr의 File 인스턴스는 읽기 전용(read-only) 인스턴스라고 말할 수 있다.

    • 파일 객체를 삭제하는 메소드가 필요한가? JDK에서 제공하는 java.io.File 클래스는 delete 메소드를 제공한다. 그렇다면, DocAr의 File 클래스 또한 delete() 메소드를 제공해야 하는가? 얼핏 보기에 그럴듯 하지만, DocAr의 File 인스턴스는 Document 인스턴스에 종속되어 있다. Document 내에 서로 다른 버전의 파일 객체가 존재할 수 있으며, Document 인스턴스는 하나 이상의 File 인스턴스를 소유해야 한다. 즉, File 인스턴스와 File에 연결된 물리적인 파일이 삭제될 경우, Document 인스턴스의 상태가 변경되거나, Document 인스턴스가 함께 삭제되어야 한다. 삭제 행위를 File 클래스와 Document 클래스 양쪽에 두는 것보다 Document 클래스에서만 제공하는 것이 구현의 복잡도를 낮출 수 있으며, File과 Document를 이용해 코딩하는 사용자(개발자)에게 보다 단순한 관점(view)을 제공한다.




Posted by 善 곽중선