Come costruire una classe per confrontare parole in un dizionario lessicale?

3

Sto cercando di creare una classe che memorizzi le parole in un set in modo che possa vedere se una parola appartiene o meno a quell'insieme. Non voglio creare mai un set ogni volta che istanziato la classe, quindi sto usando quello che penso sia l'iniezione di dipendenza. Quanto segue è una struttura di base della classe. Sono uno studente che impara a programmare, quindi usare le librerie di terze parti non è un'opzione.

È un modo appropriato per codificare i miei set con hard-disk? Sono sulla buona strada o dovrei progettare la mia classe completamente diversa da questa?

Questo codice è presentato per dimostrare se la mia comprensione del design della classe è corretta o meno.

public final class WordCollection {

    private final Set<String> chapter_types = new HashSet<>();
    private final Set<String> prepositions = new HashSet<>();

    public WordCollection(){
        buildChapterTypes();
        buildPrepositions();
    }

    public WordCollection(String collectionType) {
        if(collectionType.equals("chapters")) {
            buildChapterTypes();
        } else if(collectionType.equals("prepositions")) {
            buildPrepositions();
        }
    }

    private void buildChapterTypes() {
        chapter_types.add("Prologue");
        chapter_types.add("Section");
        chapter_types.add("Epilogue");
        chapter_types.add("Chapter");
    }

    public boolean isChapterType(String word) {
        return chapter_types.contains(word);
    }

    public void buildPrepositions(){
        prepositions.add("of");
        prepositions.add("for");
    }

    public boolean isPreposition(String word) {
        return prepositions.contains(word);
    }

}
    
posta WP0987 23.10.2016 - 07:50
fonte

2 risposte

2

Sono d'accordo con ciò che Christophe dice in la sua risposta . Una cosa che non posso ignorare è la tua richiesta di usare l'iniezione di dipendenza. Quello che stai facendo NON è affatto l'iniezione di dipendenza. Hai il tuo codice di costruzione (i metodi build ) mescolato con il tuo codice di comportamento (i metodi is ). Non c'è modo di ignorare questo codice di costruzione. Ciò rende il tuo codice difficile da modificare e difficile da testare. L'iniezione di dipendenza dice che dovresti chiedere ciò di cui hai bisogno, non cercare o costruire ciò di cui hai bisogno.

Ma se vuoi farlo, cosa penso che vuoi fare, il tuo problema fondamentale è che hai bisogno di una struttura dati diversa.

Ne ho uno che ti permetterà di fare questo:

public static void main(String[] args) {
    WordCollection wc = new WordCollection(new MapOfSetsBuilder()
        .add("prologue", "chapters")
        .add("section", "chapters")
        .add("epilogue", "chapters")
        .add("chapter", "chapters")
        .add("of", "prepositions")
        .add("for", "prepositions")
        .add("smurf", "chapters", "prepositions", "articles", "verbs", "adverbs", "nouns")
        .build()
    );

    if (wc.word("prologue").isOfType("chapters")) {
        System.out.println("Whoo Hoo!");
    }
    if (wc.word("smurf").isOfType("nouns")) {
        System.out.println("Smurftastic!");
    }
    if (wc.word("smurf").isOfType("gargamel")) {
        System.out.println("Noo! Why? This is so wrong.");
    }
}

Uscite:

Whoo Hoo!
Smurftastic!

La struttura? Map<String, Set<String>>

Sto mappando ogni parola al suo tipo. Una parola potrebbe avere più di un tipo, quindi esegue il mapping su un insieme di tipi.

Tutto ciò che serve è questo:

//Construction class
class MapOfSetsBuilder {
    Map<String, Set<String>> result = new HashMap<>();
    public MapOfSetsBuilder add(String key, String... values) {
        Set setForKey = new HashSet<String>();
        setForKey.addAll(Arrays.asList(values));
        result.put(key, setForKey );
        return this;
    }
    public Map<String, Set<String>> build() {
        return result;
    }
}

//Behavior class
class WordCollection {
    Map<String, Set<String>> categories;

    WordCollection (Map<String, Set<String>> categories){
        this.categories = categories;
    }

    public TypeTest word(String word) {
        return new TypeTest(word);
    }

    class TypeTest {
        String word;
        TypeTest(String word) {
            this.word = word;
        }
        Boolean isOfType(String category) {
            return WordCollection.this.categories.get(word).contains(category);
        }
    }
}

I'm not wanting to build ever set every time I instantiate the class ...

OK ya pigro barbuto ecco come farlo senza scattare una iniezione di dipendenza adeguata in faccia:

//Behavior class
class WordCollection {
    Map<String, Set<String>> categories;
    WordCollection(Map<String, Set<String>> categories) {
        this.categories = categories;
    }

    public TypeTest word(String word) {
        return new TypeTest(word);
    }

    class TypeTest {
        String word;

        TypeTest(String word) {
            this.word = word;
        }

        Boolean isOfType(String category) {
            return WordCollection.this.categories.get(word).contains(category);
        }
    }
    public static class Builder {
        public WordCollection buildDefault() {
            return new WordCollection(new MapOfSetsBuilder()
            .add("prologue", "chapters")
            .add("section", "chapters")
            .add("epilogue", "chapters")
            .add("chapter", "chapters")
            .add("of", "prepositions")
            .add("for", "prepositions")
            .add("smurf","chapters","prepositions","articles","verbs","adverbs","nouns")
            .build()
            );
        }
    }
}

Piuttosto che un metodo factory statico che non può essere passato in giro come riferimento nella vera dipendenza, fasion injection ho optato per una classe builder interna statica. Questo ricorda il schema di costruzione di Josh Bloch ma è meno doloroso perché della classe MapOfSetsBuilder che aiuta. Questo è invocato con:

WordCollection wc = new WordCollection.Builder().buildDefault();

Perché non un semplice metodo di fabbrica statico ? Perché sono su metodo statico che odia il kick . Non mi piace essere costretto ad uscire dalla modalità istanza quando non c'è una buona ragione a parte per la necessità di un po 'più di digitazione.

Ad ogni modo, questo ultimo bit non è necessario per l'iniezione di dipendenza. È solo una comodità. Non fa nulla che tu non abbia potuto fare in main e condiviso in giro. Il costruttore che prende la mappa è ciò che sta supportando l'iniezione di dipendenza qui. Fatto in questo modo, almeno il costruttore WordCollection può ancora prendere qualsiasi mappa di set di stringhe. Non solo questa mappa predefinita.

    
risposta data 23.10.2016 - 15:57
fonte
3

Se capisco bene, le funzioni più importanti qui sono isChapter() e isPreposition() , per riconoscere le parole conosciute e determinarne la categoria.

L'hard-coding dei set come quello qui riduce considerevolmente l'uso della tua classe in termini reali. Quindi suggerirei di esternalizzare i costruttori hard coded:

  • costruisci l'oggetto che fornisce i set da utilizzare al momento della costruzione (puoi utilizzare il modello di progettazione del builder per che)
  • o fornire un addPreposition() an addChapter() primitive e utilizzare una classe helper esterna per riempirli con valori codificati o, meglio, caricarli da un file

Infine, i capitoli e le preposizioni sono solo alcune categorie lessicali come preposizione, articolo, verbi, avverbi e nomi (capitolo essendo un sottogruppo specializzato del sostantivo più generale). Potrebbe essere sufficiente allo scopo, ma forse hai intenzione di aggiungere molti altri set per molte altre categorie lessicali. In tal caso, suggerisco di astrarre ulteriormente:

  • rendi la categoria una classe separata (o enum), cambia l'interfaccia in boolean is(Category,String) e Category getCtategory(String) e usa un singolo mappa al posto di un esercito di set.
  • o, rafforza il tuo design con il principio di responsabilità singola : imposta l'implementazione delle categorie a partire da la tua classe e le generalizza in componente lessicale (cioè ha un set per ogni categoria) e trasforma la tua classe in un "assemblaggio di componenti" (cioè una specie di appartamento semplificato composito ), inoltrando le sue chiamate in arrivo ai suoi componenti.
risposta data 23.10.2016 - 09:34
fonte