Ever been in this situation? You have been writing some code and then in the end cleaning up, realizing that you have a couple of methods that are similar in behavior but should be split up in different classes?
Well, lets give you an example.
public class Foo {
public void methodA() {
loadA();
loadB();
processX();
cleanup();
}
public void methodB() {
loadA();
loadB();
processY();
cleanup();
}
public void methodC() {
loadA();
loadB();
processZ();
cleanup();
}
}
So basically in this weird example, you have similar behavior in your methods, and some duplications. Now, you want to respect the DRY principle and remove the duplicates. Or, better yet, you never read about the DRY principle and just doesn’t like the sight of duplication. Either way. You have some options, and the two on my mind are both Dependency Inversion related, one by reference, and the other by inheritance.
We start with the one by reference.
public abstract class AbstractProcessor {
public void method() {
loadA();
loadB();
process();
cleanup();
}
public abstract void process();
}
public class X extends AbstractProcessor {
public void process() {
// Behavior X
}
}
public class X extends AbstractProcessor {
public void process() {
// Behavior Y
}
}
public class X extends AbtractProcessor {
public void process() {
// Behavior Z
}
}
public class Foo {
public void methodA() {
new X().method();
}
public void methodB() {
new Y().method();
}
public void methodC() {
new Z().method();
}
}
The duplication is gone. Do with it what you like.
Next example, you want to remove details from your implementation.
public class ProcessFile {
public void run() {
FileContent content = readFile();
loadCacheA();
loadCacheB();
loadCacheC();
if (”A”.equals(content.getType())) {
doA();
} else if (”B”.equals(content.getType())) {
doB();
} else if (”C”.equals(content.getType())) {
doC();
}
}
}
The example above is pretty common and easy to fix. It is very clear that we actually have 3 different behaviors in the same method. We can easily make this code easier to extend and easier to maintain by just moving the behaviors out of the class.
We will move the behavior to classes A, B and C. This also complies with SRP on a class level and just makes sense. Just keep it simple.
interface Processor {
void load();
void do();
}
public class A implements Processor {
public void load() { // Load something }
public void do() { // Do something }
}
public class B implements Processor {
public void load() { // Load something }
public void do() { // Do something }
}
public class C implements Processor {
public void load() { // Load something }
public void do() { // Do something }
}
public class ProcessFile {
private Map<String, Processor> processors = new HashMap<>();
public void run() {
init();
FileContent content = readFile();
loadCaches();
do(content.getType());
}
public void init() {
processors.put(”A”, new A());
processors.put(”B”, new B());
processors.put(”C”, new C());
}
public void loadCaches() {
processors.values().stream().forEach(p -> { p.load(); });
}
public void do(String type) {
Processor processor = processors.get(type);
if (processor != null) {
processor.do();
} else {
System.out.println(”Unknown type ” + type);
}
}
}
That’s basically it. Now go practice 🙂