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

[Snippet] CSS - Child element overlap parent

I searched from somewhere and found that a lot of people says a basic concept for implementing this feature looks like below: HTML code: <div id="parent">  <div id="child">  </div> </div> And, CSS: #parent{   position: relative;   overflow:hidden; } #child{   position: absolute;   top: -1;   right: -1px; } However, I had a lot of grand-parents in my case and the above code didn't work. Therefore, I needed an alternative. I presumed that my app uses Boostrap and AngularJs, maybe some CSS from them affects mine. I didn't know exactly the problem, but I believed when all CSS is loaded into my browser, I could completely handle it. www.tom-collinson.com I tried to create an example to investigated this problem by Fiddle . Accidentally, I just changed: position: parent; to position: static; for one of parents -> the problem is solved. Look at my code: <div class="modal-body dn-placeholder-parent-positi...

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

PSMDB - A MongoDB alternative for having Encryption At Rest

Encryption is the most popular tool for securing data both in transit and at rest. - For protecting data in transit, we can configure to use the TLS connection - For protecting data at rest, we can use Percona Server for MongoDB (PSMDB), an open-source alternative for MongoDB Enterprise. License PSMDB Docker images follow the SSPL license. Therefore, it is not a problem when I only have my containers deployed in on-premises environments. Running MongoDB Replication on OpenShift I have successfully installed the replication by following the guide Install Percona Server for MongoDB on OpenShift . In order to make it work properly with my needs, I disabled some features from the default deployment. See the detail in this change Basically, I needed to create a CRD (Custom Resource Definition) to let OpenShift/Kubernetes what PSMDB is. Then, I deployed the Operator pod. Finally, I deployed the PSMDB StatefulSet. I used NFS shares for Persistent Volumes. Create CRD for PSMDB 2 git clone http...

How I did customize "rasa-nlu-trainer" as my own tool

Check out my implementation here Background I wanted to have a tool for human beings to classify intents and extract entities of texts which were obtained from a raw dataset such as Rocket.chat's conversation, Maluuba Frames or  here . Then, the output (labeled texts) could be consumed by an NLU tool such as Rasa NLU. rasa-nlu-trainer was a potential one which I didn't need to build an app from scratch. However, I needed to add more of my own features to fulfill my needs. They were: 1. Loading/displaying raw texts stored by a database such as MongoDB 2. Manually labeling intents and entities for the loaded texts 3. Persisting labeled texts into the database I firstly did look up what rasa-nlu-trainer 's technologies were used in order to see how to implement my mentioned features. At first glance rasa-nlu-trainer was bootstrapped with Create React App. Create React App is a tool to create a React app with no build configuration, as it said. This too...

4 Remarkable Notes for JSF Apps Using HTML5

In the previous post , I've already shared with you how my team consults clients by using a HTML prototype. This post is about the used technologies for reusing the provided HTML template and communicating with backend. What is the issue when using HTML elements with Primefaces components? Primefaces is a great extension for developing JSF web apps. However, it is really frustrating in case we have to make it work with an existing HTML template. Why? - Primefaces has its own theme for styling. - Primefaces changes the HTML structure. Therefore, that would be a huge effort to use the Primefaces' components to replicate the elements of the HTML template; especially it is impossible for images drawing by " canvas " tag. That requires us to find a better approach. Since Java EE 7 (introducing JSF 2.2 included), it supports to use HTML5 elements . The idea is that JSF components don't effect the style and HTML structure, so we can easily reuse the provided HTM...