Separate Constructing a System from Using It - The Koch Family
The Koch Family The Koch Family

Latest news

جاري التحميل ...

Separate Constructing a System from Using It


In the real world, in order to use a building (hotel, supermarket, etc) we need to construct it first. This concern should be applied for software development as well.

Step by step, I would like to show you the issue about no separation of constructing and using it and then I'll give you some approaches to overcome this issue.

| Note: you can find the below demonstrated code here  

Take a Look the Following Simple Application

Used tools and technologies: Eclipse (Mars), JDK 1.8

I had an App which uses Controller. Controller uses Service (an interface). Finally, Service has one concrete class is DefaultService.
//package vn.nvanhuong.system.separationconstructing;
public class App {
public static void main(String[] args) {
Controller controller = new Controller();
controller.doAction();
}
}

public class Controller {
private Service service;

public void doAction(){
System.out.println("doAction in Controller");
getService().execute();
}

public Service getService(){
if(service == null){
service = new DefaultService();
}
return service;
}
}

public interface Service {
void execute();
}

public class DefaultService implements Service {

@Override
public void execute() {
System.out.println("executing Default Service");
}

}

Do you find any issue?

Okay. Now, we just pay attention on this piece of code.
public Service getService(){
if(service == null){
service = new DefaultService();
}
return service;
}

Have you ever thought that we might meet at least three following issues?
  • Compile error: if Controller could not find DefaultService.
  • Testing: if we want to test Controller, we need to mock DefaultService in case it is a heavyweight object
  • Right object: if Service has more concrete classes, DefaultService will not be always a desired object in all cases.
The root cause is that we had a hard-coded "DefaultService" as a dependency of Controller.

How to solve?

The problem is the solution! Instead, we construct the instance of Service at Controller, we move this code of constructing into another place. Yeah!

There are three approaches.

Separation of Main - Separating the constructing into "main"

//package vn.nvanhuong.system.separationconstructing.main;

public class App {
public static void main(String[] args) {
Service service = new DefaultService();
Controller controller = new Controller(service);
controller.doAction();
}
}

public class Controller {
private Service service;

public Controller(Service service) {
this.service = service;
}

public void doAction() {
System.out.println("doAction in Controller");
service.execute();
}
}

Factories - Using design pattern "Abstract Factory"

We move the constructing into a place separating from the application code.

//package vn.nvanhuong.system.separationconstructing.factory;

public class App {
public static void main(String[] args) {
ServiceFactory factory = new ServiceFactory();
Service service = factory.getService(ServiceName.CUSTOM);
Controller controller = new Controller(service);
controller.doAction();
}
}

public class ServiceFactory {

public Service getService(ServiceName serviceName) {
if (Objects.isNull(serviceName)) {
throw new IllegalArgumentException("service name must not be null");
}

switch (serviceName) {
case DEFAULT:
return new DefaultService();
case CUSTOM:
return new CustomService();
default:
return null;
}
}

}

public enum ServiceName {
DEFAULT, CUSTOM
}

public class CustomService implements Service {

@Override
public void execute() {
System.out.println("executing from Custom Service");

}

}

public class Controller {
private Service service;

public Controller(Service service) {
this.service = service;
}

public void doAction() {
System.out.println("doAction in Controller");
service.execute();
}
}

Dependency Injection

We move the second reposibility of object Controller (constructing Service) into another object. Moreover, DI helps us solve dependencies passively.

I used Google Guice for this demonstration. Because I still wanted to use a Java project (in Eclipse) instead of Maven project so that I simply found needed jars on Maven Repository and included them into project's buildpath. They are "guice-3.0.jar", "cglib-2.2.1.jar", "javax.inject-1.jar" and ''aopalliance-1.0.jar".

//package vn.nvanhuong.system.separationconstructing.di;

public class App {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AppInjector());
Controller controller = injector.getInstance(Controller.class);
controller.doAction();
}
}

public class AppInjector extends AbstractModule {

@Override
protected void configure() {
bind(Service.class).to(CustomService.class);
}

}

public class Controller {
private Service service;

public void doAction() {
System.out.println("doAction in Controller");
service.execute();
}

@Inject
public void setService(Service service) {
this.service = service;
}
}

How do you think about the issues and solutions? Leave your comments down below. :)

--------------
References:

[1]. Robert C. Martin, Clean Code - A Handbook of Agile Software Craftsmanship, Chapter 11, Systems.
[2]. https://en.wikipedia.org/wiki/Abstract_factory_pattern
[3]. http://www.journaldev.com/2403/google-guice-dependency-injection-example-tutorial

Comments



If you like the content of our blog, we hope to stay in constant communication, just enter your email to subscribe to the blog's express mail to receive new blog updates, and you can send a message by clicking on the button next ...

إتصل بنا

About the site

author The Koch Family <<  Welcome! I'm so glad that you stopped by Your Modern Family blog. Together, we will talk about raising kids, organizing the home and saving money! and Tips & tricks and more…

< Learn more ←

Blog stats

Sparkline 2513183

All Copyrights Reserved

The Koch Family

2020