Turn Enum into a Class in Java

basel.ai :

I have a class that has an enum declared in it, like this:

private enum Marker {
        NONE {
            @Override
            public String[] createResultLine(String columnName, String value, String[] buf) {
                return null;
            }
        },
        STRING {
            @Override
            public String[] createResultLine(String columnName, String value, String[] buf) {
                buf[COLUMN_VALUE_STRING] = value;
                buf[COLUMN_VALUE_FLOAT] = "";
                buf[COLUMN_NAME] = columnName;
                buf[BLOCK_ID] = blockId;
                buf[PIPELINE_ID] = pipelineId;

                return buf;
            }
        },
        FLOAT {
            @Override
            public String[] createResultLine(String columnName, String value, String[] buf) {
                buf[COLUMN_VALUE_STRING] = "";
                buf[COLUMN_VALUE_FLOAT] =  value;
                buf[COLUMN_NAME] = columnName;
                buf[BLOCK_ID] = blockId;
                buf[PIPELINE_ID] = pipelineId;
                return buf;
            }
        };

        public abstract String[] createResultLine(String columnName, String value, String[] buf);
    }

and here is the complete class and the usage

final class CSVDataModifier {

    private final Path src;
    private int rowNumber = 0;

    private static String blockId = "" ;
    private static String pipelineId = "" ;
    private static String ElasticHostURL = "";
    public CSVDataModifier(Path src, /*Path dest, */ String b_id, String p_id, String elasticUrl) {
        this.src = src;
        this.blockId = b_id;
        this.pipelineId = p_id;
        this.ElasticHostURL = elasticUrl;

    }

    private static final int ROW_NUMBER = 0;
    private static final int COLUMN_NAME = 1;
    private static final int COLUMN_VALUE_STRING = 2;
    private static final int COLUMN_VALUE_FLOAT = 3;
    private static final int BLOCK_ID = 4;
    private static final int PIPELINE_ID = 5;
    private static final String[] COLUMN_NAMES = { "row_number", "column_name", "column_value_string", "column_value_float", "blockId", "pipelineId" };
    private ExecutorService executorService = Executors.newFixedThreadPool( 100 );

    public void apply() throws IOException, InterruptedException {
        try (CSVReader reader = new CSVReader(new FileReader(src.toFile())))
        {

            List<String[]> csvLines = new ArrayList<>();

            // key - ordered list of columns in source file
            Map<String, Marker> columnNameFloatMarker = getSourceColumnNamesWithFloatMarker(reader.readNext());
            int posRowNumber = getRowNumberPosition(columnNameFloatMarker.keySet());
            if (columnNameFloatMarker.isEmpty()) {
                System.out.println( "empty!" );
                return;
            }


            String[] buf = new String[COLUMN_NAMES.length];
            reader.forEach(values -> {

                buf[ROW_NUMBER] = values[posRowNumber];

                int col = 0;
                String[] resultLine;
                for (Map.Entry<String, Marker> entry : columnNameFloatMarker.entrySet()) {
                    String columnName = entry.getKey();
                    Marker marker = entry.getValue();

                    if ((resultLine = marker.createResultLine(columnName, values[col], buf)) != null) {
//                        writer.writeNext( resultLine );
                        csvLines.add( resultLine );
                        rowNumber++;
                    }
                    col++;

                }
                if (csvLines.size() >= 75)
                {
                    List<String[]> tmp = new ArrayList<>(  );
                    tmp.addAll( csvLines );
                    csvLines.clear();
                    executorService.execute(new BulkThread(ElasticHostURL, new ArrayList<>( tmp )));

                }

            });

            if (csvLines.size() > 0) {
                List<String[]> tmp = new ArrayList<>(  );
                tmp.addAll( csvLines );
                csvLines.clear();
                executorService.execute(new BulkThread(ElasticHostURL, new ArrayList<>( tmp )));
            }
        }
        executorService.shutdown();
        executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        System.out.println( "Total Lines: " + rowNumber );
    }

    private static final String FLOAT = "_float";
    private static final String STRING = "_string";

    private enum Marker {
        NONE {
            @Override
            public String[] createResultLine(String columnName, String value, String[] buf) {
                return null;
            }
        },
        STRING {
            @Override
            public String[] createResultLine(String columnName, String value, String[] buf) {
                buf[COLUMN_VALUE_STRING] = value;
                buf[COLUMN_VALUE_FLOAT] = "";
                buf[COLUMN_NAME] = columnName;
                buf[BLOCK_ID] = blockId;
                buf[PIPELINE_ID] = pipelineId;

                return buf;
            }
        },
        FLOAT {
            @Override
            public String[] createResultLine(String columnName, String value, String[] buf) {
                buf[COLUMN_VALUE_STRING] = "";
                buf[COLUMN_VALUE_FLOAT] =  value;
                buf[COLUMN_NAME] = columnName;
                buf[BLOCK_ID] = blockId;
                buf[PIPELINE_ID] = pipelineId;
                return buf;
            }
        };

        public abstract String[] createResultLine(String columnName, String value, String[] buf);
    }

    // Source column pre-processing to avoid string comparision;
    private static Map<String, Marker> getSourceColumnNamesWithFloatMarker(String... columns) {
        if (columns == null || columns.length == 0)
            return Collections.emptyMap();

        Map<String, Marker> map = new LinkedHashMap<>();

        for (int i = 0; i < columns.length; i++) {
            String columnName = columns[i];
            Marker marker = Marker.NONE;

            if (columnName.endsWith(FLOAT)) {
                columnName = columnName.substring(0, columnName.length() - FLOAT.length());
                marker = Marker.FLOAT;
            } else if (columnName.endsWith(STRING)) {
                columnName = columnName.substring(0, columnName.length() - STRING.length());
                marker = Marker.STRING;
            }

            if (map.put(columnName, marker) != null)
                throw new IllegalArgumentException("Column duplication in the source file");
        }

        return map;
    }

    private static int getRowNumberPosition(Set<String> columnNames) {
        int i = 0;

        for (String columnName : columnNames) {
            if ("row_number".equals(columnName))
                return i;
            i++;
        }

        throw new IllegalArgumentException("Source file does not contain 'row_number' column");
    }


}

The problem is that the private members

private static String blockId = "" ;
private static String pipelineId = "" ;

can't be referenced in the private enum Marker if they're not static, and also they're being initialized in the constructor

public CSVDataModifier(Path src, /*Path dest, */ String b_id, String p_id, String elasticUrl) {
        this.src = src;
        this.blockId = b_id;
        this.pipelineId = p_id;
        this.ElasticHostURL = elasticUrl;

    }

I can see the only way to do that, to declare the private member not as static , is to turn the private enum Marker into an internal class or maybe something else.

Since I'm new to Java and to the OOP world, can I get any guidance to solve this issue?

m.antkowicz :

This code is quite hardcore and needs massive refactoring because it is violating most of programming principles

The answer for your question is to extract those fields to another class like ModifierSetup and provide it to the createResultLine as a parameter

public class ModifierSetup {
    private String pipelineId;
    private String ElasticHostURL;

    // all args constructor
}

// inside your CSVDataModifier
private ModifierSetup setup;

public CSVDataModifier(Path src, /*Path dest, */ String b_id, String p_id, String elasticUrl) {
    this.src = src;
    this.blockId = b_id;
    this.setup = new ModifierSetup(p_id, elasticUrl);
}

// ...

if ((resultLine = marker.createResultLine(columnName, values[col], buf, this.setup)) != null) {

// ...

public abstract String[] createResultLine(String columnName, String value, String[] buf, ModifierSetup modifierSetup);

but that's definitely not enough. Many of your fields should be extracted like this. Instead of weird 'singleton' enum implementation you should provide some common interface like

public interface ResultLineCreator {
    ResultData createResultLine(ColumnMetaData columnMetaData, ModifierSetup modifierSetup, ResultData result); // in ColumnMetaData you can encapsulate columnName and value, inside ResultData result data
}

and proper local implementations of it, the reader should be wrapped with some kind of supplier etc etc - just think about how many responsibilities this class has - even if you will resolve your problem it won't be working/maintanable/testable/clear

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=148346&siteId=1