Supposons que j'ai construit la classe suivante
MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation(feedForward) .setNumberThreads(10) .setBatch(5) .setEpochs(100) .setOptimizer(OptimizationAlgorithmType.MOMENTUM.setAlpha(0.01).setBeta(0.99));
Mon objectif était de construire une API qui, lorsque je sélectionne un Enum spécifique, différentes méthodes seraient disponibles. Par exemple
MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation(feedForward) .setNumberThreads(10) .setBatch(5) .setEpochs(100) .setOptimizer(OptimizationAlgorithmType.VANILLA.setEta(0.001));
Ou
public enum OptimizationAlgorithmType { VANILLA(new HashMap<String, Double>()) { private final static String ETA = "eta"; private Double eta = null; public OptimizationAlgorithmType setEta(Double eta) { this.eta = eta; return this; } @Override public Map<String, Double> getHyperarameters() { this.map.put(ETA, eta); return this.map; } }, MOMENTUM(new HashMap<String, Double>()) { private final static String ALPHA = "alpha"; private final static String BETA = "beta"; private Double alpha = null; private Double beta = null; public OptimizationAlgorithmType setAlpha(Double alpha) { this.alpha = alpha; return this; } public OptimizationAlgorithmType setBeta(Double beta) { this.beta = beta; return this; } @Override public Map<String, Double> getHyperarameters() { this.map.put(ALPHA, alpha); this.map.put(BETA, beta); return this.map; } }; protected Map<String, Double> map; private OptimizationAlgorithmType(Map<String, Double> map) { this.map = map; } public abstract Map<String, Double> getHyperarameters(); }
Malheureusement, cela n'est pas autorisé. L'ide met en garde contre les méthodes inutilisées (par exemple: setEta ()
) - et les méthodes ne sont pas du tout disponibles pour sélectionner dans l'énumération spécifique.
Puis-je utiliser une astuce pour obtenir l'API souhaitée?
Merci
Modifier Ajout d'une autre réponse ci-dessous p>
3 Réponses :
J'ai trouvé une meilleure
D'abord une interface, puis les deux classes de paramètres qui implémentent l'interface
MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation.BackpropagationBuilder(feedForward) .setBatch(5) .setEpochs(100) .setThreads(10) .setOptimizer(OptimizationType.MOMENTUM .set(MomentumParameter.ALPHA, 0.001) .set(MomentumParameter.BETA, 0.92)) .build();
Maintenant, ajoutez la classe enum principale
public enum OptimizationType { VANILLA{ private VanillaParameter eta = VanillaParameter.ETA; @Override public Hyperparameter get(Hyperparameter parameter) { switch ((VanillaParameter) parameter) { case ETA: return this.eta; } return null; } @Override public OptimizationType set(Hyperparameter parameter, Double value) { switch ((VanillaParameter) parameter) { case ETA: this.eta.setValue(value); break; } return OptimizationType.VANILLA; } }, ... }
Et l'API a l'air vraiment sympa à mon avis et ressemble à mon intention initiale. Dans l'attente des suggestions
public interface Hyperparameter {...} public enum VanillaParameter implements Hyperparameter { ETA { @Override public Double getValue() { return this.etaValue; } @Override public void setValue(Double value) { if (value == null) { throw new IllegalStateException("Parameter value cannot be set to null!"); } this.etaValue = value; } }; protected Double etaValue = 0.005; } public enum MomentumParameter implements Hyperparameter { ... }
chaque fois que vous appelez: set (paramètre Hyperparameter, Double value), vous modifiez la valeur de eta pour tous ceux qui utilisent OptimizationType.VANILLA
@RayTayek Oui très vrai et très problématique.
essayez peut-être quelque chose comme:
import java.util.Arrays; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; public class So54067082enums_to_implement_different_methods_in_java { enum Type { vanilla(new TreeSet<>(Arrays.asList(new String[] {"eta"}))), momentum(new TreeSet<>(Arrays.asList(new String[] {"alpha","beta"}))); Type(Set<String> names) { this.names=names; } final Set<String> names; } static class ParameterSet { ParameterSet(Type type) { this.type=type; } Double get(String name) { if(type.names.contains(name)) return map.get(name); else throw new RuntimeException("oops"); } void set(String name,Double value) { if(type.names.contains(name)) map.put(name,value); else throw new RuntimeException("oops"); } @Override public String toString() { return "ParameterSet [type="+type+", map="+map+"]"; } final Type type; private final SortedMap<String,Double> map=new TreeMap<>(); } public static void main(String[] args) { ParameterSet parameterSet=new ParameterSet(Type.vanilla); parameterSet.set("eta",.01); System.out.println(parameterSet); ParameterSet parameterSet2=new ParameterSet(Type.momentum); parameterSet2.set("alpha",.1); parameterSet2.set("beta",.9); System.out.println(parameterSet2); } }
Je ne vois pas pourquoi vous ne pourriez pas simplement utiliser une approche moins naïve du problème:
class OptimizationAlgorithm { public static class Vanilla implements OptimizationAlgorithm { public Vanilla setAlpha(float a) { return this; } public Vanilla setBeta(float a) { return this; } } public static Vanilla vanilla() { return new Vanilla(); } } backpropagation.setOptimizer(OptimizationAlgorithm.vanilla().setAlpha(0.05f).setBeta(0.10f));
Beaucoup moins de code standard, pas de problème d'instances statiques modifiables et fondamentalement la même interface de l'extérieur.
Il semble vraiment que vous ne devriez pas utiliser enum pour cela. En plus du problème que vous avez posé, vous utilisez également les énumérations mutuellement, ce qui est assez douteux. Il semble que vous devriez écrire des constructeurs normaux ou quelque chose à la place. (Et pour mémoire, il n'y a pas d'astuce ici pour que cela fonctionne. N'utilisez pas d'énumérations.)
@Radiodef J'essayais juste de présenter l'API d'une certaine manière - je peux la faire fonctionner avec d'autres moyens.
Vous pouvez présenter l'API de la même manière pour vos clients en utilisant des champs finaux statiques publics au lieu d'énumérer des valeurs.