2011.04.21 15:42
폭포수 모델(Waterfall Model)

File:Waterfall model.svg

특징 : 단계별 작업이 분리되어 각 단계가 다름 시작 전에 끝나야 하며, 각 단계 사이에 중복이나 상호작용이 없음
         기본 적으로 각 단계는 병행 수행되지 않고 거슬러 반복되지 않으며 한 방향으로 진행됨.
         폭포수의 흐름 처럼 전 단계로 돌아가는 경우가 없다.(피드백 힘듬)

장점 : 선형 모델로 프로세스가 단순하고 이해가 쉬워 초보자도 쉽게 적용가능
         단계별로 정형화된 접근 방법과 체계적 문서화가 가능
         중간 산출물이 명확, 관리하기 좋음
         프로젝트 진행 상황을 명확히 알 수 있음
         코드 생성 전 충분한 연구와 분석 단계를 가질 수 있음

단점 : 요구사항을 완벽하게 작성해야 함(처음 단계를 지나치게 강조하면 코딩, 테스트가 지연)
         각 단계의 전환에 많은 노력
         최종단계가 되어야 결과가 나온다.
         프로토타입과 재사용의 기회가 줄어듦
         변경을 수용하기 어려움
         대형 프로젝트에 적용하기 어려움
         문서화를 위한 노력이 지나침

적합한 경우 : 단순하거나 응용 분야를 잘 알고 있는 경우
                   한 번의 과정, 비전문가가 사용할 시스템 개발에 적합

소결 : 현재는 잘 쓰이지 않는 구시대의 산물.


 
프로토 타이핑 모델(Prototyping Model)

특징 : 개발 시작단계에서 Visible한 Protype를 만든다.

종류 : evolutionary prototyping : 잘 알고 있는 부분 부터 시작하여 계속적으로 발전시켜 완제품을 만드는 방법
        Throw-away prototyping : 고객과의 의사소통 수단으로만 프로토타입을 사용

장점 : 프로젝트의 실현 가능성, 소프트웨어의 개발 가능성을 판단 할 수 있음
         개발자와 사용자 간의 의사소통이 명확해 짐
         기능적 요구사항 외에도 성능이나 유용성 등의 품질 요구를 할 수 있음.
         시스템을 미리 사용함으로써 사용자 교육효과가 있다.
         개발 단계에서 유지보수가 일어나는 효과가 있다.

단점 :  오해, 기대심리 유발
          중간 산출물 정의가 난해하여 문서화가 힘들며 관리자는 진척 사항을 제어하기 힘들어 짐.

적합한 경우 : 개발 착수 시점에 요구가 불투명할 때
                   실험적으로 실현 가능성을 타진해 보고 싶을 때
                   혁신적인 기술을 사용해 보고 싶을 때

소결 :  일반적으로 사용자는 소프트웨어의 입출력과 처리 기능을 자세히 요구하지 못하고, 개발자도 알고리즘의 효율성이나 운영체제 호환성 및 상호 작용 형태를 정확히 파악하기 힘드므로 이럴 경우 소프트웨어 요구사항을 파악하기 위한 좋은 방법이다.



점증적 모델
 




특징 : 여러개의 모듈로 분해하고 각각을 개발하여 인도하는 방식
         각 모듈을 점증이라고 한다.

릴리스 구성 방법 :
         점증적 개발 방법 : 시스템을 기능별로 여러 서브 시스템으로 나누고 하나씩 개발(릴리스 A, B, C)한다.
         반복적 개발 방법 : 릴리스 할 떄 마다 기능의 완성도를 높임. (릴리스 1.0, 1.1, 1.2)
단계적 개발 : 
        기능이 부족하더라도 초기에 사용 교육 가능
        처음 시장에 내놓는 SW는 시장을 빨리 형성 할 수 있음
        자주 릴리스하면 가동 중인 시스템에서 일어나는 예상하지 못했던 문제를 신속 꾸준히 고쳐나갈 수 있음.

장점 : 
       중요한 점증이 먼저 개발되므로 사용자는 시스템을 이른 시기에 사용할 수 있다.
       릴리스 방식이 요구사항 변화에 대응하기 용이하다.
       점증들은 점차 규모와 기능이 축소되어 관리가 어렵지 않음
       먼저 개발되는 중요 부분이 반복적으로 테스트된다.

단점 : 
       기능적으로 분해하기 어려울 수 있다.
       적당한 크기의 점증들로 나누기 어렵다
       점증을 개발하기 전에 명확한 요구사항을 정의해야 한다.

진화형 모델과 다른 점 : 여러 점증을 동시에 개발 할 수 있음. 시스템 릴리스가 시간차를 두고 계속됨





나선형 모델(Spiral Model)




특징 : 

  전체 생명주기에 프로토타이핑과 위험 분석을 계획적으로 사용하여 위험을 최소화 하려는 목적을 가짐.
  반복 진화형 모델의 확장 형태로 주기적으로 순환되는 구조이다.  
  소프트웨어의 기능을 나누어 점증적으로 개발

장점 :
 
 대형 프로젝트에서 위험 관리를 통해 성공 가능성을 높일 수 있다.
 대규모 시스템 개발에 적합
 위험을 관리하고 최소화 함
 반복적인 개발 및 테스트를 통한 강인성 향상

V 모형 :


특징 :

 폭포수 모델에 시스템 검증과 테스트 작업을 강조한 것
 구현을 중심으로 각 단계로 V자 모양의 대칭

장점 :

 오류를 줄일 수 있음
 모든 단계에 검증과 확인 과정이 존재

단점 :

 생명주기의 반복이 없어 변경을 다루기가 쉽지 않음
 작업이 종료되고 리뷰 후에는 관련된 결과물이 동결된다.
 요구명세가 확실하여 개발하는 동안 변경이 없는 경우에만 적합

적용 :

  신뢰성이 높이 요구되는 분야

 



일정 중심 설계 모델

특징 : 

  사용자의 요구에 대하여 우선순위를 정하고 이를 기초로 각 사이클을 계획
  초기 단계에 중요한 기능을 설계, 구현하여 시스템의 골격을 만듦
  상대적으로 덜 중요한 기능을 나중에 함으로 일정 조정 가능

단점 :
 
  우선순위가 낮아 출시에 포함되지 않을 기능을 분석하고 설계하는데 시간을 낭비

적용 :
 
 소프트웨어 제품의 출시 날짜가 매우 중요한 경우
  목표 일정을 달성할 수 있을지 불확실할 떄

 사용자의 요구에 대하여 우선순위를 정하고 이를 기초로 각 사이클을 계획
 


시험 공부하다가 그냥..
신고

'프로그래밍 일반' 카테고리의 다른 글

Git 후기  (0) 2011.05.09
Git 구축 완료  (1) 2011.05.09
소프트웨어 개발 프로세스 모델  (2) 2011.04.21
Idiom 이란?  (0) 2011.04.07
Simple MATLAB  (0) 2011.04.04
기본 Design Pattern  (7) 2011.03.30
Posted by JAVA_HOME

댓글을 달아 주세요

  1. ddd 2011.10.06 22:44 신고  댓글주소  수정/삭제  댓글쓰기

    좋은정보 감사합니다^^

  2. ddd 2011.10.06 22:44 신고  댓글주소  수정/삭제  댓글쓰기

    좋은정보 감사합니다^^

2011.04.19 20:40
 2011년 4월 15일 .. 19일에 드디어 KT정식발매 넥서스원을 진저브레드로 업데이트 한다고 한다. 해외에서는 이미 한 달전 부터 시작한 업데이트..기약없는 국내 업데이트 소식에 얼마나 많은 루팅의 유혹을 뿌리쳐왓던가

 드디어 4월 19일 당일. 그러나 10시부터 시작된다는 업데이트는 소식도 없고.. 그렇게 멍하니 있던중 6시 40분경 못보던 아이콘과 팝업창이 하나 뜬다.(제일 왼쪽 위 아이콘, 팝업창은 스샷을 못찍었습니다.)

 업데이트하시겠습니까?

 3g여서 연구실로 돌아와서 업데이트 시작!


재시작 및 설치를 눌렀더니 자동으로 재부팅이 된다. 
 


그런데 으잉? 업데이트는 되지 않고 그대로 뻗어버렸다. 블랙스크린(?!)이 되어버렸다.
 


 기다려봐도, 무엇을 해도 변화가 없어서 어쩔수 없이 재부팅을 했더니.. 어라? 업데이트를 안하네.. 업데이트 매뉴에 들어가니 최신상태? 뭐지..  와이파이 문제인가해서 3g로 바꿔도 안되고, USB 디버깅때문인가 해서 USB 케이블을 뽑아도 여전히 최신상태란다.. 
 그래서 최후의 수단으로 초기화 선택!! 이래도 안되면 루팅한다는 심정으로 감행..
 그래도 꿈쩍도 안하네..라고 생각하고 설정 매뉴에서 보니 업데이트가 된다. 뭔가 지맘에 안드는게 있었는 듯 -_-
  


 근데 받으면 받는다고 말이라도 해주던가 아이콘 알림 하나없이 업데이트를 한다 -..-

다 다운받으니 또 재시작 및 설치를 누르란다.. 또 업데이트가 사라질까 두려웠지만 과감히 클릭!

박스에서 안드로이드 괴물이 튀어나오는 그림과 함께 프로그래스바가 올라간다.(이과정에서는 DDMS가 물리지 않아서 스샷은 찍지 못했습니다.) 

한참 동안 레이저빔을 쏴 주시다가 드디어 메인화면 등장!!

 

전체적으로 시커매졌고, UI 애니메이션이 부드러워졌다. 그것 말고는 아직 잘... 

막상 내 넥서스원은 업데이트 했지만, VoIP 같이 새로운 기술들을 이용한 앱을 만들기에는 진저브레드의 전파속도가 너무 느린관계로 당분간은 여전히 프로요 타겟의 앱이나 만들어야겠습니다. -..-

신고
Posted by JAVA_HOME

댓글을 달아 주세요

  1. JAVA_HOME 2011.04.19 22:39 신고  댓글주소  수정/삭제  댓글쓰기

    꺼질때 TV효과도 있군요!! ㅋㅋ 전반적으로 빨라진 듯한 느낌은 드네요

2011.04.18 20:58
  항목 16에서 상속을 위한 설계와 문서화를 하지 않은 클래스로부터 상속을 받을 때의 위험성을 경고하였다. 그렇다면 상속을 위한 설계와 문서화가 된 클래스란 무슨 의미일까?

 우선, 그런 클래스에서는 메소드 오버라이딩으로 인한 파급 효과를 분명하게 문서화해야 한다. 달리 말해 오버라이드 가능한 메소드들의 자체사용(self-use) 즉, 그 메소드들이 같은 클래스의 다른 메소드를 호출 하는지에 대해 반드시 문서화해야 한다.

 이와는 반대로, 각각의 public이나 protected 메소드 및 생성자가 어떤 오버라이드 가능한 메소드를 호출하는지, 어떤 순서로 하는지, 호출한 결과가 다음 처리에 어떤 영향을 주는지에 대해서도 반드시 문서화해야한다. 더 일반적으로 말하자면, 오버라이드 가능한 메소드를 호출 할 수 잇는 어떤 상황에 대해서도 문서화해야 한다는 것이다. 
  
 관례적으로 오버라이드 가능한 메소드를 호출하는 메소드에는 문서화 주석의 제일 끝에 그런 호출에 대한 설명을 추가한다. 그리고 설명의 시작은 "이번 구현 버전(This implementation)"이라는 구문으로 한다. 배포판이 달라지면서 메소드의 동작이 변경될 수 있다는 것을 나타내기 위해서만 그런 구문을 넣어서는 안된다. 그 구문에서는 메소드 내부에서 처리하는 작업에 관련되는 설명을 담고 있어야 한다. 

 책에서는 java.util.AbstractCollection의 명세를 예로 들고 있다. 참고하기를 바란다.

 그러나 잘된 API 문서에는 메소드가 무슨(what) 일을 하는지 기술해야 하고,   어떻게(how) 하는지를 설명하면 안된다는 통념을 어기는 것이다.
  이것은 결국 상속이 캡슐화를 위반함으로써  초래된 불행인 것이다. 서브 클래스가 안전할 수 있게끔 클래스를 문서화하려면, 클래스의 상세 구현 내역을 기술해야 한다(보통때는 하지 않지만).

 오버라이드 가능한 메소드들이 같은 클래스의 다른 메소드를 호출하는 자체 사용을 문서화 하는것 이외에도 상속을 위한 클래스 설계에는 고려할 점이 많다. 그 중 하나로써 잘 선정된 protected 메소드(또는 드물지만 protected 필드)를 제공하여 클래스 내부의 다른 메소드와 연결되도록 해야 한다.
 또한 상속 관련해서 특별히 추가되는 내용과 일반적인 내용(평범하게 클래스 인스턴스를 생성하고 메소드를 호출하는 프로그래머를 위한)이 섞임으로 API 문서를 보기가 혼란스러워 질 수 있다는것에 유의하자.

 상속을 허용하기 위해 클래스가 준수해야 하는 제약이 몇가지 더 있다. 직접 또는 간접의 어떤 형태로든 생성자에서는 오버라이드 가능한 메소드를 호출하면 안된다.

 상속을 위한 클래스를 테스트하는 유일한 방법은 서브 클래스를 만들어 보는것이다.
 그리고 그 서브클래스는 수퍼클래스 작성자가 아닌 사람이 작성해야 한다.



 상속을 위한 클래스 설계에서 Cloneable과 Serializable 인터페이스 중 어느 하나를 구현하는 상속을 위한 클래스를 설계하는 것은좋은 생각이 아니다.(textbook 항목 11과 74 참조)
 꼭 구현해야겠다면 clone과 readObject 메소드가 생성자와 흡사하게 동작하므로 생성자와 유사한 규칙이 적용된다는 것에 유의해야 한다. 즉, 직접 또는 간접의 어떤 형태로든 clone이나 readObject 모두 오버라이딩 가능한 메소드를 호출하면 안된다.
 마지막으로, 상속을 위해 설계된 클래스가 Serializable 인터페이스를 구현하기로 했는데, 그 클래스가 readResolve나 writeReplace 메소드를 가지고 있다면, 그 메소드들을 private가 아닌 protected로 해야한다. 만일 그 메소드들이 private라면 서브 클래스에서 사용할 수 없기 때문이다. 상속을 허용하기 위해 클래스 내부의 구현 메소드가 그 클래스의 API가 되어야 하는 것이다.



 많은 논점이 있었지만 중요한 것은 상속을 위해 클래스를 설게하다 보면 그 클래스에 많은 제약이 생긴다. 라는것이다. 서브 클래스를 안전하게 만들수 있도록 설계나 문서화를 제공하지 않는 클래스의 상속을 금지하는 것이 좋은 방법이라고 할 수 있겠다.
 금지하는 방법은 클래스를 final로 선언하거나, 생성자를 private 혹은 default로 두고 public static 팩토리 메소드를 추가하는것이다.(항목15)

 하지만 실제 프로그래밍에서는 실체 클래스에서 상속을 사용하는 일이 많을 것이므로 이럴 경우에는 항목 16에서 설명한 래퍼 클래스를 만들거나 어떤 오버라이드 가능한 메소드의 자체 사용(self-use)를 완전히 없애는 방법도 있다. 또는 오버라이드 가능한 메소드의 몸체코드를 private "지원(helper)메소드"로 옮기고, 오버라이드 가능한 메소드에서는 그 지원 메소드를 호출하도록 하는 방법도 있다.




 
신고
Posted by JAVA_HOME

댓글을 달아 주세요

2011.04.18 20:20
 상속은 캡슐화를 위배하기 떄문에 부실한 소프트웨어를 초래한다.(textbook에서 다루는 상속의 문제점은 구현 상속(implementation inheritance, 클래스간의 상속)에서의 문제점을 의미한다. 인터페이스 상속(interface inheritance, 클래스에서 인터페이스를 구현하거나, 인터페이스가 다른 인터페이스로 확장되는 것)에는 적용되지 않는다.)

 textbook에서 거론한 잘못된 상속 사용법은 우리가 쉽게 저지를 수 있는, 그리고 이미 저지르고 있는 방법이므로 꼭 읽어보기를 바란다.


 그렇다면 대안은 무엇이 있을까?

 상속을 하는 대신 기존 클래스(상속에서 수퍼 클래스에 해당되는)의 인스턴스를 참조하는 private 필드를 새로운 클래스(상속에서 서브 클래스에 해당되는)에 둔다. 이런 식의 설계를 컴포지션(composition)이라 하는데, 그 이유는 기존 클래스가 새 클래스의 컴포넌트로 포함되기 때문이다.

  새 클래스의 각 인스턴스 메소드에서는 포함된 기존 클래스 인스턴스의 대응되는 메소드를 호출하여 결과를 반환할 수 있다. 이것을 포워딩(forwarding)이라 하고 새 클래스의 메소드를 포워딩 메소드라 한다. 이렇게 하면, 새 클래스는 기존 클래스의 내부 구현에 종속되지 않고 강건하게 될 것이다. 기존 클래스에 새로운 매소드를 추가하더라도 새 클래스에는 영향을 주지 않는다.

 다음의 예시 코드를 보면 이해하기 쉬울 것이다. 다음 예시 코드는 두부분으로 나누어져있다. InstrumentedHashSet 클래스 코드 및 재사용 가능한 포워딩 클래스(포워딩 메소드만 갖고 있는) 코드이다.

 이러한 InstrumentedSet 클래스를 래퍼(Wrapper) 클래스라고 하는데, 그 이유는 각 InstrumentedSet 인스턴스가 다른 Set 인스턴스를 포장하듯 포함하기 때문이다. 
 이것을 데코레이터 패턴(Decorator Pattern)이라고도 하는데, InstrumentedSet 클래스가 자신의 기능을 다른 Set에 덧붙여 치장("decorates")하기 때문이다. 컴포지션과 포워딩을 결합한 것을 막연하게 위임(delegation)이라고 하는 경우도 있지만, 래퍼 객체 참조가 자신이 포함된 객체에 전달되지 않는다면 기술적으로 위임이라고 할 수 없다.
 
 래퍼 클래스의 단점은 거의 없지만, 차후에 호출("콜백")이 될 수 있도록 객체 자신의 참조를 다른 객체에게 전달하는 콜백 프레임워크(Callback framework)에서의 사용은 부적합하다. 래퍼 객체에 포함된 객체는 자신의 래퍼 객체를 알지 못하므로, 콜백을 할 경우 자신의 참조(this)만을 다른 객체에게 전달하기 때문이다. 이것은 이미 SELF 문제라고 알려져있다.

 포워딩 메소드는 작성하기 번거롭지만, 각 인터페이스에 대해 포워딩 클래스는 한번만 작성하면 되며, 관련 인터페이스가 포함된 곳과 같은 패키지에 둘 수도 있을 것이다. 

 
 컴포지션 대신 상속을 사용하기로 결정할 때는 API의 결함이 없는 super 클래스와 진정한 서브 타입 관계 즉 "is-a"관계일 때만 사용하여야 하며, 그렇지 않다면 컴포지션을 사용해야 한다.
신고
Posted by JAVA_HOME

댓글을 달아 주세요

2011.04.18 16:30
 인스턴스가 가변적이어야 할 타당한 이유가 없다면, 그 클래스는 불변 클래스가 되어야 한다.

 불변 클래스는 가변 클래스에 비해 설계와 구현 및 사용이 더 쉽고, 에러 발생이 적으며 보안이나 사용 측면에서 더 안전한 장점이 있다. 


 불변 클래스를 만들 때는 다음 다섯가지 규칙을 따르자.

1. 객체의 상태를 변경하는 그 어떤 메소드(변경자라고 하는)도 제공하지 않는다.
2. 상속(inheritance)을 할 수 없도록 하자.

더보기

3. 모든 필드를 final로 지정한다. 
4. 모든 필드를 private로 지정한다.
5. 가변 컴포넌트의 직접적인 외부 접근을 막자. 

더보기

 불변 클래스의 유일한 단점은 객체가 가질 수 있는 각 값마다 별개의 객체가 필요하다는 것이다. 

 현실적으로 불변 클래스가 되기 어려운 클래스가 있는데 이런 경우에는 가능한 가변성을 제한하자. 그리고 필드를 final로 하지 않아야 될 이유가 없는 한 모든 필드를 final로 만든다.

 책에서는 불변 클래스의 성능상 단점 및 극복방안에 대한 내용이 있는데 한번 읽어보면 도움이 될 것이다.
  
신고
Posted by JAVA_HOME

댓글을 달아 주세요

2011.04.18 16:01
 흔히 게터(getter)와 세터(setter)로 알고 있는 접근자 메소드(accessor) 메소드와 변경자(mutator)메소드를 사용한 캡슐화(encapsulation)는 모든 자바 개발자라면 하고 있을 것이다. 

 public 클래스와 같은 경우 위와 같은 방법으로 하는 것이 옳지만, 패키지 전용 클래스거나, private 중첩 클래스라면 데이터 필드를 노출해도 별로 문제 되지 않으므로 접근자 메소드를 사용하는 것보다 코드를 알아보기 쉽게 하기 위해서 노출하는 편이 좋을때도 있다. 

 한줄로 요약하면 public 클래스는 모든 가변 필드를 절대로 외부에 노출시키지 말아야 한다. 정도가 되겠다.
신고
Posted by JAVA_HOME

댓글을 달아 주세요

2011.04.18 13:28
OOP 소프트웨어 설계 기본원리 중 하나인 정보 은닉(Information hiding) 또는 캡슐화(Encapsulation)은 매우 중요하다.
 잘 설계된 모듈과 그렇지 않은 것을 구분 짓는 가장 중요한 잣대가 되며, 시스템을 구성하는 모듈들 간의 결합도를 낮추어 모듈 별로 개발, 테스트, 최적화, 사용 및 수정이 쉬워진다.

 자바에서는 정보 은닉을 지원하는 기능이 많은데, 그 중 하나인 접근 제어(access control) 메커니즘에서는 클래스와 인터페이스 및 멤버의 접근성(accessibility)을 지정하는데 그것들이 선언된 위치에 의해 접근성이 결정되며, 필요에 따라 접근 변경자(access modifier)인 private, protected, public을 선언문에 지정한다.

 멤버(필드,메소드,중첩클래스,중첩인터페이스)의 경우 위의 4가지 접근 수준이 가능하며, 접근 수준이 낮은 수준부터 높은 순으로 나열하면 다음과 같다.

- private : 멤버가 선언된 최상위 클래스 내에서만 접근 가능하다.
- (default) : 지정하지 않으면 default로 지정되는 수준이며, 멤버가 선언된 클래가 속한 패키지의 모든 클래스에서 접근 가능하다. 
- protected : default 접근 수준을 가지며, 추가적으로 멤버가 선언된 클래스의 서브 클래스로 부터 접근 가능하다.
- public : 어디에서든 접근 가능하다. 


 한가지만 기억하면된다. 각 클래스나 멤버의 접근 허용을 가능한 최소화 하는것이다. 
 
 클래스를 설계할 때는 우선적으로 public API를 신중하게 결정한 다음 그 외의 다른 멤버들은 private으로 해야 한다. 그리고 같은 패키지의 다른 클래스가 특정 멤버를 꼭 접근할 필요가 있을 떄만 그 멤버의 private 접근 지시자를 제거하여 패키지 전용이 되도록 한다. 
 접근 범위가 커질수록 지속적인 유지 보수가 필요하다는 것을 명심하자.

 메소드의 접근 수준을 줄일 수 없도록 제한 하는  규칙이 하나 있다. 서브 클래스의 메소드가 수퍼 클래스의 것을 오버라이드 하는 경우 수퍼 클래스에서 지정한 접근 수준보다 더 낮은 것을 지정할 수 없다. 그리고 이 규칙이 특별하게 적용되는 경우가 있는데, 인터페이스를 구현할 경우 반드시 public으로 선언되어야한다.


 인스턴스 필드는 절대로 public으로 하지말아야한다.
 또한 public 가변 필드를 갖는 클래스는 스레드에서 안정성을 보장할 수 없다.
 요소가 하나라도 있는 배열은 항상 가변적이라는 것에 주의하자. 따라서 클래스에서 public static final 배열 필드를 갖거나, 또는 그런 필드를 반환하는 접근자(accessor, 통칭 getter) 메소드를 갖는 것은 잘못된 것이다.

 그런 문제점을 해결하는 두 가지 방법이 있다. 하나는 public 배열을 private으로 하고, public의 불변 list를 추가하는 것이다.
 또 다른 방법으로는 배열을 private로 하고, 그 배열의 복사본을 반환하는 public 메소드를 추가하는 것이다.

신고
Posted by JAVA_HOME

댓글을 달아 주세요

2011.04.17 21:01
 클래스와 인터페이스는 자바 프로그래밍 언어의 핵심이며 추상화의 기본 단위이다. 자바에서는 클래스와 인터페이스를 설계하는데 사용할 수 있는 강력한 요소들을 제공한다. 우리가 만드는 클래스와 인터페이스가 쓸모 있고 강력하며 유연성이 있도록 하기 위해 이장에서는 그런 요소들을 최상으로 사용하는데 도움을 주는 지침을 설명한다. (textbook 인용)

  앞의 두 챕터에서는 성능이나 어떠한 메소드의 동작과 같은 국지적인 부분에 포커스를 맞추었지만(앞의 내용들이 중요하지 않다는 것은 아니다!), 이번 Chapter에서는 OOP 프로그래밍 설계를 JAVA 언어가 가진 특성을 활용하여 극대화 하는 방법 및 잘못되거나 비효율적인 방법으로 한 설계를 바로잡는 것을 다루는 Chapter 이므로 중요도가 매우 큰 Chapter라고 할 수 있겠다.
신고
Posted by JAVA_HOME

댓글을 달아 주세요

2011.04.17 20:45
  compareTo메소드는 Comparable 인터페이스에서 유일하게 존재하는 메소드이다. 비록 Object의 메소드는 아니지만, equals와 유사한 특성을 갖는다. 차이점이라면 두 객체가 동일한지를 비교하는 것과 더불어 순서까지 비교할 수 있으며, 제네릭 타입을 지원한다. 특정 클래스에서 Comparable 인터페이스를 구현한다는 것은 자신의 인스턴스들이 자연율(律)(Natural Order)을 따른 다는 것을 나타낸다. 

 Comparable 인터페이스를 구현하는 객체들이 저장된 배열의 정렬은 다음과 같이 쉽게 할 수 있다.

 Arrays.sort(a); 

  Comparable 인터페이스를 구현하는 객체들이 저장된 컬렉션을 검색하고, 최대 값을 찾고, 자동으로 정렬된 상태로 유지하는 것도 이와 유사하게 쉽다. 예를 들어, String 클래스에서 구현하는 Comparable에 의존하는 다음 프로그램에서는 명령행에서 준 인자들을 알파벳 순으로 출력하되 같은 값은 제외한다.

 
 우리 클래스에서 Comparable 인터페이스를 구현하면, 이  인터페이스에 의존하는 자바의 수많은 알고리즘 및 컬렉션 클래스들과 상호 연동이 가능하다. 실제로 자바 라이브러리의 모든 값 클래스들은 Comparable 인터페이스를 구현한다. 

 순서 판단을 위해 현재 객체(compareTo 메소드가 호출된)와 지정 객체(compareTo 메소드의 인자로 전달된)를 비교한다. 현재 객체의 값이 지정 객체보다 작으면 음수 정수 값을, 같으면 0을, 크면 양의 정수 값을 반환한다.  this(자신) - argument(비교대상)의 값을 반환한다고 생각하면 쉬울것이다. 만약 비교 할 수 없는 타입이라면 ClassCastException 예외가 발생된다.



 compareTo 메소드도 보편적 계약을 갖고 있는데 equals 메소드와 유사한 모습을 보여준다. 
 다음 설명에서 sgn(표현식)은 signum 수학함수를 나타내며, 표현식의 값이 음수면 -1을, 0이면 0, 양수면 1을 반환한다.

- 모든 x와 y에 대하여, sgn(x, compareTo(y)) == -sgn(y.compareTo(x))가 되도록 해야한다.(이것은 y.compareTo(x)가 예외를 발생시킬 때만 x.compareTo(y)가 예외를 발생시켜야 한다는 의미를 내포한다.)

- 이행적인 관계가 성립되어야 한다. 즉, (x.compareTo(y) > 0 && y.compareTo(z) > 0) 이면 x.compareTo(z) > 0 이어야 한다.

- 끝으로, x.compareTo(y) == 0 이라면, 모든 z에 대해 sgn(x.compareTo(z)) == sgn(y.compareTo(z))이 되어야 한다. 

- 반드시 요구되는 것은 아니지만, (x.compareTo(y) == 0) == (x.equals(y))가 되도록 하는 것이 좋다. 그리고 Comparable 인터페이스를 구현하면서 이 조항을 지키지 않는 클래스에서는 API문서에 나타나도록 그 사실을 분명하게 표시해야 할 것이다. 즉, 다음과 같이 하기를 권한다. "주의 : 이 클래스는 equals 메소드와 다르게 자연률 순서를 지원한다."




 compareTo 메소드의 작성 방법은 equals 메소드와 유사하지만 몇가지 중요한 차이점이 있다.
 compareTo 메소드가 정의되어있는 Comparable 인터페이스가 매개변수화 타입을 가지므로, compareTo 메소드의 인자로 전달되는 비교 객체의 타입은 컴파일 시점에서 미리 결정된다. 따라서 compareTo 메소드 내부에서 비교 객체의 타입을 확인하거나 타입 변환을 할 필요 없다. 만일 인자의 타입이 잘못되면 코드가 컴파일 되지 않기 때문이다. 만일 인자가 null이면 가능한 빨리 NullPointerException 예외를 발생시켜야 한다.


 Comparable 인터페이스를 구현하지 않고 있거나, 자연율 순서와 다른 순서 매김을 사용할 필요가 잇다면 Comparator 인터페이스를 대신 사용할 수 있다. 이때는 자신의 것을 작성하거나, 또는 String 클래스의 Comparator인 CASE_INSENSITIVE_ORDER.compare 메소드, Double.compare, Float.compare 와 같이 이미 만들어 진것을 사용하자.

 특히 부동 소수점 값을 비교할 때 관계 연산자를 사용하면 compareTo 메소드의 보편적 계약을 따르지 않기 때문에 위의 메소드를 사용하는 것이 필요하다.

 비교할 필드가 많을 경우 우선시 되는 필드의 순서대로 비교하여 동일할경우 다음 중요한 필드의 값을 비교하는 식의 방법을 쓴다. 

 비교할 때 조건문을 쓰고 임의의 정수를 return 하는 방법을 사용할 수 도 있지만, compareTo는 부호를 중요시하므로, 위에서 잠깐 언급한것 처럼 두 필드의 차를 return하는 방법을 쓴다면 더 성능이 좋은 메소드를 구현할 수 있다. 
 이 방법은 빠르지만, 두 수의 차가 Integer.MAX_VALUE(2^31 - 1) 보다 작거나 같은 범위인지를 고려해야 된다는 점을 항상 염두에 두자. 이런 실수는 대다수의 정수값에 대해서 잘 동작하기 때문에 찾기 힘들기 때문에 결함을 찾기가 쉽지 않다.

신고
Posted by JAVA_HOME

댓글을 달아 주세요

2011.04.17 16:39
 Cloneable 인터페이스는 복제를 허용하는 객체라는 것을 알리는 목적으로 사용하는 믹스인 인터페이스(mixin interface)이다.

 하지만 그런 목적에 부합하지 못하고 있다. 자신이 clone 메소드를 갖고 있지 않으며, Object 클래스의 protected인 clone 
메소드를 사용할지 여부를 결정한다.

 그리고 Cloneable 인터페이스를 구현한 효과를 보려면, 그 클래스와 그 클래스의 모든 수퍼 클래스들은 문서화된 규약을 준수해야 하는데, 그 규약이란 것이 꽤 복잡하고 강제성을 띄지 않으면서 분량도 많지 않다. 그리고 그 규약에 기인하여 만들어진 메커니즘은 자바 언어 영역을 벗어난다. 생성자를 호출하지 않고 객체가 생성되어 복제되기 때문이다.



 앞에서 살펴본 메소드들과 같이 clone에도 보편적 계약이 있는데 그 내용이 매우 빈약하다. 

- x.clone () != x 는 true를 반환한다.
- x.clone().getClass() == x.getClass() 는 true를 반환한다.
- x.clone().equals(x) 는 일반적으로 true가 될 것이지만 필수요건은 아니다.


 일반적으로 객체를 복제할 때는 그 객체의 클래스로부터 새로운 인스턴스를 생성하게 되지만, 그 때 내부 데이터 구조의 복사도 필요할 수 있다. 복제를 할 떄 어떤 생성자도 호출되지 않는다. 

 위의 계약은 많은 문제점을 내포하고 있는데 자세한 내용은 책을 참고하기 바란다.


 눈여겨 볼 점 몇가지를 소개하자면,
 1. 2번째 계약을 지키기 위해 final이 아닌 수퍼 클래스의 clone 메소드를 오버라이드 할 경우, 서브 클래스의 clone에서는 반드시 super.clone을 호출하여 얻은 객체를 반환해야 한다.

더보기


 2. Array와 같이 가변 객체를 참조하는 필드를 가지고 있을 때는 참조값만 복사하는 Shallow copy가 아니라 인스턴스 자체를 복제하는 Deep Copy를 고려하자.

더보기


 3. 복잡한 객체를 복제하는 마지막 방법은, super.clone을 호출하여 반환된 복제 객체의 모든 필드를 초기화 한 후 고주순의 메소드를 호출하여 원본 객체와 같은 상태로 재생하는 것이다.

더보기


4. 생성자 처럼, clone 메소드에서는 복제 중인 객체의 final이 아닌(달리 말해, 오버라이드 가능한) 어떤 메소드도 호출 하면 안된다.

더보기


5. Object의 서브클래스에서 clone 메소드를 오버라이딩 할 때는(특히 public으로) CloneNotSupportedException과 같은 checked외를 발생시키지 않는게 메소드를 사용하기 쉽다. 하지만 상속을 목적으로 설계된 클래스가 clone을 오버라이드 하고 있다면 clone을 protected로 지정하고 CloneNotSupportedException 예외를 발생시킨다고 메소드를 선언해야 하며, 그 클래스에서는 implements Cloneable을 선언하지 말아야한다. 그렇게 함으로써 그 클래스의 서브 클래스에서는 마치 Object로부터 직접 상속 받는것처럼 Cloneable 인터페이스의 implements 여부를 선택할 수 잇기 때문이다.

6. implements Cloneable 을 선언한 클래스가 스레드에서 안전하게 사용될 수 있게 하고자 한다면, 그 클래스의 clone 메소드는 다른 메소드처럼 반드시 올바르게 동기화 되어야 한다. Object의 clone 메소드는 동기화를 지원하지 않기 때문이다.



하지만 위와 같이 복잡하게 clone 메소드를 오버라이드 해야 하는 것일까? Cloneable을 implements하는 클래스의 서브 클래스에서는 선택의 여지가 없이 잘 동작하는 clone메소드를 구현해야하지만, 그렇지 않다면 복제 생성자나 복제 팩토리 메소드를 제공한다면(자기 자신을 타입으로 하는) 자바 언어 영역을 벗어난 형태의 위험스러운 객체 생성 메커니즘에 의존하는 clone 메소드에 비해 훨씬 강력하게 구현을 할 수 있을 것이다.

 Cloneable의 단점이 많기 때문에, 배열 복제 정도로 사용한다면 모를까, 일부 숙련 프로그래머들은 clone 메소드를 전혀 오버라이드 하지 않고 호출 하지도 않는다.

신고
Posted by JAVA_HOME

댓글을 달아 주세요


티스토리 툴바