Sepparating Data From Logic
This one is a little abstract and not RobotLegs specific but I was hoping someone may be able to help.
Say I have a User object that has an "xp" value. Now say that I have an XPLevelsModel which contains an array of XPLevels that define how many points are required for that level (among other things):
class User
{
public User(levelsModel:XPLevelsModel) : {}
xp : int;
}
class XPLevelsModel
{
levels : XPLevel;
}
class XPLevel
{
...
xpRequired : int;
name : String;
...
}
Okay so now say I have a number of methods such as "getCurrentLevel()" "getPercentProgressThroughLevel()" etc. Where should those methods live?
I could put them on the User object:
class User
{
xp : int;
getCurrentLevel() : int {}
getPercentProgressThroughLevel() : Number {}
...
}
but as we add more data such as "lives", "health" etc etc User will start to grow with lots of functions (not very good for SoC)
I could try to group these things up into components such as "XPComponent" which is an object on User:
class User
{
xp : UserXPComponent;
}
class UserXPComponent
{
public UserXPComponent(levelsModel:XPLevelsModel) {}
points : int;
getCurrentLevel() : int {}
getPercentProgressThroughLevel() : Number {}
...
}
but the problem is accessing this data from the rest of the game now means user.xp.getCurrentLevel(), which breaks the law of Demeter, plus its ugly, hard to test and then what happens if you go more levels deep?
There is one more option I thought of, to have something like:
class User
{
xp : int
}
class UserXPHelpers
{
constructor(levelsModel:XPLevelsModel) {}
getCurrentLevel(user:User) : int {}
getPercentProgressThroughLevel(user:User) : Number {}
}
Here we have separated out the logic and the data and pass the user through the methods. The only thing is, does this not break encapsulation? As all those methods rely upon a user to be passed to them, shouldn't they live with the user?
If this IS the correct solution, is "Helpers" the correct name for this?
I hope you can help a problem that has challenged me time and time again and have only now been able to articulate it into a query.
Mike
Comments are currently closed for this discussion. You can start a new one.
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by Ondina D.F. on 19 Oct, 2015 02:08 PM
Hey Mike!
Levels, lives, health... it sounds like you're talking about a game.
I'm not experienced in game programming. I'm still in the process of learning about game design.
On the one hand, I don't know enough to give a qualified answer, on the other hand I think that different types of games require different approaches ( entity system approach, or a component based architecture, etc)
From a novice point of view, the question that first popped up in my mind was: What is User?
The 'player' in a game which has states and levels and each level creates a player (the UI as a ship or something like this), or is User the person playing the game and as such outside of the game logic and you need to store and update the stats ( lives, levels, health) in a model and eventually send them to an external data store?
If the User is the latter, I would communicate between the Game and the classes outside of the 'game-loop' through events.
I would let the Game dispatch events/signals when its state changes, and I'd let a command update the User Model. But I'm sure you would have used this approach already, if the use case were that simple. Could you expand a bit on the scenario?
Your UserXPHelpers would be a Model in my opinion. A UserLevelsModel? It would aggregate data from a User and a Levels Model. If you don't mind the coupling, and if it works well for you (and it is easier to test) then use it, why not? I prefer to use commands to get data from multiple models and to write it into a composite model which would act like a junction table in a database.
Have you heard of Ash? It is a great framework for games developed by Richard Lord. It seems to be a better fit for games than the MVCS approach. http://www.ashframework.org/
Strangely enough, I'd also recommend to take a look at strangeIoC, a framework for Unity3D and C#, inspired by robotlegs ;)
An experienced robotlegs user, like yourself, will easily understand strangeIoC's philosophy, and you might find the approaches used in the 2 games-examples mentioned below inspiring.
https://github.com/strangeioc/strangeioc
and the examples :
https://github.com/strangeioc/strangerobots
https://github.com/strangeioc/strangerocks
I'm sorry for not being of much help on this. Let's hope that someone with more game-programming experience will participate in the discussion, as well:)
Ondina
2 Posted by mike.cann on 20 Oct, 2015 12:12 AM
Hi Ondina,
Thanks for your reply, really appreciated :)
The question was more of a generic one, rather than a game specific question. Just to increase my chances for understanding I also asked it on StackOverflow:
http://stackoverflow.com/questions/33209309/separating-data-from-logic
I think I understand a little more now but still for my problem not 100% sure on the correct solution. I think its one of those "it depends" sort of answers.
As it happens I am very familiar with Ash having built several games in it and have a Unity version of Ash as an experiment:
http://www.mikecann.co.uk/programming/unity-ash-upgrades/
I also have used StrangeIoC for a large Unity game (not yet released) and so am pretty familiar with that library too.
I think this is one im going to have to think more on tho I really appreciate any other thoughts you may have.
Cheers,
Mike
Support Staff 3 Posted by Ondina D.F. on 20 Oct, 2015 05:08 PM
Haha! I wasn't aware I was talking to an expert:)
Nice work on the Unity port of Ash!!!
Yes, I know, you said that in your first post. Nevertheless, I think that it matters whether it is a game or not. The relationships between classes might be depending on the game architecture that you've implemented or on the particularities of a framework that you've used, like Ash for example.
I'm glad you've got an answer on stackoverflow!
Yeah, it depends ;)
I agree with neleus on stackoverflow: "it depends on how the xp is related to User"
To me it is unclear what is the role of the UserXPComponent and why would
UserXPComponent be an object on User.
Is UserXPComponent the View in MVCS parlance and User a Model reflecting the states of UserXPComponent?
Or is User a VO?
Also what is the relationship between Levels and UserXPComponent?
Who belongs to whom?
What part of the application triggers the transition to the next level?
If you were to build data base tables for these objects, how would you structure them? Are xp, level, health, lives properties of a User? Does
UserXPComponent have layout specific properties, like x, y, rotation angle, color? Do you need to store and then re-load this layout info ?
What about using a Model containing kind of a look up table, in form of a dictionary or another collection, where you store the user's achievements with user_id as the key and level_id, score, lives etc as the values? Why wouldn't that work?
I only have questions for you, no real solutions. But, perhaps just by trying to formulate an answer to my questions, will make things more clear to you.
4 Posted by mike.cann on 22 Oct, 2015 07:37 AM
Hi Olinda,
Good points and questions, lots of think about there. I guess you are right about asking the question, is xp a property of user or not. XP is, but if XPLevel isnt then perhaps it shouldn't belong on the User class, it should be derived.
Food for thought :)
Ondina D.F. closed this discussion on 28 Oct, 2015 03:48 PM.