IRepository<T>then making your own implementation per type, such as
UserRepository : IRepository<User>which would contain the standard
CRUDbased operations of a repository but with
Userspecific methods like
When CRUD is mentioned in the context of repositories or data it is an abbreviation for
Delete. These are the common actions that data interactions fall under so although you may use different named methods like
Get, Save, Update, Removeyou are basically adhering to the CRUD concept, so it is more a notion than a pattern.
UserRepositorygoes from having the basic CRUD methods to having lots of business logic which will keep snowballing every time you want to add more query types.
Questetc, the second being the type of the key/identifier for this resource.
The key is sometimes omitted if you are not working purely with relational databases, so within here we will simplify and remove the key type generic as in the game world you may be working more with in memory of flat file style databases.
There is also the notion of a
HybridQuerybut I will leave that out and explain it later on so you can decide if you want to add it or not.
TIteminstances from the data source, as well as provide a way to execute some logic against the data source.
IDataSource, this is an abstracted notion of how you access the data. If you are using databases you could easily replace this with
IDBConnectionwhich would abstract away the database. Given in most cases game data is read into memory you would probably just have this as a wrapper around the list of data in memory, however you could make it abstract the file system if you wished it to do manual file reads/writes.
IDataSourcewill wrap this.
If you wanted to here you could expose methods for querying the data to make it more like a database, but we will try to keep it all simple for now so you are able to just see the high level picture, then customize the underlying classes and interfaces to suit your scenario.
IRepository, the query classes, the
IDataSourceinterfaces and implementations, so now lets look at making a repository instance and then using it with some queries.
Updatethe item instance as all items will be reference types (in this scenario), so you changing an item would automatically update the in-memory version.
One of the things you may have noticed in the above example is that we are saving our changes after every interaction. This is fine for now, however further down the line you may want to only save after a set of changes have occurred, like a database transaction. In this scenario you could easily make a new implementation of
IRepositorywhich doesn't save automatically, and then you can have the transaction handler manage the saving. This is also known as a Unit Of Work pattern, which we will look into later. As if you were to be using a
IDataSourceis a file system handle, you would need to manually update the file system, or an actual database every time a change occurred, which is going to be costly for performance.
GetActiveUsersQuerywhich is a type of
IFindQuery. Let's do an imaginary implementation of this query to show how it would work:
Userclass, but this shows how we have isolated our query logic into this specific class.
ExecuteQuerytype yet. So where the
FindQueryis there to get a readonly collection of matching results,
ExecuteQueryis there to alter data in some way, so this provides the write concern to the find's read concern. so lets go over that with a quick example.
Get(TKey key)method back in if you have some notion of a keyed value, this can make it easier to get individual models with an Id. In most cases this requires your models to have an interface describing the key though so I know in game development a lot of people do not bother unless it is going to a database.