'상속'에 해당되는 글 2건

  1. 2010.04.11 객체지향을 강력하게 만드는 상속
  2. 2008.06.06 상속과 구현
Objective C/상속2010. 4. 11. 21:44
C 이후의 대부분의 언어는 객체지향이라는 개념으로 언어가 설계되었다. 
이는 절차 지향보다 훨씬 편하고 다형성을 지킬 수 있도록 많은 프로그래머들이 고민하여 현재까지 발전해 왔다.
당연히 ObjC도 객체지향언어이며 모든 것은 루트클래스(최상위 클래스)에서 시작한다.
여기서는 NSObject클래스가 루트클래스이다. 루트클래스는 더 이상 위로 올라갈 수 없는 가장 기본적인 클래스(최상위 클래스)를 말하며 그것을 상속받은 클래스를 자식클래스 또는 서브 클래스라고 한다. 
서브클래스에서 루트클래스 또는 부모클래스, 슈퍼 클래스로 접근하기 위해서는 항상 super라는 키워드를 사용한다.

상속 받은 서브 클래스는 항상 부모클래스의 모든 기능을 가지고 있고 추후 개발자에 의해 새로운 기능들이 추가되거나 수정(Override)된다. 가령 어떤 클래스 A는 변수 x와 이를 설정하는 initVar라는 메서드를 가지고 있다. 누군가 이 클래스를 이용하여 변수x를 설정하고 그 값을 출력 하는 기능을 추가하고 싶다고 한다면 클래스 B를 만들어 클래스 A를 상속받아 새로운 메서드인 print를 만들어 넣으면 된다.

이렇게 상속을 하다보면 많은 메서드와 변수가 쓰이게 된다.
컴파일러는 어떤 변수와 메서드를 우선으로 실행할까? 아래는 메서드 실행 메카니즘이다.
어떠한 메시지가 인스턴스에 전달되면 컴파일러는 해당 인스턴스 클래스 내부를 우선으로 검색하고 실행한다. 
만약 해당 클래스에 메시지를 받는 메서드가 없다면 그 상위 클래스에서 메서드를 검색한다. 그래도 없다면 또 그 상위 클래스를 검색하고 발견되면 실행하지만 발견되지 않는다면, '메시지 전달에 응답하지 못할 것'(may not respond to 'method')이라고 컴파일러는 경고 또는 에러를 발생한다.
Posted by 버터백통
AS3나 자바등과 같은 OOP언어의 특징은 상속과 구현이다. 상속은 앞서 테스트해본 사용자정의 클래스만들기 부분에서 Test클래스와 같이 부모를 Sprite라는 프레임 없는 무비클립을 상속받았고 Test2클래스는 Test클래스를 상속받았으므로 위의 두성분을 모두 포함하고 있다고 설명하였다. 이처럼 부모의 성질을 이어받는 것이 상속이다.

[ 상속 ]
우선 상속이라는 개념을 살펴보면 그림과 같은 가계도를 갖는다.
사용자 삽입 이미지
그림을 보면 최상위 클래스인 James라는 놈에게서 son1과 son2가 부모의 성질을 받아 만들어졌다.
빨간색 글씨부분은 부모클래스인 James의 성질을 이어받았기 때문에 따로 만들어 주지 않아도 실행이 가능해졌다. 그리고 son1의 경우에는 추가로 2개의 성질을 더 만들어 주어 양손잡이가 되었고 게임도 좋아하게 되었다.
son2의 경우에는 1개의 성질만 추가하였기 때문에 영화를 좋아하는 성질을 갖게되었다.

이처럼 기본기능이 있는 부모클래스를 상속받아 프로젝트에 맞는 부가적인 메서드를 추가로 개발하는것을 상속이라고 생각하면 되겠다.
* 부모에게서 받은 성질중에 고치고자 하고싶은 것은 override하면 된다.
가령 james의 곱슬머리()를 자식클래스에서 기능을 바꾸고자 한다면
override 곱슬머리(){
바꾸고자하는 머리스타일...
}
위처럼 기능을 재정의 하는것이다. (메서드의 재정의 override 참고)

[ 구현 ]
구현은 구조를 만들기 위해서 아주 중요한 부분이 된다.
개발을 하다보면 상속만 가지고는 어느 한계에 마주치게 되고...그 때 아쉽게 생각이 드는 것이 다중 상속이라는 것을 생각하게 된다...하지만 그것은 불가능한 것이기 때문에 Interface라는 것을 만들어 여러형태로 바라볼 수 있게 만드는 것이다. 가령 위그림에서 son1은 James의 아들이다. 하지만 James의 아내가 Jena라면 son1은 Jena의 아들이기도 하다. 하지만 이를 표현하기 위해서 그림과 같은 상속구조에서는
절대 Jena라는 클래스와 son1의 관계를 표현할수 없는 단방향 구조를 갖게된다. OOP의 장점이자 단점인 부분인 것 같다.

그러면 이러한 구성을 Interface로 풀어보자.
사용자 삽입 이미지
James에 관해서는 상속으로 고리가 연결되어있다. 하지만 상속은 단일 상속밖에 되지않으므로 Interface Jena를 만들어 이를 구현하고자 하는 클래스에서 구현(implements)하게 된다. 그러면 Son1과 Son2에는 James의 성질을 갖으면서 Jena의 성질도 포함하게 된다. 중요한 것은 나중에 이들을 부를(호출)때 James의 아들로 호출하여도 되고 Jena의 아들로 호출하여도 이 Son클래스들은 바라보게 된다는 것이다.

만약 이들을 코드로 본다면 다음과 같다.
Package는 생략...
public class Son1 extends James implements Jena
{
//Son1의 생성자(new로 호출했을 때 실행되는 부분)
public function Son1()
{

}
기타 필요한 메서드...
}


Jana의 코드
package
{
 public interface Jena
 {
jena의 설징을 결정할 메서드들 정의
 }
}
Jena는 인터페이스이다. 인터페이스는 항상 public이나 internal로 스코프를 갖게된다.
이들은 이들을 구현할 클래스를 성질을 구분짓기 위해 주로사용되고 인터페이스 내부에는 메서드만 정의해 놓는다. 상세코드는 구형하지않는다.
가령 Jena는 Beauty()이라는 속성만 가지고있다면 아래처럼 코딩한다.
package
{
 public interface Jena
 {
    function Beauty():void

 }
}

이처럼 실행문을 코딩하지 않는다. 이들의 실행문은 이를 받아 구현하는 Son1이나 Son2에서 필요에 따라 작성하면 된다. 단 인터페이스에서 함수가 있다면 해당  함수를 반드시 포함하고있어야한다.
public class Son1 extends James implements Jena
{
  public function Son1()
  {

  }
  function Beauty():void
  {
      필요하다면 기능 구현
  }
}

이렇게 된다면 Son1은 아름답다라는 성질도 갖게되는 것이다. 이 성질은 jena에게서 온것이다.
아쉬운점은 인터페이스에서 상세 구현이 불가능하므로 상속처럼 성질을 이어받으면 호출에서 실행시킬 때 따로 구현할 필요가 없지만 인터페이스는 그때, 그때 상세 코드를 구현하여야 한다..ㅜㅜ;;
하지만 이거라도 되는게 어딘가...

만약 다음과 같은 상황이 있다고 가정하여 보자.
사용자 삽입 이미지

그림을 보면 새를 상속받은 [독수리] , [닭] , [새인형]이라는 클래스가 3개 있다.
이들은 모두 새라고 생각할 수 있다. 이제 이들을 구분지어 보면 누가 "날 수있는 것 모이세요!"하고 외쳤다. 그럼 독수리만 모이게 될것이다. 그럼 "소릴 낼수 있는것 모이세요!"라고 한다면 독수리와 닭이 모일것이며 "새들은 모이세요!"한다면 독수리,닭,새인형 모두 모일 것이다.. 이것은 클래스의 성질을 가지고 구분지을 수 있다는 말이다.

가령 화면에 객체들이 흩어져있다. 이들을 드래그하여 여러개 선택한 다음에 지우기 버튼을 눌러서 지우려고 한다. 그럼 지워질 수 있는 객체는 del()이라는 메서드가 실행되겠지만 지울수 없는 객체는 del()이라는 메서드가 포함되어 있질 않아서 에러를 발생할 것이다. 그럼 del이 있는 객체만 골라서 실행해야하는데
if( 선택된 객체 is Removable ){
    선택된 객체.del()
}
과 같이 객체의 성질을 검사하여 조건문에 의해 선택적으로 실행할 수 있다.
* Removable은 del()을 선언한 인터페이스이다. Removable인터페이스를 implements한 객체는
 반드시 del()이라는 메서드를 구현해 놓아야 하므로 조건식에 만족한다면 del()을 실행시킬 수 있다.






Posted by 버터백통