- Home /
Polymorphism question for my custom class
Hi all,
I have used polymorphism in the past, but not with anything too complicated. This time, I have a class which stores a bunch of info about the object I pass into it, and I wrote a very similar class for a different type of object in another script, which seems like a waste of time and typing since I could probably make a master class, and use copies of it instead of writing it over and over again, but I'm not 100% it's a good idea for this specific class.
Here is the first class I wrote:
public class HUDElement { // Store object information for the passed GameObject private HUDSettings.HUDType _hudType; private HUDSettings.NGUIType _nguiType; private GameObject _hudObject; private GUITexture _gTexture; private bool _hudState; private bool _isControl;
public HUDElement( HUDSettings.HUDType type )
{
this._hudType = type;
}
public void Init( GameObject go, bool state, bool isControl, HUDSettings.NGUIType nguiType )
{
_hudObject = go;
_gTexture = go.GetComponent();
_hudState = state;
_nguiType = nguiType;
_isControl = isControl;
// Toggle HUD Element to its default starting state,
// this prevents HUD elements that are turned off by default from being activated on Initialization
Toggle( state, true );
}
public void Toggle( bool isActive, bool isInit = false )
{
if( _hudType == HUDSettings.HUDType.BOOST && isInit == false )
{
if( _hudObject ) _hudObject.SetActiveRecursively( isActive );
}
else
{
if( _nguiType != HUDSettings.NGUIType.NONE && _hudObject )
{
switch( _nguiType )
{
case HUDSettings.NGUIType.UISlicedSprite:
var uiSliceSprites = _hudObject.GetComponentsInChildren();
foreach( UISlicedSprite s in uiSliceSprites ) s.enabled = isActive;
break;
case HUDSettings.NGUIType.UISprite:
var uiSprites = _hudObject.GetComponentsInChildren();
foreach( UISprite s in uiSprites ) s.enabled = isActive;
break;
}
// No need to toggle the gameObject itself nor check for a GUITexture, so let's stop here.
return;
}
if( _gTexture != null ) _gTexture.enabled = isActive;
else if( _hudObject ) _hudObject.active = isActive;
}
}
public HUDSettings.HUDType HudType{ get{ return _hudType; }}
public HUDSettings.NGUIType NGuiType{ get{ return _nguiType; }}
public GameObject HudObject{ get{ return _hudObject; }}
public GUITexture GTexture{ get{ return _gTexture; }}
public bool HudState{ get{ return _hudState; } set{ _hudState = value; Toggle( _hudState ); }}
public bool IsControl{ get{ return _isControl; } set{ _isControl = value; Toggle( _isControl ); }}
}
private HUDElement[] HUDElements = new HUDElement[(int)HUDSettings.NumOfHUDTypes];
private HUDElement _mainHUD = null;
public HUDElement MainHUD
{
get
{
if( _mainHUD == null ) _mainHUD = HUDElements[(int)HUDSettings.HUDType.MAIN];
return _mainHUD;
}
}
private HUDElement _menuHUD = null;
public HUDElement MenuHUD
{
get
{
if( _menuHUD == null ) _menuHUD = HUDElements[(int)HUDSettings.HUDType.MENU];
return _menuHUD;
}
}
// more properties.......
}
Next, here's the second - similar - class I wrote for another type of object:
public class ControlElement { private ControlSettings.ControlType _controlType; private GameObject _controlObject; private bool _controlState;
public ControlElement( ControlSettings.ControlType type )
{
_controlType = type;
}
public void Init( GameObject go, bool state )
{
_controlObject = go;
_controlState = state;
// Toggle this Control Element to its default starting state,
// this prevents Control elements that are turned off by default from being activated on Initialization
Toggle( state );
}
public void Toggle( bool isActive )
{
_controlObject.active = isActive;
}
public ControlSettings.ControlType ControlType{ get{ return _controlType; }}
public GameObject ControlObject{ get{ return _controlObject; }}
public bool ControlState{ get{ return _controlState; } set{ _controlState = value; }}
}
private ControlElement[] ControlElements = new ControlElement[(int)ControlSettings.NumOfControlTypes];
private ControlElement _accelControl = null;
public ControlElement AccelControl
{
get
{
if( _accelControl == null ) _accelControl = ControlElements[(int)ControlSettings.ControlType.ACCEL_BTN];
return _accelControl;
}
}
private ControlElement _brakeControl = null;
public ControlElement BrakeControl
{
get
{
if( _brakeControl == null ) _brakeControl = ControlElements[(int)ControlSettings.ControlType.BRAKE_BTN];
return _brakeControl;
}
}
// more properties...
}
So my question is, given the differences between those 2 classes, is it still a good idea to use polymorphism with copies of 1 master class?
Thanks for your time guys!
Stephane
Answer by dubbreak · Feb 18, 2013 at 10:58 PM
In my opinion. No. I don't see a clear relationship. It's not a good use of polymorphism.
Using inheritance for the sake of reusing a few methods that might be the same (so you can use polymorphism) is a perfect example of when not to use it. Inheritance should only be used in clases with a clear is-a realtionship. E.g. an apple is a fruit (very simple) or recently I had a black jack hand which extends a card hand (collection of cards belonging to a player) that has special methods added to it that are black jack only (such as the hand value). It was clear that is was a sub case of the general case and the base case had other legitimate ways to extend it(e.g. poker hand).
Using inheritance for the sake of polymorphism alone is an anti pattern and an abuse of OOP. I worked with someone that did that, and his stuff was not maintainable. You should look at using an interface or some kind of class composition rather than inheritance if you are worried about code duplication.
Here is a good stackoverflow post on composition vs inheritance.
Inheritance can result in some pretty nasty lock in that's difficult to refactor out. This becomes especially apparent when it gets miss-used. The end uses aren't actually related so you end up with them diverging and the base class not actually working for either very well. I remember a base communication class that should have been an interface (to guarantee each communication type had the agreed on interface to get and send data). Instead is was a base class that locked in certain behavior and created some horrible coupling that was very difficult to fix.
Just a follow up: I'd like to say your question was a good question. Polymorphism is a really cool concept. However it is prone to misuse. I've seen people with "20+ year experience" misuse it (of course I've also seen people still in University catch the misuse of various language features of people way senior to them.. I've also had co-op students $$anonymous$$ch me a thing or two.. ). It's quite admirable that you'd stop to ask, "Hey, is this a good use for this concept?" That shows a lot more restraint than your average person and the type of drive to learn and understand that is seen in great coders (actually great people in any subject).
I remember seeing some code in which the guy must have just learned about function pointers, so they were used everywhere (and none of the uses were appropriate). It's like marketing guy learning to do the apple fake reflection thing and then using it in every piece of marketing material possible. Every logo, every product picture with a "reflection"!! The toughest thing is learning the appropriate time to use features. It's nice to have a new tool in the toolbox, but don't let that shiny new hammer make every problem look like a nail.
I personally abuse linq to no end. Like someone who thinks all things can be solved by a regex I tend to attack most problems with linq if it's remotely possible I can use it (favorite tool in the box I guess?).
Thanks a lot for your explanation, made lots of sense and was very helpful. It's not always clear to me when to/not-to use Polymorphism, and since I hate code duplication, I always try to find a way to get rid of it. But maybe sometimes, code duplication is a better choice.
I think a good rule of thumb on code duplication is: if you are copying and pasting large chunks of code from one area to another, you're doing something wrong.
Lots of basic stuff is going to look similar and like duplication, but in reality that's just the nature of wiring stuff up. It's all repetitive. It's duplicating algorithms and complex logic that should be avoided. Separation of concerns but also centralization.
Sometimes what's needed is a util class with some static methods that get used frequently throughout the code. Sometimes an extension method is the right choice. I found I was making sprites blink a lot in a project. So I wrote a blink extension methos I can use on basically any game object. That way I'm not forced to extend from some base class just to get that behavior. Works on ngui items, my own items etc.
One really good way to learn is going back over your code 6 months down the road and seeing if you'd do anything differently with what you know now. Not necessarily change it (if it ain't broke don't fix it), but file a mental note. One of my friends often says, "Hey that was me 6 months ago, I can't defend what that guy did. I'm sure he had a reason, but I wouldn't do it that way now." Constructive code reviews are good as well (i.e. not nitpicking on formatting and na$$anonymous$$g conventions, but focusing on overall structure). Different people approach problems differently, and seeing things from a new perspective can be eye opening. Downside is that requires peers and they aren't always readily available. I always found architecture meetings incredibly interesting (a bunch of engineers debating the best solution to a problem can be quite fun.. well if it's an ego free environment).
And at the risk of dragging on.. my card example, while a good use of inheritance it really isn't of any use if you aren't making multiple child classes. I already had the base card code and base card game logic, so extending it made sense. If I was making a blackjack game from scratch I'd have to evaluate whether there was value in separating a base class from the blackjack specific code. When writing games you want to be efficient. If you are never going to resuse the base class it's over-engineering. If I really needed to I could decompose the class into a base and child class later (and that would be the right thing to do.. wrong thing would be copy/pasting then changing what I needed for the other game). Sometimes shortcuts aren't bad (and sometimes they are more readable). In the end you want something readable and maintainable. Simple is good, it's just sometimes harder than writing complex code.
Yes having a util class is definitively a good thing. I have an extension methods class that I use from GameObject stuff, but I keep forgetting about it and I don't add new methods to it as often as I could hahaha
I started learning program$$anonymous$$g from scratch in 2010, and since then, I have looked at my code and re-wrote the whole structure for my game 3 times...it went from crappy beginner code to okay wannabe code to decent amateur code, and I'm pretty happy with it now :)
Thanks for all the examples and advice, much appreciated!!!
Glad I could be of help and happy to hear you are co$$anonymous$$g along. Regardless of level, people that care about their craft and are always looking to improve are the best to work with (and unfortunately rare).
Your answer
Follow this Question
Related Questions
An OS design issue: File types associated with their appropriate programs 1 Answer
Can I preserve the values in a parent class when I overwrite it with a child class? 1 Answer
Calling a method of a class that is a part of another class? 1 Answer
Abstract class question 1 Answer
The name 'Joystick' does not denote a valid type ('not found') 2 Answers