GDKB

Interfaces

Intermediate18 Minutes readUpdated July 21, 20261 related concept

An interface declares a set of methods a class promises to implement, without providing any implementation itself — it is a contract, not a blueprint. `IDamageable` might declare `void TakeDamage(int amount);` with no body at all; any class that implements `IDamageable` must provide its own `TakeDamage`. Interfaces let unrelated classes (`Enemy`, `Destructible Crate`, `Player`) share a behavioral contract without needing to inherit from a common base class.

On this page

Interfaces

Summary

An interface declares a set of methods a class promises to implement, without providing any implementation itself — it is a contract, not a blueprint. IDamageable might declare void TakeDamage(int amount); with no body at all; any class that implements IDamageable must provide its own TakeDamage. Interfaces let unrelated classes (Enemy, Destructible Crate, Player) share a behavioral contract without needing to inherit from a common base class.

Mental Model

If a base class is a blueprint a house is built from, an interface is a certification a house can earn regardless of which blueprint built it: "fire-code compliant" says nothing about room layout, kitchen size, or which architect drew the plans — it only guarantees that certain requirements are met. A wooden house and a brick house, built from completely different blueprints, can both be "fire-code compliant" if each independently satisfies the certification's requirements in its own way.

This is exactly why an Enemy and a DestructibleCrate — otherwise unrelated classes with no shared base class — can both implement IDamageable and both be handled by the same "anything damageable" code, without one inheriting from the other.

Core Explanation

  • An interface declares signatures, not bodies. interface IDamageable { void TakeDamage(int amount); } specifies what a conforming class must provide, never how. Each implementing class writes its own TakeDamage body, and different classes' bodies can (and usually do) do completely different things.
  • A class can implement multiple interfaces, even though (per Inheritance) it can only inherit from one base class. Enemy might implement both IDamageable and IPoolable at once — this is the primary reason interfaces exist where single inheritance would be limiting: a class needs several independent behavioral contracts, not one linear ancestry.
  • Interfaces enable polymorphism across unrelated types. Code written against IDamageable can operate on any class that implements it, regardless of what that class actually inherits from — the same polymorphic dispatch Inheritance introduced, but decoupled from a shared base class.
  • Interfaces vs. abstract base classes. An abstract base class (a class that cannot be instantiated directly, only inherited from) can mix declared-but-unimplemented methods with real shared implementation and real shared fields — an interface, in most mainstream languages, declares no fields and (traditionally) no implementation at all. Choose an abstract base class when there is genuine shared code and state to give derived classes; choose an interface when only the contract needs to be shared, across classes that otherwise have nothing in common.
  • Naming convention. C# conventionally prefixes interface names with I (IDamageable, IPoolable) specifically so a type name alone signals "this is a contract, not a concrete class" at the point of use.

Syntax / Examples

C# (Unity)

text
public interface IDamageable
{
    void TakeDamage(int amount);
}

public class Enemy : IDamageable
{
    private int health = 100;
    public void TakeDamage(int amount) => health -= amount;
}

public class DestructibleCrate : IDamageable
{
    private int hitsRemaining = 3;
    public void TakeDamage(int amount)
    {
        hitsRemaining--;
        if (hitsRemaining <= 0) Destroy(gameObject);
    }
}

// Works on either class — only the contract matters here.
void ApplyExplosionDamage(IDamageable target, int amount) => target.TakeDamage(amount);

C++ (Unreal)

text
// Unreal's interface pattern: a UInterface + a pure C++ interface class.
UINTERFACE(MinimalAPI)
class UDamageable : public UInterface { GENERATED_BODY() };

class IDamageable
{
    GENERATED_BODY()
public:
    virtual void TakeDamage(int32 Amount) = 0; // pure virtual: no body, must be implemented
};

class AEnemy : public AActor, public IDamageable
{
    GENERATED_BODY()
public:
    virtual void TakeDamage_Implementation(int32 Amount) override { /* ... */ }
};

Unreal Blueprint

A Blueprint Interface asset declares function signatures with no implementation, mirroring IDamageable. Any Blueprint class can "Add Interface" from its class settings, then implement each declared function — the Blueprint equivalent of class Enemy : IDamageable, letting unrelated Blueprint classes share a callable contract.

Practical Example

An explosion needs to damage anything nearby that can be damaged — enemies, crates, breakable walls — regardless of what each of those classes otherwise inherits from:

csharp
void Explode(Vector3 origin, float radius, int damage)
{
    foreach (Collider hit in Physics.OverlapSphere(origin, radius))
    {
        if (hit.TryGetComponent<IDamageable>(out var damageable))
        {
            damageable.TakeDamage(damage);
        }
    }
}

Explode never mentions Enemy or DestructibleCrate by name — it only depends on IDamageable, so any future class that implements the interface automatically works with this function, with zero changes to Explode itself. This is the same "write once against a shared contract" payoff Inheritance's polymorphism example demonstrated, extended to classes with no common ancestor at all.

Common Mistakes

  • Reaching for inheritance when an interface fits better — if two classes need to share a contract but have no other meaningful shared state or implementation, forcing a common base class purely to get polymorphism is more rigid than an interface would be.
  • Interfaces that grow too large ("fat interfaces") — an interface with many unrelated methods forces every implementing class to provide all of them, even ones that don't apply; smaller, focused interfaces (IDamageable, IPoolable as separate interfaces) let a class opt into exactly the contracts it needs.
  • Forgetting that an interface reference only exposes the interface's methods — a variable typed as IDamageable cannot call a method that only Enemy (and not the interface) declares, even if the underlying object happens to be an Enemy; a downcast back to the concrete type is needed for that.
  • Assuming an interface can hold state — in most mainstream OOP languages an interface declares no fields; any shared state still needs to live in each implementing class (or a shared base class instead).

Best Practices

  • Prefer an interface over inheritance when the goal is purely a shared contract across otherwise-unrelated classes.
  • Keep interfaces small and focused — one coherent capability per interface, rather than one large interface covering several unrelated responsibilities.
  • Name interfaces after the capability they guarantee (IDamageable, IPoolable), not after a category of object.
  • Write code against the interface type, not the concrete class, whenever only the contract's behavior is actually needed — it keeps that code usable with any future implementer.

Exercises

  1. Define an IPoolable interface with OnSpawn()/OnDespawn() methods and implement it on two otherwise-unrelated classes.
  2. Rewrite the Explode example to also damage a Player class, without modifying Explode itself — only by making Player implement IDamageable.
  3. Explain when an abstract base class would be the better choice over an interface for a scenario you construct, and why.
  4. Identify a "fat interface" (write one with 5+ unrelated methods) and split it into two smaller, focused interfaces.
  • Inheritance — the other major mechanism for polymorphism, sharing implementation rather than just a contract.
  • Objects — interface references still point at concrete objects; the interface only restricts which of that object's methods are visible through it.

References

Related Concepts