Introduction
Sometimes, I just want to get rid of my own dependencies and rely only on base classes, primarily because I don't have to test them, they are well tested, and secondly they don't change.
Background
The other day I was asked to implement a solution to this problem:
This company has several .dlls that have to be translated from Spanish to other languages, meaning every message that comes up from the code now has to be replaced with some sort of code that allows the operation of translating to other languages.
What I Learned
I learned in the process that I could refactor my code to be less dependent. The code I would like to share is in C# but it could be Java 8 as well.
Story
As I mentioned earlier, the company has several .dlls that have to be translated from Spanish to other languages, meaning every message that comes up from the code, now has to be replaced with some sort of code that permits the operation.
The initial idea was to inject an interface called Translator
with the next signature:
interface translator {
String translate(String code);
}
So far, it seems a correct solution, it is easy to implement and easy to test or even Mock.
But, what about the .dll having an extra dependency on the package or namespace where the translator lives.
I thought about this for a while and I came up with this solution:
First of all, the act of translating is pretty much unique, it has an input code and an output message. As simple as String->String
function.
So instead of injecting a translator interface, why don't I inject a function.
Func<String, String>
I had the legacy code:
class DLL_Code {
public DLL_Code(Dependency1 dep1, Dependency2 dep2) {
this.dep1 = dep1;
this.dep2 = dep2;
}
private Dependency1 dep1;
private Dependency2 dep2;
}
Now I have:
class DLL_Code {
public DLL_Code(Dependency1 dep1, Dependency2 dep2, Func<String, String> translate) {
this.dep1 = dep1;
this.dep2 = dep2;
this.translate = translate;
}
private Dependency1 dep1;
private Dependency2 dep2;
Func<String, String> translate;
}
This is not only C# compatible but also JDK8 has similar Interface Function<T, R>
signature.
I felt good about not adding dependencies of mine but instead, rely on .NET base classes. This is not a big deal, but personally I always prefer relying on base classes rather than depending on user classes. Just my personal preference.
Hours later, I asked myself:
What Design Patterns Could Be Refactored to Functions?
Suppose we have a typical decorator pattern, let's say:
interface Tracer {
void trace(String info);
}
class NormalTracer : Tracer {
public NormalTracer(String fileName) {
this.fileName = fileName;
}
public void trace(String info) {
}
private String fileName;
}
class BufferedTracer : Tracer {
public BufferedTracer(Tracer tracer) {
this.tracer = tracer;
listOfTraces = new List<String>();
}
public void trace(String info) {
listOfTraces.Add(info);
}
public void flush() {
foreach (String s in listOfTraces) {
tracer.trace(s);
}
}
private Tracer tracer;
private List<String> listOfTraces;
}
On the other hand, we have this legacy piece of code that makes use of Tracer
.
class LegacyClass {
public LegacyClass(Tracer tracer) {
this.tracer = tracer;
}
private Tracer tracer;
}
If I want to get rid of the class dependency Tracer
which I do, then I would refactor LegacyClass
to:
class LegacyClass {
public LegacyClass(Action<String> tracer) {
this.tracer = tracer;
}
private Action<String> tracer;
}
At first, it does not look like a great leap, but since in every company the code is definitely not homogenous as anyone would like.
Now you can use the refactored LegacyClass
with Log4Net, Log4J or the fast buffered tracer without worrying about the isolated class in the .dll.
Now what happened to the other patterns, do they behave equally?
Think of an Observer Pattern
class IsolatedClass {
public IsolatedClass(Action<IsolatedClass> notify) {
this.notify = notify;
}
public void someProcess() {
notify(this);
}
private Action<IsolatedClass> notify;
}
class Obvserver {
public void someMethod() {
new IsolatedClass(z => someOtherMethod(z));
}
public void someOtherMethod(IsolatedClass ic) {
}
}
From the testing point of view, the IsolatedClass
is easy to test and refactor in the future.
Other Patterns?
Think if I want to convert the above Observer into a Mediator. I don't have to change the signature or dependencies on the IsolatedClass
.
So far I don't see any drawbacks, some feedback will be really appreciated.