Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently fromclients that use it..

Structure

Class diagram (UML):

Strategy Class Diagram (UML)

When Would You Use It?

  • many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors.
  • you need different variants of an algorithm. For example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms [HO87].
  • an algorithm uses data that clients shouldn’t know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.
  • a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class.

References

  • Design Patterns: Elements of Reusable Object-Oriented Software, by Book by Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm (GoF)
  • Pro Objective-C Design Patterns for iOS, by Carlo Chung
  • Pro Design Patterns in Swift, by Adam Freeman

Example #1 – Text Validator

  • When user enters a text, it needs certain validation method to validate its format.
  • The validator may have different types like “checking if it is numeric”, “checking if it is email”, etc.
  • Abtract all of these validators to a general “Validator Strategy”

Here is the class diagram:

Strategy Class Diagram (UML) - Text Validator

Code

import UIKit

let REGEX_PATTERN_NUMERIC = "[0-9]+$"
let REGEX_PATTERN_EMAIL = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9._%+-]+.[a-zA-Z0-9._%+-]{2,4}"

// Class Context
class TextField {

    // property
    var text: String
    var textValidator: InputValidator?

    // initializer
    init(text: String) {

        self.text = text
    }

    init(text: String, validator: InputValidator) {

        self.text = text
        self.textValidator = validator
    }

    // Context Interface
    func validate() -> Bool {

        var error: NSError?
        if let result = textValidator?.validateInput(text, error: &error) {
            return result
        }
        return false
    }
}


// Protocol Strategy
protocol InputValidator {
    func validateInput(text: String, inout error: NSError?) -> Bool
}


// Class ConcreteStrategy
class NumericInputValidator: InputValidator {

    func validateInput(text: String, inout error: NSError?) -> Bool {

        // use NSRegularExpression to do regex match
        let regexPattern = REGEX_PATTERN_NUMERIC
        let regex = NSRegularExpression(pattern: regexPattern, options: nil, error: &error)
        let range = NSMakeRange(0, count(text))
        let numberOfMatches = regex?.numberOfMatchesInString(text, options: nil, range: range)

        // check the matches
        if numberOfMatches == 0 {

            // check the error
            if let e = error {
                println("Input validation failed withe error: (e.localizedDescription).")
            }
            println("Input validation failed: "(text)" is NOT all numeric!")
            return false
        }
        println("Input validation successful: "(text)" is all numeric!")
        return true
    }
}

class EmailInputValidator: InputValidator {

    func validateInput(text: String, inout error: NSError?) -> Bool {

        // use NSRegularExpression to do regex match
        let regexPattern = REGEX_PATTERN_EMAIL
        let regex = NSRegularExpression(pattern: regexPattern, options: nil, error: &error)
        let range = NSMakeRange(0, count(text))
        let numberOfMatches = regex?.numberOfMatchesInString(text, options: nil, range: range)

        // check the matches
        if numberOfMatches == 0 {

            // check the error
            if let e = error {
                println("Input validation failed withe error: (e.localizedDescription).")
            }
            println("Input validation failed: "(text)" is NOT a valid email adress!")
            return false
        }
        println("Input validation successful: "(text)" is a valid email address!")
        return true
    }
}


// Usage
let textFieldNumeric = TextField(text: "12345678", validator: NumericInputValidator())
textFieldNumeric.validate()

let textFieldEmail = TextField(text: "foxshee@gmail.com", validator: EmailInputValidator())
textFieldEmail.validate()

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>