WEBVTT 00:00.420 --> 00:00.970 Hi. 00:01.080 --> 00:08.850 Welcome to the next section of this course visitor state mediator and observer design patterns in this 00:08.850 --> 00:09.540 section. 00:09.540 --> 00:12.030 We are going to look at four more design patterns. 00:12.030 --> 00:17.640 We'll start with the visitor pattern which is very useful when you want to abstract away some functionality 00:17.640 --> 00:19.500 from a set of objects. 00:19.500 --> 00:24.720 Then we'll move on to the state design pattern used commonly to build finite state machines. 00:24.720 --> 00:30.110 That is F S M and also we will learn about the mediated design pattern. 00:30.120 --> 00:35.790 Finally we will look into the observer pattern which is commonly used in event driven architectures 00:36.120 --> 00:41.210 and is gaining a lot of traction again especially in the micro services world. 00:41.220 --> 00:48.400 Now we move on to the first video of the section that deals with visitor design patterns in this video. 00:48.430 --> 00:54.220 We are going to delegate some logic of an object type to an external type called the visitor that will 00:54.220 --> 00:57.550 visit our object to perform operations on it. 00:57.550 --> 01:03.400 Then we will emulate an online shop with a few products in the visitor design pattern. 01:03.430 --> 01:09.500 We are trying to separate the logic needed to work with a specific object outside of the object itself. 01:09.790 --> 01:14.800 So we could have many different visitors to do some things to specific types. 01:14.800 --> 01:19.290 For example imagine that we have a log writer that writes to console. 01:19.420 --> 01:24.500 We could make the logger visible so that you can prepare and any text to each log. 01:24.610 --> 01:31.150 We could write a visitor pattern that prepares the date the time and the host to a field stored in the 01:31.150 --> 01:34.410 object with behavioral design patterns. 01:34.420 --> 01:39.340 We are mainly dealing with algorithms visitor patterns are not an exception. 01:39.370 --> 01:43.070 The objectives that we are trying to achieve are listed here. 01:43.070 --> 01:49.210 The first and foremost is to separate the algorithm of some type from its implementation within some 01:49.300 --> 01:50.440 other type. 01:50.440 --> 01:56.650 Secondly to improve the flexibility of some types by using them with little or no logic at all. 01:56.680 --> 02:02.110 So all new functionality can be added without altering the object structure. 02:02.110 --> 02:09.340 And lastly to fix a structure or behavior that would break the open or closed principle and a type you 02:09.340 --> 02:15.680 might be thinking what the open or closed principle is in computer science the open or closed principles 02:15.730 --> 02:21.940 states that entity should be open for extension but closed for modification. 02:21.940 --> 02:28.240 This simple statement has lots of implications that allows building more maintainable software and less 02:28.240 --> 02:34.150 prone to errors and the visitor pattern helps us to delegate some commonly changing algorithm from a 02:34.150 --> 02:40.750 type that we need to be stable to an external type that can change often without affecting our original 02:40.750 --> 02:41.640 one. 02:41.710 --> 02:47.890 We are going to develop a simple log Pender as an example of the visitor pattern following the approach 02:47.920 --> 02:49.780 we have had in previous sections. 02:49.780 --> 02:55.330 We will start with an extremely simple example to clearly understand how the visitor design pattern 02:55.330 --> 02:58.700 works before jumping to a more complex one. 02:58.720 --> 03:04.860 We have already developed similar examples modifying texts too but in slightly different ways. 03:04.930 --> 03:07.740 But this particular example we will create a visitor. 03:07.740 --> 03:08.350 That depends. 03:08.350 --> 03:13.990 Different information to the types it visits to effectively use the visitor design pattern. 03:13.990 --> 03:21.350 We must have two roles a visitor and a visitor all the visitors is the type that will act within a visitor 03:21.350 --> 03:28.130 all type so a visitor will interface implementation has an algorithm detached to the visitor type. 03:28.540 --> 03:30.860 So we need to message loggers. 03:30.880 --> 03:37.510 Message A and message be that will print a message with an A or B respectively before the message. 03:37.510 --> 03:41.800 Also we need a visitor to be able to modify the message to be printed. 03:41.800 --> 03:47.400 It will append the text visited a or visited B to them respectively. 03:47.410 --> 03:53.100 Now that you are well aware of what we want to do in this video let's start with the unit tests. 03:53.260 --> 03:57.970 As we mentioned before we will need a role for the visitor and the visitor visible interfaces. 03:57.970 --> 03:59.680 They will be interfaces. 03:59.710 --> 04:03.270 We also need the message a and message be struct. 04:03.280 --> 04:09.640 I have created two files visit dot go and visitor underscore test dot go within the folder named visitor 04:10.180 --> 04:12.960 open the file visitor dot go. 04:12.960 --> 04:20.530 Let us start adding the code first we add the package name import statement struct and interfaces the 04:20.530 --> 04:23.320 types message a and message be struct. 04:23.380 --> 04:29.410 Both have a message field to store the text that they will print the output Io dot writer will implement 04:29.440 --> 04:36.700 the OS dot FTD out interface by default or a new Io dot writer interface like the one we will use to 04:36.700 --> 04:43.710 check the contents are correct the visitor interface has a visit method one for each visitor all interfaces 04:43.720 --> 04:50.470 message J and message B type the visitor will interface has a method called accept visitor that will 04:50.470 --> 04:56.560 execute the decoupled algorithm like in previous examples we will create a type that implements the 04:56.590 --> 05:03.190 IO dot write a package so that we can use it in tests close this file and move to the visitor test dot 05:03.190 --> 05:04.780 go file. 05:04.780 --> 05:11.740 Here we first at the package name testing helper struct and a function test helper the test helper struct 05:11.740 --> 05:17.650 implements the IO dot writer interface its functionality is quite simple it stores the written bytes 05:17.680 --> 05:24.190 on the received field later we can check the contents of received to test against our expected value 05:24.910 --> 05:30.550 we will write just one test that will check the overall correctness of the code within this test we 05:30.550 --> 05:35.590 will write two sub tests one for message J and one for message B types. 05:35.590 --> 05:37.640 Let's do this okay. 05:37.960 --> 05:44.140 We will use a test helper struct and the message visitor struct on each test for each message type. 05:44.140 --> 05:48.420 Now let's first add the code to test the message a type done. 05:48.540 --> 05:50.230 This is the full first test. 05:50.430 --> 05:56.490 We created message a struct giving it a value hello world for the message field and the pointer to test 05:56.500 --> 05:59.540 helper which we created at the beginning of the test. 05:59.640 --> 06:06.390 Then we execute its accept method inside the accept visitor method on the message a struct the visitor 06:06.390 --> 06:11.480 a message a method is executed to alter the contents of the message field. 06:11.490 --> 06:15.490 That's why we passed the pointer to visit a method without a pointer. 06:15.540 --> 06:21.660 The contents won't be persisted to test if the visitor type has done its job within the accept method 06:21.960 --> 06:28.620 we must call the print method on the message a type later this way the message a struct must write to 06:28.620 --> 06:35.970 the contents of MSA G to the provided I O dot right to interface that is our test helper the last part 06:35.970 --> 06:41.760 of the test is the check according to the description of the acceptance criteria to the output text 06:41.760 --> 06:48.600 of message a type must be prefixed with the text a the stored message and the text visited just at the 06:48.600 --> 06:48.960 end. 06:49.410 --> 06:57.570 So for the message a type the expected text must be in double quotes a call on hello world and in parentheses 06:57.840 --> 06:58.890 visited. 06:58.890 --> 07:05.030 This is the check that we did in the F section the message B type has a very similar implementation. 07:05.160 --> 07:07.960 Let's write the code for message B type. 07:08.310 --> 07:14.690 In fact we have just changed the type from message to message B and the expected text now is B. 07:14.760 --> 07:22.680 Hello world in parentheses visited b the message field is also hello world and we also use the test 07:22.680 --> 07:23.800 helper type. 07:24.060 --> 07:29.700 We still like the correct implementations of the interfaces to compile the code and run the tests the 07:29.700 --> 07:36.090 message and message be struct have to implement the accept visitor method let's save this file and to 07:36.090 --> 07:42.420 go back to our visitor dot go file so here let us had the code to implement the accept visitor method 07:42.420 --> 07:46.080 for message and message be perfect. 07:46.080 --> 07:52.090 Next we add the code for the implementations of the visitor method for message a and message be that 07:52.090 --> 07:54.450 are declared on the visitor interface. 07:54.570 --> 07:58.420 The message visitor interface is the type that must implement them. 07:58.640 --> 08:02.630 Finally we will create a print method for each message type. 08:02.630 --> 08:08.130 This is the method that we will use to test the contents of the message field on each type. 08:08.130 --> 08:08.810 Let's do it 08:12.500 --> 08:15.970 now we can run the tests to really check if they are failing yet. 08:16.100 --> 08:17.790 So let's save the file. 08:17.930 --> 08:26.800 Open the terminal run the command go test hyphen v the outputs of the tests are clear the expected messages 08:26.800 --> 08:29.680 were incorrect because the contents were empty. 08:29.740 --> 08:32.730 It's time to create the implementations. 08:32.830 --> 08:39.190 We will start completing the implementation of the visit a message a and visit B message B methods open 08:39.190 --> 08:43.590 the visitor dot go file in the type of message visitors struct. 08:43.630 --> 08:48.540 Add this line similarly we do this for message B Co. 08:48.820 --> 08:51.240 Its functionality is quite straightforward. 08:51.310 --> 08:57.670 The FMC dot Sprint f method returns a formatted string with the actual contents of m dot message. 08:57.850 --> 09:04.420 A white space and the message visited this string will be stored on the message field overriding the 09:04.420 --> 09:05.760 previous contents. 09:05.800 --> 09:12.820 Now let us develop the accept method for each message type that must execute the corresponding visitor. 09:12.820 --> 09:15.320 This small code has some implications on it. 09:15.430 --> 09:20.980 In both cases we are using a visitor which in our example is exactly the same as the message visitor 09:20.980 --> 09:24.400 interface but they could be completely different. 09:24.400 --> 09:30.370 The key is to understand that the visitor pattern executes an algorithm in its visit method that deals 09:30.370 --> 09:32.380 with the vegetable object. 09:32.380 --> 09:35.500 What could the visitor be doing in this example. 09:35.500 --> 09:40.230 It alters the visitor will object but it could be simply factoring information from it. 09:40.240 --> 09:46.420 For example we could have a person type with lots of fields names or name age address City postal code 09:46.450 --> 09:47.410 and so on. 09:47.410 --> 09:54.430 We could write a visitor to fetch the name and the surname from a person as a unique string of visitors 09:54.430 --> 09:59.050 to fetch the address info for different sections of an app and so on. 09:59.050 --> 10:05.050 Finally there is the print method which will help us to test the types we mentioned before that it must 10:05.050 --> 10:08.390 print to the SD out called by default. 10:08.410 --> 10:15.940 Let's add the print method next it first checks the contents of the output field to assign the output 10:15.940 --> 10:23.730 of the OS dot SD out call in case it is no in our tests we are storing a pointer there to our test helper 10:23.740 --> 10:24.290 type. 10:24.430 --> 10:27.680 So this line is never executed in our test. 10:27.700 --> 10:31.410 Finally each message type prints to the output field. 10:31.570 --> 10:34.450 The full message stored in the message field. 10:34.540 --> 10:40.390 This is done by using the F print f method which takes an IO dot write the package as the first argument 10:40.690 --> 10:45.520 and the text to format as the next argument before you save the file. 10:45.520 --> 10:50.390 Remember to include LMT and OS in the import statement. 10:50.620 --> 10:56.200 Our implementation is now complete and we can run the test again to see if they all pass. 10:56.290 --> 11:02.950 Now save the file move to the terminal and run the command go test hyphen V. 11:02.950 --> 11:04.380 Everything is okay. 11:04.420 --> 11:09.760 The visitor pattern has done its job flawlessly and the message contents were altered after calling 11:09.760 --> 11:11.150 their visit methods. 11:11.320 --> 11:17.380 The very important thing here is that we can add more functionality to both the struct message and message 11:17.380 --> 11:19.870 B without altering their types. 11:19.870 --> 11:24.030 We can just create a new visitor type that does everything on the vegetable. 11:24.070 --> 11:30.110 For example we can create a visitor to add a method that prints the contents of the message failed. 11:30.190 --> 11:33.710 Let's go back to visit a dot go at the end of the code. 11:33.880 --> 11:36.790 Add the message field visitor printer struct. 11:36.790 --> 11:42.360 So we have just added some functionality to both types without altering their contents. 11:42.370 --> 11:45.820 That's the power of the visitor design pattern. 11:45.850 --> 11:48.180 Now we will develop a second example. 11:48.310 --> 11:50.120 This one a bit more complex. 11:50.320 --> 11:54.400 In this case we will emulate an online shop with a few products. 11:54.460 --> 11:59.230 The products will have plane types with just fields and we will make a couple of visitors to deal with 11:59.230 --> 12:00.870 them in the group. 12:00.940 --> 12:08.080 First of all we will develop the interfaces open visitor dot go and the interfaces the product info 12:08.080 --> 12:13.810 retrieval type has a method to get the price and the name of the product the visitor interface like 12:13.810 --> 12:20.860 before has a visit method that accepts the product info retrieval type finally visible interface is 12:20.860 --> 12:22.260 exactly the same. 12:22.300 --> 12:26.500 It has an accept method that takes a visitor type as an argument. 12:26.500 --> 12:31.420 All the products on the online shop must implement the product info retrieve or type. 12:31.510 --> 12:37.170 Also most products will have some common fields such as name or price. 12:37.270 --> 12:43.600 Now we'll create the product type implements the product info receiver and visitor will interfaces and 12:43.600 --> 12:45.520 embed it on each product. 12:45.520 --> 12:47.110 Let's do it. 12:47.110 --> 12:52.480 Now we have a very generic product type that can store the information about almost any product of the 12:52.480 --> 12:53.240 shop. 12:53.290 --> 12:57.360 For example we could have a rice and pasta product. 12:57.430 --> 13:00.590 So next we add the rice and pasta struct. 13:00.610 --> 13:03.190 Each has the product type embedded. 13:03.190 --> 13:08.980 Now we need to create a couple of visitors interfaces one that sums the price of all products and one 13:08.980 --> 13:11.130 that prints the name of each product. 13:11.140 --> 13:13.060 Here we are the code for it. 13:13.060 --> 13:19.240 The price visitors struct takes the value of the price variable of the product info retrieval type past 13:19.390 --> 13:22.930 as an argument and it adds it to the sum field. 13:22.930 --> 13:28.810 The name printers struct stores the name of the product info retrieve type past as an argument and appends 13:28.810 --> 13:33.900 it to a new line on the product list field time for the main function. 13:34.030 --> 13:38.590 Let's add the main function we create a slice of two visible objects. 13:38.680 --> 13:42.210 A rice and pass the type with some arbitrary names. 13:42.280 --> 13:46.930 Then we iterate for each of them using a price visitor instance as an argument. 13:47.170 --> 13:50.480 We print the total price after the range for. 13:50.500 --> 13:55.930 Finally we repeat this operation with the name printer and print the resulting product list. 13:55.990 --> 14:02.710 Let's go ahead and check the output of this main function save the file and move to the terminal type. 14:02.710 --> 14:06.040 The command go run visitor dot go. 14:06.290 --> 14:09.090 Okay this is a nice example of the visitor pattern. 14:09.100 --> 14:12.750 But what if there are special considerations about a product. 14:12.850 --> 14:17.450 For example what do we need to some twenty to the total price of a fridge type. 14:17.920 --> 14:19.950 Okay let's write the fridge structure. 14:20.710 --> 14:27.900 Let's go back to visit her daughter go and write the fridge structure over here the idea here is just 14:27.900 --> 14:32.220 to override the get price method to return the product's price plus 20. 14:32.430 --> 14:35.610 For this we add the next lines of code. 14:35.610 --> 14:38.670 Unfortunately this isn't enough for our example. 14:38.790 --> 14:45.030 The fridge structure is not of a visitor will type the product struct is of a visible type and the fridge 14:45.030 --> 14:47.040 struct has a product struct embedded. 14:47.130 --> 14:53.320 But as we mentioned in earlier sections a type that embeds a second type cannot be considered. 14:53.370 --> 15:00.180 Of that latter type even when it has all its fields and methods the solution is to implement the accept 15:00.180 --> 15:03.600 visitor method so that it can be considered as a vegetable. 15:04.350 --> 15:10.200 Let's rewrite the main function to add this new fridge product to the slice since we added a third product. 15:10.200 --> 15:15.010 Let's change this value to 3 everything else continues the same. 15:15.010 --> 15:18.290 Running this new main function produces a different output. 15:18.350 --> 15:25.450 Let's run the test and check it first save the file and move to the terminal run the command go run 15:25.450 --> 15:32.980 visitor dot go as expected the total price is higher now outputting the sum of the rice the pasta and 15:32.980 --> 15:33.970 the fridge. 15:33.970 --> 15:40.000 We could be adding visitors forever to these products but the idea is clear we decoupled some algorithms 15:40.090 --> 15:42.400 outside of the types to the visitors. 15:42.400 --> 15:43.600 Superb. 15:43.870 --> 15:49.930 In this video we have seen a powerful abstraction to add new algorithms to some types using the visitor 15:49.930 --> 15:52.720 design patterns in the next video. 15:52.750 --> 15:55.390 We will learn about the state design patterns.