Crea funzioni che implicano altre funzioni

2

Sto sviluppando un gioco che ha spesso codice ripetitivo che non riesco a semplificare.

Esempio di ciò che faccio normalmente:

Square object;
GL11.glPushMatrix();   // Pushes a matrix stack down by one. This allows us to rotate our object freely 
GL11.glTranslated(object.x, object.y, 0); // Move object to its position
object.drawToScreen(); // Draws the object
GL11.glPopMatrix();    // Pushes a matrix stack up by one, prevents the translation from affecting other objects in the scene

Il codice precedente diventa ingombrante e deve essere ripetuto spesso.

Che cosa voglio fare:

Square object;
bumpMatrix()
{ // implies GL11.glPushMatrix();   
GL11.glTranslated(object.x, object.y, 0); // Move object to its position
object.drawToScreen();  // Draws the object
} // implies GL11.glPopMatrix();

C'è un modo per farlo accadere? Giuro che ho visto qualcosa di simile da qualche parte prima in Java. Mi permetterebbe di creare una singola funzione e non dover copiare / incollare lo stesso gruppo di codice più volte, basta chiamare quella funzione.

    
posta White Development Studios 18.03.2015 - 04:41
fonte

2 risposte

2

È simile all'idioma RAII in C ++, dove inserisci il codice di gestione delle risorse nel costruttore & distruttore di una classe. Poiché Java non ha distruttori, se hai come target Java 7 o versioni successive potresti invece (ab) utilizzare try-with-resources e un AutoCloseable (non testato):

class GLCoordGuard implements AutoCloseable {
    protected GL _gl;

    public Manager(GL gl) {
        _gl = gl;
        gl.glPushMatrix();
    }

    public void close() {
        _gl.glPopMatrix();
    }
}

...
    Square object;
    try (GLCoordGuard guard = new GLCoordGuard(GL11)) {
        GL11.glTranslated(object.x, object.y, 0); // Move object to its position
        object.drawToScreen(); // Draws the object
    }

La sintassi nella domanda ricorda i blocchi di Ruby, che vengono impacchettati in un Proc oggetto e passato a una funzione, che può invocarlo ogni volta che gli piace. Puoi fare qualcosa di simile in Java passando un lambda (che richiede Java 8) a un'altra funzione (non testata):

public void bumpMatrix(Function<Void,Void> glOps) {
    GL11.glPushMatrix();
    glOps.apply(null);
    GL11.glPopMatrix();
}
...
    Square object;
    bumpMatrix((Void unused) -> {
        GL11.glTranslated(object.x, object.y, 0); // Move object to its position
        object.drawToScreen(); // Draws the object
    });
    
risposta data 18.03.2015 - 06:06
fonte
1

Posso darti una risposta piuttosto generica che ritengo sia adatta solo a casi d'uso più complessi a causa dell'eccessiva richiesta di piastra di riscaldamento. È possibile simulare perfettamente un paradigma funzionale in Java (lo hanno fatto in Scala) e utilizzare, ad esempio, lo schema di decorazione per risolvere il problema. L'approccio generale consiste nel definire una classe astratta o un'interfaccia con un singolo metodo che possa quindi essere sovrascritta da una classe interna anonima.

Semplice esempio per il tuo caso d'uso:

public class SimpleDecorator {
    public static void main(String[] args) {
        new PrintingDecorator() {
            protected void execute() {
                System.out.println("Calling my first function");
            }
        }.apply();
        new PrintingDecorator() {
            protected void execute() {
                System.out.println("Calling my second function");
            }
        }.apply();
    }
}

abstract class PrintingDecorator {
    public void apply(){
        System.out.println("Doing this action before");
        execute();
        System.out.println("Doing this action after");
    }
    protected abstract void execute();
}

Esecuzione di queste stampe:

Doing this action before
Calling my first function
Doing this action after
Doing this action before
Calling my second function
Doing this action after

Ora potresti andare molto più lontano e in effetti solo definire le funzioni e così basare il tuo decoratore su questo. Quindi per l'esempio inutilmente complesso:

public class FunctionalDecorator {
    public static void main(String[] args) {
        Function1<Function0<Void>, Function0<Void>> printDecorator = new Function1<Function0<Void>, Function0<Void>>() {
            public Function0<Void> apply(final Function0<Void> arg1) {
                Function0<Void> resultFunc = new Function0<Void>() {
                    public Void apply() {
                        System.out.println("Doing this action before");
                        arg1.apply();
                        System.out.println("Doing this action after");
                        return null;
                    }
                };
                return resultFunc;
            }
        };

        printDecorator.apply(
            new Function0<Void>() {
                public Void apply() {
                    System.out.println("Calling my first function");
                    return null;
                }
            }
        ).apply();

        //Slightly more complex example (with taking arguments and returning values)
        Function1<Function1<String, String>, Function1<String, String>> addTextDecorator = new Function1<Function1<String,String>, Function1<String,String>>() {
            public Function1<String, String> apply(final Function1<String, String> funcArg) {
                Function1<String, String> resultFunc = new Function1<String, String>() {
                    public String apply(String arg1) {
                        return "Before string\t" + funcArg.apply(arg1) + "\tAfter string";
                    }

                };
                return resultFunc;
            }
        };

        Function1<String, String> echo = new Function1<String, String>() {
            public String apply(String arg1) {
                return arg1;
            }
        };

        Function1<String, String> decoratedEcho = addTextDecorator.apply(echo);

        System.out.println(decoratedEcho.apply("Echo!"));
    }

}

interface Function0<ReturnType> {
    public ReturnType apply();
}

interface Function1<ArgType1, ReturnType>{
    public ReturnType apply(ArgType1 arg1);
}

abstract class Decorator implements Function0<Void>{
    private Function0<Void> func;
    public Decorator(Function0<Void> func){
        this.func = func;
    }
    public Void apply() {
        beforeAction();
        Void result = func.apply();
        afterAction();
        return result;
    }
    protected abstract void beforeAction();
    protected abstract void afterAction();
}

class PrintDecorator extends Decorator {
    public PrintDecorator(Function0<Void> func) {
        super(func);
    }
    protected void beforeAction() {
        System.out.println("Doing this action before");
    }
    protected void afterAction() {
        System.out.println("Doing this action after");
    }
}

Esecuzione di queste stampe:

Doing this action before
Calling my first function
Doing this action after
Before string Echo!   After string
    
risposta data 18.03.2015 - 10:02
fonte

Leggi altre domande sui tag