BlackBox-Framework - Plugin Programmierung

coree

Mitglied
Hallo,

ich habe für eine Aufgabe in der Uni ein Programm geschrieben, welches verschiedene Features besitzt.
In der aktuellen Aufgabe sollen diese Features in Plugins "ausgelagert" und zum Programmstart geladen werden (also kein Nachladen während das Programm läuft).

Relativ unproblematisch sind so einfache Dinge wie Buttons etc. zur GUI hinzuzufügen. Sprich Plugin wird geladen, Button wird an entsprechender Stelle in die GUI gesetzt.

Probleme habe ich bei der Funktionalität. Denn mein Programm besitzt Methoden, welche in der Regel immer die gleichen sind, jedoch verändert ein Feature diese Methode leicht. Und hier fehlt mir im Moment der Ansatz das vernünftig um zu setzen.
Ich kann mir nicht vorstellen, dass es Sinn und Zweck ist im Interface diese Methode zur Verfügung zu stellen und somit jedes Plugin diese Methode quasi mit gleichem Inhalt zu überschreiben (Ausnahme das Plugin mit der kleinen Änderung).

Ebenso weiß ich leider auch nicht so recht, wie ich Abhängigkeiten zwischen Plugins realisieren kann. Sprich ein Plugin benötigt unbedingt ein anderes um geladen werden zu können. Wobei man hier wohl ne einfache Abfrage machen könnte, ob das benötigte Plugin geladen wurde oder nicht. Eine Liste aller Plugins besitze ich ja.

Also wenn mir jemand bei meiner Denkblockade bzgl. der Funktionalität weiterhelfen kann, wäre ich dafür sehr dankbar.

Wenn nötig kann ich auch gewisse Codeteile posten.

PS: Die Plugins werden per Hand über die Mainmethode geladen (PluginLoader ist nicht gefordert).
 
Zuletzt bearbeitet:
Ich habe mich glaube ich auch recht umständlich ausgedrückt.

Also so sieht bisher das Programm in etwa aus:


Java:
public class Player extends JFrame{

    private Actions actions = new Actions(this);
    private MP3Player mp3player = new MP3Player(this);

    public static void main(String[] args){
        new Player().setVisible(true);
    }
        
    private Player()  {
        ...
        volumeCtrl = new JSlider(SwingConstants.HORIZONTAL,0,100,50);
        volumeCtrl.addChangeListener(new ChangeListener(){
            @Override
                public void stateChanged(ChangeEvent arg0) {
                    FloatControl gainControl = 							(FloatControl)mp3player.getClip().getControl(FloatControl.Type.MASTER_GAIN);
                    double gain = volumeCtrl.getValue()/100.0;
                    float dB = (float)(20*Math.log(gain)/Math.log(10.0)); 
                    gainControl.setValue(dB);
                }
        });
    }
}

public class MP3Player {

    Player n;
    public MP3Player(Player n){
        this.n = n;
    }

    public void open(String filename) throws UnsupportedAudioFileException, IOException, LIneUnavailableException {
        
        [...]
        clip = AudioSystem.getClip();
        clip.open(stream);

//Dieser Teil wird derzeit mithilfe von FeatureIDE nur bei entsprechend ausgewähltem Feature kompiliert 
//und soll in das entsprechende VolumeControl-Plugin ausgelagert werden.
        //#if VolumeControl
			/*FloatControl gainControl = 
					(FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);
			double gain = n.getVolumeCtrl().getValue()/100.0;
	        float dB = (float)(20*Math.log(gain)/Math.log(10.0)); 
	        gainControl.setValue(dB);*/

        //#endif

	}
    }
}

Bisher habe ich folgendes:
Java:
public class Player extends JFrame implements InputProvider{

    private Actions actions = new Actions(this);
    private MP3Player mp3player = new MP3Player(this);

    public JFrame getContentFrame(){
        return this;
    }
	
    public JPanel getLeftPanel(){
        return leftPanel;
    }

    private static String pluginName;
    private	List plugins;
	
    public static void main(String[] args){
        if (args.length == 0) System.out.println("Plugin name not specified");
        else {
            List pluginList = new ArrayList();
            try {
                for(int i = 0; i<args.length;i++){
                    pluginName = args[i];
                    Class<?> pluginClass = Class.forName(pluginName);
                    pluginList.add(pluginClass.newInstance());
                }
                new Player(pluginList).setVisible(true);
            } catch (Exception e) {
               System.out.println("Cannot load plugin " + pluginName +	 ", reason: " + e);
            }
        }
    }
    private Player(List plugins)    {		
        this.plugins=plugins;
        for	(Object plugin: plugins) {
            if (plugin instanceof GraphicalInterface){
                ((GraphicalInterface) plugin).setApplication(this);
                ((GraphicalInterface) plugin).addGraphicalElement();
            }
        }
    }
}

public interface InputProvider {

    JFrame getContentFrame();
    JPanel getLeftPanel();
    MP3Player getMP3Player();
}

public interface GraphicalInterface {
    void setApplication(InputProvider app);
    void addGraphicalElement();
}

public class VolumeControl implements GraphicalInterface{

    private InputProvider application;
    private JPanel volumePanel;
    private JSlider volumeCtrl;
    private JLabel volumeLabel;
	
    @Override
    public void setApplication(InputProvider app) {this.application = app;}

    @Override
    public void addGraphicalElement() {
    volumeCtrl = new JSlider(SwingConstants.HORIZONTAL,0,100,50);
    volumeCtrl.addChangeListener(new ChangeListener(){
        @Override
        public void stateChanged(ChangeEvent arg0) {
            FloatControl gainControl = 						(FloatControl)mp3player.getClip().getControl(FloatControl.Type.MASTER_GAIN);
            double gain = volumeCtrl.getValue()/100.0;
            float dB = (float)(20*Math.log(gain)/Math.log(10.0)); 
            gainControl.setValue(dB);
        }
    });
    application.getLeftPanel().add(volumeCtrl);
}
Also grafische Elemente, wie diesen Slider kann ich problemlos per Plugin meiner GUI hinzufügen. Nur reicht dies allein nicht, um die komplette Funktionalität des Features im Plugin unterzubringen. Für den auskommentierten Teil im oberen Programmausschnitt fehlt mir einfach der Ansatz bzw. die Idee, diesen entsprechend im Plugin zu verstauen. Bzw. wie ich hier den Hotspot am besten setzen könnte.

Solche kleinen Änderungen an einer Methode bei geladenem Feature habe ich leider an mehreren Stellen. Ich kann ja schlecht für jedes Plugin ein Interface schreiben, wessen Methode dann an genau dieser Stelle ausgeführt wird. Wäre ja nicht Sinn und Zweck eines Pluginsystems.

Ich hoffe das Ganze ist nun etwas klarer.
 
Zuletzt bearbeitet:
Ich versteh immer noch nicht ganz, was du genau machen willst, aber ich bin auch müde und hab gerade keine große Lust übermäßig viel nachzudenken... 😳

Auf den ersten Blick hört sich das für mich aber nach einem Decorator-Problem an. Falls dir das nichts sagt, dann schau für einen ersten Überlick mal die entsprechende Wiki-Seite (Decorator ? Wikipedia) an. Vielleicht bringt dich das ja schon weiter...
 
Einen groben Überblick zu dem Thema findest du in dieser Arbeit. Du musst deine Anforderung und Zeitaufwand präzisieren, sonst [STRIKE]kann[/STRIKE] will man dir keine Antwort geben ... das Thema kann man in 2 Sätzen abhandeln oder in 1000 Seiten.

Genau so wie du deine Buttons hinzufügst, kannst du auch neue Funktionalitäten hinzufügen. Habe mal schnell was zusammengetippt, evt. hilft es dir weiter.

Intern:
Java:
private HashMap<String, JComponent> components = new HashMap<>();
private JButton exit;

/* Wird einmal beim Programmstart aufgerufen */
public void init() {
    components = new HashMap<>();
     components.put("exit", exit);
    for(IPlugin plugin: plugins) {
      plugin.init(components);
   }
}

/* Wird jede Sekunden aufgerufen */
public void doLogic() {
    // XYZ-Code
    for(IPlugin plugin: plugins) {
      plugin.doLogic();
   }
   // ZYX-Code
}

Plugin (Button eigenschaften verändern und neuen Button hinzufügen):
Java:
public class MyPlugin extends IPlugin {
   public void init(HashMap<String, JComponent> components) {
      JButton button = (JButton) components.get("exit");
      if(exit != null) {
          // exit.addActionListener() ...
      }
      
       JButton newButton = new JButton("A");
       // aufs Panel setzen und andere Sachen mit dem Button machen.
       components.put("plugin3_hack", newButton);
   }
   public void doLogic() {}
}

Plugin (LSD-Blinker):
Java:
public class MyPlugin extends IPlugin {
   private HashMap<String, JComponent> components;

   public void init(HashMap<String, JComponent> components) {
      this.components = components;
   }

   // Ändert von allen Komponenten zufällig die Hintergrundfarbe. (Wird vom Hauptprogramm jede Sekunde aufgerufen.
   public void doLogic() {
      for(JComponent c: components.values()) {
         // c.setBackground(Random Color)
      }
   }
}
 

Neue Themen


Zurück
Oben