Home > Net >  Multiple types of a class within a class
Multiple types of a class within a class

Time:05-19

I'm making a simple game to learn some kotlin, and I'm a little confused on the best way to change this around using OOP.

I have a class for backpack setup so that it has another class called items that will be different types of items. Below I've recreated a simple version of what I have.

class backPack {
      private val item: Item

init { item = Item() }

     fun display() {
     item.display()
     }
}

class Item {
      fun display() { println("Your item!") }
}

fun main() {
 val examplePack = backPack()
 examplePack.display()
}

I want to change the backPack class to allow for different types of items. For example, health potions and mana potions. I considered making the item an open class, then having something like this:

class healthPotion : Item() {
    override fun display() {
        println("health potion!")
    }
}
class manaPotion : Item() {
    override fun display() {
        println("mana potion!")
    }
}

which seems correct, but I'm a little stuck on how to refactor the backpack class to allow different types of items and I want to make sure this seems like a proper way to do this. Any assistance is very appreciated, thank you!

CodePudding user response:

That's basically the idea! If your BackPack class (it should start with a capital by convention) handles Items, then any class that has that type will work. You have two options - inheritance, and composition.

Inheritance is where you build a hierarchy of classes, e.g.:

open class Item {
    val weight: Int
    fun Describe()
}

open class Potion : Item() {
    fun drink() {}
}

// this is a Potion, and a Potion is an Item, meaning this is an Item
class ManaPotion : Potion() {
    override fun drink() {
       println("whoa!")
    }
}

// etc

The problem there is you're locked into a rigid hierarchy - what if you had a StaleBread that's a Food, but you also want it to be a Weapon? You can use interfaces to compose your object by giving it multiple types, not just a parent:

open class Item

interface Food {
    fun eat() {
        println("yum")
    }
}

interface Weapon {
    fun attack() {
        println("RARGH")
    }
}

class StaleBread : Item(), Food, Weapon

Because StaleBread has all of those types, you can treat it as any of them, because it is all of them. Because it's a Weapon, it's guaranteed to have an attack() method, etc. You can add it to a List<Food>, because it is a Food. Being able to treat objects as different types is called polymorphism. So you're able to compose an object from different types, to give it certain properties, certain behaviours, etc


Your example should work as-is because you're handling Items in your backpack, and your two potion classes are both Items (descendants of that class, specifically, since you're inheriting from the Item class). There are lots of ways to organise it - if you get into some tutorials about inheritance, composition and polymorphism (this is only a simple overview that skips over a bunch of things) you'll start to get some ideas about how to move forward


oh yeah, for the actual backpack, you probably want something like this:

class BackPack {
    private val items = mutableListOf<Item>()

    fun addItem(item: Item) {
        // here you could do things like check it doesn't exceed the allowed weight
        // or capacity of the backpack
        items.add(item)
    }
}

This way, your backpack can contain multiple Items, and you can control how those are accessed through the class's functions and properties. Anything which is an Item type can go in there!

CodePudding user response:

If you want to be able to change which item is in the bag, the property should be a var. If you want the bag to be able to be empty, then it should be a nullable property (declared with a ? after the type so it can hold null).

By the way, class names should always start with a capital letter so your code will be easier to read. And you can initialize properties at the declaration site instead of using a separate init block.

class Backpack {
    var item: Item? = null

    fun display() {
        item?.display()
    }
}

Your generic Item() doesn't seem like it would be useful in practice. Therefore, Item should probably be either an abstract class or an interface. A general OOP principle is that you should avoid deep class hierarchies. If your superclass doesn't contain logic that must be shared by all its children, it should probably be an interface instead.

  • Related