Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Start a free Basic trial
to watch this video
To understand the problem delegation solves we're going to work through an example with the knowledge we have at hand. Once things start to get unwieldy, we'll look at what delegation brings to the table.
Code Snippets
-
0:00
Let's say that we've been tasked with designing a very simple horse racing game
-
0:05
to keep a friend entertained, and this is what we came up with.
-
0:08
Let's walk through the code.
-
0:10
At the top, we have a class Horse that represents a racer in our game.
-
0:16
As an aside, you can get all this code from
-
0:18
the code snippet linked in the teacher's notes with this video.
-
0:22
A horse has a name and a max speed.
-
0:26
We initialize a horse with these properties, a name and a max speed.
-
0:31
A horse does not run at their top speed for
-
0:33
the entire race, at least, I don't think they do.
-
0:36
Honestly, I have absolutely no idea how any of this works.
-
0:40
Anyway, because we're assuming they don't, we're going to vary their speed during
-
0:44
the race randomly by querying a currentSpeed computed property.
-
0:51
This returns a random value between a lower bound,
-
0:54
which I have set to be 13 meters per second, and the horse's maximum speed.
-
1:00
Very scientific, I know.
-
1:02
And to do that, we're using the arc4random uniform function to generate a random
-
1:06
number and then getting a value between the lower bound and that upper range,
-
1:11
which is the max speed.
-
1:13
In this naive implementation of the game, a horse keeps track of its progress
-
1:18
through a race in terms of the distance traveled per lap and which lap it is on.
-
1:25
Below that, we have a class called race.
-
1:28
A race is initialized with a number of laps and a set of participants.
-
1:33
So if you go all the way to the bottom, you'll see that we've defined a set of
-
1:37
participants with some speed values in meters per second.
-
1:41
We've put those in an array to define our participants and
-
1:44
then we've initialized a race.
-
1:47
A race also stores, if you look up at the top, a value for the length of each lap,
-
1:52
which for simplicity's sake, we've kept fixed at 300 meters.
-
1:57
The main logic of the race works around this timer.
-
2:01
So I've defined a timer over here that repeats every second and
-
2:06
calls the updateProgress method, which you see down here.
-
2:12
To start the race, we call the start method.
-
2:15
For now, that method is at the bottom, but it's commented out.
-
2:18
So I don't wanna start the race just yet.
-
2:21
When we call the start method,
-
2:23
this simply starts the timer by adding the timer to this program's run loop.
-
2:28
Ignore all that technicality for now, but
-
2:30
to get all this to work because we're using a timer and some complicated stuff.
-
2:35
We have to ask the playground page to run indefinitely,
-
2:38
rather than the one time execution that we're used to.
-
2:42
To do this, I have imported a framework up at the top.
-
2:46
Or I need to, rather.
-
2:47
So I'm going to import PlaygroundSupport.
-
2:52
And then we're gonna set a property on the current page to allow it to
-
2:54
continue indefinitely.
-
2:56
So we'll say PlaygroundPage.current.needsIndefiniteExe-
-
3:01
cution = true.
-
3:03
Okay, so now rather than normal, when the playground page
-
3:08
just executes top to bottom, this keeps the process running in the background.
-
3:13
The race logic is quite simple.
-
3:15
It's actually very simple and is housed in the updateProgress method.
-
3:20
So here, we iterate through all the participants in the race.
-
3:25
We increase the amount of distance traveled by the current speed.
-
3:30
Because our distance is in meters and our current speed in meters per second.
-
3:34
Since the updateProgress function is called every second by our timer,
-
3:38
this is an easy way to get the current distance traveled right there.
-
3:45
Note that we're using the current speed to do this,
-
3:47
which means that we'll get random values every time.
-
3:49
Now, the values won't vary by much.
-
3:52
But hopefully, that should make our races exciting.
-
3:55
After a horse has traveled a distance that's equal to the length of a lap,
-
4:01
we set the distance back to 0 and increase the lap count on that horse.
-
4:06
If any of the horses hit the max lap limit, the first one to do so wins.
-
4:11
And we immediately stop the race and break out of this for loop.
-
4:15
Okay, and then the stop method just invalidates that timer, so
-
4:19
the race is over.
-
4:21
And at the bottom, as mentioned earlier, we have a few players and
-
4:24
there's a race set up.
-
4:25
So let's see how this works.
-
4:26
If we uncomment the call to the start method, you'll notice in the console, once
-
4:32
this starts executing, that the race goes into progress, it's going and it's going.
-
4:39
And then after a while, this should stop.
-
4:41
Remember it's going in seconds, and there's 300 meters to cover.
-
4:45
So this is fun, but none of this is core to our problem.
-
4:49
The issue is that once a race is over, we don't really know anything.
-
4:53
We don't know who won the race, so all we see is it says race complete.
-
4:57
We don't know which horse was in lead at any particular time,
-
5:00
we don't know who finished lap one first and so on.
-
5:03
And we'd love to review these stats, so we can perform better in the next race.
-
5:07
So let's introduce a new class called tracker to handle this.
-
5:12
Okay, so I'm going to paste this code in above Race, and remember,
-
5:17
you can grab this code as well from the code snippets linked below.
-
5:22
So we've started here with a really naive, so to speak, implementation, and
-
5:26
we're just going to store all our data as key value pairs in this stats dictionary.
-
5:31
For now, the Tracker class just has a series of methods for
-
5:35
different data points that we want to capture.
-
5:37
And it logs these data points back to that dictionary using relevant keys.
-
5:41
So here, you see the updateRaceStart method that takes a time value,
-
5:46
and inside, we update the value on the stats dictionary.
-
5:50
And we use a set of keys that we've defined in the Tracker class as a private
-
5:54
struct, or a nested struct, rather.
-
5:58
Okay, so similarly, we update the LapLeader,
-
6:00
update the RaceEnd time with the winner, and so on.
-
6:03
And then we have a method to print the summary of the race.
-
6:08
To use this tracker, one way we can go about it is to create an instance
-
6:12
of the tracker as a stored property in the Race class.
-
6:15
So inside Race, we can say let tracker = Tracker.
-
6:20
Now let's log a few events.
-
6:22
So when we call the start method on the race, we'll log the event and the time, so
-
6:27
we'll say tracker.updateRaceStart with the current date,
-
6:33
and we'll use use the Date class to do this.
-
6:35
This is part of the foundation framework, and
-
6:37
when you initialize an instance, it captures the current time.
-
6:41
Inside the updateProgress method, we can also log events.
-
6:45
So this is not very technically correct, but we'll see.
-
6:49
So after, rather not here, but after this line,
-
6:54
we'll say let lapKey =, and we'll create a key.
-
6:59
So we'll say Tracker, and we'll use that nested struct,
-
7:04
Keys, to get the lapLeader string.
-
7:08
And then to this, we'll append the current lap, so we'll say horse.currentLap.
-
7:16
So this creates a key where we say,
-
7:18
if we look at the keys list and lap leader, it says leaderForLap,
-
7:23
and then we're adding one, two, or depending whatever lap they're on.
-
7:27
So now that we have a key, let's go back down here, and we'll say,
-
7:31
if !tracker.stats.keys.,
-
7:36
already contains this lapKey, then we don't wanna do anything.
-
7:41
But if it doesn't contain it, then we're going to call the tracker to update
-
7:45
the lap leader with the horse's current lap, the horse, and the current time.
-
7:55
Once the race is over, we'll log that as well.
-
7:57
So in the stop method, before we invalidate the timer,
-
8:00
we'll say tracker.updateRaceEndWith.
-
8:04
Actually, we can't do that in here, we need access to the horse, so
-
8:08
I'll do that right here in our logic, where we break out of the loop.
-
8:12
Right before we call stop,
-
8:15
we'll say tracker.updateRaceEndWith horse and current time.
-
8:21
Inside the stop method, once the race is over,
-
8:24
let's print the output of our tracker, so we'll say tracker.PrintRaceSummary.
-
8:28
I'm gonna let this run for a few seconds, and you'll see that once the race is over,
-
8:34
we should get more information about the race.
-
8:37
And I'm gonna drag this up, so you can see it.
-
8:41
Okay, so this seems like we've solved the problem, right?
-
8:45
Yay us, now some time passes, and our friend wants an addition to this game.
-
8:51
Not only does he want to race his horses, but
-
8:54
he wants to broadcast the race live to his friends on social media.
-
8:58
Now this includes information, like the start time of the race,
-
9:01
which horse led a particular lap, when the race ended, and who won.
-
9:06
Obviously, you notice that this is the same information that the Tracker
-
9:10
class cares about.
-
9:11
It would be easy to stuff this new functionality into the Tracker class.
-
9:16
But remember, classes shouldn't do more than one job.
-
9:19
So let's go ahead and create a new class right after Tracker here.
-
9:23
And we'll call this class RaceBroadcaster.
-
9:32
RaceBroadcaster cares about the same information that Tracker does,
-
9:35
except it does something entirely different with it.
-
9:39
Because this is a different class, we now need to add the RaceBroadcaster class
-
9:44
as a stored property inside class as well.
-
9:46
So we can say let broadcaster = RaceBroadcaster, and
-
9:50
we'll create an instance and assign it to the property.
-
9:55
So let's assume that we do this and make the changes where we want to broadcast.
-
10:00
So we provide methods here that capture the information and
-
10:04
then put it on social media, and we make the relevant calls inside race.
-
10:08
Things are great.
-
10:09
Down the road, our friend asks for yet another request.
-
10:13
Maybe I should have written up a contract?
-
10:15
Anyway he wants to gather all the data from all our races to build
-
10:20
a leaderboards page for various statistics.
-
10:23
Here's where we start to run into difficulties.
-
10:26
Tracker objects are tied to a particular race, as you can see.
-
10:31
This means that to gather up all the data across different races,
-
10:34
we either need to go through the race instances, or
-
10:37
after a particular race, extract the tracker and save the data elsewhere.
-
10:42
Leaving that aside, we also have this problem where the race knows about
-
10:46
the tracker and the broadcaster.
-
10:48
Does it really need to?
-
10:49
The race object shouldn't care about the fact that it needs to be tracked or
-
10:54
broadcasted.
-
10:56
This is far too tight of a coupling.
-
10:58
In a real race, does a jockey know that a particular TV station is showing this?
-
11:03
No, I mean these are concerns that need to be separate.
-
11:06
The important facts are as follows.
-
11:08
A race has certain events that are important.
-
11:12
When did the race start?
-
11:13
Who led at the end of a particular lap?
-
11:15
When did the race end?
-
11:18
Lots of other objects care about these events, but
-
11:21
it isn't the race objects' responsibility to implement them.
-
11:26
All the race object should do is indicate when these events happened.
-
11:31
If a tracker wants to track it, if a broadcaster wants to broadcast it, or
-
11:35
if any other object whose role we haven't defined yet
-
11:39
wants to use these events, it should just listen for them indirectly.
-
11:44
While the solution we have in place works right now, it leads to pretty tight
-
11:49
coupling, where only the objects we've currently defined can use this event data.
-
11:54
And to use it, it needs to be part of the Race class.
-
11:59
We want to implement a more flexible solution, where any object that adheres
-
12:03
to a particular contract can use the race's event data.
-
12:06
Let's do that in the next video.
You need to sign up for Treehouse in order to download course files.
Sign up