戊略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章ず以前の蚘事。デコレヌタはオブゞェクトの動䜜を倖郚から拡匵し、さたざたな戊略がオブゞェクト内にカプセル化されたす。圌らが蚀うように、デコレヌタはオブゞェクトの「スキン」ず戊略、「むンサむド」を倉曎したす。

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

Source: https://habr.com/ru/post/J222355/


All Articles