Inyección de dependencia en una clase abstracta -- java campo con unit-testing campo con dependency-injection campo con constructor camp codereview Relacionados El problema

Dependency Injection into an Abstract Class


6
vote

problema

Español

yo y otro desarrollador recientemente han bifurcado y tomado sobre un proyecto abandonado y de código abierto. También he estado leyendo artículos y viendo videos por Misko Hevery .

Cuando estaba leyendo el código del proyecto, noté muchos constructores de incumplimiento. Y que los campos se están asignando a través de llamadas estáticas en lugar de ser asignadas a través de los parámetros del constructor. Cuando vi esto, quería refactar a los constructores para declarar explícitamente sus dependencias.

Esto es fácil de hacer.

Sin embargo, el problema es que hay clases abstractas con campos que deben inicializarse. Entonces, ¿cuál es la mejor manera de hacer DI para las clases de padres?

Quiero decir, la clase infantil puede solicitar parámetros (que realmente no necesita) en su constructor ... solo para pasarlo al padre a través de super(arg1, arg2) .

Pero esto me parece realmente extraño: que el niño está pidiendo algo solo para pasarlo al padre. ¿Es realmente la forma en que se supone que debe hacerse?

¿Qué pasa con un constructor abstracto (con parámetros necesarios) que obliga al niño a implementar?

Estas son las dos jerarquías que quiero refactor para usar DI:

  • Clase Arena extiende la clase ArenaContainer extiende la clase abstracta AbstractAreaContainer

  • clase BAExecutor extiende la clase abstracta #pragma once #include <vector> #ifdef GAME_EXPORTS #define GAME_API __declspec(dllexport) #else #define GAME_API __declspec(dllimport) #endif class GameBoard { // Order important for construction. std::vector<std::vector<double>> board; const size_t N; public: GAME_API GameBoard(std::vector<std::vector<double>> board); GAME_API const std::vector<std::vector<double>> &getBoard(); GAME_API void moveRight(); GAME_API void moveLeft(); GAME_API void moveDown(); GAME_API void moveUp(); private: size_t getNextNonzeroOrLastColumn(size_t row, size_t col); size_t getPreviousNonzeroOrFirstColumn(size_t row, size_t col); size_t getNextNonzeroOrLastRow(size_t row, size_t col); size_t getPreviousNonzeroOrFirstRow(size_t row, size_t col); }; 0 extiende la clase abstracta 99887776655443311

  #pragma once  #include <vector>  #ifdef GAME_EXPORTS     #define GAME_API __declspec(dllexport) #else     #define GAME_API __declspec(dllimport) #endif  class GameBoard {     // Order important for construction.     std::vector<std::vector<double>> board;     const size_t N; public:     GAME_API GameBoard(std::vector<std::vector<double>> board);     GAME_API const std::vector<std::vector<double>> &getBoard();     GAME_API void moveRight();     GAME_API void moveLeft();     GAME_API void moveDown();     GAME_API void moveUp();  private:     size_t getNextNonzeroOrLastColumn(size_t row, size_t col);     size_t getPreviousNonzeroOrFirstColumn(size_t row, size_t col);     size_t getNextNonzeroOrLastRow(size_t row, size_t col);     size_t getPreviousNonzeroOrFirstRow(size_t row, size_t col); }; 2  
Original en ingles

Me and another developer have recently forked and taken over an abandoned, open-source project. I have also been reading articles and watching videos by Misko Hevery.

When I was reading through the project's code, I noticed a lot of no-argument constructors. And that the fields are being assigned via static calls instead of being assigned via the constructor parameters. When I saw this, I wanted to refactor the constructors to declare their dependencies explicitly.

This is simple to do.

However, the problem is that there are abstract classes with fields that need to be initialized. So what is the best way to do DI for parent classes?

I mean, the child class can ask for parameters (that it doesn't really need) in its constructor... only to pass it to the parent via super(arg1, arg2).

But this seems really bizarre to me: That the child is asking for something just to pass it to the parent. Is this really the way it's supposed to be done?

What about an abstract constructor (with necessary parameters) that forces the child to implement?

These are the two hierarchies that I'm wanting to refactor to use DI:

  • class Arena extends class ArenaContainer extends abstract class AbstractAreaContainer

  • class BAExecutor extends abstract class CustomCommandExecutor extends abstract class BaseExecutor

public class BAExecutor extends CustomCommandExecutor {     Set<String> disabled = new HashSet<String>();      final TeamController teamc;     final EventController ec;     final DuelController dc;     final WatchController watchController;      public BAExecutor() {         super();         this.ec = BattleArena.getEventController();         this.teamc = BattleArena.getTeamController();         this.dc = BattleArena.getDuelController();         this.watchController = BattleArena.getSelf().getWatchController();     }   public abstract class CustomCommandExecutor extends BaseExecutor{      protected final BattleArenaController ac;     protected final EventController ec;     protected final ArenaEditor aec;      protected CustomCommandExecutor(){         super();         this.ac = BattleArena.getBAController();         this.ec = BattleArena.getEventController();         this.aec = BattleArena.getArenaEditor();     }  public abstract class BaseExecutor implements ArenaExecutor{     public static final String version = "2.1.0";     static final boolean DEBUG = false;     private HashMap<String,TreeMap<Integer,MethodWrapper>> methods =             new HashMap<String,TreeMap<Integer,MethodWrapper>>();     private HashMap<String,Map<String,TreeMap<Integer,MethodWrapper>>> subCmdMethods =             new HashMap<String,Map<String,TreeMap<Integer,MethodWrapper>>>();      protected PriorityQueue<MethodWrapper> usage = new PriorityQueue<MethodWrapper>(2, new Comparator<MethodWrapper>(){         @Override         public int compare(MethodWrapper mw1, MethodWrapper mw2) {             MCCommand cmd1 = mw1.getCommand();             MCCommand cmd2 = mw2.getCommand();              int c = new Float(mw1.getHelpOrder()).compareTo(mw2.getHelpOrder());             if (c!=0) return c;             c = new Integer(cmd1.order()).compareTo(cmd2.order());             return c != 0 ? c : new Integer(cmd1.hashCode()).compareTo(cmd2.hashCode());         }     });     static final String DEFAULT_CMD = "_dcmd_";      /**      * Custom arguments class so that we can return a modified arguments      */     public static class Arguments{         public Object[] args;     }      protected static class MethodWrapper{         public MethodWrapper(Object obj, Method method){             this.obj = obj; this.method = method;         }          public Object obj; /// Object instance the method belongs to         public Method method; /// Method         public String usage;         Float helpOrder = null;         public MCCommand getCommand(){             return this.method.getAnnotation(MCCommand.class);         }         public float getHelpOrder(){             return helpOrder != null ?                     helpOrder : this.method.getAnnotation(MCCommand.class).helpOrder();         }     }      /**      * When no arguments are supplied, no method is found      * What to display when this happens      * @param sender the sender      */     protected void showHelp(CommandSender sender, Command command){         showHelp(sender,command,null);     }      protected void showHelp(CommandSender sender, Command command, String[] args){         help(sender,command,args);     }      protected BaseExecutor(){         addMethods(this, getClass().getMethods());     } 
           
       
       

Lista de respuestas

5
 
vote
vote
La mejor respuesta
 

Baexecutor

Todas las variables pueden ser instanciadas en su declaración. Podrías reducirlo a medio usando

  final TeamController teamc = BattleArena.getTeamController();   

También suelte el vacío super() , el JVM sabe que tiene que llamarlo.

Sin embargo, cuando quiere usar DI, esto ya no se aplica. Solo puedo recomendar Lombok , lo que le permite escribir

  @RequiredArgsConstructor(onConstructor=@__(@Inject)) public class BAExecutor extends CustomCommandExecutor {     Set<String> disabled = new HashSet<String>();      final TeamController teamc;     final EventController ec;     final DuelController dc;     final WatchController watchController; }   

Crea el constructor y agrega el 9988777665544333 anotación. Bastante a mano, donde sea aplicable.

baseexecutor

  static final boolean DEBUG = false;   

DEBUG5 Probablemente se debe inicializar usando una entrada correspondiente de java.util.Properties , para que pueda cambiarlo sin recompilar. Se usa en ninguna parte en el fragmento proporcionado.

  private HashMap<String,TreeMap<Integer,MethodWrapper>> methods =         new HashMap<String,TreeMap<Integer,MethodWrapper>>();   

Utilice Java 7 Diamantes o Guava's 998877655544338 . Los genéricos de repetición son un dolor.

  c = new Integer(cmd1.order()).compareTo(cmd2.order());   

Boxeo Para usar super()0 ? Utilice super()1 en su lugar.

  super()2  

un comentario pendiente.

Argumentos

  super()3  

Clase no utilizada, espacio faltante. Sin embargo, nunca usaría campos mutables públicos, especialmente sin colecciones ni matrices. OTOH Una clase con todos los consertantes y colonos públicos no es mucho mejor, por lo que hará que toda la placa de la caldera valga la pena (OTOH, LOMBOK super()4 y super()5 hazlo trivial) . Supongo que no pasas los argumentos en ningún lugar es mejor.

General

Casi no hay nada que revise, o lo perdí al copiar. A la pregunta:

Sin embargo, el problema es que hay clases abstractas con campos que deben inicializarse. Entonces, ¿cuál es la mejor manera de hacer DI para las clases de padres?

No hay una solución agradable. Realmente tienes que pasarlos a todos a la superclase. Esto es especialmente malo para mí, ya que no puedo usar mi anotación favorita de Lombok.

A veces, puede tener suerte y ver que la superclase en realidad no necesita nada o tal vez pueda declarar algunos entallones abstractos, por lo que puede obtener lo que necesita de las subclases.

A veces, puede descubrir que la delegación es mejor que la herencia. Puede terminar con una jerarquía paralela simple, donde la superclase no tiene campos. Necesito un ejemplo más concreto para esto.

 

BAExecutor

All variables can be instantiated in their declaration. You could shrink it to a half using

final TeamController teamc = BattleArena.getTeamController(); 

Also drop the empty super(), the JVM knows it has to call it.

However, when you want to use DI, this doesn't apply anymore. I can only recommend Lombok, allowing you to write

@RequiredArgsConstructor(onConstructor=@__(@Inject)) public class BAExecutor extends CustomCommandExecutor {     Set<String> disabled = new HashSet<String>();      final TeamController teamc;     final EventController ec;     final DuelController dc;     final WatchController watchController; } 

It creates the constructor and adds the @Inject annotation. Pretty handy, wherever applicable.

BaseExecutor

static final boolean DEBUG = false; 

DEBUG should probably be initialized using a corresponding entry from java.util.Properties, so you can change it without recompiling. It's used nowhere in the provided snippet.

private HashMap<String,TreeMap<Integer,MethodWrapper>> methods =         new HashMap<String,TreeMap<Integer,MethodWrapper>>(); 

Use Java 7 diamonds or Guava's Maps.newHashMap(). Those repeating generics are a pain.

c = new Integer(cmd1.order()).compareTo(cmd2.order()); 

Boxing in order to use Integer#compareTo? Use Integer#compare instead.

    public Method method; /// Method 

An outstanding comment.

Arguments

public static class Arguments{     public Object[] args; } 

Unused class, missing space. However, I'd never use public mutable fields, especially no collections nor arrays. OTOH a class with all public getters and setters is not that much better, so it'd make all the boilerplate worth it (OTOH, Lombok's @Getter and @Setter make it trivial). I guess, not passing the arguments anywhere is best.

General

There's hardly anything to review, or I lost it when copying. To the question:

However, the problem is that there are abstract classes with fields that need to be initialized. So what is the best way to do DI for parent classes?

There's no nice solution. You really have to pass them all to the superclass. This is especially bad for me as I can't use my favorite Lombok annotation.

Sometimes, you may be lucky and see that the superclass actually needs nothing or maybe you can declare some abstract getters, so it can obtain what it needs from the subclasses.

Sometimes, you may find out that delegation is better than inheritance. You may end up with a simple parallel hierarchy, where the superclass has no fields. I'd need a more concrete example for this.

 
 

Relacionados problema

6  Argumentos en los constructores campos a juego  ( Arguments in constructors matching fields ) 
Bloqueado . Esta pregunta y sus respuestas son bloqueadas porque la pregunta es off-topic pero tiene importancia histórica. Actualmente no está a...

1  Establecimiento de parámetros para un informe de ventas, con valores de booleano y fecha predeterminados  ( Establishing parameters for a sales report with default boolean and date values ) 
La idea es que estoy obteniendo los parámetros de una solicitud 99887766655443311 appendSoldier2 y appendSoldier3 . appendSoldier4 El problema es qu...

3  Construyendo una función de distribución acumulada de un histograma  ( Constructing a cumulative distribution function from a histogram ) 
Durante algún proceso de funcionamiento, se acumulará un histograma de valores. Cuando se hace, el CDF se derivará de ella y se utilizará para obtener los c...

41  Conexión de la base de datos en constructor y destructor  ( Database connection in constructor and destructor ) 
Estoy jugando con diferentes maneras de hacer la interacción de la base de datos en PHP, y una de las ideas con las que he estado jugando está conectando a la...

2  Subclajusing Uibutton  ( Subclassing uibutton ) 
Necesito SUBCLASS UIButton , sin embargo, el diseño de UIButton hace que esto sea realmente doloroso. Esto se debe a que para crear un botón que realmente ...

45  Objetos instantáneos con muchos atributos  ( Instantiating objects with many attributes ) 
Tengo una clase con bastantes atributos, la mayoría de los cuales se conocen cuando creo una instancia del objeto. Así que paso todos los valores en el constr...

3  Cambiando el estado de otra entidad en el método del constructor  ( Changing the state of another entity into the constructor method ) 
Tengo una clase de reloj que se hereda de Pyglet.clock.clock y se comporta como un singleton. en el método del constructor Tengo la siguiente línea: pygl...

5  Conexión de base de datos MySQL en el constructor  ( Mysql database connection in the constructor ) 
Soy un principiante absoluto en PHP OOP en busca del "Santo Grial" de la base de datos de MySQL una vez y reutilizando esta conexión para todo el sitio. cl...

7  Constructores: muchos parámetros o ninguno y uso de métodos establecidos  ( Constructors lots of paramters or none and use of set methods ) 
En su opinión, cuál es la mejor manera de construir un objeto, dado los dos ejemplos a continuación: Opción 1: muchos parámetros private string firstna...

7  Solución alternativa para constructor sobrecargado en PHP  ( Workaround for overloaded constructor in php ) 
Tengo una clase cuyo propósito es mostrar un comentario. Me gustaría poder instanciarlo al pasar un objeto 998877666555443319 template <std::size_t N> cla...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos