Interfaces can be difficult to grasp for beginning programmers...
Today I want to go over Interfaces. What are they? Why do you need them? Why should you use them? What are the Advantages and Disadvantages of using them?
Now the reason I've decided to go over this today is that early on I got some extremely vague and misleading information about them and their use that really did not explain what the advantages of using interfaces were. And I got the electrical socket example....
Size is for emphasis as that is more or less 90% of every tutorial you will ever find's emphasis on this example. Seriously, the eletrical socket example is not helpful. I lost count of how many people broke into how electricity works through a plug and yet did not explain interfaces in a way that made any sense. Great news though, I now know the difference between US and UK electricity standards...
Its like you asked for a recipe for soup and instead got an hour long lecture on the history of pots and fire. Wow!
Never fear though, it took a while and a lot of digging, but I really was able to uncover just a portion of their power and some real reasons you will find them to be useful.
This one is obvious and true. Classes that inherit from interfaces will be forced to use the same method calls. This does help enforce standards on your code and make it much more uniform. Is that alone a good reason to use interfaces? Hardly. Call it a side benefit.
Getting closer to the magic. Lets start with 2 classes. Both have a method on it called Run().
Both of these implement a Run method. Now lets try to call them as is from a new script:
In this example, we construct new instances of each class and use the Run Command. One thing we CANNOT DO is use one variable reference for both (well without boxing). Ok, so now what if we want to add these to a collection of executed commands? Well we can do that, but we would have to box and unbox the commands. I'm definitely not recommending this as it is very computationally expensive, but this is how it would look:
Now in this example, you are boxing the objects into a list of objects. This is not good. Now at this point, you could unbox them and use them, but lets not do that. Instead lets backtrack and implement an Interface instead.
So now we have an Interface called IRunMethods (Interfaces generally start with an uppercase "I"). Here we've created a generic Run() method that every object that implements the Interface must also implement. Its important to note that you do not create any concrete code here, just define the method. It is important to note that if the method requires an argument, it must be indicated in the Interface. Ok now we are going to have each of our items inherit from the interface.
Note. They now both inherit from the IRunMethods Interface. Since they both already had a Run method, the interface is technically already implemented. Otherwise, Visual Studio would have made us implement it (note: it won't compile until implemented). Now we can do something way different. Lets go to our original Test class:
Apologies. A lot done here. Ok so first we stored the values as the interface IRunMethods instead as an instance of their classes. Just like before, we were able to get to the run command. Unlike before, we were able to add it to a list of IRunMethods, requiring no boxing or unboxing.
Look at RecallMethods. This is a simple method that hits the Run() command on each IRunMethods in savedCommands without caring whether they are the same class or not.
True power time. Look at the variable e3. In the ExecuteMethods() method, we were able to instantiate that as an Example1, then use the Run() command, then we instantiated it as an Example2 and used the Run() command. This means we can instantiate variables of differing types through their collective interfaces and run the interface commands! Ok, so lets do some cleanup:
A little house cleaning. I'm reaching through the interface, I was able to use a simple loop to change the value of executedCommand to be either Example1 or Example2, then use the Run() command, then save it to the list of Saved Commands.
YES! Now since Interfaces do not implement concrete code, the actual Run() command on Example1 and Example2 can be wildly different and still will run.
The above example will result in 2 separate lines in your Unity Debug console. (Also note, you really shouldn't put different classes in the same file -- you will regret it as your project grows).
By this method, you can only call things that are implemented by the interface. If another method is on the class object that isn't in the interface, you will not be able to call it. There are ways around that, but that is for a different post. Also your classes have to implement everything on the interface. If you have 7 items on an interface, each item that uses it must implement all 7 each time. Keep in mind though that classes can inherit from multiple interfaces, so it is a better practice to split them up to keep them at a minimum must-need basis.
Also make sure that the platform you are designing to does not limit your usage of Interfaces. Some VR platforms will tell you not to use interfaces (or foreach loops for that matter). Most devices, including mobile, are robust enough to handle interfaces.
YES! Many coding design patterns utilize interfaces in their strategies. While this is for another post, understanding their power will really help you in understanding and implementing these patterns in your games.
Lets say you have a bunch of objects that need to perform actions when the game ends. You could call these manually...or you could have them implement a IGameOver interface with a command or a series of Commands which run when the game ends. If you use the Observer Pattern on startup, each object with the command could register itself with an Observer carrying a IGameOver List or Array. When the game ends, all you have to do is dial up the observer and Update the subscribers, which would perform a command on each of them.
Observer Pattern though is for a different post...But for now this video does a good job explaining the theory.