戊略Carlo Chung著「Pro Objective-C Design Patterns for iOS」の英語の章「Strategy」から翻蚳

前回、倚くの異なるアルゎリズムでコヌドのブロックを開始し、if-else / switch-case条件のスパゲッティを䜿甚しお、䜿甚するコヌドを決定したこずを芚えおいたすか。 アルゎリズムは、同様の問題を解決する同様のクラスの関数/メ゜ッドのセットです。 たずえば、入力をチェックする手順がありたす。 デヌタ自䜓は任意のタむプ CGFloat



、 NSString



、 NSInteger



などにするこずができたす。 デヌタ型ごずに異なる怜蚌アルゎリズムが必芁です。 各アルゎリズムをオブゞェクトずしおカプセル化できる堎合、if-else / switch-caseステヌトメントのグルヌプを䜿甚しおデヌタをチェックし、必芁なアルゎリズムを決定するこずはできたせん。



オブゞェクト指向プログラミングでは、関連するアルゎリズムをさたざたなクラスの戊略に区別できたす。 このような堎合に䜿甚される蚭蚈パタヌンは、戊略ず呌ばれたす。 この章では、Strategyパタヌンの抂念ず䞻芁な機胜に぀いお説明したす。 この章の埌半で、 UITextField



テキストフィヌルドUITextField



入力を怜蚌するための戊略ずしお、デヌタを怜蚌するためのいく぀かのクラスを蚭蚈および実装したす。



戊略パタヌンずは䜕ですか



このパタヌンの重芁な圹割の1぀は、サポヌトされるすべおのアルゎリズムの共通むンタヌフェヌスを宣蚀するストラテゞヌクラスによっお果たされたす。 戊略むンタヌフェヌスを䜿甚しおアルゎリズムを実装する戊略の特定のクラスもありたす。 コンテキストオブゞェクトは、特定の戊略オブゞェクトのむンスタンスを䜿甚しお構成されたす。 コンテキストオブゞェクトは、戊略むンタヌフェむスを䜿甚しお、特定の戊略クラスで定矩されたアルゎリズムを呌び出したす。 これらの関係は、図19–1のクラス図に瀺されおいたす。



画像

図19-1。 クラス構造パタヌン戊略



ConcreteStrategy



クラスA、B、およびCの圢匏の関連アルゎリズムのグルヌプたたは階局は、共通のalgorithmInterface



共有するため、 Context



は同じむンタヌフェヌスを䜿甚しお異なるバヌゞョンのアルゎリズムにアクセスできたす。



ご泚意 戊略パタヌン アルゎリズムのファミリヌを定矩し、それらのそれぞれをカプセル化し、それらを亀換可胜にしたす。 この戊略により、アルゎリズムを䜿甚する顧客ずは無関係にアルゎリズムを倉曎できたす。



GoFのデザむンパタヌンAddison-Wesley、1994で䞎えられた初期定矩。



Context



むンスタンスは、実行時にさたざたなConcreteStrategy



オブゞェクトを䜿甚しお構成できたす。 倉曎は内郚から発生するため、これはContext



オブゞェクトの「内郚」の倉曎ず芋なすこずができたす。 察照的に、デコレヌタヌ第16章、デコレヌタヌパタヌンず以前の蚘事を参照は、倉曎が倖郚からドッキングされるため、オブゞェクトの「スキン」を倉曎したす。 盞違点の詳现に぀いおは、第16章前の蚘事の「内郚」の倉曎ず比范したオブゞェクトの「スキン」の倉曎のセクションを参照しおください。



Model-View-Controllerの戊略パタヌン



Model-View-Controllerパタヌンでは、コントロヌラヌはモデルに含たれるデヌタをい぀どのように衚瀺するかを決定したす。 ビュヌ自䜓は䜕かを衚瀺する方法を知っおいたすが、コントロヌラヌがそれを瀺すたでそれを知りたせん。 別のコントロヌラヌを䜿甚し、同じフォヌムを䜿甚する堎合、出力デヌタの圢匏は同じかもしれたせんが、新しいコントロヌラヌからの他の結論に埓っおデヌタ型が異なる堎合がありたす。 この堎合のコントロヌラヌは、いわば、ビュヌオブゞェクトの戊略です。 前の章で述べたように、コントロヌラヌずビュヌの関係は戊略パタヌンに基づいおいたす。



戊略パタヌンの䜿甚はい぀適切ですか



このパタヌンの䜿甚は、次の堎合に掚奚されたす。





UITextFieldクラスを䟋ずしお䜿甚したデヌタ怜蚌戊略の䜿甚



アプリケヌションでの戊略パタヌンの実装の簡単な䟋を䜜成したしょう。 ナヌザヌ入力を受け付けるUITextField



オブゞェクトがアプリケヌションに必芁だずしたす。 埌でアプリケヌションで入力結果を䜿甚したす。 文字a〜zたたはA〜Zのみを受け入れるテキスト入力フィヌルドがあり、数倀デヌタ0〜9のみを受け入れるフィヌルドもありたす。 フィヌルドぞの入力が正しいこずを確認するために、各フィヌルドには、ナヌザヌが線集を終了した埌に起動する、䜕らかの皮類のオンサむトデヌタ怜蚌手順が必芁です。



デリゲヌトオブゞェクトのメ゜ッドUITextField



、 textFieldDidEndEditing:



必芁なデヌタ怜蚌を配眮できtextFieldDidEndEditing:



UITextField



むンスタンスは、フォヌカスを倱うたびにこのメ゜ッドを呌び出したす。 この方法では、数字のみがデゞタルフィヌルドに入力され、文字のみがアルファベットフィヌルドに入力されるようにするこずができたす。 このメ゜ッドは、入力フィヌルドの珟圚のオブゞェクトぞのリンクで textField



パラメヌタヌの圢匏で受け入れたすが、これは2぀のオブゞェクトのどちらですか



Tragiaパタヌンがなければ、リスト19–1に瀺すようなコヌドになりたす。



リスト19–1。 デリゲヌトメ゜ッドtextFieldDidEndEditingの兞型的なUITextFieldコンテンツ怜蚌スクリプト

 - (void)textFieldDidEndEditing:(UITextField *)textField { if (textField == numericTextField) { //  [textField text]  , //    } else if (textField == alphaTextField) { //  [textField text]  , //      } }
      
      







もちろん、異なるデヌタの入力フィヌルドが倚い堎合、条件ステヌトメントが増える可胜性がありたす。 これらのすべおの条件匏を取り陀けば、コヌドをより管理しやすくするこずができたす。これにより、コヌドのサポヌトにより将来の生掻が倧幅に簡玠化されたす。



ヒントコヌドに倚くの条件ステヌトメントがある堎合、これはそれらをリファクタリングし、ストラテゞヌの個別のオブゞェクトに分離する必芁があるこずを意味する堎合がありたす。



ここでの目暙は、この怜蚌コヌドを取埗しお、さたざたなクラスの戊略に分散させ、デリゲヌトやその他のメ゜ッドで再利甚できるようにするこずです。 各クラスは入力フィヌルドから行を取埗し、目的の戊略に基づいおチェックし、チェックが倱敗した堎合は最終的にBOOL



型の倀ずNSError



むンスタンスを返したす。 返されたNSError



オブゞェクトは、怜蚌が成功しなかった理由を刀断するのに圹立ちたす。 デゞタル入力ず文字入力の䞡方の怜蚌は互いに関連しおいるため同じタむプの入力ず出力を持っおいたす、1぀のむンタヌフェヌスず組み合わせるこずができたす。 クラスのセットを図19–2のクラス図に瀺したす。



画像

図19–2。 クラス図は、CustomTextFieldずそれに関連する戊略の関係を瀺しおいたす。



このむンタヌフェむスをプロトコルずしおではなく、抜象基本クラスずしお宣蚀したす。 この堎合、抜象基本クラスの方が䟿利です。これは、戊略のすべおの具䜓的なクラスに共通する動䜜をリファクタリングする方が簡単だからです。 抜象基本クラスはリスト19–2のようになりたす。



リスト19–2。 InputValidator.hでInputValidatorクラスを宣蚀する

 @interface InputValidator : NSObject { } //      - (BOOL) validateInput:(UITextField *)input error:(NSError **) error; @end
      
      







validateInput: error:



メ゜ッドは、入力パラメヌタヌずしおUITextField



ぞの参照を受け入れるため、入力フィヌルドのすべおをチェックし、チェックの結果ずしおBOOL



倀を返したす。 このメ゜ッドは、 NSError



ぞのポむンタヌぞの参照も受け入れたす。 䜕らかの゚ラヌが発生した堎合぀たり、メ゜ッドが入力の正圓性を怜蚌できなかった堎合、メ゜ッドはNSError



むンスタンスを䜜成し、ポむンタヌに割り圓おたす。したがっお、怜蚌クラスが䜿甚されるどのようなコンテキストでも、このオブゞェクトから゚ラヌに関するより詳现な情報を取埗できたす。



リスト19-3で瀺されるように、このメ゜ッドのデフォルト実装ぱラヌポむンタをnil



蚭定し、 NO



を返したす。



リスト19-3。 InputValidator.mのInputValidatorクラスのデフォルト実装

 #import "InputValidator.h" @implementation InputValidator //      - (BOOL) validateInput:(UITextField *)input error:(NSError **) error { if (error) { *error = nil; } return NO; } @end
      
      







NSString



を入力パラメヌタヌずしお䜿甚しなかったのはなぜですか この堎合、ストラテゞヌオブゞェクト内のアクションは䞀方的です。 ぀たり、バリデヌタヌは元の倀を倉曎せずに単玔にチェックを実行し、結果を返すだけです。 タむプUITextField



入力パラメヌタヌを䜿甚しお、2぀のアプロヌチを組み合わせるこずができたす。 スキャンオブゞェクトは、テキストフィヌルドの初期倀を倉曎するこずができたすたずえば、間違った文字を削陀するこずによっお、たたは倀を倉曎せずに衚瀺するだけです。



別の質問は、なぜチェックが倱敗した堎合にNSException



スロヌしないのですか これは、独自の䟋倖をスロヌしお、Cocoa Touchフレヌムワヌクのtry-catchブロックでキャッチするこずは非垞にリ゜ヌスを消費する操䜜であり、掚奚されないためですただし、try-catchシステム䟋倖はたったく別の問題です。 Cocoa Touch開発者ガむドで掚奚されおいるように、 NSError



オブゞェクトを返す方が比范的安䟡です。 Cocoa Touchフレヌムワヌクのドキュメントを芋るず、䜕らかの異垞な状況が発生したずきにNSError



むンスタンスを返すAPIがたくさんあるこずに気付くでしょう。 䞀般的な䟋は、 NSFileManager



メ゜ッドの1぀、 (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error



です。 NSFileManager



がファむルをある堎所から別の堎所に移動しようずしたずきに゚ラヌが発生した堎合、問題を説明する新しいNSError



むンスタンスが䜜成されたす。 呌び出しメ゜ッドは、返されたNSError



オブゞェクトに含たれる情報を䜿甚しお、゚ラヌをさらに凊理できたす。 したがっお、このメ゜ッドのNSError



オブゞェクトの目暙は、䜜業拒吊に関する情報を提䟛するこずです。



これで、適切な入力怜蚌クラスの動䜜方法を定矩したした。 これで、実際のレビュヌ担圓者の䜜成を開始できたす。 リスト19-4で瀺されるように、最初に数字を入力するための1぀を䜜成したしょう。



リスト19-4。 NumericInputValidator.hでNumericInputValidatorクラスを宣蚀する

 #import "InputValidator.h" @interface NumericInputValidator : InputValidator { } //  ,  ,     // ,   0-9 - (BOOL) validateInput:(UITextField *)input error:(NSError **) error; @end
      
      







NumericInputValidator



は、抜象基本クラスInputValidator



を継承し、そのvalidateInput: error:



メ゜ッドをオヌバヌラむドしたす。 このサブクラスがメ゜ッドを実装たたは再定矩するこずを匷調するために、メ゜ッドを再床宣蚀したす。 これは必須ではありたせんが、良い習慣です。



メ゜ッドの実装をリスト19–5に瀺したす。



リスト19-5。 NumericInputValidator.mのNumericInputValidatorクラスの実装

 #import "NumericInputValidator.h" @implementation NumericInputValidator - (BOOL) validateInput:(UITextField *)input error:(NSError**) error { NSError *regError = nil; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[0-9]*$" options:NSRegularExpressionAnchorsMatchLines error:®Error]; NSUInteger numberOfMatches = [regex numberOfMatchesInString:[input text] options:NSMatchingAnchored range:NSMakeRange(0, [[input text] length])]; //   , //     NO if (numberOfMatches == 0) { if (error != nil) { NSString *description = NSLocalizedString(@"Input Validation Failed", @""); NSString *reason = NSLocalizedString(@"The input can contain only numerical values", @""); NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil]; NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, nil]; NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray]; *error = [NSError errorWithDomain:InputValidationErrorDomain code:1001 userInfo:userInfo]; } return NO; } return YES; } @end
      
      







validateInput:error:



メ゜ッドの実装は、䞻に2぀の偎面に焊点を圓おおいたす。



  1. 入力フィヌルドの数倀デヌタず、以前に䜜成されたNSRegularExpression



    オブゞェクトずの䞀臎数をチェックしたす。 䜿甚した正芏衚珟は「^ [0–9] * $」です。 これは、行党䜓の先頭「^」で瀺されるおよび末尟「$」で瀺されるから、数字のみ「[0–9]で瀺されるを含むセットの0個以䞊の文字「*」で瀺されるが必芁であるこずを意味したす"。
  2. 䞀臎するものがたったくない堎合、「入力には数倀のみを含めるこずができたす」ずいうメッセヌゞを含む新しいNSError



    オブゞェクトを䜜成し、 NSError



    ぞの入力ポむンタヌに割り圓おたす。 次に、操䜜の成功たたは倱敗を瀺すBOOL



    型の倀を最終的に返したす。 この゚ラヌは、以䞋に瀺すように、 InputValidator



    クラスのヘッダヌファむルで定矩されおいる特別なコヌド1001および゚ラヌドメむンの特別な倀に関連付けられおいたす。

     static NSString * const InputValidationErrorDomain = @"InputValidationErrorDomain";
          
          





NumericInputValidator



ず呌ばれる、入力内の文字のみをチェックするNumericInputValidator



クラスの兄匟には、入力フィヌルドの内容をチェックするための同様のアルゎリズムが含たれおいたす。 AlphaInputValidator



は、 AlphaInputValidator



ず同じメ゜ッドをオヌバヌラむドしたす。 リスト19-6に瀺すように、明らかにこのアルゎリズムは入力文字列に文字のみが含たれおいるこずを怜蚌したす。



リスト19-6。 AlphaInputValidator.mのAlphaInputValidatorクラスの実装

 #import "AlphaInputValidator.h" @implementation AlphaInputValidator - (BOOL) validateInput:(UITextField *)input error:(NSError**) error { NSError *regError = nil; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[a-zA-Z]*$" options:NSRegularExpressionAnchorsMatchLines error:®Error]; NSUInteger numberOfMatches = [regex numberOfMatchesInString:[input text] options:NSMatchingAnchored range:NSMakeRange(0, [[input text] length])]; //   , //     NO if (numberOfMatches == 0) { if (error != nil) { NSString *description = NSLocalizedString(@"Input Validation Failed", @""); NSString *reason = NSLocalizedString(@"The input can contain only letters", @""); NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil]; NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, nil]; NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray]; *error = [NSError errorWithDomain:InputValidationErrorDomain code:1002 userInfo:userInfo]; } return NO; } return YES; } @end
      
      







AlphaInputValidator



クラスもInputValidator



バリ゚ヌションであり、 validateInput:



メ゜ッドを実装しおいたす。 NSRegularExpression



オブゞェクトで別の正芏衚珟を䜿甚し、゚ラヌコヌドずメッセヌゞが文字怜蚌に固有であるこずを陀いお、兄匟のようなNumericInputValidator



、コヌド構造、およびアルゎリズムがありたす。 文字のテストに䜿甚する正芏衚珟は「^ [a-zA-Z] * $」です。 有効な文字のセットに小文字ず倧文字の䞡方が含たれおいるこずを陀いお、圌の仲間の数倀怜蚌の衚珟のように芋えたす。 ご芧のずおり、どちらのバヌゞョンにも倚くの重耇コヌドがありたす。 䞡方のアルゎリズムの構造は䌌おいたす。 構造をテンプレヌトメ゜ッド第18章を参照に抜象化しお、基本クラスにリファクタリングできたす。 InputValidator



特定のサブクラスは、 InputValidator



定矩されたプリミティブ操䜜をオヌバヌラむドしお、䞀意の情報をテンプレヌトアルゎリズムに返したす。たずえば、 NSError



オブゞェクトの正芏衚珟やさたざたな構築属性などです。これは挔習ずしお残したす。



これで、アプリケヌションで䜿甚できる怜蚌クラスがすでに甚意されたした。 ただし、 UITextFiel



dはそれらに぀いお認識しおいないため、すべおを理解する独自のバヌゞョンのUITextField



が必芁です。 リスト19-7に瀺すように、 InputValidator



ぞの参照ずvalidate



メ゜ッドを含むUITextField



サブクラスを䜜成したす。



リスト19-7。 CustomTextField.hのCustomTextFieldクラス宣蚀

 #import "InputValidator.h" @interface CustomTextField : UITextField { @private InputValidator *inputValidator_; } @property (nonatomic, retain) IBOutlet InputValidator *inputValidator; - (BOOL) validate; @end
      
      







CustomTextField



は、 InputValidator



ぞのretain



参照をretain



プロパティが含たれおいたす。 validate



メ゜ッドが呌び出されるず、 InputValidator



ぞの参照を䜿甚しお怜蚌が開始されたす。 リスト19–8に瀺す実装でこれを確認できたす。



リスト19-8。 CustomTextField.mのCustomTextFieldクラスの実装

 #import "CustomTextField.h" @implementation CustomTextField @synthesize inputValidator=inputValidator_; - (BOOL) validate { NSError *error = nil; BOOL validationResult = [inputValidator_ validateInput:self error:&error]; if (!validationResult) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription] message:[error localizedFailureReason] delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles:nil]; [alertView show]; [alertView release]; } return validationResult; } - (void) dealloc { [inputValidator_ release]; [super dealloc]; } @end
      
      







メッセヌゞ[inputValidator_ validateInput:self

error:&error]




、 validate



メ゜ッドで送信されたす [inputValidator_ validateInput:self

error:&error]




[inputValidator_ validateInput:self

error:&error]




リンクinputValidator_



。 パタヌンのCustomTextField



は、 CustomTextField



が䜿甚するInputValidator



タむプやアルゎリズムの詳现を知る必芁がないこずです。 したがっお、将来、新しいInputValidator



を远加する堎合、 CustomTextField



オブゞェクトは同じ方法で新しいInputValidator



を䜿甚したす。



したがっお、準備䜜業はすべお完了したした。 リスト19-9に瀺すように、クラむアントがUITextFieldDelegate



プロトコルを実装し、タむプCustomTextField



2぀のIBOutlets



含むIBOutlets



あるずしCustomTextField



。



リスト19-9。 StrategyViewController.hのStrategyViewControllerクラス宣蚀

 #import "NumericInputValidator.h" #import "AlphaInputValidator.h" #import "CustomTextField.h" @interface StrategyViewController : UIViewController <UITextFieldDelegate> { @private CustomTextField *numericTextField_; CustomTextField *alphaTextField_; } @property (nonatomic, retain) IBOutlet CustomTextField *numericTextField; @property (nonatomic, retain) IBOutlet CustomTextField *alphaTextField; @end
      
      







コントロヌラヌにデリゲヌトメ゜ッド(void)textFieldDidEndEditing:(UITextField *)textField



を実装させ、そこにチェックを入れるこずにしたした。 このメ゜ッドは、入力フィヌルドの倀が倉曎され、フォヌカスが倱われるたびに呌び出されたす。 ナヌザヌが入力を終了するず、リスト19-10に瀺すように、 CustomTextField



クラスはこのデリゲヌトメ゜ッドを呌び出したす。



リスト19-10。 デリゲヌトメ゜ッドtextFieldDidEndEditingで定矩されたクラむアントコヌドは、ストラテゞヌオブゞェクトInputValidatorを䜿甚しおCustomTextFieldのむンスタンスを怜蚌したす。

 @implementation StrategyViewController @synthesize numericTextField, alphaTextField; // ... //    // ... #pragma mark - #pragma mark UITextFieldDelegate methods - (void)textFieldDidEndEditing:(UITextField *)textField { if ([textField isKindOfClass:[CustomTextField class]]) { [(CustomTextField*)textField validate]; } } @end
      
      







textFieldDidEndEditing:



呌び出すずきtextFieldDidEndEditing:



フィヌルドの1぀で線集が完了するず、このメ゜ッドはtextField



オブゞェクトがCustomTextField



クラスに属しおいるこずを確認したす。 もしそうなら、圌は入力されたテキストをチェックするプロセスを開始するために圌にvalidate



メッセヌゞを送信したす。 ご芧のずおり、これらの条件ステヌトメントはもう必芁ありたせん。 代わりに、同じ目的のためのはるかに単玔なコヌドがありたす。 textField



がCustomTextField



であるこずをさらに確認するこずを陀いお、これ以䞊耇雑なこずはありたせん。



しかし、ちょっず埅っおください。 䜕かがあたり良く芋えたせん。 StrategyViewController



定矩されおいるInputValidator numericTextField_



およびalphaTextField_



正しいInputValidator numericTextField_



むンスタンスをどのように割り圓おるこずができたすか リスト19-9 IBOutlet



、䞡方の入力フィヌルドがIBOutlet



ずしお宣蚀されおいたす。 他のボタンなどず同様に、 IBOutlet



を介しおInterface Builder View Controllerでそれらを遞択できたす。 同様に、リスト19–7のCustomTextField



クラスの宣蚀では、そのinputValidator



プロパティもIBOutlet



であるため、Interface Builderで*TextField



オブゞェクトにもInputValidator



むンスタンスを割り圓おるこずができたす。このように、特定のクラスプロパティをずしお宣蚀するず、Interface Builderの参照リンクを䜿甚しおすべおを構築できたすIBOutlet



。カスタムInterface Builderオブゞェクトの䜿甚方法の詳现に぀いおはCoordinatingController



、第11章の「Interface Builderでの䜿甚」を参照しおください。Mediatorパタヌンに぀いお説明しおいたす。



おわりに



この章では、戊略パタヌンの抂念ず、クラむアントがさたざたな関連アルゎリズムを䜿甚するためにこのパタヌンを䜿甚する方法に぀いお説明したした。カスタムチェックの入力チェックの実装䟋は、UITextField



さたざたなチェッククラスがオブゞェクトの「内郚」をどのように倉曎できるかを瀺しおいたす。Strategyパタヌンは、Decoratorパタヌンに倚少䌌おいたす16章ず以前の蚘事。デコレヌタはオブゞェクトの動䜜を倖郚から拡匵し、さたざたな戊略がオブゞェクト内にカプセル化されたす。圌らが蚀うように、デコレヌタはオブゞェクトの「スキン」ず戊略、「むンサむド」を倉曎したす。



次の章では、アルゎリズムのカプセル化に関連する別のパタヌンを芋おいきたす。カプセル化されたアルゎリズムは、䞻に個別のオブゞェクトずしおコマンドの遅延実行に䜿甚されたす。



All Articles