Inheriting from a class that inherits from monobehaviour
Hey guys i was wondering if it is bad practice to inherit from a class you have made which also inherits from monobehaviour.
EX:
Item : Monobehaviour
Weapon : Item
Is this a bad practice? I need my weapon script to be a monobehaviour so i can place it on a GameObject and I have alot of functionality needed from Item already made. If i was to do it this way what would be the benefits/downfalls, and also would it cause the weapon script to be running 2 main functions each time? Such as calling Update from Item and Weapon each frame?
Thanks for the help guys any advice on this matter will be greatly appreciated as I am still trying to get into the programming side.
Answer by Bunny83 · May 21, 2011 at 12:37 AM
A bad idea? - No!, that's the point in OOP :D
Just some basics:
public class Item : Monobehavior
{
public void SomeFunction() { Debug.Log("Item.SomeFunction"); }
}
public class Weapon : Item
{
public void SomeFunction()
{
base.SomeFunction(); // Calls Item.SomeFunction
Debug.Log("Weapon.SomeFunction");
}
}
This first case is "hiding" a function because you implement the same function that is inherited from Item. I've seen a lot people that do such things. But that's not good because a reference to a weapon class that is of type Item will not call the new function.
Weapon weaponRef = GetComponent<Weapon>();
weaponRef.SomeFunction(); // prints: Item.SomeFunction + Weapon.SomeFunction
Item itemRef = weaponRef;
itemRef.SomeFunction(); // prints just: Item.SomeFunction
Now the real OOP way:
public class Item : Monobehavior
{
public virtual void SomeFunction() { Debug.Log("Item.SomeFunction"); }
}
public class Weapon : Item
{
public override void SomeFunction()
{
base.SomeFunction(); // Calls Item.SomeFunction
Debug.Log("Weapon.SomeFunction");
}
}
Now the function is declared as "virtual". In our Weapon-class we override the virtual function. That way the base function get "replaced" by the new version in the Weapon class.
Weapon weaponRef = GetComponent<Weapon>();
weaponRef.SomeFunction(); // prints: Item.SomeFunction + Weapon.SomeFunction
Item itemRef = weaponRef;
itemRef.SomeFunction(); // prints: Item.SomeFunction + Weapon.SomeFunction
You don't have to call the inherited function in your derived class if you don't need it.
public class Ammo : Item
{
public override void SomeFunction()
{
Debug.Log("Ammo.SomeFunction");
}
}
Ammo ammoRef = GetComponent<Ammo>();
ammoRef.SomeFunction(); // prints: Ammo.SomeFunction
Item itemRef = ammoRef;
itemRef.SomeFunction(); // prints: Ammo.SomeFunction
There are still some special cases but that's the most important thing about OOP ;)
Wow, Thanks a ton bunny! I feel that i have the knowledge that I need for what I am doing now. So in other words taking the tooltip for example as a function of my Item(which inherits monobehaviour) Items Tooltip Function would display the items name only. So if i was in Weapon class that inherits from Item and called the tooltip it would only list the name, but if i overloaded it i could add more to the tooltip such as the weapons type(ie. Handgun) aswell as still gettin the items name...
Thank you so much for taking the time to type all this and if im incorrect please tell me
That's correct ;) but you have to be careful: overloading and overriding is something different. Overloading: http://en.wikipedia.org/wiki/Function_overloading Overriding: http://en.wikipedia.org/wiki/Virtual_function#C.23
Excellent answer. I was looking for that. I cant thumb up because system does not allow me :(
Where would you put this line ? public Weapon weaponRef = GetComponent();
I assume Item and Weapon are part of a script attached to a GameObject.
The "public" actually was a typo ^^. I removed it. That code is supposed to be executed inside some method where you want to use that item or specifically use the methods that the item implements.
Item and Weapon are classes which are defined above in my answer. Each class should be in it's own file which has the same filename as the containing class name.
If you have further questions, please ask a seperate question.
Thanks a lot, your kick-ass answer is super helpful ! Now, how can I extend the Ammo class once more but I this time I want to call Item's SomeFunction() method ?
Answer by DaveA · May 20, 2011 at 10:59 PM
I think it's a good idea actually, but make sure you're following the correct procedures to call the 'base' class as needed (a little different JS vs C#) Functions like Update and Start and such will only run the lowest-level subclass, so if you need to call something in Item from Weapon, you may need to be explicit about that, but it won't run both, no.
Hey man thanks for the quick reply, by calling something in item from weapon do you mean functions? Such as in my item class i have a public function called Tooltip which builds a string with the tooltip info and returns it. So if i used it in Weapon all i have to do is call the Tooltip function... is this what you were referring to? Thanks again for the help man and glad to hear that is not a bad idea =P
No actually if you don't overload Item's Tooltip in Weapon, it will just automatically work for Weapons. FWIW I also have a Tooltip function in a base class, so I know this works ;-) You can of course overload Tooltip in Weapon, and it will use that ins$$anonymous$$d. The point is, if you want to call both, you might need to call Item's Tooltip from inside Weapon's Tooltip (if you needed to have Weapon.Tooltip, that is). That's pretty standard behavior, but just in case you're new to OO languages.
As an illustration, I have a subclass (call it B) which inherits from, let's say A. A has a Start function. B has a Start function too. I have instance of both A and B in my scene. B will not automatically call A's Start function, only B's Start function. But I need the functionality in A's Start function. I could copy/paste it into B's, but ins$$anonymous$$d I have (in A) an Init function. Then both A and B's Start function just call Init(), and they both end up using the same Init (B does not have it's own Init() function to overload A's).
Also thank you very much for your time Dave, I gave you a thumbs up for your time. Thanks again!
Answer by DanielSig · Sep 04, 2012 at 08:57 PM
I'm sorry but OOP is an outdated design pattern. I used OOP for many years before I realized the beauty and power of the component-based design pattern and I don't want to see others waste the same amount of time. There's a VERY GOOD reason why Unity favors the component-based pattern and encourages everyone to inherit only from Monobehaviors.
Making tightly coupled code is like gluing a driver to his seat and his clothes to his skin. Inheritance is the incarnation of tight coupling. How can one class be more tightly coupled to another than inheriting from it?
Code is supposed to be reused by creating reusable components and we're supposed to use SendMessage for all component-component communications.
Personally I use sendMessage only for discrete data (like OnDie, OnJump, OnSpawn etc). For continuous data (like IsAlive, IsGrounded, Speed, Health, Ammo etc.) I made an extension method for components so they can request data anonymously, in which any component with a fitting handler will reply.
The component-based pattern in Unity is very similar to the mediator pattern where each GameObject is in fact a mediator.
Once you see it... ...you'll shit bricks!
Sorry, but OOP is not a design pattern. It's the base for almost all software design patterns.
Inheritance is also a base feature of OOP and is used in most design patterns. The "component based" pattern is actually called Strategy where you usually inherit your class either from a base class or an interface. In the case of Unity it's a bit tricky since the calling system is a native code system which is outside of the $$anonymous$$ono environment. But Unity still defines a clear interface which you can use.
I don't see any mediator pattern in Unity. GameObjects can actually do nothing. It's just a container for components. It's only purpose is to store the components and offer a way to access them. A mediator is also a class which has to be created and in this class you handle interactions between the other classes. The mediator is (like a lot other patterns) a derivative of the Strategy pattern.
Dear oh dear - people do get religious about these things ;) Perhaps you've never built any really big systems!
You cannot define in the inheritance tree I wrote above the correct place to put aggressive methods, because of the straightjacket.
Animal > AggressiveAnimal > Swim$$anonymous$$gAnimal
Clearly mad, everything that swims is not aggressive
Animal > Swim$$anonymous$$gAnimal > AggressiveAnimal > Fish
You get my point I'm sure. Fish aren't all aggressive.
Animal > Swim$$anonymous$$gAnimal > Fish > AggressiveAnimal
Where do you put tiger?
OOP by long inheritance trees is really really old hat.
What you're doing there there is over-classifying. You're example weaves attributes in with classification. For example, "aggression" should be an attribute on Animal that can be adjusted on each sub-species. Further, it would allow the developer to make an animal more aggressive in specific cases by changing that value.
And it would depend on your application for whether or not you'd actually classify hundreds of fish. If you were making an aquarium simulation or Ridiculous Fishing then yes, you would classify them all. If you just need randomized behaviour, then probably not.
Just don't go around clai$$anonymous$$g that OOP is a "straight jacket" when you're not experienced enough at modeling objects. Component-based design can be abused just as badly.
Yeah, we've all been there and there's no hard answers; It's all done on a case by case basis, sometimes even within the same project. One part may be all component-driven, another part may require a (clean) object hierarchy.
I just didn't like the blanket statements @whydoidoit was using. It might cause new developers to disregard probably one of the most common and crucial program$$anonymous$$g methodologies before they understand it. You're allowed to disregard any coding practice so long as you understand what you're giving up. Likewise, there is no "one true way" so blanket statements like "don't use X" are foolish at best.
I fail to see the point of arguing over whether inheritance is better than a component-based approach (or vice versa). It's the same as arguing whether a screwdriver is better than a hammer; apples and oranges, as vargonian said above.
In OOP, you have two ways to build objects: inheritance and composition. Saying one is not needed, limits you very severely.