Search Unity

Repository pattern or Inventory in games?

Discussion in 'Scripting' started by mahdiii, Apr 26, 2019.

  1. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    Hi. I would like to know differences between Repository pattern used in software applications as an abstract layer to access data and collections) and Inventory in games.
    We can utilize inventories in games (especially in RPG games) to keep and store player items as well as add and remove items.
    In Repository pattern, we have collections as well and we can add, remove and find elements.
    Can we see them the same?
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    These are the times where "design patterns" begin to annoy me.

    Like... OK, I should rephrase that. From a purely academic perspective I sort of get naming all of these names to distinguish them when discussing. But at the end of the day... they have no real use in the real world.

    I never heard of the repository pattern. I could guess what it is based on the name... because it's a pattern it probably abstracts something, and because it's called 'repository' it's probably abstracting repositories (collections of data). It's probably not a simple collection type abstraction (like ICollection) because they wouldn't have called it 'repository', so it probably assists in querying your collection.

    And sure enough... that's exactly what it is.

    But... looking at it...

    I wouldn't call it a 'repository pattern'.

    It's the same damn pattern used when abstracting ANY interface... repository or not. If we go all "Gang of Four" on this, to the original traditional set of "design patterns" we might call it the "Bridge Pattern". You define a general purpose interface that represents how you interact with some class of object, while allowing the concrete versions of it deal with the how that object actually behaves.

    In the case of this... how repositories behave.

    :smh:

    I mean like... yeah... I use bridge's all the damn time! It's kind of like the first obvious feature of an 'interface' (though you can implement bridges with other things than interfaces).

    ...

    NOW... to your question, or rather trying figure out what your question is:
    The repository pattern, based on what I've read on it being basically a bridge between your repository and your actual implementation of how you query your repository, is a bit overkill for inventory in a game.

    OK, lets start here:
    https://blog.kylegalbraith.com/2018/03/06/getting-familiar-with-the-awesome-repository-pattern/

    Here's a brief simple explanation of repository pattern and a useful scenario for it.

    So ironically, one thing I LOVE about this example is that... it demonstrates a repository pattern by wrapping it around an already existing repository pattern!

    IPersonDataContext is already in and of itself a bridge between the CompanyLogic and the database that person's can be queried from.

    See a bridge like this is to abstract away how retrieving data is actually performed. So like say you're building a website and your website stores all of its information in a MS-SQL database. Thing is once you're done developing, what if you want to support more databases? MySQL, postgresql, mongodb, d3, whatever the hell. Well... since all your code is abstracted to the basic interface of IPersonDataContext, if you can create an implementation of IPersonDataContext that returns linq-able entries that connect to these other databases. Then all of your code just falls in line and works. Because it doesn't expect a "MSSQLPersonDataContext", it expects the abstracted "IPersonDataContext".

    So onto how the person basically just abstracted their abstraction... they're effectively just making it where CompanyLogic doesn't need to know linq, it just needs to know IPersonRepository.

    This could be helpful if your IPersonRepository wants to be able to support more than just databases... what if you wanted to have a repository that is just an in memory List, or a repository that connects to a database that isn't easily linqable (my previous example of d3 is one of those... not easily queryable via the linq-able IDataContext in C#).

    Sort of making their example ALSO an example of what is called the "Adapter Pattern".

    Which brings me back to the whole design patterns when you start getting this granular become useless to discuss in any literal sense when more basic patterns cover it.

    It's like saying I invented a new machine and I call it the "Dead Squirrel Flipper". See I take a stick, and I prod it under the dead squirrel and I angle the stick lifting the dead squirrel and rolling it over onto a piece of cardboard.

    OR, if we really wanted to be academic about it... your created a lever. A very SPECIFIC lever, but a lever none the less.

    ...

    Now back at your questions:
    Yes.

    But the 'repository pattern' isn't just for having containers of stuff. It's about abstracting containers of stuff.

    Sure, cause they are the same. They're both queryable containers of stuff.

    But abstracting that container any more than you have to? Why?

    Do you plan on down the line changing how your inventory is contained and need to have it hot swappable?

    We're now getting into the whole aspect of "over engineering".

    Sure, we could create an abstraction layer for all potential inventory repositories. We could even abstract the abstraction like in the example. Like for instance you could allow your inventory to consume an 'ICollection' so that it doesn't define the KIND of collection it stores things in...

    Lets look at this:

    Code (csharp):
    1.  
    2. public interface IInventoryRepository
    3. {
    4.  
    5.     InventoryItem GetInventoryByName(string name);
    6.     bool AddItem(InventoryItem item);
    7.     bool RemoveItem(InventoryItem item);
    8.  
    9. }
    10.  
    11. public class CollectionInventoryRepository : IInventoryRepository
    12. {
    13.  
    14.     private ICollection<InventoryItem> _itemContext;
    15.  
    16.     public CollectionInventoryRepository(ICollection<InventoryItem> itemContext)
    17.     {
    18.         _itemContext = itemContext;
    19.     }
    20.  
    21.     public InventoryItem GetInventoryByName(string name)
    22.     {
    23.         return _itemContext.FirstOrDefault(o => o.Name == name);
    24.     }
    25.  
    26.     public void AddItem(InventoryItem item)
    27.     {
    28.         _itemContext.Add(item);
    29.         return true;
    30.     }
    31.  
    32.     public bool RemoveItem(InventoryItem item)
    33.     {
    34.         return _itemContext.Remove(item);
    35.     }
    36.  
    37. }
    38.  
    39. public interface IEntity
    40. {
    41.  
    42.     IInventoryRepository Inventory { get; }
    43.  
    44. }
    45.  
    46. public class PlayerEntity : MonoBehaviour, IEntity
    47. {
    48.  
    49.     public IInventoryRepository Inventory { get; private set; }
    50.  
    51.     private void Awake()
    52.     {
    53.         Inventory = new CollectionInventoryRepository(new HashSet<InventoryItem>());
    54.     }
    55.  
    56. }
    57.  
    58. public class ItemPickup : MonoBehaviour
    59. {
    60.  
    61.     public InventoryItem Item;
    62.  
    63.     private void OnTriggerEnter(Collider other)
    64.     {
    65.         var entity = other.GetComponentInParent<IEntity>();
    66.         if(entity == null || entity.Inventory == null) return;
    67.      
    68.         entity.Inventory.AddItem(Item);
    69.     }
    70.  
    71. }
    72.  
    Like sure, this all makes sense... but do we really need it all?

    Like what is IInventoryRepository doing for us? Are we going to implement different versions of IInventoryRepository? Are there inventories that behave differently?

    ...

    If the answer is YES, then sure, go right ahead!

    And I could see things where this might happen. Like if you have an inventory system that changes based on the character your playing. Like maybe one character can only hold items of a certain class/weight, or can only hold a single instance of any given type of item, or has unlimited storage of only a single kind of item...

    All of these are potential things.

    But are they what you need?

    I mean, if you gave me the time.. I could abstract EVERY single part of my game. I could abstract my abstractions that are abstractions of abstractions of abstractions. I mean that's what programming in languages like C# is honestly... we're so abstracted away from what is actually going on it's kind of hilarious.

    But you gotta draw the line at some point damn it.

    Ugh I hate arbitrary design pattern names...
     
    Last edited: Apr 26, 2019
  3. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    I'm not OP but I thank you nonetheless!
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    "How can I use complex Pattern X to solve relatively simple Task Y" is a very common question on these forums. I should probably keep a link to @lordofduct's post in order to just copypaste it in.
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    Sorry... I went on a bit of a long drunk rant last night...
     
  6. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    You forget one important aspect of not querying the data context directly, testability. With a repository pattern you can mock the data easily a linq or EF datacontext isn't easy to mock or even impossible
     
    mrwellman_work and mahdiii like this.
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    I didn't forget it...

    Mind you I wasn't saying the pattern by and large was bad. I was making fun of superfluous names for patterns that are really just existing generic patterns specialized to a specific use case.

    As for my joking about abstractions of abstractions was to contrast it against the usefulness in one's game. Sure, abstraction layers are useful for things like testing, and swapping out functionality, and other stuff. But is it really necessary? Or are we just over engineering the problem in the context of an inventory system.

    Of course I was also piss drunk, so I was more rambly than cogent.

    Or... you know... I could have spent 8 more paragraphs making sure I cover every other potential tangent topic in relation to my post. Just in case you want to come in and say I forgot about the moon.
     
    Last edited: Apr 26, 2019
  8. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    Thank you man for spending your precious time.

    "I mean, if you gave me the time.. I could abstract EVERY single part of my game. I could abstract my abstractions that are abstractions of abstractions of abstractions."
    It is absolutely true. :)

    The goal of Repository pattern is to abstract complex queries and you referred to it appropriately.
    Also it becomes more bold when we have different types of databases with different abilities and have to abstract it.
     
    Last edited: Apr 26, 2019
  9. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    And you implement different inventories for different purposes using scriptable objects?
    break player assets to small assets? weapons, items, currencies, ...
     
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    Personally?

    Yes, we use ScriptableObjects for our inventory items. This way my designer can easily add new inventory items by just right clicking in the asset folder and adding an "InventoryItem". We have some special case sub-classes of InventoryItem like 'WeaponInventoryItem' that have added properties to it that relate to weapons.

    I'm not home so I can't exactly show it all... otherwise I would take some screenshots. But I'm out of town for the next few days, some long needed vacation in the Florida Keys.
     
    mahdiii likes this.
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
  12. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    I would never be so irresponsible, never write on this forum intoxicated

    cheers!

    IMAG1341.jpg