Programación orientada a aspectos e implicitud

7

Supongamos que tengo una clase que representa una imagen y tiene varios métodos.

class Image
{
    circle(x,y,radius,color);
    square(x,y,w,h,color);
    floodfill(x,y,color)
    clear();
}

Además, quiero tener la funcionalidad de deshacer. Una forma sencilla de implementar esto es mantener una lista de todas las acciones que se han realizado. Cuando deshago, simplemente vuelvo a ejecutar todas las acciones. Una solución sería implementar un aspecto, algo como esto:

aspect ImageUndo
{
    on Image::circle, Image::square, Image::floodfill
    precall(object, args)
    {
         object.actions_list.add(args)
    }
}

Esencialmente, este aspecto ahora ha modificado el comportamiento de la imagen. Eso me preocupa. En particular, otro programador que no esté familiarizado con la existencia del aspecto ImageUndo puede tener los siguientes problemas:

  1. Agrega un método y no funciona con la funcionalidad de deshacer.
  2. Al intentar depurar el mecanismo de deshacer, no está claro a dónde se agrega la lista actions_list.

Por otro lado podríamos tener

class Image
{
    @undoable_action
    circle(x,y,radius,color);

    @undoable_action
    square(x,y,w,h,color);

    @undoable_action
    floodfill(x,y,color)

    @undoable_action
    clear();
}

Lo que no me molesta tanto, porque da una idea de dónde buscar el código de deshacer y las creaciones es para que el nuevo codificador probablemente lo note y lo agregue automáticamente a un nuevo método.

Para resumir: aspectos (al menos aquellos como el que mostré) parecen traer "magia implícita" a lo que hace el código. Me parece que la implicación es peligrosa y realmente deberíamos hacerlo explícito.

¿Hay buenas razones para la implicación? ¿Las personas que realmente usan AOP escriben códigos que hacen este tipo de modificación?

Nota: esta es una revisión de Son ciertas ¿Los problemas se resolvieron de manera más elegante con AOP? que se cerró porque mi versión anterior se encontró, sin querer, como ranting.

    
pregunta Winston Ewert 16.11.2010 - 15:43

2 respuestas

3

Las clases que implementan inquietudes transversales no tienen nada que decir sobre la funcionalidad principal de las clases que son AOP'd. Esa es la idea central aquí.

Su ejemplo es uno de una acción de deshacer. Vayamos un paso más allá: haremos una copia detallada de su clase y la almacenaremos en algún lugar. Si queremos realizar un deshacer, todo lo que tenemos que hacer es revertir la copia profunda. Esto "hará retroceder" a la clase a su estado original. Ni siquiera necesita anotar a los miembros de la clase para que funcione (aunque podría anotar la propia clase si quisiera que la copia en profundidad funcionara automáticamente).

Aquí está mi pregunta: ¿Este proceso de copia profunda tiene que ver con nada y la copia de la funcionalidad principal de la clase? ¿A la copia profunda le importa lo que hace la clase?

Serializar y deserializar objetos es otro ejemplo de esto. El proceso de serialización no tiene conocimiento del comportamiento de la clase, y los objetos que se están serializando no tienen conocimiento del proceso de serialización. Es una poderosa herramienta de desacoplamiento.

    
respondido por el Robert Harvey 16.11.2010 - 16:55
1

Una cosa es señalar que el código no se está modificando a través de los aspectos, sino que se está extendiendo. Puede ser semántico, pero la distinción es importante, especialmente cuando se considera el principio abierto / cerrado .

Ahora, por lo que puedo decir (¿se parece a aspectj quizás?), su primer ejemplo aplica el aspecto a los métodos en la declaración del aspecto, mientras que su segundo ejemplo aplica el aspecto a través de atributos en los métodos. El problema que parece tener es uno en el que cualquiera que extienda su código no pueda descubrir cómo incluir deshacer en sus adiciones usando el primer ejemplo.

  1. La documentación está ahí por una razón. Así que escribe algo para tus consumidores.
  2. Use un diseño diferente para implementar la funcionalidad para ellos.

Creo que algo como lo siguiente funcionará:

public abstract class ImageOperation
{
    @undoable_action
    public abstract void Action(Image img);
}

public class Flood_Fill : ImageOperation
{
    public void Action(Image img)
    {
        // flood-fill
    }
}

Ahora, al abstraer las operaciones de imagen, puede suministrar ImageOperation en un archivo .dll diferente al de su programa, y puede usarlo como base para los complementos de ImageOperation, para que otros no tengan que ver su código en absoluto para producir una operación, y todavía implícitamente proporcionas deshacer para ellos.

    
respondido por el Steven Evers 16.11.2010 - 17:58

Lea otras preguntas en las etiquetas