Skip to content

Instantly share code, notes, and snippets.

@xpmatteo
Created July 26, 2025 11:37
Show Gist options
  • Save xpmatteo/9d54357190cbceb6b51c201e935a8b3f to your computer and use it in GitHub Desktop.
Save xpmatteo/9d54357190cbceb6b51c201e935a8b3f to your computer and use it in GitHub Desktop.
RPG Combat Kata - Polymorphic Refactoring to Eliminate IF Statements
// ABOUTME: Represents a living RPG character with full capabilities
// ABOUTME: Handles damage reception, healing, and state transitions to dead when health reaches zero
public class AliveCharacter extends RPGCharacterBase {
public AliveCharacter() {
super(MAX_H);
}
public AliveCharacter(int health) {
super(Math.max(0, Math.min(health, MAX_H)));
}
@Override
public boolean isAlive() {
return true;
}
@Override
public RPGCharacterBase heal(int hp) {
int newHealth = Math.min(health + hp, MAX_H);
return new AliveCharacter(newHealth);
}
@Override
protected RPGCharacterBase receiveDamage(RPGCharacterBase attacker, int damage) {
if (attacker != this) {
int newHealth = Math.max(health - damage, 0);
if (newHealth == 0) {
return new DeadCharacter();
}
return new AliveCharacter(newHealth);
}
return this;
}
}
// ABOUTME: Represents a dead RPG character with limited capabilities
// ABOUTME: Cannot heal or change state, but can still receive damage without effect
public class DeadCharacter extends RPGCharacterBase {
public DeadCharacter() {
super(0);
}
@Override
public boolean isAlive() {
return false;
}
@Override
public RPGCharacterBase heal(int hp) {
return this;
}
@Override
protected RPGCharacterBase receiveDamage(RPGCharacterBase attacker, int damage) {
return this;
}
}
// ABOUTME: Facade class that maintains API compatibility with original RPGCharacter
// ABOUTME: Delegates to polymorphic implementation while preserving mutable interface
public class RPGCharacter {
public static final int MAX_H = 1000;
private RPGCharacterBase delegate;
public RPGCharacter() {
this.delegate = RPGCharacterFactory.createNewCharacter();
}
public boolean isAlive() {
return delegate.isAlive();
}
public boolean isDead() {
return delegate.isDead();
}
public void dealDamage(RPGCharacter victim, int damage) {
victim.delegate = this.delegate.dealDamage(victim.delegate, damage);
}
public int health() {
return delegate.health();
}
public void heal(int hp) {
delegate = delegate.heal(hp);
}
}
// ABOUTME: Abstract base class for RPG characters with shared behavior
// ABOUTME: Provides common interface and delegates state-specific behavior to subclasses
public abstract class RPGCharacterBase {
public static final int MAX_H = 1000;
protected int health;
protected RPGCharacterBase(int health) {
this.health = health;
}
public int health() {
return health;
}
public abstract boolean isAlive();
public boolean isDead() {
return !isAlive();
}
public abstract RPGCharacterBase heal(int hp);
public RPGCharacterBase dealDamage(RPGCharacterBase victim, int damage) {
return victim.receiveDamage(this, damage);
}
protected abstract RPGCharacterBase receiveDamage(RPGCharacterBase attacker, int damage);
}
// ABOUTME: Factory for creating properly configured RPG characters
// ABOUTME: Encapsulates character creation logic and provides convenient factory methods
public class RPGCharacterFactory {
public static RPGCharacterBase createNewCharacter() {
return new AliveCharacter();
}
public static RPGCharacterBase createCharacterWithHealth(int health) {
if (health > 0) {
return new AliveCharacter(health);
} else {
return new DeadCharacter();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment