5
\$\begingroup\$

I am learning design patterns from Head First Design Patterns and a principle I came across is to "code to interface". Now I want to apply this principle in my Android app, which includes lots of API calls. I am using Retrofit for network calls.

I have applied this principle considering some scenario something like this:

  1. Suppose in the future I need to shift from Retrofit to Volley or any other networking library.
  2. I created an interface with a method to getData from the server and created a class which implements that interface. The implemented method includes the Retrofit code to getData from the server.
  3. Now in the future, if I want to use Volley, I'll create a new class implementing the interface and with Volley code to retrieve data from the server.

Interface

public interface NetworkCallsApi {
    String getDataFromServer(Activity activity,String url,String Callback);
}

Retrofit Implementation

public class NetworkCallsRetrofitImpl implements NetworkCallsApi{

private Retrofit retrofit;

@Override
public String getDataFromServer(Activity activity, String url, String Callback) {

retrofit = new Retrofit.Builder()
        .baseUrl(StringConstants.ROOT_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

RetrofitApi apiCalls = retrofit.create(RetrofitApi.class);

Call<ResponseBody> call = apiCalls.getDataFromServer(url);

call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {

    }
});
return null;
}
}

ServiceCalls.java

public class ServiceCalls {

    public static ServiceCalls serviceCalls;
    public NetworkCallsApi networkCallsimpl;

    public static ServiceCalls getInstance(){

        if(serviceCalls == null){
            serviceCalls = new ServiceCalls();
        }
        return serviceCalls;
    }

    public void setNetworkCallsimpl(NetworkCallsApi networkCallsimpl) {
        this.networkCallsimpl = networkCallsimpl;
    }

    public String getDataFromServer(Activity activity, String url, String callback){
        networkCallsimpl.getDataFromServer(activity,url,callback);
        return null;
     }
 }

MainFragment:

public class StatusFragment extends Fragment {    

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Setting the implementation
        ServiceCalls.getInstance().setNetworkCallsimpl(new NetworkCallsRetrofitImpl());
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        view = inflater.inflate(R.layout.fragment_pnr_status,container,false);

        try{
          init();
        }catch(Exception e){}
        return view;
   }

    private void init() {

        ok.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            setUrlKeysAndValues();
            ServiceCalls.getInstance().getDataFromServer(getActivity()
                ,Utils.getInstance().buildUrl(urlKeys,urlValues),"onResponse");
        }
        });

    }
}

Have I correctly applied that principle? If not, what changes do I need to make? In onCreate of MainFragment, I am hardcoding the implementation. How can the interface design pattern be better applied in this scenario?

\$\endgroup\$
5
  • \$\begingroup\$ Welcome to Code Review! Please edit your question to write a title that summarizes what your code does, as your current title is generic and could apply to other questions. Thank you!. \$\endgroup\$
    – Phrancis
    Commented Feb 13, 2017 at 5:40
  • \$\begingroup\$ There is a little confusion about the terms: "Coding against interfaces" is NOT a design pattern. It is a principle that was formulated as one of the SOLID principles: "D" - dependency inversion principle. As a principle you should always follow it. A design pattern will be applied in single situation where it is appropriate. \$\endgroup\$
    – oopexpert
    Commented Feb 13, 2017 at 14:07
  • \$\begingroup\$ @oopexpert : thanks for such a big correction. Its a design principle. \$\endgroup\$ Commented Feb 13, 2017 at 15:39
  • \$\begingroup\$ I didn't want to be pedantic by no means. But I personally was asked "What is this kind of a design pattern?" in an interview. After offering 3 design patterns ordered by descending probability I gave up and he told me that he was searching for "dependency inversion". \$\endgroup\$
    – oopexpert
    Commented Feb 13, 2017 at 17:24
  • \$\begingroup\$ I have implemented something similar. I had to interchange the network api used to get movies data from server. So I created a interface called MovieFetch and an abstract class called AbstractMovieFetcher. I extended this classe and implement interface to use OkHttpFetcher and VolleyFetcher libraries to fetch data. Here is the code: github.com/alexpfx/P1-PopularMovies/tree/dev/app/src/main/java/… \$\endgroup\$
    – alexpfx
    Commented Mar 2, 2017 at 15:42

3 Answers 3

1
\$\begingroup\$

Let me point out an other aspect, you might miss at the moment. I know that this is a learner question/example, but keep in mind that your approach is designing an application where you can switch the network library (in extreme) at runtime. This is not the most common scenario.

Actually it is more likely that you want to switch Retrofit against a mock or Volley against a mock in your tests. So you have done everything right, if you can write tests for related parts of your system (e.g. the StatusFragment) without doing real network calls and there is no test switch in your production code. (The network stuff will get it's own test.)

On the other hand, although I'm not familiar with the libraries, Google told me that both seems to have some own mocking concepts. So there might not even be a need to switch your implementation in unit tests. In case you finally end up with only one implementation now and optional a total replacement in the future, this approach might add an unnecessary (mental and technical) overhead.

Implementing against an interface don't necessary mean a Java interface. If you only have one implementation at a time, it is totally fine to drop NetworkCallsApi and move NetworkCallsRetrofitImpl into ServiceCalls. Afterwards you are implementing against the interface of ServiceCalls and you don't care about is implementation. It is also pretty straight forward to change the implementation in ServiceCalls later without changing other parts of the application.

\$\endgroup\$
1
\$\begingroup\$

What you are doing is making explicit that you are requesting info from network directly in a fragment. That's not a desirable approach, you should have a presenter that decide from where the info can be get. This way, in your fragment you only have:

presenter.getUsers(); // Then, the presenter can decide if the the users are saved locally (SQLite, SharedPreferences, data...) or have to be retrieved from your server. 

This follows the MVP pattern, in other platforms, you'll have MVC or MVVM.

Having said that, what I do is having.. let's say a UserServiceImpl which call directly to the network framework that I use to talk to my API and what I inject in the StatusPresenter is the UserService interface.

\$\endgroup\$
0
\$\begingroup\$

Yes, you have implemented it.

But, changing the implementation via a setter on your singleton is not a good idea. (at least check against null).

Many applications are using Dependency Injection to receive the correct implementation. But If you don't want to, you should have a look to the creationals patterns. A factory can help, and/or you can extends your fragment with an abstract one that you reuse everywhere and will give access to the implementation (so that you have to change it once).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.