1 00:00:06,570 --> 00:00:09,660 - When you take a TDD approach to development, 2 00:00:09,660 --> 00:00:13,350 you often find that your classes evolve independently. 3 00:00:13,350 --> 00:00:15,420 So you add stuff to one class, 4 00:00:15,420 --> 00:00:18,570 you add stuff to another class as each test goes by. 5 00:00:18,570 --> 00:00:22,170 And what you might find is that over a period of time, 6 00:00:22,170 --> 00:00:25,140 you start to notice similarities between these classes. 7 00:00:25,140 --> 00:00:27,330 You've developed each of these classes, 8 00:00:27,330 --> 00:00:29,460 kind of with an independent thought process, 9 00:00:29,460 --> 00:00:30,780 just one test at a time, 10 00:00:30,780 --> 00:00:32,400 and then you take a step back and you realize, 11 00:00:32,400 --> 00:00:34,560 well, look there are some similar properties 12 00:00:34,560 --> 00:00:36,360 between these two classes. 13 00:00:36,360 --> 00:00:38,190 Or maybe there are similar methods, you know, 14 00:00:38,190 --> 00:00:40,443 that each class is doing the same thing. 15 00:00:41,370 --> 00:00:43,080 Or maybe they've got similar properties 16 00:00:43,080 --> 00:00:45,900 to get and set data in the class. 17 00:00:45,900 --> 00:00:48,420 So, obviously, having duplication between classes 18 00:00:48,420 --> 00:00:51,030 is not good because you have to maintain 19 00:00:51,030 --> 00:00:53,130 two separate sets of code. 20 00:00:53,130 --> 00:00:55,810 So you should refactor to eliminate 21 00:00:56,700 --> 00:00:59,280 the similarities between your classes. 22 00:00:59,280 --> 00:01:01,350 So, we're gonna have a look at an example. 23 00:01:01,350 --> 00:01:02,850 We're gonna use inheritance 24 00:01:02,850 --> 00:01:05,160 to remove duplication between two classes. 25 00:01:05,160 --> 00:01:09,180 So if you go into the Refactoring_Inheritance folder, 26 00:01:09,180 --> 00:01:11,270 here it is, Refactoring_Inheritance, 27 00:01:11,270 --> 00:01:13,950 if you open that folder in the code editor, 28 00:01:13,950 --> 00:01:15,960 I've actually got two separate subfolders. 29 00:01:15,960 --> 00:01:19,710 One containing original code that needs to be refactored, 30 00:01:19,710 --> 00:01:22,530 and one that contains the refactored code afterwards. 31 00:01:22,530 --> 00:01:24,630 So here's my code editor. 32 00:01:24,630 --> 00:01:29,435 My original code, which has opportunities for refactoring, 33 00:01:29,435 --> 00:01:31,470 and, but works at the moment. 34 00:01:31,470 --> 00:01:34,980 And then I'll refactor it into a better code set afterwards. 35 00:01:34,980 --> 00:01:39,180 Okay, so, if you have a look at the original code first, 36 00:01:39,180 --> 00:01:43,140 and what you'll find is I've got two similar classes: 37 00:01:43,140 --> 00:01:45,600 a Basket, a shopping basket, 38 00:01:45,600 --> 00:01:49,560 items that a customer has added to their shopping basket, 39 00:01:49,560 --> 00:01:52,800 and a Catalog class, which is a collection of products 40 00:01:52,800 --> 00:01:54,570 that a company sells. 41 00:01:54,570 --> 00:01:57,090 So if you look at these two classes, you'll see there's, 42 00:01:57,090 --> 00:01:58,950 obviously for the purposes of the example, 43 00:01:58,950 --> 00:02:02,400 I've made these two classes very similar indeed. 44 00:02:02,400 --> 00:02:04,230 You probably wouldn't have so much similarity 45 00:02:04,230 --> 00:02:06,240 between classes in reality, 46 00:02:06,240 --> 00:02:11,100 but it's, I've exaggerated for the demonstration purposes. 47 00:02:11,100 --> 00:02:13,200 So let's have a look at those two classes. 48 00:02:14,160 --> 00:02:18,090 So in the original folder, my Basket class. 49 00:02:18,090 --> 00:02:20,790 In my shopping basket I have a collection of products, 50 00:02:20,790 --> 00:02:22,440 which is empty initially. 51 00:02:22,440 --> 00:02:24,930 I can add a product to my catalog. 52 00:02:24,930 --> 00:02:26,730 I just call the push function. 53 00:02:26,730 --> 00:02:29,160 You know, the Array class in JavaScript, 54 00:02:29,160 --> 00:02:30,570 it has a push function? 55 00:02:30,570 --> 00:02:33,720 It appends a product at the end of the array. 56 00:02:33,720 --> 00:02:36,150 I can display all products, 57 00:02:36,150 --> 00:02:37,710 it just does a console.log. 58 00:02:37,710 --> 00:02:41,550 Obviously, in reality, this method would be more complex. 59 00:02:41,550 --> 00:02:43,530 But it just iterates through all my products 60 00:02:43,530 --> 00:02:46,560 and it just displays each one on the console. 61 00:02:46,560 --> 00:02:48,180 So, and then I've got another method, 62 00:02:48,180 --> 00:02:52,710 which is kind of unique to the Basket class, really. 63 00:02:52,710 --> 00:02:56,610 In your shopping basket, are you eligible for free delivery? 64 00:02:56,610 --> 00:02:57,750 Well, yes. 65 00:02:57,750 --> 00:03:01,350 If you have three or more items in your shopping basket 66 00:03:01,350 --> 00:03:04,560 then you get free delivery, according to my business rules. 67 00:03:04,560 --> 00:03:07,680 So that's the Basket class and what you'll notice, 68 00:03:07,680 --> 00:03:10,380 let's look at the Catalog class, it's very similar. 69 00:03:10,380 --> 00:03:12,120 Very similar, indeed actually. 70 00:03:12,120 --> 00:03:14,700 It also maintains a collection of products 71 00:03:14,700 --> 00:03:16,680 which the company sells. 72 00:03:16,680 --> 00:03:19,200 And you can add an item to your catalog. 73 00:03:19,200 --> 00:03:21,750 You basically add in an item to your portfolio 74 00:03:21,750 --> 00:03:25,380 as an online retailer and you can display products. 75 00:03:25,380 --> 00:03:26,550 So as it happens, 76 00:03:26,550 --> 00:03:30,060 in my overly kind of simple example here 77 00:03:30,060 --> 00:03:31,980 for the demonstration purposes, 78 00:03:31,980 --> 00:03:35,050 the methods in my Catalog class are actually the same 79 00:03:36,030 --> 00:03:37,470 as my Basket class. 80 00:03:37,470 --> 00:03:40,200 I also have another method here, a luckyDip. 81 00:03:40,200 --> 00:03:42,660 It generates a random number 82 00:03:42,660 --> 00:03:46,233 between zero and n minus one, basically. 83 00:03:47,280 --> 00:03:49,260 And it'll pluck one of those products. 84 00:03:49,260 --> 00:03:53,490 It's a luckyDip to choose the product from the catalog. 85 00:03:53,490 --> 00:03:58,490 So these methods are the same, exactly the same as Basket. 86 00:03:58,650 --> 00:04:01,350 So we should, we shouldn't, 87 00:04:01,350 --> 00:04:03,900 we shouldn't have duplicate code. 88 00:04:03,900 --> 00:04:05,940 The code does work at the moment. 89 00:04:05,940 --> 00:04:07,740 (keyboard clicking) 90 00:04:07,740 --> 00:04:10,470 I do have a test for it. 91 00:04:10,470 --> 00:04:11,820 So I'll show you the test as well. 92 00:04:11,820 --> 00:04:13,800 This is still in my original code. 93 00:04:13,800 --> 00:04:15,200 Let's have look at the test. 94 00:04:16,470 --> 00:04:19,050 So example test.js. 95 00:04:19,050 --> 00:04:22,350 I've got two test functions, a test for the Basket class, 96 00:04:22,350 --> 00:04:23,730 it creates a basket. 97 00:04:23,730 --> 00:04:26,700 It adds some products to my shopping basket 98 00:04:26,700 --> 00:04:30,720 and then it checks, am I eligible for free delivery? 99 00:04:30,720 --> 00:04:33,660 It should be true if you have three or more items 100 00:04:33,660 --> 00:04:34,950 in your basket. 101 00:04:34,950 --> 00:04:37,650 So that's a test for my Basket class. 102 00:04:37,650 --> 00:04:40,110 And then I've got another test for my catalog. 103 00:04:40,110 --> 00:04:43,260 I've added a ski jacket into my catalog. 104 00:04:43,260 --> 00:04:45,330 'Cause my catalog only contains one item, 105 00:04:45,330 --> 00:04:49,380 I only sell ski jackets in my online retailer. 106 00:04:49,380 --> 00:04:53,490 If I do a luckyDip, the luckyDip must be the ski jacket 107 00:04:53,490 --> 00:04:55,920 because that's the only thing that my company sells. 108 00:04:55,920 --> 00:04:58,260 It specializes in ski jackets. 109 00:04:58,260 --> 00:05:01,140 So the luckyDip must be a ski jacket. 110 00:05:01,140 --> 00:05:04,860 So it's probably worthwhile running those tests to verify 111 00:05:04,860 --> 00:05:06,690 that the code does actually work at the moment. 112 00:05:06,690 --> 00:05:08,790 So if you open a command window. 113 00:05:08,790 --> 00:05:10,530 If you go into the original folder 114 00:05:10,530 --> 00:05:12,810 and only just run the tests, 115 00:05:12,810 --> 00:05:14,940 it'll run the Basket class test 116 00:05:14,940 --> 00:05:18,030 and it'll run the Catalog class test, 117 00:05:18,030 --> 00:05:19,980 and they've both worked fine. 118 00:05:19,980 --> 00:05:22,890 Right. So, so far so good. 119 00:05:22,890 --> 00:05:24,810 Then we take a step back and we realize, 120 00:05:24,810 --> 00:05:25,650 well look at this, 121 00:05:25,650 --> 00:05:29,220 my Basket class and my Catalog class are largely similar. 122 00:05:29,220 --> 00:05:34,220 I can reduce, I can reduce the overlap by using inheritance. 123 00:05:34,578 --> 00:05:36,780 (keyboard clicking) 124 00:05:36,780 --> 00:05:41,190 So, extracting duplicate features into a superclass. 125 00:05:41,190 --> 00:05:44,880 Visual Studio Code doesn't help me do this automatically. 126 00:05:44,880 --> 00:05:47,130 You have to kind of do this manually. 127 00:05:47,130 --> 00:05:51,090 So in the Refactored folder, I'll show you in a moment, 128 00:05:51,090 --> 00:05:55,050 I manually defined a new class called ProductCollection. 129 00:05:55,050 --> 00:05:57,240 This is going to be the superclass. 130 00:05:57,240 --> 00:06:01,680 Basket and Catalog will inherit from this class. 131 00:06:01,680 --> 00:06:05,610 Okay. So, I'll manually modify the Basket class 132 00:06:05,610 --> 00:06:07,800 and the Catalog class so that they inherit from 133 00:06:07,800 --> 00:06:10,140 or extend ProductCollection. 134 00:06:10,140 --> 00:06:11,850 And then I'll delete the code, 135 00:06:11,850 --> 00:06:14,610 the common code, from Basket and Catalog. 136 00:06:14,610 --> 00:06:18,003 And I'll move that common code up into the superclass. 137 00:06:18,930 --> 00:06:21,480 Okay? So have a look at the Refactored folder 138 00:06:21,480 --> 00:06:24,903 to see how that all looks after I've done the refactoring. 139 00:06:25,770 --> 00:06:27,330 So in the Refactored folder, 140 00:06:27,330 --> 00:06:30,420 first of all the ProductCollection, I added that file. 141 00:06:30,420 --> 00:06:33,420 It's the superclass that contains the commonality 142 00:06:33,420 --> 00:06:35,133 between Basket and Catalog. 143 00:06:35,970 --> 00:06:39,090 Okay. So that's my ProductCollection, defined once. 144 00:06:39,090 --> 00:06:41,250 And then my Basket class is simpler. 145 00:06:41,250 --> 00:06:44,010 My Basket class extends ProductCollection. 146 00:06:44,010 --> 00:06:45,330 Obviously, I have to import 147 00:06:45,330 --> 00:06:47,730 the ProductCollection class first. 148 00:06:47,730 --> 00:06:50,760 So Basket inherits from ProductCollection. 149 00:06:50,760 --> 00:06:53,280 It inherits everything from there, the constructor, 150 00:06:53,280 --> 00:06:55,830 the add product, the display products method. 151 00:06:55,830 --> 00:06:59,490 It just adds a unique method for my shopping basket. 152 00:06:59,490 --> 00:07:01,140 Am I eligible for free delivery? 153 00:07:01,140 --> 00:07:04,080 It inherits everything else from the superclass. 154 00:07:04,080 --> 00:07:06,840 Same for Catalog. Catalog also inherits 155 00:07:06,840 --> 00:07:07,890 from product collection. 156 00:07:07,890 --> 00:07:11,310 So the common code is now isolated once. 157 00:07:11,310 --> 00:07:14,070 instead of being repeated twice, it's just defined once. 158 00:07:14,070 --> 00:07:17,190 My Catalog class just adds another method, a luckyDip. 159 00:07:17,190 --> 00:07:19,863 Choose one of these products out of the Catalog. 160 00:07:20,850 --> 00:07:23,100 Okay, so after you've done the refactoring, 161 00:07:23,100 --> 00:07:25,500 it's important that you then run the test again 162 00:07:25,500 --> 00:07:27,600 to make sure that it still works. 163 00:07:27,600 --> 00:07:30,960 So the test here is pretty much the same as it was before. 164 00:07:30,960 --> 00:07:33,600 Refactoring doesn't change the semantics, 165 00:07:33,600 --> 00:07:36,510 it just reorganizes the layout of the code. 166 00:07:36,510 --> 00:07:39,360 So the tests here should still work. 167 00:07:39,360 --> 00:07:42,213 So let's run them to make sure that they do still work. 168 00:07:43,140 --> 00:07:45,073 I'll go into the Refactored folder 169 00:07:45,073 --> 00:07:47,400 (keyboard clicking) 170 00:07:47,400 --> 00:07:49,863 and I'll just run the tests again. 171 00:07:50,700 --> 00:07:53,793 And I've tried these out before, and they work. 172 00:07:55,440 --> 00:07:57,570 Okay. So, refactoring: take two classes. 173 00:07:57,570 --> 00:08:00,480 Inheritance isn't always the way to refactor, 174 00:08:00,480 --> 00:08:01,710 but it's often the good way. 175 00:08:01,710 --> 00:08:03,570 When you have similarities between classes, 176 00:08:03,570 --> 00:08:06,480 introduce a superclass, and then you can inherit from it 177 00:08:06,480 --> 00:08:07,923 and avoid duplication. 178 00:08:09,360 --> 00:08:12,390 Okay. Other common refactorings: trying to decide 179 00:08:12,390 --> 00:08:15,750 where to place a method in an inheritance hierarchy. 180 00:08:15,750 --> 00:08:18,900 So you got a superclass and some subclasses, 181 00:08:18,900 --> 00:08:21,390 and you decide to add a new method. 182 00:08:21,390 --> 00:08:24,540 Maybe you attempted to add that method into the Basket class 183 00:08:24,540 --> 00:08:25,980 or the Catalog class. 184 00:08:25,980 --> 00:08:27,540 Maybe what you should consider 185 00:08:27,540 --> 00:08:29,790 is put it in the superclass instead. 186 00:08:29,790 --> 00:08:32,070 So when you're adding, when you're considering adding 187 00:08:32,070 --> 00:08:34,200 a new method in a subclass, 188 00:08:34,200 --> 00:08:36,390 think whether it's a more general method. 189 00:08:36,390 --> 00:08:39,330 Can I actually put it into the superclass instead? 190 00:08:39,330 --> 00:08:41,970 And then it'll apply for all subclasses, 191 00:08:41,970 --> 00:08:43,140 not just one of them. 192 00:08:43,140 --> 00:08:45,630 Okay. So, you know, the Basket class, 193 00:08:45,630 --> 00:08:47,040 I could add a method into there, 194 00:08:47,040 --> 00:08:49,890 but maybe that method also works for Catalog, 195 00:08:49,890 --> 00:08:51,450 put it into the superclass, 196 00:08:51,450 --> 00:08:53,850 and then both subclasses inherit from it. 197 00:08:53,850 --> 00:08:55,950 It makes it more general. 198 00:08:55,950 --> 00:08:59,130 The opposite is to pull a method down from superclass. 199 00:08:59,130 --> 00:09:02,280 You might decide, as you add another subclass, 200 00:09:02,280 --> 00:09:05,850 you might find that one of the methods in the superclass, 201 00:09:05,850 --> 00:09:08,370 ProductCollection, doesn't actually apply 202 00:09:08,370 --> 00:09:11,790 for all subclasses now. It's not that general. 203 00:09:11,790 --> 00:09:13,440 So in that case, you have to move the method 204 00:09:13,440 --> 00:09:14,850 from the superclass, 205 00:09:14,850 --> 00:09:18,900 and move it down into the subclasses for which it applies. 206 00:09:18,900 --> 00:09:20,790 Okay? So that's another decision 207 00:09:20,790 --> 00:09:22,920 from a design point of view. 208 00:09:22,920 --> 00:09:25,650 Extracting a method into an interface: 209 00:09:25,650 --> 00:09:28,440 if you're using TypeScript as opposed to JavaScript, 210 00:09:28,440 --> 00:09:30,720 in TypeScript, you can define interfaces. 211 00:09:30,720 --> 00:09:33,240 An interface is kind of like a class, 212 00:09:33,240 --> 00:09:35,520 but where each method is unspecified, 213 00:09:35,520 --> 00:09:38,610 you declare the method name, but you don't implement it. 214 00:09:38,610 --> 00:09:40,080 It's just a specification 215 00:09:40,080 --> 00:09:42,180 of what functions need to be implemented, 216 00:09:42,180 --> 00:09:44,100 but not how to implement them. 217 00:09:44,100 --> 00:09:45,600 There would be some other class 218 00:09:45,600 --> 00:09:47,220 which implements the interface. 219 00:09:47,220 --> 00:09:49,860 So, extracting methods into an interface 220 00:09:49,860 --> 00:09:51,900 is a common technique if you're using TypeScript. 221 00:09:51,900 --> 00:09:53,670 You can also do it in other languages as well, 222 00:09:53,670 --> 00:09:57,330 like Java and C# and C++. 223 00:09:57,330 --> 00:09:58,890 Another thing to consider is to 224 00:09:58,890 --> 00:10:02,149 replace inheritance with delegation. 225 00:10:02,149 --> 00:10:04,350 Inheritance is a very strong relationship 226 00:10:04,350 --> 00:10:07,830 between classes, and sometimes it's too strong. 227 00:10:07,830 --> 00:10:10,680 So, imagine there's a superclass. 228 00:10:10,680 --> 00:10:12,990 Imagine there's a class here. 229 00:10:12,990 --> 00:10:15,810 I'll call it class C, okay. 230 00:10:15,810 --> 00:10:17,823 And that class defines some methods. 231 00:10:19,140 --> 00:10:22,620 You might find you're defining another class down here. 232 00:10:22,620 --> 00:10:24,633 Okay, I'll call this class D, 233 00:10:25,590 --> 00:10:27,450 and you might imagine that that class 234 00:10:27,450 --> 00:10:30,000 also wants to have these methods. 235 00:10:30,000 --> 00:10:32,730 So you might consider using inheritance, 236 00:10:32,730 --> 00:10:35,040 that's the UML notation for inheritance, 237 00:10:35,040 --> 00:10:38,160 that means that this class automatically inherits 238 00:10:38,160 --> 00:10:41,010 the methods from the superclass. 239 00:10:41,010 --> 00:10:45,060 Okay? So, that's often the correct relationship. 240 00:10:45,060 --> 00:10:47,823 Inheritance is basically free reuse, 241 00:10:48,720 --> 00:10:52,110 but sometimes inheritance is too strong a relationship. 242 00:10:52,110 --> 00:10:53,910 Sometimes, instead of inheriting, 243 00:10:53,910 --> 00:10:55,800 instead of inheriting from a class, 244 00:10:55,800 --> 00:10:57,630 you should have delegation 245 00:10:57,630 --> 00:11:01,980 where some class contains some methods. 246 00:11:01,980 --> 00:11:03,570 Instead of inheriting from it, 247 00:11:03,570 --> 00:11:06,030 maybe the other class over here 248 00:11:06,030 --> 00:11:08,853 should contain a pointer to one of those objects. 249 00:11:09,690 --> 00:11:11,550 Okay? Like that. 250 00:11:11,550 --> 00:11:13,980 So this is more of a "has a" relationship 251 00:11:13,980 --> 00:11:15,453 than an "is a" relationship. 252 00:11:16,350 --> 00:11:18,270 So this, these methods here 253 00:11:18,270 --> 00:11:20,760 are not automatically surfaced in the D class. 254 00:11:20,760 --> 00:11:23,460 You would have to define like a rapid method here, 255 00:11:23,460 --> 00:11:25,350 and a rapid method here, 256 00:11:25,350 --> 00:11:27,480 So that if you call that method, 257 00:11:27,480 --> 00:11:31,380 in there, you would kind of manually invoke that method 258 00:11:31,380 --> 00:11:34,200 or you would manually invoke that method. 259 00:11:34,200 --> 00:11:38,040 So the question between inheritance and delegation 260 00:11:38,040 --> 00:11:39,990 is whether you have an "is a" relationship, 261 00:11:39,990 --> 00:11:42,420 like ProductCollection, 262 00:11:42,420 --> 00:11:44,700 a Basket is a kind of ProductCollection, 263 00:11:44,700 --> 00:11:46,530 or whether you have a "has a" relationship. 264 00:11:46,530 --> 00:11:48,670 So for example, you know, a person 265 00:11:49,560 --> 00:11:52,860 has a car, okay, that's a "has a" relationship. 266 00:11:52,860 --> 00:11:56,190 A person has a car, a person isn't a kind of car, 267 00:11:56,190 --> 00:11:59,130 which would suggest inheritance, a person has a car. 268 00:11:59,130 --> 00:12:01,470 So that suggests a delegation model. 269 00:12:01,470 --> 00:12:03,420 Objects pointed at other objects. 270 00:12:03,420 --> 00:12:04,890 So it's a design question. 271 00:12:04,890 --> 00:12:07,590 You just have to consider what fits 272 00:12:07,590 --> 00:12:08,850 in any particular situation 273 00:12:08,850 --> 00:12:10,850 when you have multiple classes involved.