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

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.