I have a service that gets users from DB and creates a file from them. It can be different types of files (SCV, XML, etc).
import java.io.File;
import java.util.Iterator;
public class MyService {
private final EventsDao eventsDao;
private final UserDao userDao;
public MyService(EventsDao eventsDao, UserDao userDao) {
this.eventsDao = eventsDao;
this.userDao = userDao;
}
public void start() {
Iterator<Event> events = eventsDao.getEvents();
FileBuilder fileBuilder = new CsvFileBuilder();
while (events.hasNext()) {
Event event = events.next();
User user = userDao.getUser(event.getUserId());
if (user == null) {
System.out.println("user is null and won't be included to the file");
continue;
}
fileBuilder.addLine(UserMapper.toViewableUser(user));
}
File scvFile = fileBuilder.build();
sendFile(scvFile);
deleteFile(scvFile);
}
private void sendFile(File scvFile) {}
private void deleteFile(File scvFile) {}
}
You can notice the problem here: I create new CsvFileBuilder()
in the service. It violates SOLID principles. I am looking for how to refactor that and decouple service and builder implementation. I could pass FileBuilder
in the service's constructor but after that, it will be a singleton and I don't know how to implement it correctly. Take a look at builder implementation:
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class CsvFileBuilder implements FileBuilder {
private final File targetFile;
private final CSVWriter writer;
public CsvFileBuilder() {
try {
this.targetFile = File.createTempFile("users", "test.csv");
this.writer = new CSVWriter(new FileWriter(targetFile), '|', CSVWriter.NO_QUOTE_CHARACTER);
addHeader();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void addHeader() {
List<String> headers = new ArrayList<>();
//....
writer.writeNext(headers.toArray(new String[0]));
}
@Override
public void addLine(ViewableUser viewableUser) {
List<String> cells = new ArrayList<>();
//...
writer.writeNext(cells.toArray(new String[0]));
}
@Override
public File build() {
try {
writer.flush();
writer.close();
return targetFile;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
If I create it as singleton it will be mutable because I add a new line on every step and only in the end build file.
If I pass literature to the builder it looks bad because it is not the builder's responsibility to go to the DB, get user, map it. etc.
So, How can I move builder implementation creation from direct service?