光学薄膜設計ソフトの設計 その23 - 具体的な実装例のやりなおし [考え中 - 光学薄膜設計]
実装を始めると、さっさとやらないとすぐ忘れてしまって、なにをやっているのかわからなくなる。集中してやろう。
前回、薄膜の光学特性の計算のありがちなパターンを利用して計算量を減らすために、同じ計算結果を返すオブジェクトをオブジェクトの実体が自動的に同じになるようなメカニズムを考えた。それを具体的に実装してみる。
継承の深さについて
実装に入る前に、継承関係をもう一度書き直す。
ここまでの実装は継承を多用して親子関係が深くなっている。これはあまりObjective-Cのスタイルではない。C++では比較的小さなクラスを、継承を駆使して積み重ねて必要な機能を実現することが多いが、Objective-Cではあまり深い継承関係は歓迎されない。
それはC++はルートクラスを持たず、オブジェクトは専用のメソッドを内蔵する構造体のような軽いものなのに対して、Objective-Cの場合は、すべてのクラス(NSProxyを例外として)がNSObjectというルートクラスのサブクラスであり、NSObjectがいろいろな機能を持つ巨大な抽象クラスだという違いが大きい。
さらにC++は多重継承の機能があるが、Objective-Cは単一継承しかできない、ということもある。C++の場合、深い継承によって特殊化しても別の抽象クラスを継承することでクラスとてしての自由度を増やすことができるが、Objective-Cでは継承が深くなると蛸壺化してしまう。そのかわりObjective-Cにはprotocolという継承関係によらない機能インターフェイスの表現ができるのと、カテゴリと言う機能追加のメカニズムがあるのでこれらに慣れれば多重継承の必要性はほとんど感じなくなる。
また、Objective-Cのスタイルとして効率や精密なアクセス制御よりもシンプルさを重視しているということもある。継承が深くなると祖先の誰が定義したメソッドなのかもわかりにくくなる。気がつかないで上書きしていて動作がおかしくなる、というようなわかりにくいバグを生むことにもなる。
例えば具体的なクラスはOTFDependedVariableを親クラスにすることになるが、その祖先の3つのクラスまで頭に入れて実装しなければいけない。
OTFPrimerなどはプロトコルにして継承を浅く、シンプルにした方がいいだろう。でももうここまでやったのでとりあえずこのまま進めてみることにする。
具体的な従変数の実装
こないだやった屈折率の場合の実装をやりなおしてみる。
@interface OTFDielectricModelRefractiveIndex : OTFDependedVariable { NSString *modelName; OTFVariable *lambda; const sellmeierCoefficients *coefficient; } + (NSArray *)modelNames; - (id)initWithModelName:(NSString *)mName withLambdaVariable:(OTFVariable *)lambdaVar; @endOTFDependedVariableを継承しているが、これはさっきのOTFMergableのサブクラスとなっている。そして前の実装では捨てていた材料の名前を保持できるようにする。あとは同じである。
implementationの中にいくつか追加する。 まず、クラス変数とクラスメソッド
static NSMutableSet *allocatedObjects = nil; @implementation OTFDielectricModelRefractiveIndex + (void)initialize { if (!allocatedObjects) allocatedObjects = [[NSMutableSet alloc] initWithCapacity:0]; } + (NSMutableSet *)allocatedObjects { return allocatedObjects; }これはOTFMergableと全く同じであるが、OTFDielectricModelRefractiveIndex専用のNSMutableSetができる。こうやってクラスごとのNSMutableSetを作っておけば、同一性チェックは速くなるのと、メソッドに応答するかどうかのチェックをしなくてもすむ。
そしてinit...の最後でmergeメソッドを呼ぶようにする。
- (id)initWithModelName:(NSString *)mName withLambdaVariable:(OTFVariable *)lambdaVar; { unsigned int idx; self = [super init]; modelName = [mName retain]; lambda = [super setDependOn:lambdaVar]; if ((idx = [modelNames indexOfObject:mName]) == NSNotFound) { [self release]; return nil; } coefficient = coefficients[idx]; return [self merge]; }このmergeメソッドはOTFMergableのものが呼ばれる。このmergeメソッドの中でOTFDielectricModelRefractiveIndex専用のNSMutableSetの中で同じものがあればそちらが返って自分は解放され、なければNSMutableSetに登録されて自分自身が返ることになる。 最後に
- (BOOL)isMergableTo:(id)obj { if ([modelName isEqualToString:[obj modelName]] && (lambda == [obj lambdaVariable])) return YES; else return NO; }とする。つまり、同じ材料で同じ波長変数を参照しているときは同じと見なす。
これでOK、のはず。コンパイラさえ通してないので、実際にコーディングを始めると破綻するかもしれないけど。やっぱりmergeメソッドがかっこわるい。でもこれだと簡単にすますことができる。
コメント 0