Post

Episode 3: Abstraction – Making Hero an Abstract Class

Goal: Learn how to use abstract classes to enforce that all heroes implement key behaviors while allowing flexibility in implementation.

Episode 3: Abstraction – Making Hero an Abstract Class

1. Project Structure

Create the following folder layout:

1
2
3
4
5
6
src/
└── model/
    ├── Hero.java (now abstract)
    ├── Warrior.java
    └── Mage.java

2. Update Hero.java to be abstract

Create the file: src/main/Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package model;

public abstract class Hero {
    protected String name;
    protected int hp;
    protected int attack;

    public Hero(String name, int hp, int attack) {
        this.name = name;
        this.hp = hp;
        this.attack = attack;
    }

    public void takeDamage(int damage) {
        this.hp -= damage;
        if (this.hp < 0) this.hp = 0;
        System.out.println(name + " now has " + hp + " HP.");
    }

    public String getName() {
        return name;
    }

    public int getHp() {
        return hp;
    }

    // Abstract method: subclasses must implement this
    public abstract void attack(Hero target);
}

3. Override attack() in subclasses

Warrior.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package model;

public class Warrior extends Hero {

    public Warrior(String name) {
        super(name, 150, 20);
    }

    @Override
    public void attack(Hero target) {
        System.out.println(this.name + " attacks " + target.getName() + " with a sword for " + this.attack + " damage.");
        target.takeDamage(this.attack);
    }

    public void heavyStrike(Hero target) {
        int damage = this.attack + 10;
        System.out.println(this.name + " uses Heavy Strike on " + target.getName() + " for " + damage + " damage!");
        target.takeDamage(damage);
    }
}

Mage.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package model;

public class Mage extends Hero {

    public Mage(String name) {
        super(name, 100, 25);
    }

    @Override
    public void attack(Hero target) {
        System.out.println(this.name + " casts a magic missile at " + target.getName() + " for " + this.attack + " magic damage.");
        target.takeDamage(this.attack);
    }

    public void fireball(Hero target) {
        int damage = this.attack + 5;
        System.out.println(this.name + " casts Fireball on " + target.getName() + " for " + damage + " magic damage!");
        target.takeDamage(damage);
    }
}


4. Test in Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import model.*;

public class Main {
    public static void main(String[] args) {
        Hero garen = new Warrior("Garen");
        Hero ahri = new Mage("Ahri");

        garen.attack(ahri);
        ahri.attack(garen);
        ((Mage) ahri).fireball(garen);
        ((Warrior) garen).heavyStrike(ahri);
    }
}

This post is licensed under CC BY 4.0 by the author.