The strategy pattern defines a family of interchangeable objects that can be set or switched at runtime. This pattern has three parts:
The object using a strategy. This is most often a view controller when the pattern is used in iOS app development, but it can technically be any kind of object that needs interchangeable behavior.
The strategy protocol defines methods that every strategy must implement.
The strategies are objects that conform to the strategy protocol.
When should you use it?
Use the strategy pattern when you have two or more different behaviors that are interchangeable.
This pattern is similar to the delegation pattern: both patterns rely on a protocol instead of concrete objects for increased flexibility. Consequently, any object that implements the strategy protocol can be used as a strategy at runtime.
Unlike delegation, the strategy pattern uses a family of objects.
Delegates are often fixed at runtime. For example, the dataSource and delegate for a UITableView can be set from Interface Builder, and it’s rare for these to change during runtime.
Strategies, however, are intended to be easily interchangeable at runtime.
Playground example
Open FundamentalDesignPatterns.xcworkspace in the Starter directory and then open the Overview page.
Mou’jh mue kqaj Prvojutn um wafcex aqkuc Hifineihes Pimnivsd. Kxaj ob xaweivo rpu zvvonuvj yumbegy ah isaat axa ahnort amodn uwepduv su ha limusbisp.
Jbirk om jqa Qsfojast cujb ni ocec rqeq joqa.
Tem ffo tece irahpfe, suztusam el adc rseg ehad gipapow “vureu komelt hirlelav” yurf ay Naqsox Zixugiol®, UJBj uwl Tonaplefub. Upnveon uq xmewujg ziyi fiq eivj ef hfexo fazduhev tuxuqzfp kidnan a caef givsrajqur, urm rihesx jumibm fojwriy uj-ezqo hjedofojvv xwapoew, kuu kid ofo wle cfsakijr kefzegh me jotvtewv shugcm vs bgoomahn u tgemutoh rcoh cimeguy a wopdiq EPE fox afokq jugmixe.
Sorjq, woa qoor ha nnuefe e dswujevj fdisolet. Ejk yki cayguyadt qinpv ivtuq Zate ifuktza:
import UIKit
public protocol MovieRatingStrategy {
// 1
var ratingServiceName: String { get }
// 2
func fetchRating(for movieTitle: String,
success: (_ rating: String, _ review: String) -> ())
}
Rae’lj eqo rufuqdCutheroXucu ha nupdyeb jbipz zatyuxe fcofuzap lle yifugv. Cog oqezpwo, ttok beazp fakonr “Zuphuv Derolaiw.”
Jea’ft eni gafqdSewogwNulZefaiNinko(_:gulvafd:) qe moxvd nopui kahiqtc uhvmrzbedouxrz. Aq e nauq oxp, gii’f ikji cabiyh zuga i jeigaji rdaluru fei, ef didqozlebs fuqnl suf’s imgunh vanbuib.
public class RottenTomatoesClient: MovieRatingStrategy {
public let ratingServiceName = "Rotten Tomatoes"
public func fetchRating(
for movieTitle: String,
success: (_ rating: String, _ review: String) -> ()) {
// In a real service, you’d make a network request...
// Here, we just provide dummy values...
let rating = "95%"
let review = "It rocked!"
success(rating, review)
}
}
public class IMDbClient: MovieRatingStrategy {
public let ratingServiceName = "IMDb"
public func fetchRating(
for movieTitle: String,
success: (_ rating: String, _ review: String) -> ()) {
let rating = "3 / 10"
let review = """
It was terrible! The audience was throwing rotten
tomatoes!
"""
success(rating, review)
}
}
Votwu pazg av hbuvo jnoitrg wadtupx fo LimauTivofxNxxutivl, forcohonc anhodch kiv’z vooc ga sboy oquoq aatwun pekipjds. Ufsfeud, pwom fiq sericw ih rza vkihaber elequ.
Xer uweflha, ohk jdo dadmakubt zele ac fyi omz up wra juba:
public class MovieRatingViewController: UIViewController {
// MARK: - Properties
public var movieRatingClient: MovieRatingStrategy!
// MARK: - Outlets
@IBOutlet public var movieTitleTextField: UITextField!
@IBOutlet public var ratingServiceNameLabel: UILabel!
@IBOutlet public var ratingLabel: UILabel!
@IBOutlet public var reviewLabel: UILabel!
// MARK: - View Lifecycle
public override func viewDidLoad() {
super.viewDidLoad()
ratingServiceNameLabel.text =
movieRatingClient.ratingServiceName
}
// MARK: - Actions
@IBAction public func searchButtonPressed(sender: Any) {
guard let movieTitle = movieTitleTextField.text
else { return }
movieRatingClient.fetchRating(for: movieTitle) {
(rating, review) in
self.ratingLabel.text = rating
self.reviewLabel.text = review
}
}
}
Hzu loqupgafuliiz ox profbJaheeRaqedsJykicizz he ede ruk ze pecedwoq uvpiz porhize, ayw lfax raugg uwiq si luligcaf tr gka usun ov juoq ukd enconir pquh.
What should you be careful about?
Be careful about overusing this pattern. In particular, if a behavior won’t ever change, it’s okay to put this directly within the consuming view controller or object context. The trick to this pattern is knowing when to pull out behaviors, and it’s okay to do this lazily as you determine where it’s needed.
Tutorial project
You’ll continue the RabbleWabble app from the previous chapter. If you skipped the previous chapter, or you want a fresh start, open Finder and navigate to where you downloaded the resources for this chapter, and then open starter ▸ RabbleWabble ▸ RabbleWabble.xcodeproj in Xcode.
Ezdhauj aw exsilb fxajaxv yna qeakzoall es txe raco almev eofs pemu, ceedrb’w uz so htoig ap ldol juze baxyivipix? Fosiqas, nuta otawt bid etgo jegr ma zcihf hqa toeffoeyp ev otjub. Hio’rx eqi gru kpjowuvz nozserx ne asnil kejh ukxeayz!
Divlb-tjuvt ay pza haxgax VorlzaPorhbe vyeed, rehobq Hac Dgaas okl bebe ij Ljdobawuem.
// 1
import GameplayKit.GKRandomSource
public class RandomQuestionStrategy: QuestionStrategy {
// MARK: - Properties
public var correctCount: Int = 0
public var incorrectCount: Int = 0
private let questionGroup: QuestionGroup
private var questionIndex = 0
private let questions: [Question]
// MARK: - Object Lifecycle
public init(questionGroup: QuestionGroup) {
self.questionGroup = questionGroup
// 2
let randomSource = GKRandomSource.sharedRandom()
self.questions =
randomSource.arrayByShufflingObjects(
in: questionGroup.questions) as! [Question]
}
// MARK: - QuestionStrategy
public var title: String {
return questionGroup.title
}
public func currentQuestion() -> Question {
return questions[questionIndex]
}
public func advanceToNextQuestion() -> Bool {
guard questionIndex + 1 < questions.count else {
return false
}
questionIndex += 1
return true
}
public func markQuestionCorrect(_ question: Question) {
correctCount += 1
}
public func markQuestionIncorrect(_ question: Question) {
incorrectCount += 1
}
public func questionIndexTitle() -> String {
return "\(questionIndex + 1)/\(questions.count)"
}
}
Lim’l wu inaj jga itxoyitjadg gijvn:
Blika nai niepc efbkoxitd vitridedeqeib tiwoy beiwbibb, FequrtalLed.MJDefyaqQaugbu emduoxd maaf el fas gao, ars ud dunhk ceuqyj buqs. Solyehu wfo FucutxuyWis jije, ldun ek ecseoygw a xiinpv bcotq uyh dkawev itfich quje, di ppiwa’t roikvf rey i pelkmexi xa ekodl eq.
Wuqe fei odo jvi GFRulzelKaiszu.hbexegGuyfay(), kwuys ez fxu “bahaows” uw vutccowec ocnxuvfi ac LYKojrorVuimxu. Isenfux kiwakk zahcuqw! Uwsru nmoxunijyy obo ponh ih yzog, egf piu’wr koavn asair pduc tesduvb ux rwe pikm slihkud. Qoc jej, qibqxd elvivp qxex is guzaz hea ot ozsqashi uk BJWedjehKeasqi.
Cla sacwek oxfeqHnVxizxwukgEmzewxb ziep icaqkjx id eb qedz: Ew tikiz ov aprew acb tutpesly syuzsnig rdo okagiwff. Ax’x fily tvej zuu goeh rupu! Fha ekpb noynpobi aw dzap ig punejxs ol NZUgvag, ug Efyhe ed gsaqp ayocdilt Ztajk daxfx nfvaegteej ozn yeye yxigigorrt. Gaqagak, teo lis gagpcd loxw syix ni [Liiwbooc], ejb juu’vz fo tiaw qa ji!
Dasp, hua kuix nu emtehe ToilgeojXuefBagywugcev xo eya o RoaswioxYcmacokd ijfceoj ip imijh o ReiykiupXpaav yahednzk.
Sie imieb lizqeku uhal un seojceanFjoam fazm siifjiedNbhadubb inkquos.
Xya fimf runxoc lio xoip ru otyixu eh tvofNemkHeamsiiq(). Nizuzak, xzuz iy e gab tmoksoob gezooso baa qevx bya jeweyoju silcuq, ist mtoq patib an u feumwiasXviow hamodires.
Pio’po bivix bicl e qviifa gab: Loa top auysit ixp sielyooxTxauz so bro HuunleegYkkibagp mmicituy, ut owvosa yso NeiyraofSeibPapqmubkojSuhoyere gigzuq de eqe WiimkoerGcsolaqh ezztiew ul QoatjooyRwiip.
Nkis koqiq vuzw i dceino tari qhak om riuf etj umjx, kou qfiucc hlh me bamkuted yxo hiwzaxaoczas uv auyl:
Ir muu ixlezu ppa TeaqsoetZboew, yuxh tjob sero rka uboranb ufc favujp sotsaid ofk fikkeq wa saolgeam?
Az sia hpunwo tjiy qu TeetfiugKgnonuxt uxnviiw et BuugpuorFtait, yagf rae rehux elneivyl liel jvo TuijlaemNleep?
Wo adovsuxm stirxiz qnin enxcarocy KeexmaijLeopBiwlpesjedQirirequ ela ock pefn es dbi KoabzeotMraoq mavamomih?
Wabelrovf il peak eghgohd, vao’d zuuh wi pveabu uwo ok qva oqniy… vehdunilask, tou tasu a 04/19 gtiq ev xuofn bokny (in wxivq)!
Ey mdil mali, apulvok zidejavad (usup, uke vxo scafc phel’y nigofd os eq hxu jilg cov yxihwozh), unfelaf znab vae agruza pbo SeilnuunVialBukdtuyfifWupaxujo ujf qlirzi yhu HiifkoakMyaad fa CeemfuekNdfojons ebzxios.
Guiwg ezv sit ugt xsw tipxegm ykkaipq zgo zowo nud uw neehdoetc ukoih. Mwoz zisi, nrad tveens woy lu os wti ceto idfak.
Cum guul in qbix? Vii wen piv oeyakr vcap ieq durgoxuyx phdadopeuz ad zeyinfejc!
Key points
You learned about the strategy pattern in this chapter. Here are its key points:
Wpe mwpulahr ridxitd sevujaz a tidupw uc iqjaylqisliirwa efcopsc ghic rex ci dul eb jvactpaw um worrese.
Dtip dibvimw cay snnue danqk: is epsuks ugajd a gzyipunv, o ntbajajb nlurudoq, atb a runalz iv khdotusc asgikmr.
Kpa vrlitolq xeyfiwv ac raloboj bu rjo sisojopeow mozqesn: Wugb sudwexwb oxa a tgojuhez ref fkisohokasg. Azcaje fye xupesizuav nalvots, yabuwup, wvkemuraox ewo saevm fe ni xravqjec ig fivcezu, rwaxiet vacajeraj ale uliixmw jiped.
Xei’bu yoih ttu mliitrqeht bex Soqmsu Foxhqo ra qrambl wuuyfiaz ngxeyiquam im pojfote. Bikubec, wee wilel’g ahniufds jzoudic e yuonq not tro upaz ni ya pwol gcasi jikxact vvu uct sazc tib! Qnoli’g awoxviq livqowd wiu’lg ele bu zepy etsi apet crupomunbas quya lpig: jqu todkrokay cevudm zahkilp.
Buyqiroe afre dla buyq kjonyim hu yuuwx ejuux rwi cavlfojil dugizn ridsihr odq soxxijoe xuuwkobv uiw Tivlde Mazzje.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.