質問
完全な免責事項:私はCSの学生です。この質問は、オブジェクト指向プログラミング用に最近割り当てられたJavaプログラムに関連しています。コンソールを作成しましたが、GUIとSwingまたはAwtを使用したのは今回が初めてです。いくつかのテキストと、テキストの異なる色で回転するボタンを備えたウィンドウを作成するコードが与えられました。次に、プログラムを変更して、代わりに色のラジオボタンを作成するように求められました。既に課題を提出し、インストラクターからここにコードを投稿する許可を得ています。
Javaでボタンアクションを実装する最良の方法は何ですか?いじくり回した後、次のようなボタンを作成しました。
class HelloComponent3 extends JComponent
implements MouseMotionListener, ActionListener
{
int messageX = 75, messageY= 175;
String theMessage;
String redString = "red", blueString = "blue", greenString = "green";
String magentaString = "magenta", blackString = "black", resetString = "reset";
JButton resetButton;
JRadioButton redButton, blueButton, greenButton, magentaButton, blackButton;
ButtonGroup colorButtons;
public HelloComponent3(String message) {
theMessage = message;
//intialize the reset button
resetButton = new JButton("Reset");
resetButton.setActionCommand(resetString);
resetButton.addActionListener(this);
//intialize our radio buttons with actions and labels
redButton = new JRadioButton("Red");
redButton.setActionCommand(redString);
...
そしてアクションリスナーを追加...
redButton.addActionListener(this);
blueButton.addActionListener(this);
...
スタブはactionPerformedメソッド用にすでに作成されており、使用方法がわかりますが、テンプレートにはボタンが1つしかないため、複数のボタンを実装する方法は明確ではありませんでした。 Stringを切り替えてみましたが、Stringはプリミティブ型ではないため、switchステートメントに使用できないことにすぐに気付きました。 if-elseチェーンを使用して即興演奏することもできましたが、代わりにこれが思いつきました。エレガントとはほど遠いようで、もっと良い方法があるはずです。ある場合、それは何ですか?文字列をオンにする方法はありますか?または、よりスケーラブルな方法でアクションを選択しますか?
public void actionPerformed(ActionEvent e){
if (e.getActionCommand().equals(resetString)) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
return;
}
if ( e.getActionCommand().equals(redString) ) {
setForeground(Color.red);
repaint();
return;
}
if ( e.getActionCommand().equals(blueString) ) {
setForeground(Color.blue);
repaint();
return;
}
if ( e.getActionCommand().equals(greenString) ) {
setForeground(Color.green);
repaint();
return;
}
if ( e.getActionCommand().equals(magentaString) ) {
setForeground(Color.magenta);
repaint();
return;
}
if ( e.getActionCommand().equals(blackString) ) {
setForeground(Color.black);
repaint();
return;
}
}
解決
これを書く代わりに:
resetButton.addActionListener(this);
これを書くこともできます:
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
resetButtonActionPerformed(evt);
}
});
そして、すべてのアクションに対して1つの大きなactionPerformed()を書く代わりに、これを書くことができます(そして、そうしなければなりません):
public void resetButtonActionPerformed(ActionEvent evt) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
これが最もエレガントな解決策であるかどうかはわかりませんが、少なくともあなたはもはや大きなifコンストラクトを持っていません。
他のヒント
2つの代替アプローチ:
- Actionインターフェースを実装し、Colorフィールドと、色を設定するactionPerformedメソッドを持つ新しいクラスを作成します
- HashMapをコマンド名からColorインスタンスに保持し、マップでコマンド名を検索します
十分なアプローチの1つは、文字列に一致する要素を持つ列挙を宣言することです。 valueOf(str)をオンにします(リンクされた例は、かなりの安全性でこれを行う方法を示しています)。
匿名の内部クラスを避ける理由は、おそらくそれが最良の解決策かもしれませんが、クラスにその構造が(まだ)ないからです。
すでに提案したように、匿名内部クラスを使用してActionListenerインターフェイスを実装できます。別の方法として、匿名の内部クラスを使用する必要はありませんが、代わりに単純なネストされたクラスを使用できます。
resetButton = new JButton(new ResetAction());
redButton = new JButton(new ColorAction("Red", Color.red));
そして......
private class ResetAction extends AbstractAction {
public ResetAction() {
super("Reset");
}
public void actionPerformed(ActionEvent e) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
}
private class ResetAction extends AbstractAction {
private Color color;
public ColorAction(String title, Color color) {
super(title);
this.color = color;
}
public void actionPerformed(ActionEvent e) {
setForeground(color);
repaint();
}
}
このアプローチ、または内部クラスを含むアプローチが外部クラスにActionListenerを実装するよりも優れている理由については、「デザインパターン」を参照してください:
"「クラス継承」よりも「オブジェクト構成」を優先します。」 (ギャングオブフォー1995:20)
匿名の内部クラスとこれらの名前付き内部クラスの選択は、主にスタイルの問題ですが、このバージョンの方が理解しやすく、多くのアクションがある場合に明確になると思います。
アー1つのメガクラスに大量の無関係なインターフェイスを実装しないでください。代わりに、匿名の内部クラスを使用してください。それらは少し冗長ですが、あなたが望むものです。各イベントに1つ使用すると、大きなif-elseチェーンは必要ありません。イベントをデコードし、ターゲットオブジェクトにとって意味のあるメソッドを呼び出すのに十分なコードを内部クラス内に保持することをお勧めします。さらに、内部クラスをパラメーター化できます。おそらく、実際のウィジェットへの参照を保持する必要はないでしょう。
この例では、JPanelとしてJComponentを使用しているようです。大きな違いはありませんが、ウィジェットのブロックを収集するためにJPanelを使用します。さらに、サブクラス化する必要はほとんどないので、そうしないでください。
例えば:
addColorButton("Green" , Color.GREEN );
addColorButton("Red" , Color.RED );
addColorButton("Yellow", Color.YELLOW);
addColorButton("Blue" , Color.BLUE );
...
private void addColorButton(String label, Color color) {
JRadioButton button = new JRadioButton(label);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
target.setForeground(color);
target.repaint();
}
});
colorGroup.add(button);
panel.add(button);
}