Je viens de JavaScript, dans lequel les rappels sont assez faciles. J'essaie de les implémenter dans Java, sans succès.
J'ai une classe de parent:
import Java.net.Socket;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
public class Server {
ExecutorService workers = Executors.newFixedThreadPool(10);
private ServerConnections serverConnectionHandler;
public Server(int _address) {
System.out.println("Starting Server...");
serverConnectionHandler = new ServerConnections(_address);
serverConnectionHandler.newConnection = function(Socket _socket) {
System.out.println("A function of my child class was called.");
};
workers.execute(serverConnectionHandler);
System.out.println("Do something else...");
}
}
Ensuite, j'ai une classe enfant, appelée par le parent:
import Java.io.IOException;
import Java.net.ServerSocket;
import Java.net.Socket;
import Java.util.logging.Level;
import Java.util.logging.Logger;
public class ServerConnections implements Runnable {
private int serverPort;
private ServerSocket mainSocket;
public ServerConnections(int _serverPort) {
serverPort = _serverPort;
}
@Override
public void run() {
System.out.println("Starting Server Thread...");
try {
mainSocket = new ServerSocket(serverPort);
while (true) {
newConnection(mainSocket.accept());
}
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void newConnection(Socket _socket) {
}
}
Quelle est la bonne façon de mettre en œuvre le
serverConnectionHandler.newConnection = function(Socket _socket) {
System.out.println("A function of my child class was called.");
};
partie, dans la classe des parents, qui est clairement pas correct?
Définissez une interface et implémentez-la dans la classe qui recevra le rappel.
Ayez l'attention sur le multi-threading dans votre cas.
Exemple de code de http://cleancodedevelopment-qualityseal.blogspot.com.br/2012/10/understanding-callbacks-with-Java.html
interface CallBack { //declare an interface with the callback methods, so you can use on more than one class and just refer to the interface
void methodToCallBack();
}
class CallBackImpl implements CallBack { //class that implements the method to callback defined in the interface
public void methodToCallBack() {
System.out.println("I've been called back");
}
}
class Caller {
public void register(CallBack callback) {
callback.methodToCallBack();
}
public static void main(String[] args) {
Caller caller = new Caller();
CallBack callBack = new CallBackImpl(); //because of the interface, the type is Callback even thought the new instance is the CallBackImpl class. This alows to pass different types of classes that have the implementation of CallBack interface
caller.register(callBack);
}
}
Dans votre cas, à part le multi-threading, vous pouvez faire comme ceci:
interface ServerInterface {
void newSeverConnection(Socket socket);
}
public class Server implements ServerInterface {
public Server(int _address) {
System.out.println("Starting Server...");
serverConnectionHandler = new ServerConnections(_address, this);
workers.execute(serverConnectionHandler);
System.out.println("Do something else...");
}
void newServerConnection(Socket socket) {
System.out.println("A function of my child class was called.");
}
}
public class ServerConnections implements Runnable {
private ServerInterface serverInterface;
public ServerConnections(int _serverPort, ServerInterface _serverInterface) {
serverPort = _serverPort;
serverInterface = _serverInterface;
}
@Override
public void run() {
System.out.println("Starting Server Thread...");
if (serverInterface == null) {
System.out.println("Server Thread error: callback null");
}
try {
mainSocket = new ServerSocket(serverPort);
while (true) {
serverInterface.newServerConnection(mainSocket.accept());
}
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Multi-threading
Rappelez-vous que cela ne gère pas le multi-threading, ceci est un autre sujet et peut avoir différentes solutions selon le projet.
Le motif observateur
L'observateur-modèle fait presque cela, la principale différence est l'utilisation d'un ArrayList
pour ajouter plus d'un auditeur. Là où ce n'est pas nécessaire, vous obtenez de meilleures performances avec une seule référence.
Utilisez le motif observateur. Cela fonctionne comme ceci:
interface MyListener{
void somethingHappened();
}
public class MyForm implements MyListener{
MyClass myClass;
public MyForm(){
this.myClass = new MyClass();
myClass.addListener(this);
}
public void somethingHappened(){
System.out.println("Called me!");
}
}
public class MyClass{
private List<MyListener> listeners = new ArrayList<MyListener>();
public void addListener(MyListener listener) {
listeners.add(listener);
}
void notifySomethingHappened(){
for(MyListener listener : listeners){
listener.somethingHappened();
}
}
}
Vous créez une interface comportant une ou plusieurs méthodes à appeler lorsqu'un événement se produit. Ensuite, toute classe devant être notifiée lorsque des événements se produisent implémente cette interface.
Cela permet plus de flexibilité, car le producteur ne connaît que l’interface écouteur, pas une implémentation particulière de l’interface écouteur.
Dans mon exemple:
MyClass
est le producteur ici comme notifiant une liste d'auditeurs.
MyListener
est l'interface.
MyForm
s'intéresse à quand somethingHappened
, de sorte qu'il implémente MyListener
et s'enregistre lui-même avec MyClass
. Maintenant, MyClass
peut informer MyForm
des événements sans référencer directement MyForm
. C'est la force du modèle d'observateur, cela réduit la dépendance et augmente la possibilité de réutilisation.
OMI, vous devriez jeter un oeil au Observer Pattern , et voici comment la plupart des auditeurs travaillent
Je ne sais pas si c'est ce que vous recherchez, mais vous pouvez y parvenir en transmettant un rappel à la classe enfant.
d'abord définir un rappel générique:
public interface ITypedCallback<T> {
void execute(T type);
}
créez une nouvelle instance ITypedCallback sur l'instanciation ServerConnections:
public Server(int _address) {
serverConnectionHandler = new ServerConnections(new ITypedCallback<Socket>() {
@Override
public void execute(Socket socket) {
// do something with your socket here
}
});
}
appelez la méthode execute sur l'objet de rappel.
public class ServerConnections implements Runnable {
private ITypedCallback<Socket> callback;
public ServerConnections(ITypedCallback<Socket> _callback) {
callback = _callback;
}
@Override
public void run() {
try {
mainSocket = new ServerSocket(serverPort);
while (true) {
callback.execute(mainSocket.accept());
}
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
au fait: je n'ai pas vérifié si c'est correct à 100%, je l'ai codé directement ici.
Dans ce cas particulier, ce qui suit devrait fonctionner:
serverConnectionHandler = new ServerConnections(_address) {
public void newConnection(Socket _socket) {
System.out.println("A function of my child class was called.");
}
};
C'est une sous-classe anonyme.