Design Pattern to access same data from different sources
There is no GoF (Gang of Four) pattern that address your scenario. The GoF patterns are more low level while you're facing a more complex issue that involves a local cache and a remote storage. GoF patterns does not address networking.
Maybe you can find something useful in the Catalog of Patterns of Enterprise Application Architecture by Fowler, for example Remote Facade and Data Transfer Object, but these are only a part of the possible solution.
I think that this is a matter of subsystem design so you need to define a suitable abstraction and then use GoF or other patterns where appropriate for the implementation details.
The abstraction you'll define to represent the cache / remote storage subsystem does not have to respond to a specific single pattern; as I'm aware of, there isn't a public available blueprint of such a system.
In my opinion, there are two problems which your api design has to solve.
Firstly, Repository implementations should be abstracted from its clients. By doing that, it allows you to make changes to Repository implementation without affecting the existing code, the clients of Repository.
Secondly, we should have two separate implementations which are CloudRepository and LocalRepository. Because they have very specific responsibilities, one deals with a Cloud-related persistent storage, and the other deals with a Device-related persistent storage. I'm not a mobile developer so I assume these two implementations might be complicated and swapping Local or Cloud persistent technologies are likely to happen
Here is the design solution. It's somehow a mixture of the Strategy, Proxy patterns.
The first one is simple. As long as you inject a concrete implementation of Repository into it clients through constructors or setters, then the clients are not coupled to any repository. In this case, I strongly suggest constructor injection because clients cannot function probably without a Repository.
public class Client {
private final Repository repository;
public Client(Repository repository) {
this.repository repository;
}
}
For the second problem, we just need one more Repository implementation which I call SwitchRepository. Basically, it coordinates Cloud, Local repositories to accomplish your goal of data access which depends on the Internet connection status.
public SwitchRepository implements Repository {
private Repository cloudRepository;
private Repository localRepoistiry;
public SwitchRepository(Repository cloudRepository, Repository localRepository) {
this.cloudRepository = cloudRepository;
this.localRepository = localRepository;
}
public void save(Data data) {
// do whatever we want here
if(isInternetConnected()) {
} else {
}
}
// the same for any operations of the Repository interface
}
To sum up:
public static void main(String[] args) {
Repository localRepository = new LocalRepository();
Repository cloudRepository = new CloudRepository();
Repository switchRepository = new SwitchRepostitory(cloudRepository, localRepository);
Client client = new Client(switchRepository);
}