はじめに
最近のプログラムの多くは、最初から機能がすべて確定したものではなく、後からプログラマが拡張していけるような形をとるようになってきています。この種のプログラムでよく用いられるのが「プラグイン」でしょう。仕様にそって作成すれば誰でもプラグインを作り機能を拡張していくことができるというのはなかなか魅力的です。そこで、Javaプログラムでこうした「プラグインによる機能拡張」を実装する方法について考えてみることにしましょう。
対象読者
- Javaを使ったプログラム作成を行っている中級レベルのプログラマ。
- プラグイン型のプログラム拡張に興味がある方。
プラグインによるプログラム拡張とは?
多くのプログラムでは、プラグインと呼ばれるプログラムを作成することで機能を拡張することができます。このプラグインというのは、大抵の場合、次のような働きをします。
- あらかじめ用意されている仕様に沿ってプログラムを作成するだけでいい。本体側でどんな処理がなされているかなど知る必要がない。
- 指定されたディレクトリなどにファイルを入れておくだけでプログラムが自動認識し、プログラム本体に組み込まれる。
- 複数のプラグインを個別に認識し、随時切り替えるなどして利用することができる。
こういうプラグイン方式による機能拡張というのはなかなか便利です。自分で、あるいは自社でプログラムを作成するとき、こうした仕組みを用意して、あわよくば超人気のプラグインなどが登場してくれれば……などと虫のいいことを考えたこと、誰しもあるはずですね、ハイ。
が、実際にプログラムを作成しようとすると、どのような形でプラグインの仕組みを組み込めばいいのか、うまくイメージできないということもあるのではないでしょうか。そこで、プラグインの仕組みはどうなっているか、考えてみましょう。
サンプルとなる計算プログラム
では、最初にベースとなる簡単なプログラムを用意しましょう。これは、ごく単純な計算をするアプリケーションです。入力フィールドに書かれた数字に1.05をかけた答えを別のフィールドに表示する、という、要するに「消費税の税込価格を計算するプログラム」です。
package jp.tuyano.codezine; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class SamplePluginApp extends JFrame { private static final long serialVersionUID = 1L; private JTextField result,input; private JPanel panel; public static void main(String[] args) { new SamplePluginApp().setVisible(true); } public SamplePluginApp(){ result = new JTextField(); this.add(result,BorderLayout.NORTH); panel = new JPanel(); panel.setLayout(null); input = new JTextField(); input.setBounds(new Rectangle(25,25,50,20)); panel.add(input); this.add(panel,BorderLayout.CENTER); JButton button = new JButton("calc"); button.addActionListener(new buttonAction()); this.add(button,BorderLayout.SOUTH); this.setSize(new Dimension(200,150)); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } class buttonAction implements ActionListener { public void actionPerformed(ActionEvent ev){ try { int n = Integer.parseInt(input.getText()); result.setText(Double.toString(n * 1.05)); } catch (NumberFormatException e) { e.printStackTrace(); } } } }
これ自体は、取り立ててどうということもないものです。画面中央にあるフィールドに数値を入れてボタンをクリックすると、それを1.05倍した値を上のフィールドに表示するというものです。
このプログラムでは、入力した値を1.05倍するという計算をして結果を表示しています。この「与えられた値を元に計算した結果を返す」という部分を、プラグインとして入れ替え可能にしてみましょう。
プラグインの仕様を考える
Javaの場合、最も利用しやすいファイル形式は「Jarファイル」でしょう。プラグインも、作成したクラスファイルなどをJarファイルとしてまとめたものを指定のフォルダに入れておくような形にするのが、最もスマートだろうと思います。Jarなら複数のファイルを1つにまとめられますし、必要な情報をマニフェストファイルに入れて読み込ませることもできます。そこで、今回は次のような形でプラグインを作成することにしました。
- プラグイン用にインターフェイスを用意する。このインターフェイスで、値の設定と計算結果の取得、プラグイン用GUIオブジェクト作成などのメソッドを定義しておく。
- プラグインクラスは、必ず上記のインターフェイスを実装したものとして設計する。
- 作成したプラグインクラスは、Jarファイルとしてまとめる。
- Jarファイルには、プラグインクラス名を記したマニフェストファイルを用意する。アプリケーション側では、Jarからマニフェストファイルを読み込み、その値を元にクラスを読み込むようにする。
- 作成したプラグインのJarファイルは、アプリケーションと同じ階層に用意された「plugins」フォルダの中に保管する。アプリケーションは、起動時のこのフォルダ内を調べ、プラグインのJarをすべて読み込むようにする。
- 便宜上、今回は読み込んだプラグイン・クラスの
toString()
で得られたものをプラグインの名前として利用する形にしておく。
基本的な形は次のようになります。では、プラグイン用のインターフェイスを用意しましょう。ここでは、SamplePluginAppPluginという名前で次のように用意することにします。
package jp.tuyano.codezine; import javax.swing.JPanel; public interface SamplePluginAppPlugin { public String getResult(); // 計算結果の取得 public void setInputData(String input); // データのSetter public String getInputData(); // データのGetter public JPanel getPanel(); // GUIとなるJPanelを返す }
プラグイン・クラスでは、setInput
でString値が渡され、getResult
で結果が返されます。まぁ、一つにまとめて引数としてStringを渡したら結果を返すようにしても良いのですが、後々拡張することを考えて、データとなる値を保管するinputData
プロパティを用意し、そのSetter/Getterと計算結果を得るgetResult
の3つに分けておくことにしました。また、値は数値ではなくStringとしてやりとりするようにしてあります。
今回は、計算に必要な情報を入力するためのGUIもプラグインにもたせることにしました。getPanel
でプラグインが使用するGUIとなるJPanelを取得できるようにしてあります。JPanelの内部はどのようにデザインされていてもかまいません。結果と現在のデータの値がgetResult
などで取得できれば、内部がどうなっているか知る必要はないのですから。