Make Our Code More Testable with Proxy Design Pattern - The Koch Family
The Koch Family The Koch Family

Latest news

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

Make Our Code More Testable with Proxy Design Pattern


If you heard about the term separation of concerns, you could agree this concept is very important for making a system "clean". One of the most important characteristics of a clean system is testable.

Let me tell you my story about how I've just come acrosss the design pattern Proxy.

Note: to get started, you can find a very simple example of the pattern Proxy here

Let's start!

I have an interface as below:
public interface DocumentGenerator {
File generate(Document document) throws BusinessException;
}

The following is my first implementation for a concrete class of DocumentGenerator.
public class DocumentGeneratorImpl implements DocumentGenerator {
private Dossier dossier;
private Locale locale;

public DocumentGeneratorImpl(Dossier dossier, Locale locale) {
validateNotNullParams(dossier, locale);
this.dossier = dossier;
this.locale = locale;
}

private void validateNotNullParams(LibertyDossier dossier, Locale locale) {
if(Objects.isNull(dossier)) {
throw new IllegalArgumentException("Dossier must not be null!");
}

if(Objects.isNull(dossier.getDossierType())){
throw new IllegalArgumentException("Dossier type must not be null!");
}

if(Objects.isNull(locale)) {
throw new IllegalArgumentException("Locale must be defined!");
}
}

@Override
public File generate(Document document, boolean temporary)
throws BusinessException {
setCobIdForDossierIfNotExist();
switch (dossier.getDossierType()) {
case TYPE_A:
// Do some logic here in case TYPE_A
case TYPE_BA:
// Do some logic here in case TYPE_B
default:
throw new BusinessException("Not supported dossier type");
}
}

private void setCobIdForDossierIfNotExist() {
if(StringUtils.isEmpty(dossier.getCobId())) {
String generatedCobId = DossierService.getInstance().generateCobId();
dossier.setCobId(generatedCobId);
}
}
}

The client code constructs the concrete class DocumentGeneratorImpl.
DocumentGenerator  generator = new DocumentGeneratorImpl(dossier, locale);

I saw that some private methods "validateNotNullParams(dossier, locale)" and "setCobIdForDossierIfNotExist()" are just a second responsibility of class DocumentGeneratorImpl.

Follow "S" of SOLID  - Single Responsibility

Firstly, I was thinking about how to separate these methods into another class. Then, I create a class called DocumentGeneratorHelper which contains to these methods. It was just improved a bit.

public class DocumentGeneratorImpl implements DocumentGenerator {
private Dossier dossier;
private Locale locale;

public DocumentGeneratorImpl(Dossier dossier, Locale locale) {
DocumentGeneratorHelper.validateNotNullParams(dossier, locale);
this.dossier = dossier;
this.locale = locale;
}

@Override
public File generate(Document document, boolean temporary)
throws BusinessException {
DocumentGeneratorHelper.setCobIdForDossierIfNotExist();
switch (dossier.getDossierType()) {
case TYPE_A:
// Do some logic here in case TYPE_A
case TYPE_BA:
// Do some logic here in case TYPE_B
default:
throw new BusinessException("Not supported dossier type");
}
}

}

However, if I create tests for DocumentGeneratorImpl, I need to mock DocumentGeneratorHelper. Huh....! Any better approach?

I was thinking about that it is somehow I need to apply an Aspect-Oriented Programming (AOP) approach. As my google searching result, there are some approaches but they are quite complicated and heavy. They use Java Proxy or AOP Frameworks such as AspectJ.

A Simple AOP Approach

Fortunately, the keyword "proxy" leads me to the pattern Proxy. The idea of Proxy is really simple and cool!



Okay, now I create a Proxy instead of a Helper as before.

public class DocumentGeneratorProxy implements DocumentGenerator{
private DocumentGenerator generator;
private Dossier dossier;

public DocumentGeneratorProxy(Dossier dossier, Locale locale) {
validateNotNullParams(dossier, locale);
this.dossier = dossier;
this.generator = new DocumentGenerator(this.dossier, locale);
}

private void validateNotNullParams(Dossier dossier, Locale locale) {
if(Objects.isNull(dossier)) {
throw new IllegalArgumentException("Dossier must not be null!");
}

if(Objects.isNull(dossier.getDossierType())){
throw new IllegalArgumentException("Dossier type must not be null!");
}

if(Objects.isNull(locale)) {
throw new IllegalArgumentException("Locale must be defined!");
}
}

@Override
public File generate(Document document, boolean temporary)
throws BusinessException {
setCobIdForDossierIfNotExist();
return generator.generate(document, temporary);
}

private void setCobIdForDossierIfNotExist() {
if(StringUtils.isEmpty(dossier.getCobId())) {
String generatedCobId = DossierService.getInstance().generateCobId();
dossier.setCobId(generatedCobId);
}
}

}

Done! DocumentGeneratorImpl  doesn't contain its second responsibilities anymore. These methods are tested by the Proxy and we don't need to mock in DocumentGeneratorImpl.
the public class DocumentGeneratorImpl implements DocumentGenerator {
private Dossier dossier;
private Locale locale;

public LibertyDocumentGenerator(Dossier dossier, Locale locale) {
this.dossier = dossier;
this.locale = locale;
}

@Override
public File generate(Document document, boolean temporary)
throws BusinessException {
switch (dossier.getDossierType()) {
case TYPE_A:
// Do some logic here in case TYPE_A
case TYPE_BA:
// Do some logic here in case TYPE_B
default:
throw new BusinessException("Not supported dossier type");
}
}

}

So, instead of calling directly DocumentGeneratorImpl, we call DocumentGeneratorProxy in the client code as below:

DocumentGenerator  generator = new DocumentGeneratorProxy(dossier, locale);

Happy codings!

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