Objective-c-composite-objects
Objective-C複合オブジェクト
オブジェクトを埋め込むクラスを定義するクラスクラスター内にサブクラスを作成できます。 これらのクラスオブジェクトは複合オブジェクトです。 そのため、クラスクラスターとは何かを疑問に思うかもしれません。 そのため、最初にクラスクラスターとは何かを確認します。
クラスクラスター
クラスクラスターは、基盤フレームワークが広範囲に使用する設計パターンです。 クラスクラスターは、パブリック抽象スーパークラスの下にプライベートコンクリートサブクラスの数をグループ化します。 このようにクラスをグループ化することで、オブジェクト指向フレームワークの一般に公開されているアーキテクチャを、その機能性を損なうことなく単純化することができます。 クラスクラスターは、 abstract factory デザインパターンに基づいています。
簡単にするために、同様の機能のために複数のクラスを作成する代わりに、入力の値に基づいてその処理を処理する単一のクラスを作成します。
たとえば、NSNumberには、char、int、boolなどのクラスのクラスターが多数あります。 それらすべてを単一のクラスにグループ化し、単一のクラスで同様の操作を処理します。 NSNumberは、これらのプリミティブ型の値を実際にオブジェクトにラップします。
複合オブジェクトとは何ですか?
プライベートクラスタオブジェクトを独自のデザインのオブジェクトに埋め込むことにより、複合オブジェクトを作成します。 この複合オブジェクトは、その基本機能をクラスターオブジェクトに依存し、複合オブジェクトが特定の方法で処理したいメッセージのみをインターセプトします。 このアーキテクチャにより、記述する必要のあるコードの量が減り、Foundation Frameworkが提供するテスト済みコードを活用できます。
これを次の図で説明します。
複合オブジェクトは、自身をクラスターの抽象スーパークラスのサブクラスとして宣言する必要があります。 サブクラスとして、スーパークラスのプリミティブメソッドをオーバーライドする必要があります。 派生メソッドをオーバーライドすることもできますが、派生メソッドはプリミティブメソッドを介して機能するため、これは必要ありません。
NSArrayクラスのcountメソッドは一例です。それがオーバーライドするメソッドの介在オブジェクトの実装は、次のように簡単にすることができます-
- (unsigned)count {
return [embeddedObject count];
}
上記の例では、埋め込みオブジェクトは実際にはNSArray型です。
複合オブジェクトの例
次に、完全な例を見るために、以下に示すAppleのドキュメントの例を見てみましょう。
#import <Foundation/Foundation.h>
@interface ValidatingArray : NSMutableArray {
NSMutableArray *embeddedArray;
}
+ validatingArray;
- init;
- (unsigned)count;
- objectAtIndex:(unsigned)index;
- (void)addObject:object;
- (void)replaceObjectAtIndex:(unsigned)index withObject:object;
- (void)removeLastObject;
- (void)insertObject:object atIndex:(unsigned)index;
- (void)removeObjectAtIndex:(unsigned)index;
@end
@implementation ValidatingArray
- init {
self = [super init];
if (self) {
embeddedArray = [[NSMutableArray allocWithZone:[self zone]] init];
}
return self;
}
+ validatingArray {
return [[self alloc] init] ;
}
- (unsigned)count {
return [embeddedArray count];
}
- objectAtIndex:(unsigned)index {
return [embeddedArray objectAtIndex:index];
}
- (void)addObject:(id)object {
if (object != nil) {
[embeddedArray addObject:object];
}
}
- (void)replaceObjectAtIndex:(unsigned)index withObject:(id)object; {
if (index <[embeddedArray count] && object != nil) {
[embeddedArray replaceObjectAtIndex:index withObject:object];
}
}
- (void)removeLastObject; {
if ([embeddedArray count] > 0) {
[embeddedArray removeLastObject];
}
}
- (void)insertObject:(id)object atIndex:(unsigned)index; {
if (object != nil) {
[embeddedArray insertObject:object atIndex:index];
}
}
- (void)removeObjectAtIndex:(unsigned)index; {
if (index <[embeddedArray count]) {
[embeddedArray removeObjectAtIndex:index];
}
}
@end
int main() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
ValidatingArray* validatingArray = [ValidatingArray validatingArray];
[validatingArray addObject:@"Object1"];
[validatingArray addObject:@"Object2"];
[validatingArray addObject:[NSNull null]];
[validatingArray removeObjectAtIndex:2];
NSString *aString = [validatingArray objectAtIndex:1];
NSLog(@"The value at Index 1 is %@",aString);
[pool drain];
return 0;
}
プログラムをコンパイルして実行すると、次の結果が得られます。
2013-09-28 22:03:54.294 demo[6247] The value at Index 1 is Object2
上記の例では、配列の1つの関数を検証しても、通常のシナリオではクラッシュにつながるnullオブジェクトを追加できないことがわかります。 しかし、検証配列がそれを処理します。 同様に、検証配列の各メソッドは、通常の操作シーケンスとは別に検証プロセスを追加します。