How to avoid repeatng for loops in Java?

Viewed 107

I have a code that works fine, but it looks too big as it has repetitive line of codes, can someone help me to make it shorter?

public void writeToFile(String file)
{
    try
    {
            PrintWriter pWrite = new PrintWriter(file);
            pWriter.println("[Auto data]");
            for (Auto line : autoMap.values())
            {
                if (line instanceof Auto) 
                {
                    String getLine = line.writeData(file);
                    pWrite.println(getLine); 
                }
            }
            pWriter.println();
            pWriter.println("[Nature data]"); 
            for (Nature line : natureMap.values())
            {
                if (line instanceof Nature) 
                {
                    String getLine = line.writeData(file);
                    pWrite.println(getLine); 
                }
            }
            pWriter.println();
            pWriter.println("[Sport data]");
            for (Sport line : sportMap.values())
            {
                if (line instanceof Sport) 
                {
                    String getLine = line.writeData(file);
                    pWrite.println(getLine); 
                }
            } 
            pWriter.println();
            pWriter.println("[Animal data]");
            for (Animal line : animalMap.values())
            {
                if (line instanceof Animal) 
                {
                    String getLine = line.writeData(file);
                    pWrite.println(getLine); 
                }
            }
            pWrite.close();
    }
    // catch block omitted
}

writeData method example class Sport:

public class Sport
{
     // codes omitted

    public String writeData(String file)
    {
        return title + type + cost + location;
    }
    // codes omitted
}

As you have noticed the for loops are necessary to subdivide data in order when writing to a file, but doing so, it made the code longer and repetitive, I cannot think of an efficient way to make it shorter.

2 Answers

Ensure Nature, Sport, and Animal all implement an interface such as:

public interface Writable {
    public String writeData(String file);
}

And since all of the elements of each Map implement the interface, the duplicate code can be extracted, and pass the Map to the new method which uses the interface:

public void writeToFile(String file) {
    PrintWriter pWriter = new PrintWriter(file);

    pWriter.println("[Auto data]");
    writeDataToFile(file, pWriter, autoMap);

    pWriter.println();
    pWriter.println("[Nature data]");
    writeDataToFile(file, pWriter, natureMap);

    pWriter.println();
    pWriter.println("[Animal data]");
    writeDataToFile(file, pWriter, animalMap);

   pWrite.close();
}

// assuming the map key is a string
private void writeDataToFile(String file, PrintWriter pWriter, Map<String, Writable> data) {
    for (Writable line : data.values()) {
        String getLine = line.writeData(file);
        pWriter.println(getLine); 
    } 
}

That is a classic case when you can take advantage from Interface and Inheritance. In particular, I would define an interface which all class can inherits.

Then, I would define a private method in the same class where writeToFile is, which implements just the for loop on a given Line.

Finally, you can simply invoke that method in the writeToFile for each map. However, I would change a bit the implementation, as it is best practice to use the try-with-resource block, which guarantees to always close your stream before leaving the block, even in case of possible exception.

To give you an idea of what I am referring to, here is an example of possible implementation:

import java.util.*;
import java.io.*;

interface Line {
    String getType();
    default void writeData(PrintWriter pWrite) {
        pWrite.println("I am a " + getType());
    }
}

class Auto implements Line {
    public String getType() {
        return "Auto";
    }
}

class Sport implements Line {
    public String getType() {
        return "Sport";
    }
}

class Animal implements Line {
    public String getType() {
        return "Animal";
    }
}

public class Test {
    private Map<String, Line> itemMap;
    
    public Test() {
        initMap();
    }
    
    private void initMap() {
        itemMap = new HashMap<>();
        itemMap.put("auto1", new Auto());
        itemMap.put("sport1", new Sport());
        itemMap.put("animal1", new Animal());
    }
    
    public static void main(String ... args) throws IOException {
        
        new Test().writeToFile("Test.txt");
    }
    
    public void writeToFile(String file) throws IOException {
        try (PrintWriter pWrite = new PrintWriter(file);) {
                writeLineToFile(pWrite, itemMap);
        }
        // catch block omitted
    }
    
    private void writeLineToFile(PrintWriter pWrite, Map<String, Line> map) {
        for (Line line : map.values()) {
            //Common part
            line.writeData(pWrite);
        }
    }
}

Here is the content of the file:

I am a Sport
I am a Animal
I am a Auto

In my opinion that appears cleaner and it is also much easier to maintain.

Surely, that is just an idea, you can personalize and add more things according to your needs, and in particular, by taking advantage of inheritance, the possibilies are limiteless.

Related