Pregunta

¿Es mejor poner una implementación por defecto de un método en una superclase, y anular cuando subclases quieren desviarse de esto, o en caso de que acaba de abandonar el método de una clase abstracta, y tener la aplicación normal de repetir en muchos subclases

Por ejemplo, un proyecto en el que estoy involucrado en cuenta una clase que se utiliza para especificar las condiciones en que debe detenerse. La clase abstracta es la siguiente:

public abstract class HaltingCondition{
    public abstract boolean isFinished(State s);
}

Una aplicación trivial podría ser:

public class AlwaysHaltingCondition extends HaltingCondition{
    public boolean isFinished(State s){
        return true;
    }
}

La razón por la que hacemos esto con objetos es que entonces podemos componer arbitrariamente estos objetos juntos. Por ejemplo:

public class ConjunctionHaltingCondition extends HaltingCondition{
    private Set<HaltingCondition> conditions;

    public void isFinished(State s){
        boolean finished = true;
        Iterator<HaltingCondition> it = conditions.iterator();
        while(it.hasNext()){
            finished = finished && it.next().isFinished(s);
        }
        return finished;
    }
}

Sin embargo, tenemos algunas condiciones vacilantes que necesitan ser notificado de que se han producido eventos. Por ejemplo:

public class HaltAfterAnyEventHaltingCondition extends HaltingCondition{
    private boolean eventHasOccurred = false;

    public void eventHasOccurred(Event e){
        eventHasOccurred = true;
    }

    public boolean isFinished(State s){
        return eventHasOccurred;
    }
}

¿Cómo debemos representar mejor eventHasOccurred(Event e) en la superclase abstracta? La mayoría de las subclases pueden tener una aplicación no-op de este método (por ejemplo AlwaysHaltingCondition), mientras que algunos requieren una implementación significativa para funcionar correctamente (por ejemplo HaltAfterAnyEventHaltingCondition) y otros no tienen que hacer nada con el mensaje, sino que debe transmitir a sus subordinados para que puedan funcionar correctamente (por ejemplo ConjunctionHaltingCondition).

podría tener una implementación por defecto, lo que reduciría la duplicación de código, pero haría que algunas subclases para compilar aún no funciona correctamente si no se ha anulado, o que podría tener el método declarado como abstracto, lo que requeriría el autor de cada subclase de pensar acerca de la aplicación que estaban proporcionando, aunque nueve de cada diez veces que sería una aplicación no-op. ¿Cuáles son las otras ventajas y desventajas de estas estrategias? Es uno mucho mejor que el otro?

¿Fue útil?

Solución

Una opción es tener otra subclase abstracta, para su uso como la superclase de todas las implementaciones de los cuales hacer que desee utilizar la aplicación por defecto.

Personalmente general abandonan los métodos no-final abstracta en una clase abstracta (o interfaces de uso solo lugar) pero definitivamente depende de la situación. Si usted tiene una interfaz con muchos métodos, y que desea ser capaz de simplemente opción en la que algunos de ellos, por ejemplo, entonces una clase abstracta que implementa la interfaz de una manera no-op para cada método está bien.

Es necesario evaluar cada caso según sus méritos, básicamente.

Otros consejos

Si va a poner cualquier aplicación en la clase base abstracta, debería ser el código para las sub-clases que utilizan la aplicación no-op, ya que se trata de una aplicación que tenga sentido para la clase base también. Si no existiera la aplicación sensata para la clase base (por ejemplo, si no había sensata no-op para el método que está discutiendo aquí), entonces me gustaría sugerir dejándolo abstracto.

Con respecto al código duplicado, si hay "familias" de las clases que todo uso de la misma implementación del método y no desea duplicar el código en todas las clases en la familia, simplemente podrían usar ayudante clases por familia que suministran estas implementaciones. En su ejemplo, un ayudante para las clases que pasan por los acontecimientos, un ayudante para las clases de aceptar y registrar el evento, etc.

Parece que usted está preocupado acerca de la configuración variable booleana que cuando ocurra el evento. Si las sustituciones de usuario eventHasOccurred(), entonces la variable booleana no será set y isFinished() no devolverá el valor correcto. Para ello, se puede tener un método abstracto que las sustituciones de los usuarios controlen el evento y otro método que llama al método abstracto y define el valor booleano (véase el ejemplo de código siguiente).

Además, en lugar de poner el método en la clase eventHasOccurred() HaltingCondition, sólo puede tener las clases que necesitan eventos mango se extiende una clase que define este método (como la clase más adelante). Cualquier clase que no tiene de qué eventos manillares justo puede extender HaltingCondition:

public abstract class EventHaltingCondition extends HaltingCondition{
  private boolean eventHasOccurred = false;

  //child class implements this
  //notice how it has protected access to ensure that the public eventHasOccurred() method is called
  protected abstract void handleEvent(Event e);

  //program calls this when the event happens
  public final void eventHasOccurred(Event e){
    eventHasOccurred = true; //boolean is set so that isFinished() returns the proper value
    handleEvent(e); //child class' custom code is executed
  }

  @Override
  public boolean isFinished(){
    return eventHasOcccurred;
  }
}

Edit (ver comentarios):

final EventHaltingCondition condition = new EventHaltingCondition(){
  @Override
  protected void handleEvent(Event e){
    //...
  }
};
JButton button = new JButton("click me");
button.addActionListener(new ActionListener(){
  public void actionPerformed(ActionEvent actionEvent){
    //runs when the button is clicked

    Event event = //...
    condition.eventHasOccurred(event);
  }
});

Me encontré con un escenario similar cuando creé el esquema básico (jerarquía de clases) de una aplicación que estaba desarrollando junto con otros en el trabajo. Mi elección para la colocación de un método Extracto (y por lo tanto a la fuerza de su aplicación) era de comunicación propósitos.

Básicamente los otros compañeros tenían algún modo para poner en práctica de manera explícita el método y, por tanto, en primer lugar notificación de su presencia y la segunda de acuerdo en lo que regresan allí, incluso si es sólo la implementación predeterminada.

implementaciones predeterminadas en las clases base a menudo se pasan por alto.

Si va a salir el método super clase abstracta es posible que desee ver en la utilización de una interfaz (que no debe confundirse con una interfaz). A medida que la interfaz es uno que no proporciona una implementación concreta.

Scott

Para ampliar, cuando nosotros como programadores tienen instrucciones de código para una interfaz de nuevo muchas veces, y algunas veces experimentados, los desarrolladores a suponer erróneamente que es en referencia a la palabra clave de interfaz en la que no hay detalles de implementación pueden ser encontrados. Sin embargo, la manera más clara de decir esto es que cualquier objeto de nivel superior puede ser tratada como una interfaz que se puede interactuar con. Por ejemplo, una clase abstracta llamada animal sería una interfaz sería una clase llamada gato que heredar de animal.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top