0

I'm making a console game in C#, and have encountered a problem with the OOP paradigm.

I have an Item class:

class Item
    {
        public int SellPrice;
        public string Name;
    }

And a ShopItem class, which represents Item objects that can be bought in shops:

class ShopItem : Item
    {
        public int Price;
    }

And finally, a weapon class:

class Weapon
    {
        public int Attack, Durability;
    }

Now, every Weapon object is an Item (the inheritance here was purposefully removed), but that creates a major problem.

While you can buy some weapons in shops, you can only get others by completing quests.

So, a Weapon is sometimes a ShopItem (because it could be bought in shops), and sometimes it's just an Item (which cannot be bought in shops).

Items sometimes can be bought in shops if they're ShopItem, though (becuase ShopItem inherits from Item).

So what can I do here? I obviously cannot inherit from both, and if I inherit from the Item class, than I cannot sell the Weapon object in shops (because I wouldn't be able to cast it into a ShopItem).

What do you think is the best solution for such a thing?

davioooh
  • 23,742
  • 39
  • 159
  • 250
Travier
  • 205
  • 2
  • 9
  • Things to look into: Interfaces - specifically e.g. this question: http://stackoverflow.com/questions/56867/interface-vs-base-class - look at Pet vs Animal as your ShopItem vs Item. Also, possibly composition over inheritance, although I don't feel it applies to this case. – JimmiTh May 20 '14 at 15:27

2 Answers2

4

You should be thinking in terms of behaviors (methods) instead of properties when designing class hierarchies, i.e. what can you do with an item?

In your example, I don't think ShopItem should inherit from Item, but rather you should define an interface IBuyable which indicates an Item can be bought.

Rik
  • 28,507
  • 14
  • 48
  • 67
  • Or alternatively an IItem interface which was going to be my suggestion. But it's still the right approach IMO. – Bob Tway May 20 '14 at 15:27
  • I have thought of this, but think what happens if I put ShopItem's fields into Item. The problem would be that all classes that inherit from Item will see all ShopItem's fields, even if they are not ShopItems themselves (for exmaple a Rare Sword inherits from Item but it isn't a ShopItem, yet it will have ShopItem's fields because they're part of Item). Isn't it similar to your suggestion? – Travier May 20 '14 at 15:29
  • Also, if weapon inherits from IBuyable, that means all Weapons are buyable. That's exactly my problem right now, and the interface IBuyable won't change it (becuase if Weapon : IBuyable, that means all weapons would still be Buyable). – Travier May 20 '14 at 15:30
  • 1
    I think you are over thinking it, all weapons CAN be buyable not must be in the shop. Only your shop will expose the price when it is in the shop. – Bit May 20 '14 at 15:34
  • In this specific case, agree with N4TKD - the properties needed for a buyable item don't really need to be separated into anything separate. Thinking in real world terms, there are few physical objects in existence that can't *somehow* be bought. They're just not necessarily found in a shop. All that requires is to set their price to, say, "null" - no need for an interface or a base class. A buyable object doesn't really have any special behavior compared to other objects. It isn't buying/selling itself - that's the shop's or shop keeper's job. – JimmiTh May 20 '14 at 15:38
  • So, from what you're saying, I can just add a boolean field "InShop" to the Weapon class, and then display Weapon objects in the shop only if their "InShop" is set to true? – Travier May 20 '14 at 15:47
  • 1
    @Travier or another way to look at it is if the Weapon is being given as a reward the user has no need to know what the price would be if the user was buying it in a the shop. – Bit May 20 '14 at 16:04
1

You could define an interface:

interface IWeapon{}

And change your hierarchy this way:

class ShopWeapon : IWeapon, ShopItem {}
class Weapon : IWeapon, Item {}
davioooh
  • 23,742
  • 39
  • 159
  • 250
  • Thank you very much. I have decided instead to just use a boolean field on the Weapon class, so no matter what, it always has the ShopItem fields (the boolean field indicates whether the Weapon is available in shops or not). But still, this is the closest idea to what I wanted, and actually a great way to solve this problem. Thanks again! – Travier May 20 '14 at 16:02