Skip to main content

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

Popular posts from this blog

BIRT - Fix the size of an image

I use a dynamic image as a logo my report in pdf. At the beginning, I use table to align the logo in left or right. I meet a problem with some images with a large width or height. My customer requires that the logo should be displayed in original size. These following steps solves my problem: 1. Use Grid instead of Table 2. Set Grid "Height" is 100%  and "Width" is blank 3. Set "Fit to container" for images are "true". Download the the template here .

Strategy Design Pattern

For example, I have a program with an Animal abstract class and two sub-classes Dog and Bird. I want to add new behavior for the class Animal, this is "fly".  Now, I face two approaches to solve this issue: 1. Adding an abstract method "fly" into the class Animal. Then, I force the sub-classes should be implemented this method, something like: public abstract class Animal{ //bla bla public abstract void fly(); } public class Bird extends Animal{ //bla bla public void fly(){ System.out.println("Fly high"); } } public class Dog extends Animal{ //bla bla public void fly(){ System.out.println("Cant fly"); } } 2. Creating an interface with method "fly" inside. The same issue to an abstract class, I force the classes these implement this interface should have a method "fly" inside: public interface Flyable{ public void fly(); } public class Bird implements Flyable{ //bla bla public void fly(){ System.out.pr...

AngularJS - Build a custom validation directive for using multiple emails in textarea

AngularJS already supports the built-in validation with text input with type email. Something simple likes the following: <input name="input" ng-model="email.text" required="" type="email" /> <span class="error" ng-show="myForm.input.$error.email"> Not valid email!</span> However, I used a text area and I wanted to enter some email addresses that's saparated by a comma (,). I had a short research and it looked like AngualarJS has not supported this functionality so far. Therefore, I needed to build a custom directive that I could add my own validation functions. My validation was done only on client side, so I used the $validators object. Note that, there is the $asyncValidators object which handles asynchronous validation, such as making an $http request to the backend. This is just my implementation on my project. In order to understand that, I supposed you already had experiences with ...

Sharing a virtualenv across several Python projects using Pipenv

There is a standard library for all projects in Python. However, several projects don’t always have the same dependencies all the time. That is where virtual environments come to play. You can follow this official document to use two separated tools  virtualenv and pip to  fulfill that need. My preferred alternative is to use pipenv . Pipenv is easy to use and convenient. The following are my steps to make a shared virtualenv for my all projects which requires the same dependencies. Step 1. Create an isolated virtualenv. python -m venv my-shared-env Step 2. Create a symbolic link to the created virtualenv. cd project_1 ln -s ~/.local/share/virtualenvs/my-shared-env .venv I have encountered the following issue at step 1. FileNotFoundError: [Errno 2] No such file or directory: '{my_project_path}/.venv/bin/pip': '{my_project_path}/.venv/bin/pip' The root cause was I tried to create virtualenv by running pipenv install and renaming the generated virtualenv to ...

The HelloWorld example of JSF 2.2 with Myfaces

I just did by myself create a very simple app "HelloWorld" of JSF 2.2 with a concrete implementation Myfaces that we can use it later on for our further JSF trying out. I attached the source code link at the end part. Just follow these steps below: 1. Create a Maven project in Eclipse (Kepler) with a simple Java web application archetype "maven-archetype-webapp". Maven should be the best choice for managing the dependencies , so far. JSF is a web framework that is the reason why I chose the mentioned archetype for my example. 2. Import dependencies for JSF implementation - Myfaces (v2.2.10) into file pom.xml . The following code that is easy to find from  http://mvnrepository.com/  with key words "myfaces". <dependency> <groupId>org.apache.myfaces.core</groupId> <artifactId>myfaces-api</artifactId> <version>2.2.10</version> </dependency> <dependency> <groupId>org.apache.myfaces.core<...