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

Monday vhandit #2

  Introduction to OpenLDAP directory service "A directory is a specialized database specially designed for searching and browsing, in addition to supporting basic lookup and update functions" A directory service can be local, providing a restricted context; or global, providing service to a much broader context. Curlie is a good example of a directory service. LDAP (Lightweight Directory Access Protocol) is a protocol for accessing directory services , specifically X.500-based directory services. OpenLDAP is an open-source implementation of LDAP. Writing system software: code comments I have read Clean Code (by Uncle Bob) and I thought that I should void comments since the code it explains its implementation itself. That is right but not always true. In this post, the author categorized the comments into 9 types. Only "trivial comments" and "backup comments" are the ones that should be avoided. I myself agree with "writing good comments is harder tha...

My must-have apps for daily work

There is no doubt that cool apps can help us be more productive and enjoyable at work. For the time being, I really love the following apps which are used by me almost every day. 1. A personal Kanban In fact, a personal kanban is the most useful app for me. Why does it matter? It is not just a to-do list, but it keeps me motivated every day because it helps me be able to know what my "big picture" is. I usually set up my plans together with a path to reach them.  KanbanFlow  is my preferred tool. KanbanFlow 2. A terminal Needless to say, a terminal is a must-have app for every developer, especially the ones use macOS/Linux. Due to its importance, I love to decorate and enhance it to be super exciting with various tools such as  iTerm ,  oh-my- zsh , and  thefuck . ;) iTerm + oh-my-zsh 3. A documentation "ecosystem" As a developer, I can not remember all things that I have experimented a day. Moreover, a document is really useful for sharing an...

Creating a Chatbot with RiveScript in Java

Motivation "Artificial Intelligence (AI) is considered a major innovation that could disrupt many things. Some people even compare it to the Internet. A large investor firm predicted that some AI startups could become the next Apple, Google or Amazon within five years"   - Prof. John Vu, Carnegie Mellon University. Using chatbots to support our daily tasks is super useful and interesting. In fact, "Jenkins CI, Jira Cloud, and Bitbucket" have been becoming must-have apps in Slack of my team these days. There are some existing approaches for chatbots including pattern matching, algorithms, and neutral networks. RiveScript is a scripting language using "pattern matching" as a simple and powerful approach for building up a Chabot. Architecture Actually, it was flexible to choose a programming language for the used Rivescript interpreter like Java, Go, Javascript, Python, and Perl. I went with Java. Used Technologies and Tools Oracle JDK 1.8...

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...

Styling Sort Icons Using Font Awesome for Primefaces' Data Table

So far, Primefaces has used image sprites for displaying the sort icons. This leads to a problem if we want to make a different style for these icons; for example, I would make the icon "arrow up" more blurry at the first time the table loading because I want to highlight the icon "arrow down". I found a way that I can replace these icons with Font Awesome icons. We will use "CSS Pseudo-classes" to achieve it. The hardest thing here is that we should handle displaying icons in different cases. There is a case both "arrow up" and "arrow down" showing and other case is only one of these icons is shown. .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s { background-image: none; margin-left: 5px; font-size: 1.1666em; position: relative; } .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s:not(.ui-icon-triangle-1-s)::before { content: "\f106"; font-family: "FontAwesome"; position: ...