SOURCE: StackOverflow, Hannes Dorfmann
In short, Repository pattern in Android has a different meaning in comparison with the original Martin Fowler's proposal (see the linked Hannes Dorfmann for details).
The Repository pattern is really just a specific type of the Facade pattern. It is just an abstraction layer which sits between the true data sources (Models) and the consumers.
Let's say we have a system that deals with weather data. In this case I see nothing wrong with having three Modal classes:
WeatherHttp
WeatherDb
WeatherPrefs
These can all be members of your repository class injected through the constructor. All three of these are hidden from the consumers (UI) behind a single repository class:
WeatherRepository
which might have a single public method:
public void getWeatherForecasts(Callback<List<Forecast> forecastCallback);
or Rx:
public Observable<List<Forecast>> getWeatherForecasts();
Behind the scenes of that method you might well make an http call to fetch the latest data, a database call to save the majority of the details and a shared prefs edit to save the timestamp of the last time the data was fetched. The idea is you are free to change the implementation at any time and the consumers don't care.
The single most important thing about implementing a repository is you must not leak the implementation details. No network classes, database DAOs or SharedPreference keys should be exposed by the public API of the repository.
If you wants to load an User
by his id:
interface UserRepository {
User user(int userId);
}
The concrete implementation of that repository:
class UserDataRepository implements UserRepository {
private UserDataStoreFactory userDataStoreFactory;
@Override
public User user(int userId) {
UserDataStore userDataStore = userDataStoreFactory.create(userId);
return userDataStore.get(userId);
}
}
An UserDataStore
is, as the name already suggests, responsible to load a User
from a store like disk or a backend (cloud).
interface UserDataStore {
User getUser(int userId);
}
class DiskUserDataStore implements UserDataStore {
private DiskCache<Int, User> userCache;
@Override
public User getUser(int userId){
return userCache.get(userId);
}
}
class CloudUserDataStore implements UserDataStore {
@Override
public User getUser(int userId){
// Make an HTTP request and return User.
}
}
UserDataStoreFactory
is the component that decides which UserDataStore
to use when someone calls UserRepository.getUser(userId)
. For example the factory could check if DiskUserDataStore
has a User
and if the User
object is expired then the factory returns CloudUserDataStore
, otherwise DiskUserDataStore
. The factory could also take into account if the smartphone has an active internet connection or not. I guess you get the point.
For why the Android Blueprint using the Repository as a Singleton, see this post.