The real problem I had for a long time was programming too much like procedural style in C++. When you are programming in OOP inheritance is actually the first and most important way to implement things, not composition. Class should always have only one task, although sometimes the task is complex. Writing good OO code is quite difficult, that's why programmers result in composition etc.
It's actually not that simple to say that C program is one class, because functional paradigm can work in procedural code as well. But often C programs have lots of global access to "member data" so it works a lot like class.
Interesting. I have a big reply for you! I'm curious to know how you handle your OO. OO Is definitely difficult, and it doesn't help that gurus use a lot of pseudocode for both example and comparison. It probably doesn't help that the first language I learned was Java, meaning I missed out on a lot of the procedural stuff early on (I'm just now catching up), so my view on how to handle things is distorted by a modern view of OO.
...but I care about learning how to produce good code! So,
On inheritance:How do you handle scenarios where some
creatures have different capabilities to other
creatures (i.e. a bird can fly, but a cat cannot)?
How do you handle scenarios where creature's capabilities change during runtime (for example, a cat gains the ability to fly)?
Do you:
- Use inheritance (i.e. have a LandCreature and FlyingCreature class; create a new FlyingCreature instance with our cat's data, which was previously a LandCreature)
- Use component-based design (pluggable "FlyComponent" class that extends an IComponent interface; creatures have a Vector/List of IComponents)
- Use composition (creatures having a public IFly interface we can use to swap functionality in and out; that cat would have a CantFly object that implements IFly. To make it fly, we can replace IFly with some kind of Fly object implementation. Like function pointers in C, but OO with interfaces. Though function pointers would work just fine.)
- Use data-driven design (there is no cat or bird, or LandCreature or FlyingCreature; they're all just a creature but with different data. Every creature can technically fly, but the data has to be in the correct state to allow that to happen; data is more or less just PODS)
- Handle it differently?
On single responsibility/tasks:Say your game has shops (NPCs that sell items), containers (chests that store items), and regular characters that have inventories. How would you go about organising those classes (or class/class hierarchies)? Would you have one Inventory class, acting a bit like a generic List/Vector, or would you have different implementations for each of these scenarios since they technically have different responsibilities (i.e. ShopInventory, ContainerInventory, and CharacterInventory have no relation, despite their similar names)?
On that same note, do you still use something like the command pattern that connects different classes via a common function? For example, when a player purchases an item from a store, that item needs to move from the shop's inventory to the player's inventory (assuming their different implementations)...
Alternatively, if a character equips an item, that item needs to move from the player's inventory to their equipment (is the inventory or equipment class responsible for handling this interaction? Does this interaction go into a separate class? Would the inventory and equipment be implemented inside the character class, so there is no generic inventory or equipment?).