While there are some schools of thought about Object orientation which insist in combining data entities with logic to align classes with physical entities, this is by no means a guideline that you should follow unless you truly believe it's the best solution to your particular problem (That approach may naturally lend itself to some problems, but frequently causes more problems than it solves if just followed blindly). There are other schools of thought which emphasise clean separation of concerns, and prioritise 'SOLID Principles' instead.
Physical entities as classes is a valid outlook on class design, but is also extremely constraining and only provides a narrow perspective which doesn't resolve issues about how classes might interact with each other, how to test them, or what happens when behavioural logic could involve multiple physical entities. Nor does it necessarily consider the way that software naturally changes and evolves over time as its requirements change or issues are discovered in the original implementation.
It's important to keep in mind the goals of object orientation rather than treating it as a set of rules to be followed. Broadly, the goals are about grouping logically-related behaviour (methods) into classes, keeping clean separation between different unrelated classes/behaviour. The extent to which you decide to do that is up to you, but you can often make a lot of decisions based on whether something 'feels' helpful in the way you structure your program.
If something doesn't feel helpful, or it seems counter-productive, or leads to code which feels 'messy' and unnecessarily complex, then that should be enough reason not to do it. Instead consider the practical implications such as whether it makes your code easier to unit-test or whether the rationale for your code structure would be immediately obvious to other developers who are trying to reason over your code. Consider principles such as KISS, DRY and YAGNI.
It seems extremely unlikely that any core business/application logic would ever need to care about the specifics of CRUD operations relating to a data store, so it would seem rather odd to have the DealDamage
method in the same class as a Save
or Read
method; plain common-sense dictates that these things should probably be unrelated.
For example, your DealDamage
method probably isn't going to behave differently if the data happens to be stored in a SQL database compared with being stored in a Flat file or NoSQL store, so from a logical/semantic point of view it makes sense to keep them separated. Furthermore, if your DealDamage
method does behave differently depending on those things, it might be an indicator of a possible problem in your design, or a problem with the way your requirements are specified.
There's absolutely nothing wrong in Object-oriented programming with creating plain 'entity' models whose purpose is merely to hold data which has been retrieved from a database. Nor is there anything wrong with creating stateless behavioural classes which have no data of their own and operate entirely on those entity models, in fact such a design is fairly typical in a Layered Architecture, and even necessary if you happen to be building a system based on a Microservices Architecture.
Naturally, it depends upon your requirements as to whether you need or would benefit from one of those architectural styles. In general, any kind of very small or trivial app may not get much benefit from the clean separation of concerns that you'd get from a more complex design, and you can get code working fairly quickly with the Just Do It approach which tends to ignore any wider concerns about design/architecture or possible future change.
More complex designs are intended to solve a multitude of problems that people typically associate with larger systems whose requirements are more complex and ever changing. Particularly where the complexity of those systems would make them difficult to reliably test or change as a monolith. The separation between layers/modules/subsystems allows those different components to be developed in isolation, more easily reused by other systems, more flexible/scaleable to meet future requirements, and easier for other developers to reason about.