Repository Pattern with Repository Factory

3.7k views Asked by At

I'm trying to improve my Android persistence layer to be used across multiple applications.

What i have done so far is to setup a base repository abstract class and also a base repository interface, the complete code can be check here: https://github.com/grmaciel/android-repository-ormlite

Interface:

public interface IRepository<T, Id> {
    public void save(T entity) throws SQLException;
    public void saveBatch(List<T> entities) throws Exception;
    public List<T> queryAll() throws SQLException;
    public T findById(Id id) throws SQLException;
    public void delete(T entity) throws SQLException;
}

Now all my repositories extends my base repository, like this:

public class DependencyRepository extends BaseRepository<Dependency> 
                                  implements IDependenceyRepository {
    public DependencyRepository(Context context) {
        super(context);
    }
}

What i'm trying to achieve now is to create a repository factory that would allow people to not have to instantiate their repositories all over the place with the new Instance()

What i did was to create a Singleton factory that has to be initialized with a container that has the class relations, like this:

public abstract class BaseRepositoryContainer {
    private final Context context;

    public BaseRepositoryContainer(Context context) {
        this.context = context;
    }

    public abstract <T extends IRepository> Map<Class<T>, Class<T>> getRepositoriesMap();

    public Context getContext() {
        return context;
    }
}

The factory:

public class RepositoryFactory {
    private Map<Object, Object> repositories = new HashMap<>();
    private final String LOG_TAG = RepositoryFactory.class.getSimpleName();
    private Context context;
    private static RepositoryFactory instance;
    private BaseRepositoryContainer container;

    private RepositoryFactory() {}

    public void init(BaseRepositoryContainer container) {
        this.container = container;
        this.context = container.getContext();
        this.configureRepositories(container.getRepositoriesMap());
    }

    private <T extends IRepository> void configureRepositories(Map<Class<T>, Class<T>> repositoriesMap) {
        for (Entry<Class<T>, Class<T>> entry : repositoriesMap.entrySet()) {
            this.registerRepository(entry.getKey(), entry.getValue());
        }

    }

    private <T extends IRepository> void registerRepository(Class<T> repInterface, Class<T> realRepository) {
        repositories.put(repInterface, this.createRepository(realRepository));
    }

    public <T extends IRepository> T getRepository(Class<T> repInterface) {
        if (container == null) {
            throw new UnsupportedOperationException("You should call init method providing a container.");
        }

        return (T) repositories.get(repInterface);
    }

    private <T extends IRepository> T createRepository(Class<T> repoClass) {
        try {
            T instance = repoClass.getConstructor(Context.class).newInstance(context);
            Log.d(LOG_TAG, "Repository " + repoClass.getSimpleName() + " created");
            return instance;
        } catch (InstantiationException e) {
            Log.d(LOG_TAG, e.toString());
        } catch (IllegalAccessException e) {
            Log.d(LOG_TAG, e.toString());
        } catch (InvocationTargetException e) {
            Log.d(LOG_TAG, e.toString());
        } catch (NoSuchMethodException e) {
            Log.d(LOG_TAG, e.toString());
        }

        return null;
    }

    public static RepositoryFactory getInstance() {
        if (instance == null) {
            instance = new RepositoryFactory();
        }

        return instance;
    }
}

And then it could be called like this:

// when the application is first run
RepositoryFactory.getInstance().init(new RepositoryContainer(this));
// retrieving the repository
IDependenceyRepository repository = RepositoryFactory.getInstance()
                .getRepository(IDependenceyRepository.class);

So i was wondering if is this a good approach to help to implement towards an abstraction? I don't like much the idea of to have the calling the init method of the factory without obligating people to it, the only way to know is if you dont call will throw an exception which i dont like.

Does anyone can point me the right direction? A way to improve this design? I dont want to discover later on my project that i have created lots of strong dependencies and have a hard time to change something.

Any advices would be appreciated.

1

There are 1 answers

0
gmemario On BEST ANSWER

What i did to improve my code was instead of reinventing the wheel i started using a dependency injection library (Dagger 2 - http://google.github.io/dagger/).

You can define modules that return your desidered repository across an application or activities depending on your needs.