Click here to Skip to main content
15,894,362 members
Articles / Web Development / ASP.NET
Tip/Trick

Refactor to Functions

Rate me:
Please Sign up or sign in to vote.
4.73/5 (10 votes)
16 Oct 2014CPOL3 min read 12.4K   10   3
Get rid of dependencies through the use of Functions

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:

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

C#
Func<String, String>

I had the legacy code:

C#
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:

C#
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:

C#
interface Tracer {
    void trace(String info);
}

class NormalTracer : Tracer {
    public NormalTracer(String fileName) {
        this.fileName = fileName;
    }
    public void trace(String info) {
        // Code to trace
    }
    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.

C#
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:

C#
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

C#
class IsolatedClass {
    public IsolatedClass(Action<IsolatedClass> notify) {
        this.notify = notify;
    }
    public void someProcess() {
        // Some code
        notify(this);
        // Some code
    }
    private Action<IsolatedClass> notify;
}

class Obvserver {
    public void someMethod() {
        new IsolatedClass(z => someOtherMethod(z));
    }
    public void someOtherMethod(IsolatedClass ic) {
        // act in consequence
    }
}

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionLoose abstraction Pin
abdurahman ibn hattab16-Oct-14 18:35
abdurahman ibn hattab16-Oct-14 18:35 
Your technique is a loose abstraction and generally that's not a good thing. Func<string, string> says nothing about its purpose. It is ok to use it locally inside a method or class. but use it as a contract between subsystems... well, it's like saying curse words to the future maintainers of your code.
Question5+! Pin
Volynsky Alex16-Oct-14 8:09
professionalVolynsky Alex16-Oct-14 8:09 
QuestionUse of delegates instead of Lambda Pin
Rafael Nicoletti16-Oct-14 3:51
Rafael Nicoletti16-Oct-14 3:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.