본문 바로가기

C/Objective C/Objective C

Objective C category(카테고리) 예제 따라하기

카테고리

클래스 정의를 다루던 도중 새 메서드를 추가하고 싶을 때가 있을 것이다. 이를테면 Fraction 클래스에서 두 부수를 더하는 add: 메서드 외에도 뺄셈, 곱셈, 나눗셈을 하는 메서드가 필요할 수도 있다.
카테고리는 클래스 정의를 그룹짓거나, 연관된 메서드를 카테고리로 쉽게 모듈러할 수 있게 해준다.
또한 원본 소스코드에 접근하거나 서브클래스를 생성하지 않고도 현존하는 클래스의 정의를 쉽게 확장하 는 방법도 제공한다. 카테고리는 강력하면서도 매우 쉬운 기법이다.


- 소스코드 

#import <Foundation/Foundation.h>

#import <stdio.h>


@interface Fraction : NSObject

{

int numerator;

int denominator;

}

@property int numerator, denominator; 

-(void) setTo: (int) n over: (int) d;

-(void) reduce;

-(double) convertToNum;

-(void) print;

@end


@interface Fraction (MathOps) 

-(Fraction *) add: (Fraction *) f; 

-(Fraction *) mul: (Fraction *) f; 

-(Fraction *) sub: (Fraction *) f; 

-(Fraction *) div: (Fraction *) f; 

@end 

#import "Fraction.h"


@implementation Fraction

@synthesize numerator, denominator; -(void) print {

if ( denominator < 0 )

NSLog (@"-%i/%i", numerator, -denominator);

else

NSLog (@"%i/%i", numerator, denominator);

}

-(double) convertToNum {

if (denominator != 0)

return (double) numerator / denominator;

else

return 1.0;

}

-(void) setTo: (int) n over: (int) d { 

numerator = n;

denominator = d; 

}

-(void) reduce {

int u = numerator

int v = denominator

int temp;

while (v != 0) { 

temp = u % v;

u = v;

v = temp; 

}

numerator /= u;

denominator /= u; 

}

@end


@implementation Fraction (MathOps) -(Fraction *) add: (Fraction *) f

{

// To add two fractions:

// a/b + c/d = ((a*d) + (b*c) / (b * d)

Fraction *result = [[Fraction alloc] init]; 

int resultNum, resultDenom;

resultNum = (numerator * f.denominator) + (denominator * f.numerator); 

resultDenom = denominator * f.denominator;

[result setTo: resultNum over: resultDenom]; 

[result reduce];

return result; 

}


-(Fraction *) sub: (Fraction *) f {

// To sub two fractions:

// a/b - c/d = ((a*d) - (b*c)) / (b * d)

Fraction *result = [[Fraction alloc] init]; 

int resultNum, resultDenom;

resultNum = (numerator * f.denominator) - (denominator * f.numerator); 

resultDenom = denominator * f.denominator;

[result setTo: resultNum over: resultDenom]; [result reduce];

return result; 

}


-(Fraction *) mul: (Fraction *) f {

Fraction *result = [[Fraction alloc] init];

[result setTo: numerator * f.numerator over: denominator * f.denominator]; 

        [result reduce];

return result; 

}


-(Fraction *) div: (Fraction *) f {

Fraction *result = [[Fraction alloc] init];

[result setTo: numerator * f.denominator over

denominator * f.numerator]; [result reduce];

return result; 

}

@end 

#import <Foundation/Foundation.h>

#import "Fraction.h"



int main(int argc, const char * argv[])

{


Fraction *a = [[Fraction alloc] init]; 

Fraction *b = [[Fraction alloc] init]; 

Fraction *result;

[a setTo: 1 over: 3]; [b setTo: 2 over: 5];

[a print]; NSLog (@" +"); [b print]; NSLog (@"-----"); 

result = [a add: b];

[result print];

NSLog (@"\n");


[a print]; NSLog (@" -"); [b print]; NSLog (@"-----"); 

result = [a sub: b];

[result print];

NSLog (@"\n");


[a print]; NSLog (@" *"); [b print]; NSLog (@"-----"); 

result = [a mul: b];

[result print];

NSLog (@"\n");


[a print]; NSLog (@" /"); [b print]; NSLog (@"-----"); 

result = [a div: b];

[result print];

NSLog (@"\n");

return 0;

} 

@interface Fraction (MathOps) 

-(Fraction *) add: (Fraction *) f; 

-(Fraction *) mul: (Fraction *) f; 

-(Fraction *) sub: (Fraction *) f; 

-(Fraction *) div: (Fraction *) f; 

@end

이는 인터페이스 부분을 정의한 코드인데, 이미 존재하는 인터페이스를 확장한 것이다. 따라서 (새 카테고 리를 원래 클래스 헤버파일인 Fraction.h에 추가하지 않은 한) 원래 인터레이스를 포함시켜야 컴파일러가 Fraction 클래스에 대해 알 수 있다.

#import Fraction (MathOps)
이 코드는 컴파일러에게 Fraction 클래스의 새 카테고리로 MathOps를 정의한다고 알린다. 카테고리 이름 은 클래스 이름 다음에 괄호로 감싸서 적어 준다. 여기서 Fraction의 부모 클래스를 언급하지 않음에 주 의하자. 컴파일러는 Fraction.h에서 이미 이 정보를 알게 되었다. 앞에서 정의한 인터페이스 부분과 달리, 인스턴스 변수에 대해서도 따로 언급하지 않았다. 사실, 부모 클래스나 인스턴스 변수를 나열하면 컴파일 러가 구문 오류를 표시할 것이다.
이 인터페이스 부분은 컴파일러에게 MathOps 카테고리를 Fraction이라는 클래스에 추가하여 클래스를 확장할 것이라고 알려 준다. MathOps 카테고리는 add:, mul:, sub:, div:이라는 네 가지 메서드를 포함 한다. 각 메서드는 분수를 인수와 반환 값으로 사용한다.
하나의 구현부에 모든 메서드 정의를 넣을 수 있다. 이것은 Fraction.h의 인터페이스 부분에 있는 모든 메서드와 MathOps 카테고리에 든 메서드를 몽땅 단일한 구현 부분에 정의할 수 있다는 의미다. 혹은 카 테고리의 메서드를 별도의 구현부에 정의해도 된다. 이런 경우, 이 메서드들을 구현한 부분은 메서드가 속 한 카테고리가 어딘지를 언급해 주어야 한다. 인터페이스 부분에서와 마찬가지로 클래스 뒤에다 카테고리 이름을 괄호로 감싸면 된다.

카테고리는 원래 클래스의 인스턴스 변수에 접근할 수는 있지만 새 인스턴스 변수를 추가할 수는 없다. 인 스턴스 변수를 추가해야 한다면, 서브클래스를 만드는 것을 고려해야 한다.
또한 카테고리는 클래스에 있는 메서드를 재정의할 수 있는데, 이 방법은 보통 나쁜 프로그램래밍 습관으 로 여겨진다. 한 가지 이유를 대자면, 메서드를 재정의한 후에 원래 메서드에 접근할 방법이 없어지기 때 문이다. 따라서 재정의를 선택할 경우, 원래 메서드의 기능을 모두 추가해 줘야 한다. 만일 메서드를 재정 의해야 한다면, 서브클래스를 만드는 것이 올바른 선택일 것이다.

일반적인 인터페이스 부분과 달리, 카테고리의 모든 메서드를 구현할 필요가 없다. 일잔 모든 메서드를 카 테고리에 정의하고, 추후에 점차 구현해 나가면 되므로, 점진적 개발을 할 때 카테고리가 유용하다. 


- 실행화면