When to use components and when to use interfaces? (Damage example)

Hi everyone. I am working on a game and have been researching interfaces lately. I have always known about them, but never really used them myself (other than Unitys own like iPointHandlerDown)

I have a really hard time understanding when an interface would be more “useful” than a component. Many examples include an interface such as IDamageable. Then they say that interfaces are useful since you can have a Barrel, Player, Wall and they can all have the IDamageable interface if they are supposed to take damage. I understand why this is useful, but what i have been used to doing is creating a Health component. That health component contains a method called TakeDamage(int amount). Now when i take damage, i invoke a OnDamageTaken event/action, when i eventually have 0 health i invoke a OnHealthDepleted event/action. This way i have a Health component, but a Player component can subscribe to that Health components events/actions. The same goes for a Wall or Barrel component. For this exact reason i have a hard time understanding why an interface implementation of this would be more useful?

Thanks for helping me figuring it out!

Interfaces are useful for when you’ll have a wide distribution of implementations, all expressing the same, well… Interface!

Ergo, a variety of objects that maybe handle things slightly or very differently, but can still be interfaced with in the same manner.

Yes you could always use inheritance to permutate the implementations, but an interface doesn’t lock you into a concrete type.

And this isn’t to say you can’t use an interface and then a component that handles 90% of the use cases. Just then you have the flexibility to add an implementation that does something wildly different to the norm without any need for major hacks or refactoring.

(Dind’t mean to reply twice. I just thought i have posted an answer to my own question)

But wouldn’t i still be able to do this with just having a Health component and then having different events that get triggered? When i take damage i can have another script/component that subscribes to an event like OnDamageTaken and then do something slightly or very differently for each implementation? So i could have Player which has a reference to Health and then subscribes to Healths OnDamageTaken and then make it do something very different from Wall which also has a reference to Healths OnDamageTaken?

First of all, thank you for taking the time with helping me learning, it means a lot. I know this seems off topic, but i have another question that you might be able to answer?

I have a GameObject. On this GameObject i attach a script called House. House inherits from Building. Building has a field for BuildingData which i can assign in the editor. This means that from House i have access to the BuildingData.

Now on the House script i would like to show to the Player how much Income that house has and what the Population of the house is. The Income and Population are part of the HouseBuildingData, so even though i have attached a HouseBuildingData to the House component i would need to cast that from BuildingData to HouseBuildingData in order to access it, since House is inherting from Building and therefore i could technically just assign a BuildingData, which does not have an Income or Population field. Is this just the way it is or is there something i could do better / work around?

This below code is just to illustrate the relationship in the two ScriptableObjects

BuildingData : ScriptableObject
{
public string Name;
public int Price;
}
HouseBuildingData : BuildingData
{
public int Income
public int Population
}

I mean this is a lot of complexity and boilerplate which could be distilled down to:

if (someGameObject.TryGetComponent(out IDamageable damageable) == true)
{
    damageable.DealDamage(someDamage);
}

The point of interfaces is to help not have to care about any particular concrete implementation.

Sure your component may be all you need for now, but those requirements could change throughout the lifetime of the project. If you were abstracting this through an interface, you would have to change nothing on the consumer side. An interface is generally safer in this regard, and also gives you the freedom to do things like have a faux implementation for testing purposes:

public sealed class TestDamageable : MonoBehaviour, IDamageable
{
	void IDamageable.DealDamage(int damage)
	{
		Debug.Log($"{name} was dealt {damage} damage!", this);
	}
}

From my experience, I’ve very often had to refactor things to use an interface, but never the reverse.

Until recently I wouldn’t have agreed with what I’m about to say, but you shouldn’t be using inheritance to modify/add data only. The point of inheritance is to be able treat derived types as their parent type without having to ever care about any of the given derived types. The moment you do, then you have an anti pattern and shouldn’t be relying on inheritance for this.

Ironically, this is an instance where you probably want to be using interfaces to compose your various types of buildings:

public interface IBuilding
{
	string Name { get; }
	
	int Price { get; }
}

public interface IHomeBuilding : IBuilding
{
	int Income { get; }
	
	int Population { get; }
}

public class Building : ScriptableObject, IBuilding
{
	[SerializeField]
	private string _name;
	
	[SerializeField]
	private int _price;
	
	public string Name => _name;
	
	public int Price => _price;
}

public sealed class HomeBuilding : Building, IHomeBuilding
{
	[SerializeField]
	private int _income;
	
	[SerializeField]
	private int _population;
	
	public int Income => _income;
	
	public int Population => _population;
}

Then you can test for particular types of buildings with pattern matching, ergo (if someBuilding is IHomeBuilding homeBuilding).

It may seem similar and more long-winded, but as you need more types of buildings, it’s easier to compose this via interfaces rather than end up with some messy inheritance hierarchy.

But on top of that, you aren’t locked into using a scriptable object to represent your building’s. Because you’ve abstracted via an interface, you could potentially substitute the implementation with plain C# objects or monobehaviours.

Long story short, you just get more flexibility out of interfaces.

Alright the thing with interfaces definently makes more sense to me now. I could use the Health component for a lot of stuff (an Enemy, a Tree, a Player), but then by having Health : IDamageable, i am still able to add something later on that would never require any Health such as _currentHealth etc, but can still react if something “damages” it.

I think i get where you are going with Interfaces regarding the Building situtation, but im still a bit confused, especially with the pattern matching. My scriptable objects are only meant as a form of database which contains information such as (Health if im in Level 1, Health if im in Level 2) etc. Right now i am using pattern matching on my House script, since my House script is inherting from Building which has a BuildingData field on it. To access the Income field from BuildingData i am doing (BuildingData is HouseBuildingData houseBuildingData), so overall i don’t really understand what the difference would be with these two approaches (Interface or Inheritance), because in the end my House component would still need to do (BuildingData is HouseBuildingData houseBuildingData) to access Income and other fields

The difference with interfaces is that you can only inherit from one class, but you can implement any number of interfaces.

Thus you can compose a wider variety of combinations. With C# single-inheritance, you will very quickly hit a wall with how widely you can compose your data/functionality. What if you need a building that combined two different buildings? Can’t do that with inheritance, but you can with interfaces very easily.

That said, I don’t know why every building can’t have an Income and Population value, and some buildings just have these values set to zero if they’re not applicable. And then you can skip them from calculations, and not show the value for UI purposes as well. You know, KISS and all that.

to answer your question of “why not just use components” components work on GameObjects, there are other unity things where you can’t add components.

Alright i get that, but the thing with every building having every possible value isn’t that discouraged and “bad programming”? Some buildings might have an Attack Range, Damage, Fire Rate, while an economy building such as a House would only have Income, Population and other stuff like that. You could of course just have one big class called Building and then have values set to 0 if they do not “apply” thats true, but this does not seem like “clean code” haha

I only really implied it makes sense for those two particular values, and only potentially. But if you plan to have lots of values, then no you wouldn’t lump it all into the one class and instead want to compose all that. Which is all the more reason to be using interfaces.

Oh alright that makes more sense. I’m still having a hard time understanding my whole idea with having a Building such as a Tower and them i want to assign a scriptable objec to that prefab. The scriptable object contains the data it needs to know rather than having it on the Tower prefab directly (serializedFields). I dont get how Interfaces changes that at all. In the Interface example the building is inherting from a scriptable object, which i would not be interested in doing since the building needs to inherit from MonoBehaviour to have a transform and all that stuff.

By data i mean initial attack range, initial health, initial firing rate etc and other stuff such as name and price

To me it just seems very weird that i have a script called House which is a monoheaviour and acts as the house you actually have in a Unity scene. Then because that House inherits from Building which has a BuildingData field i have to in the House script cast BuildingData to a HouseBuildingData before i can access the proper values that should be on House. This means i could also accidently attach a TowerBuildingData to the House, since that is of Type BuildingData, but i really only want to be able to attach HouseBuildingData to a House. The reason im doing this is pretty much just that i have a List of Buildings which are all the placed buildings in the scene. I don’t mind casting in that list, but it seems really weird to cast BuildingData to HouseBuildingData on the House monobehaviour script and i was just wondering if there were any better way to do the whole setup.

Is anything just a Building? Or should it be abstract?

You can do something like this:

public interface IBuilding
{
	IBuildingData BuildingData { get; }
}

public interface IHouse : IBuilding
{
	IHouseData HouseData { get; }
}

public abstract class Building : MonoBehaviour, IBuilding
{
	public abstract IBuildingData BuildingData { get; }
}

public class House : Building, IHouse
{
	[SerializeField]
	private HouseData _houseData;
	
	public override IBuildingData BuildingData => _houseData;
	
	public IHouseData HouseData => _houseData;
}

Hm i can see how this makes sense, but is the IBuildingData supposed to be the ScriptableObject (BuildingData where i right now are having HouseData inherit from BuildingData) or are you suggesting that i should ditch them and use Interfaces for the Data also?

I mean interfaces are an abstraction. An interface on its own is not a concrete implementation. By using an interface, you could have any kind of underlying implementation, be it monobehaviour, scriptable object, or regular C# class. The point is that outside consumers do not need to care about what is behind the interface, they only care about the interface it provides.

So its implementation can still be by a scriptable object internally, but expressed via an interface to outside objects. But you have the flexibility moving forwards for different kinds of internal implementations.

Both of these are valid, and which one is best really comes down to the game.

Is there some data that’s shared between every single thing that takes damage in your game? Does everything have an int hitpoints and perhaps some other values, and is every single instance of that treated the same? In that case a component is for sure best, so you don’t have a bunch of different instances of

class SomeClass : IDamageable { 
    private int hp; 
    public void OnTakeDamage() { 
        hp --;
    }
}

If, on the other hand, many things that are IDamageable has no overlap in functionality whatsoever (you want some npc’s to just play an sound, but not have any hitpoints), the interface approach ends up being better.

You can also, of course, mix the two approaches! Have one IDamageable that things do damage to, and also a shared HitpointContainer object that are attached to most things that take damage.

Generally, testing for the underlying type at runtime is a code smell. Behavior change can be done either through dynamic polymorphism, if the underlying object is responsible for that behavior by having a method on the interface that is implemented differently, or by by using the visitor pattern.

If there is a need for depending on concrete implementations in classes that depend on interfaces, then a redesign is probably needed.

I agree with everything else, and to add to the conversation here’s my take on interfaces: https://giannisakritidis.com/blog/Conceptual-Meaning-Of-Interfaces/

(If TLDR: An interface shows us what a class can do, the implementation shows us what a class represents.)

​​

​​

all your question have clear answers if you study programming patterns. If you are not familiar with this information I strongly advise investing some time to learn them. Is not easy but once you figure them out you will understand why this things like interfaces or other programming concepts exist

To put it into most simple terms interfaces are basically a promise that whatever has them will contain the contract functions / properties.

I use interfaces for adjictives with the “able” suffix. So IDamagable is a good example inside the interface will contain the functions to damage. Interfaces are different from inheritance. With interfaces, multiple classes can implement the same interface, allowing polymorphic behavior without forcing a rigid inheritance structure. This makes interfaces very flexible, especially in cases where multiple behaviors are needed. You may have a house and a condo that share MOST of the same Building base class, but one may be IRentable when the other isn’t.

In my opinion the most important way to use interfaces is for Dependency Injection, if you explore DI enough interfaces will click as they are the perfect environment to use them in.

Interfaces are just another tool in your toolbox, unless you find a specific need then you’re free to continue doing whatever you do that’s the fastest and best way for you.

I don’t disagree… but what do you think happens when we Get/TryGetComponent?

Which isn’t to say components are code smell, but more so to say if you want an object that could express a variety of properties, it’s likely better for that object to simply be a container for components (much like a game object is).

Which we can do nowadays with [SerializeReference].

To be fair, if you’re using GameObject Component correctly, you shouldn’t need interfaces.

There isn’t that much difference between a “Damageable” Component and an “IDamageable” interface.

I would advise minimum viable abstraction, which means don’t use an interface if you don’t need to.

public void OnTriggerEnter(other collider)
{
    if(other.TryGetComponent<Damageable>(out var dmg)
    {
        dmg.ApplyDamage(1);
    }
}

However if you find that you have multiple implementations

public void OnTriggerEnter(other collider)
{
    if(other.TryGetComponent<Damageable>(out var dmg)
    {
        dmg.ApplyDamage(1);
    }
    else if (other.TryGetComponent<SpecialDamageable>(out var spDmg)
    {
        spDmg.ApplyDamage(1);
    }
}

In the above case you could solve the complexity by using inheritance, or using interfaces. I’d use an interface.

Actually here’s maybe a better example. A while a go a decompiled a unity game that had a large spell system and they where using inheritance.

    flowchart
        SpellBase --> ProjectileSpell
        SpellBase --> SwingSpell
        SpellBase --> ThrustSpell

        ProjectileSpell --> ProjectileExplodeSpell
        ProjectileSpell --> ProjectilePierceExplodeSpell
        ProjectileSpell --> ProjectileFreezeSpell
        ProjectileSpell --> ProjectileSummonSpell

        ProjectileExplodeSpell --> ProjectilePierceExplodeFreezeSpell
        ProjectilePierceExplodeSpell --> ProjectileSummonExplodeSpell

        ThrustSpell --> ThrustPierceSpell
        ThrustSpell --> ThrustSwingExplodeSpell
        ThrustSpell --> ThrustChainLightningSpell

        ThrustPierceSpell --> ThrustPierceChainLightningSpell
        ThrustSwingExplodeSpell --> ThrustSwingExplodeAOESpell

Anyway, the developers spell system obviously got quite complex and rigid due to his inheritance based approach. They’d be much better off just using interfaces.

public class ThrustPierceChainLightningSpell : ThrustPierceSpell {}

// becomes

public class LightningStab : IThrust, IPierce, ILighting, IChainable

Like I said above, in my experience I have had to refactor concrete implementations into an interface a large number of times, but never the reverse. Even in the short or long run, an interface is just going to make your life easier, even if there is only a few implementations.

And lets be real… a system never ends up being as simple as we initially envision it to be. And there are ways you can write code that prevent you having to do major refactors when you hit a wall. Interfaces are one of those tools, imo.