Would you like to inspect the original subtitles? These are the user uploaded subtitles that are being translated:
1
00:00:04,350 --> 00:00:10,410
So lots of the early testers of this course ask me whether I was going to cover the solid principles.
2
00:00:10,650 --> 00:00:16,950
And at first I wasn't thinking of doing it, but because I got so much request for it and to know how
3
00:00:16,950 --> 00:00:24,360
it fits in with my coding commandments, I decided that it would probably be worthy of some bonus material.
4
00:00:24,480 --> 00:00:31,110
So here are the solid principles originally originated by Robert C. Martin of clean coding fame and
5
00:00:31,110 --> 00:00:34,770
other books, and just generally being a behemoth of the space.
6
00:00:35,130 --> 00:00:40,230
So a little word of caution, and this is something that Robert C. Martins has himself in his original
7
00:00:40,230 --> 00:00:46,410
paper on the solid principles is that following the rules on the patent won't teach you how to paint.
8
00:00:46,680 --> 00:00:47,600
And this is important.
9
00:00:47,610 --> 00:00:50,580
You can't just follow the rules and expect to be an amazing programmer.
10
00:00:50,820 --> 00:00:52,410
There is lots of skill involved.
11
00:00:52,410 --> 00:00:55,450
It's a craftsmanship and you need to learn it with experience.
12
00:00:55,860 --> 00:01:00,510
And if you don't know when to break the rules as well, that is a problem.
13
00:01:00,510 --> 00:01:07,560
So these rules are to be followed with caution and to be done with skill and applied judiciously.
14
00:01:07,860 --> 00:01:13,470
So let's have a look at the single responsibility principle, the source of the solid principles.
15
00:01:14,040 --> 00:01:17,670
This is very straightforwardly already in amendments.
16
00:01:17,670 --> 00:01:20,640
I think it was point three or something the class should have.
17
00:01:20,640 --> 00:01:25,470
Only one reason to change should have ultimately only one responsibility.
18
00:01:25,890 --> 00:01:28,260
I don't think we have to dig into this one too much.
19
00:01:28,620 --> 00:01:35,040
It's a very good tenet of object oriented programming in general, and it's important to note that all
20
00:01:35,040 --> 00:01:39,030
of these principles aim to deal with change management.
21
00:01:39,120 --> 00:01:44,820
If you have changing requirements in the world and you're writing software for a changing requirements
22
00:01:44,820 --> 00:01:51,000
world, then you want your software to be flexible and allow you to easily adapt to new requirements.
23
00:01:51,330 --> 00:01:55,290
So all of these are trying to aid you in that purpose.
24
00:01:55,410 --> 00:02:01,050
So having a class responsible for just one thing aids you in that purpose by allowing you to know where
25
00:02:01,050 --> 00:02:06,750
to go to make that change and also to have changes destabilise as few things as possible.
26
00:02:07,530 --> 00:02:08,690
The open closed principle.
27
00:02:08,699 --> 00:02:14,310
Now, this is probably the crux of the solid principles, at least as Robert C. Martin puts it forth.
28
00:02:14,700 --> 00:02:17,190
And it's also the hardest to understand.
29
00:02:17,520 --> 00:02:22,560
So, at its core, open for extension, but close to modification, what does this mean?
30
00:02:23,010 --> 00:02:32,190
Well, it ultimately means that you can create subclasses or you can create extensions of an interface,
31
00:02:32,520 --> 00:02:36,690
but without modifying the stuff that people are depending upon.
32
00:02:36,840 --> 00:02:41,190
So we're closing down the interface so that we can easily depend upon it.
33
00:02:41,460 --> 00:02:45,270
But then we're allowing extension of it to add new functionality.
34
00:02:45,840 --> 00:02:53,310
So attempting again to make things easy to change by adding functionality through extension, but hard
35
00:02:53,310 --> 00:02:59,670
to break by keeping it closed for modification now, how do we typically do this in something like C-sharp
36
00:02:59,670 --> 00:03:00,270
or unity?
37
00:03:00,510 --> 00:03:06,960
Well, we typically would use interfaces because we would rarely modify the interface that's closed
38
00:03:06,960 --> 00:03:07,740
for modification.
39
00:03:08,100 --> 00:03:13,890
But we would implement extensions or implementations of the interface, which allow us to implement
40
00:03:13,890 --> 00:03:14,460
change.
41
00:03:14,760 --> 00:03:19,740
And the same thing with abstract classes, you might make the abstract classes interface close for modification,
42
00:03:19,950 --> 00:03:21,450
but open for extension.
43
00:03:21,990 --> 00:03:22,950
So let's take a look.
44
00:03:23,190 --> 00:03:29,400
As an example from the programming pattern we've been using, the strategy pattern is a classic example
45
00:03:29,400 --> 00:03:36,270
of you implementing the open closed principle in the previous example where we had just the ability
46
00:03:36,270 --> 00:03:43,050
runner and it worked on the basis of a Switch statement or an enum, then obviously this was a problem
47
00:03:43,050 --> 00:03:48,840
because if we wanted to make any changes, we had to go and change the ability runner.
48
00:03:48,840 --> 00:03:53,520
So this was both open for extension and open for modification.
49
00:03:53,730 --> 00:03:54,950
Not what we wanted.
50
00:03:54,960 --> 00:04:00,750
So using the strategy pattern, we pull out an interface for the ability.
51
00:04:00,750 --> 00:04:03,330
Now that interface is closed for modification.
52
00:04:03,330 --> 00:04:10,890
We very rarely going to change the number of methods on that interface and the parameters to that interface
53
00:04:10,890 --> 00:04:11,580
methods.
54
00:04:12,000 --> 00:04:18,450
So that's closed for modification, but open to extension because we can add in new abilities really
55
00:04:18,450 --> 00:04:22,890
easily, and it allows us to adapt to new gameplay requirements.
56
00:04:22,890 --> 00:04:26,090
Our game design decides, Hey, we need this new ability.
57
00:04:26,100 --> 00:04:28,770
You can easily go and add it by extension.
58
00:04:29,370 --> 00:04:35,970
So the next thing we've had S.A. of our solid principles, we now have the L, which is the list of
59
00:04:35,970 --> 00:04:43,680
substitution principle named after Barbara Lisk of who states it like so that subclasses of this is
60
00:04:43,680 --> 00:04:44,670
how it is stated.
61
00:04:44,670 --> 00:04:49,470
Sometimes I think it's been stated by Barbara Lisk, often a bit more of a mathematical sense, but
62
00:04:49,470 --> 00:04:55,740
here is the crux of it that subclasses should be substitutable for their base classes.
63
00:04:55,740 --> 00:04:59,030
And you're probably looking at this and going, Why is this even a thing?
64
00:04:59,040 --> 00:05:00,300
This is obvious.
65
00:05:00,630 --> 00:05:03,570
You know, obviously, if I have a subclass, I should be able.
66
00:05:03,680 --> 00:05:06,830
To substitute it for the base class, that's entirely the point.
67
00:05:07,460 --> 00:05:14,630
Well, yes, I mean, obviously the interfaces will be substitutable, but the point is they also need
68
00:05:14,630 --> 00:05:17,750
to be behaviorally substitution also.
69
00:05:17,960 --> 00:05:20,480
So an example here is with rectangles and squares.
70
00:05:21,140 --> 00:05:28,100
So we've got a clients that might have access to a rectangle class and that rectangle class has a width
71
00:05:28,100 --> 00:05:28,700
and height.
72
00:05:28,730 --> 00:05:32,570
Now you might easily say, well, square is just a rectangle.
73
00:05:32,570 --> 00:05:35,690
It's a rectangle where the width and height are equal.
74
00:05:36,170 --> 00:05:36,500
Cool.
75
00:05:36,500 --> 00:05:38,750
That sounds that sounds like a reasonable thing.
76
00:05:39,130 --> 00:05:44,630
Now on our rectangle, we've obviously got set width and height and get width and height.
77
00:05:45,110 --> 00:05:46,700
And that seems reasonable.
78
00:05:46,970 --> 00:05:52,130
So when we're using the rectangle, we might happily say, let's set the height and set the width,
79
00:05:52,130 --> 00:05:57,380
and then we can assert that the height that we set is actually the height that we get back.
80
00:05:57,380 --> 00:06:00,230
When we do it, get height seems reasonable enough.
81
00:06:00,500 --> 00:06:05,070
The problem is we inherit a square, which also seemed like a reasonable thing to do.
82
00:06:05,070 --> 00:06:11,540
It has the same interface, but to do this because we've got this extra height and width property that
83
00:06:11,540 --> 00:06:16,640
in a square, we need to make sure that the same we override their settings so that width and height
84
00:06:16,640 --> 00:06:17,810
always get set to the same.
85
00:06:17,810 --> 00:06:24,860
And suddenly we have a problem because if we substitute a square into that client code that we've got
86
00:06:24,860 --> 00:06:29,810
over here on the slide, this asset isn't going to be true and we go and set the width to three.
87
00:06:30,440 --> 00:06:35,600
It sets the both the width and height to three and so overrides the value that the client was expecting.
88
00:06:36,110 --> 00:06:42,320
And so here, apart from the programmatic interface that we have to this rectangle class, you've also
89
00:06:42,320 --> 00:06:45,830
got some sort of implicit interface about how it should work.
90
00:06:45,860 --> 00:06:50,720
You've got the implicit that if I set something, I expect it to still be there when I get it back.
91
00:06:51,290 --> 00:06:57,980
And so this fails in terms of substitution, lisk of substitution and all sorts of bugs can arise from
92
00:06:57,980 --> 00:06:58,130
this.
93
00:06:58,140 --> 00:07:00,230
This is a very simplistic case.
94
00:07:00,650 --> 00:07:07,550
However, there are many such cases that are harder to find, and one typical example in object oriented
95
00:07:07,550 --> 00:07:15,830
programming is equality, where equality gets defined, redefined further down the inheritance hierarchy
96
00:07:15,830 --> 00:07:21,050
and can break when you substitute a child class in for a parent class.
97
00:07:21,510 --> 00:07:24,590
And now I'm not going to go into details of how that breaks down.
98
00:07:24,590 --> 00:07:30,950
I'll leave a link in the resources to a discussion of equality and lack of substitution principle.
99
00:07:31,670 --> 00:07:33,020
But it's interesting.
100
00:07:33,020 --> 00:07:38,870
Further reading If you want to go down that rabbit hole now interface segregation, this is the eye
101
00:07:38,870 --> 00:07:39,530
of solid.
102
00:07:40,310 --> 00:07:46,940
What does this mean that many client specific interfaces are better than one general purpose interface?
103
00:07:47,300 --> 00:07:54,230
So, for example, imagine we have a health component that we've been working on in this course and
104
00:07:54,230 --> 00:08:01,820
was being used in potentially multiple places in the UI to display the health, but also by combat to
105
00:08:01,850 --> 00:08:03,560
subtract from the health.
106
00:08:03,920 --> 00:08:08,570
Now what we're saying is that those are two very different use cases by two very different clients.
107
00:08:08,870 --> 00:08:15,260
So perhaps you'd be better off having an interface for each, maybe an eye health display to facilitate
108
00:08:15,260 --> 00:08:21,040
the interface for the UI and an eye damage ball for combat code.
109
00:08:21,050 --> 00:08:27,290
And the reason this is a good thing is that it allows for all sorts of dynamic JBL's later down the
110
00:08:27,290 --> 00:08:27,680
line.
111
00:08:27,950 --> 00:08:31,370
It allows us to swap the health display to something else.
112
00:08:31,610 --> 00:08:39,230
It also means that there are fewer opportunities to accidentally get extra dependencies in there.
113
00:08:39,240 --> 00:08:47,780
So, for example, the UI accessing some methods that it shouldn't and later causing problems when we
114
00:08:47,780 --> 00:08:50,060
need to refactor our health components.
115
00:08:50,060 --> 00:08:55,550
So it keeps those dependencies kind of segregated in this interface segregation principle.
116
00:08:55,790 --> 00:09:00,440
And finally, the D of the solid principle is dependency inversion.
117
00:09:00,450 --> 00:09:01,200
What does this mean?
118
00:09:01,220 --> 00:09:07,520
This is typically implemented with interfaces so depend upon abstractions do not depend upon concretions.
119
00:09:07,760 --> 00:09:16,970
Now the idea here is that suppose you have your gang code and you are depending on a concrete and you're
120
00:09:16,970 --> 00:09:23,180
depending on a concrete system such as the Unity audio subsystem or maybe your asset pack super dialogue
121
00:09:23,180 --> 00:09:26,420
system x that you've downloaded from the Udemy Asset Store.
122
00:09:27,020 --> 00:09:32,330
Now everything is well and good until you realize that, oh no, you know what I wanted to use actually
123
00:09:32,330 --> 00:09:38,390
was Super Dialogue System Y, because it has this really cool feature that I need and a DNC audio system
124
00:09:38,390 --> 00:09:39,590
just isn't quite cracking.
125
00:09:39,800 --> 00:09:45,110
For me, I now need the f mod subsystem to do the work much better.
126
00:09:46,010 --> 00:09:51,140
Now you've obviously got a headache because you've hardcoded all of the interfaces of the U.S. audio
127
00:09:51,140 --> 00:09:56,930
system and the super dialogue system into your game code, and you've got to go and change a whole heck
128
00:09:56,930 --> 00:10:00,410
of a lot of code in order to swap these two out.
129
00:10:00,560 --> 00:10:03,520
So the dependency inversion principle would say instead of Japan.
130
00:10:03,560 --> 00:10:07,860
Running on those concrete subsystems, why don't you depend on an interface instead?
131
00:10:07,970 --> 00:10:13,610
So you depend on a dialogue system interface in your game code and an audio system interface, and that
132
00:10:13,610 --> 00:10:16,610
would define how you're going to interact with these subsystems.
133
00:10:17,090 --> 00:10:21,710
And then the subsystems would all inherit from their relevant interface.
134
00:10:22,100 --> 00:10:27,230
And now swapping them out becomes a doddle because they interfaces are the same.
135
00:10:27,800 --> 00:10:35,010
Now, obviously, you can't make the unity audio subsystem conform to your own subsystem interface.
136
00:10:35,030 --> 00:10:40,730
So for this kind of thing, we might use the adapter pattern, which is a little bit like the decorator
137
00:10:40,730 --> 00:10:41,870
pattern in its structure.
138
00:10:42,170 --> 00:10:48,860
But the idea is that we would put a little components, a little object in between the implements,
139
00:10:48,860 --> 00:10:56,690
the audio subsystem interface and uses the audio that basically acts as a translation unit and translates
140
00:10:56,990 --> 00:11:01,940
from your audio subsystem interface into the unity audio subsystem interface.
141
00:11:02,330 --> 00:11:06,230
That is a situation when you can't change the actual code itself.
142
00:11:06,230 --> 00:11:08,300
You can add those little adapters in again.
143
00:11:08,570 --> 00:11:13,010
If you are interested in the details of the adapter pattern, do go and have a look at the resources
144
00:11:13,010 --> 00:11:18,560
because of links to refactor guru there, where they could do a very good explanation of that pattern.
145
00:11:18,980 --> 00:11:24,770
So those are the solid principles, the single responsibility where we say a class should do one thing
146
00:11:25,070 --> 00:11:32,210
open closed, meaning that we should allow extension of interfaces but not modify those interfaces too
147
00:11:32,210 --> 00:11:32,570
much.
148
00:11:32,990 --> 00:11:38,780
The list of substitution principle where we're saying that behaviorally, if there is some sort of implicit
149
00:11:38,780 --> 00:11:45,710
contract on a parent class, we should obey that in the child classes, the interface segregation principle,
150
00:11:45,710 --> 00:11:49,550
which means splitting out interfaces for different clients.
151
00:11:49,550 --> 00:11:51,740
So there's less reasons to change those.
152
00:11:52,100 --> 00:12:00,380
And the dependency inversion principle, which allows us to swap out large bits of concrete implementation
153
00:12:00,590 --> 00:12:06,680
to keep our game code or any code really less fragile to change.
154
00:12:07,100 --> 00:12:13,430
So hopefully you found that interesting and you've taken away some points that you may want to use in
155
00:12:13,430 --> 00:12:14,780
your own projects.
156
00:12:14,930 --> 00:12:17,750
If so, please do go and share them in the discussions.
157
00:12:18,020 --> 00:12:20,080
I would love to hear your thoughts.
17488
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.