[Effective Objective-C] #9 클래스 클러스터 패턴을 사용해 구현의 상세 내용을 숨기라
출처 : Effective Objective-C
-
클래스 클러스터(class cluster)는 추상 기본 클래스(abstract base class) 뒤편에 상세 구현을 숨길 수 있는 아주 훌륭한 방법이다.
이 패턴은 오브젝티브-C 시스템 프레임워크 전반에 걸쳐 사용된다.
아래 함수가 예이다.
+ (UIButton*)buttonWithType:(UIButtonType)type;
클래스 클러스터 생성하기
-
typedef NS_ENUM(NSUInteger, EOCEmployeeType){
EOCEmployeeTypeDeveloper,
EOCEmployeeTypeDesigner,
EOCEmployeeTypeFinance,
}
@interface EOCEmployee : NSObject
@property (copy) NSString *name;
@property NSUInteger salary;
+ (EOCEmployee*) employeeWithType:(EOCEmployeeType)type;
- (void)doADaysWork;
@end
@implementation EOCEmployee
+ (EOCEmployee*)employeeWithType:(EOCEmployeeType)type{
switch(type){
case EOCEmployeeTypeDeveloper;
return [EOCEmployeeDeveloper new];
break;
case EOCEmployeeTypeDesigner:
return [EOCEmployeeDesigner new];
break;
case EOCEmployeeTypeFinance:
return [EOCEmployeeFinance new];
break;
}
}
- (void)doADaysWork{
// 하위 메서드에서 이 메서드를 반드시 구현하라.
}
@end
@interface EOCEmployeeDeveloper : EOCEmployee
@end
@implementation EOCEmployeeDeveloper
- (void)doADaysdWork{
[self writeCode];
}
@end
-
불행히도 Objective-C 에는 기본 클래스를 추상으로 만드는 기능이 없다.
대신 클래스의 사용 방법을 문서로 만들어야 한다.
이 경우 인터페이스에 초기화 메서드들의 정의가 없는데 이는 우연히라도 기본 클래스의 인스턴스를 직접 생성할 수 없게 막아야 하기 때문이다.
기본 클래스의 인스턴스가 생성되지 않도록 보장하는 또 다른 방법은 기본 클래스의 doADaysWork 메서드에서 예외를 던지도록 하는 것이다.
그러나 이 방법은 너무 과하고 보통은 불필요하다.
코코아의 클래스 클러스터
-
시스템 프레임워크에는 많은 클래스 클러스터가 있다.
NSArray 와 NSMutableArray 같은 대부분의 컬렉션 클래스들은 클래스 클러스터다.
두 개의 추상 기본 클래스가 있다.
각각 불변 배열(immutable array)와 가변 배열(mutable array)를 위한 것이다.
이것들은 모두 클래스 클러스터이지만 두 개의 퍼블릭 인터페이스가 있다.
불변 클래스는 모든 배열에 대한 공통 메서드를 정의하고, 가변 클래스는 오직 가변 배열에 대한 메서드들만 정의한다.
-
NSArray 의 경우 NSArray 의 인스턴스를 할당하면 플레이스홀더 배열이라는 다른 클래스의 인스턴스가 할당된다. (alloc 메서드 호출 중 )
이 플레이스 홀더 배열은 결국엔 NSArray 의 구성 하위 클래스 중 하나로 변경된다.
-
NSArray(그리고 대부분의 다른 컬렉션 클래스들) 같은 클래스가 클래스 클러스터라는 사실을 꼭 알고 있어야 한다.
그 사실을 모르면 다음과 같은 코드를 작성할 수도 있다.
id maybeAnArray = /*…*/;
if ( [maybeAnArray class] == [NSArray class] ){
// 절대 실행되지 않는다.
}
-
NSArray 초기화 메서드가 반환하는 인스턴스는 클래스 클러스터 퍼블릭 파사드(facade)에 있는 내부 타입의 인스턴스이다.
그래서 위의 코드 대신 내성 메서드를 사용해야 한다.
id maybeAnArray = /*…*/;
if ( [maybeAnArray isKindOfClass:[NSArray class] ] ) {
// 실행될 수 있다.
}
-
클래스 클러스터에 구상 구현 ( concrete implementation )을 추가하는 것은 흔히 요구되는 것이지만
그렇게 했을 때 발생할 수 있는 경고를 처리해야 한다.
Employee 예제의 경우 새로운 직원 타입을 추가하는 것은 팩터리 메서드 소스코드에 추가하는 방법밖에 없다.
NSArray 같은 코코아의 클래스 클러스터의 경우, 할 수는 있지만 몇 가지 규칙을 따라야 한다.
-
코코아 클래스 클러스터 하위 클래스 생성 규칙
1. 하위 클래스는 클래스 클러스터의 추상 기본 클래스를 상속해야 한다.
NSArray 의 경우 추상 기본 클래스는 가변 또는 불변 기본 클래스이다.
2. 하위 클래스는 자신의 저장 공간을 정의해야 한다.
하위 클래스 내부에 배열이 포함하고 있는 객체들을 담는 인스턴스 변수가 꼭 있어야 한다.
이는 기대했던 것과는 반대다.
이 인스턴스 변수는 확실히 NSArray 에 있어야 할 것 같다.
그러나 NSArray 자체는 배열에 대한 인터페이스만 정의한, 숨겨진 객체들을 연결하는 간단한 연결고리일 뿐이라는 사실을 기억해야 한다.
3. 하위 클래스는 상위클래스의 문서에 정의된 메서드들을 재정의해야 한다.
각 추상 기본 클래스는 하위 클래스가 반드시 구현해야 할 메서드 집합을 가지고 있다.
NSArray 의 경우 구현해야 하는 메서드들은 count 와 objectAtIndex: 이다.
-
클래스 클러스터의 하위 클래스를 만들 때 따라야 할 규칙은 클래스 문서에 정의되어야 하고 사용자가 항상 가장 먼저 읽어야 할 문서다.
기억할 점
클래스 클러스터 패턴은 간단한 퍼블릭 퍼사드 뒤편에 상세 구현을 숨길 때 사용할 수 있다.
클래스 클러스터는 시스템 프레임워크에서 널리 사용된다.
클래스 클러스터의 퍼블릭 추상 클래스의 하위 클래스를 만들 때는 항상 주의를 기울여야 한다.
그리고 문서가 있으면 반드시 먼저 읽어봐야 한다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[Effective Objective-C] #11 objc_msgSend 의 역할을 이해하라 (0) | 2017.08.13 |
---|---|
[Effective Objective-C] #10 연관 객체를 사용해 기존 클래스에 사용자 정의 데이터를 연관 지으라 (0) | 2017.08.12 |
[Effective Objective-C] 목차와 요약을 통해 한 눈에 알아보는 Effective Objective-C #1 ~ #8 (0) | 2017.08.10 |
[Effective Objective-C] #8 객체의 동등 비교를 이해하라 (0) | 2017.08.09 |
[Effective Objective-C] #7 인스턴스 변수에 내부에서 접근할 때는 직접 접근하라. (0) | 2017.07.28 |
댓글