2009-12-08 4 views
2

현재 많은 ActionListeners (각 JButton에 대해 하나)가 정의되어 있고 약 60 개의 버튼이있는 일부 Java 코드를 작성하고 있습니다. 이들은 모두 JButton.addActionListener 메서드에서 익명 내부 클래스로 정의됩니다. 나는 코드를 더 깔끔하게 보이게하기 위해 이것을 리팩토링하는 방법을 생각해 보았다. 필자는 청취자를 반환하는 정적 클래스의로드가있는 별도의 클래스로 리스너를 가져갈 수 있다고 생각했습니다. 이는 코드가 addActionListener(GetActionListener.addActionListener())과 같은 모양을 갖게됨을 의미합니다. 이것이 이것이 더 깔끔하게 만드는 동안 나는 그것이 정말로 우아한 해결책이 아니라고 느낍니다. 나는 청취자 이름 자체와 함께 KV 쌍을 지닌 정적 인 최종지도도 생각했다. 그러나 이것은 여전히 ​​아주 우아한 해결책처럼 보이지 않습니다. 아무도 아이디어가 있다면 궁금해하던가요? 나는 또한 모든 actionListeners가 꽤 다르다고 말해야한다.여러 ActionListener 리팩토링

답변

2

ActionListener을 사용하여 직접 작업을 추가하지 않는 것이 좋습니다. 이런 식으로하면 재사용 할 수 없게됩니다. 대신 귀하의 행동을 javax.swing.Action 클래스로 감싸십시오. 따라서 원하는 곳 어디에서나 작업을 재사용 할 수 있습니다. 예를 들어 복사 동작의 메뉴 바로 가기와 툴바의 복사 버튼에 대해 동일한 동작을 사용할 수 있습니다. 기본적으로 아이디어는 실행 가능한 동작을 GUI 요소와 직접 연결하는 것이 아닙니다.

이제 질문에 올 것입니다. public 메서드 public Action getAction(String)을 사용하여 ActionRepsoitory라는 클래스에 repository of actions을 만들 것입니다. 각 작업은 저장소에서 작업을 검색하는 데 사용하는 String 상수로 식별됩니다. 일반적으로 해당 문자열은 요소에 대해 actionCommand이됩니다. HasMap 등을 통해 ActionRepository에서 액션을 관리하는 방법은 전적으로 당신에게 달려 있습니다.

이것은 AFAIK에서 가장 많이 사용되는 코드입니다.

+0

많은 간단한 GUI에서 Action 프레임 워크를 사용하면 잔인합니다. 그러나 여기에서는 60 개 정도의 ActionListeners를 관리하려고 할 때 메뉴 나 바로 가기가없는 경우에도 리팩터링을 수행하는 것이 좋습니다. – akf

+0

@akf ... 나는 그것에 동의해야하지만 특정 매개 변수에 따라 달라집니다 : a) 다른 장소에서 사용되는 작업 b) 유지 보수. 결정하기 전에이 두 가지 이상을 고려해야합니다. –

+1

개인적으로 ActionListener를 일회적으로 사용하면 거의 항상 Action을 사용합니다. a) 메뉴와 툴바 + 버튼을 대화 상자에 붙이려면 b) 메뉴를 자유롭게 사용할 수 있고 모두 활성화/아이콘/이름 변경과 동기화됩니다. – PSpeed

0

Not a duplication of this question (질문에 대한 대답은 ... 과우가 아니지만) 대답이 적용되어야합니다.

내부 클래스가 외부 클래스 내부에서 메서드를 호출하는 것 이상의 기능을 수행하고 있다면 "잘못된"("올바른"이라는 정의에 따라)하고있는 것입니다. 게시 된 코드에서 increment() 및 decrement()에 대한 호출은이를 수행하는 "올바른"방법입니다. 코드를 리팩토링하면 리스너가 메서드 호출을 외부 클래스로 전달하여 코드를 더 쉽게 관리 할 수 ​​있습니다.

UI에서 60 개의 버튼이 표시됩니다. 정말! 아야! 그것들은 모두 한 화면에 있습니까? 아니면 탭이나 다른 것으로 끝났습니까? (그것이 탭이거나 뭔가 대답할만한 것이 있다면).

+0

다양한 창에 걸쳐 있습니다. 나는 그가 버튼으로 메뉴 아이템을 포함하고 있다고 생각한다. (저는 같은 프로젝트에서 일하고 있습니다.) –

+0

그래서 한 클래스가 모든 청취자를 처리합니까? – TofuBeer

0

reflection을 사용하여 주어진 메소드 이름을 호출하는 ActionListener의 특수 하위 클래스를 만들 수 있습니다. 그런 다음 모든 60 개의 액션을 일반 메소드로 구현할 수 있습니다.

package com.example; 

import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

public class MethodAsActionListener implements ActionListener { 

    private Object receiver; 
    private Method method; 

    public MethodAsActionListener(Object receiver, String name) { 
     this.receiver = receiver; 
     try { 
      this.method = receiver.getClass().getDeclaredMethod(name); 
     } catch (SecurityException e) { 
      throw new RuntimeException(e); 
     } catch (NoSuchMethodException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public void actionPerformed(ActionEvent event) { 
     try { 
      method.invoke(receiver); 
     } catch (IllegalArgumentException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } catch (InvocationTargetException e) { 
      throw new RuntimeException(e); 
     } 
    } 

} 

다음 경우 방법 call 다음 방법 중 하나가 누락 된 경우

button1.addActionListener(call("methodName1")); 
button2.addActionListener(call("methodName2")); 
button3.addActionListener(call("methodName3")); 
button4.addActionListener(call("methodName4")); 
button5.addActionListener(call("methodName5")); 

을 다음과 같이

private call(String name) { 
    return new MethodAsActionListener(this, name); 
} 

는 다음 프로그램이 것, 당신의 행동을 추가 할 수 있습니다 클래스 UI가 빌드 될 때 실패합니다 (액션 리스너가 생성 될 때 메소드를 조회하기 때문에). 컴파일 시간만큼 좋지는 않지만, 액션이 트리거 될 때 완전히 늦은 시각보다 나은 것입니다.

0

나는 당신이 제안한 것과 비슷한 것을 권하고 싶다 - 모든 이벤트를 구독하는 하나의 청취자 클래스를 만든다. 당신은 아마도 각 이벤트에 대해 클래스의 다른 인스턴스를 사용하고, 생성자의 인스턴스에이 특정 이벤트로 수행 할 작업을 알리고 싶을 것입니다.

이점은 일반적으로 꽤 비슷하기 때문에 리스너 내부의 코드를 더 적은 수의 메소드로 분해 할 수 있다는 것입니다. 때때로 당신은 그것을 하나의 방법으로 얻을 수 있습니다.

"순수한 디스패치"상황에서 사용한 메뉴 작성 방법은 메뉴, 메뉴 구조 및 각 메뉴 항목이 데이터에 링크하는 방법을 지정하는 것이 었습니다. 약간의 반사가 필요하지만 작동합니다.

사실 - 보겠습니다.

그래

, 나는 Google 문서의 클래스를 유지 :) 데이터는 다음과 같이 지정 :

final static String[] menus = { "File:*", "Save:save", "Load:load", "(", "Print:-", "Preview:preview", ")", "Quit:quit" }; 

그냥이 구문 분석. 파일은 시작으로 인해 최상위 항목이되고, 저장은 "저장"메소드를 호출하고,로드는 "로드"메소드를 호출하고 인쇄는 하위 메뉴 (따라서 괄호)이며 그 아래의 미리보기와 인쇄는 바운드되지 않습니다 무엇이든.

이 문자열은 한 번의 호출로 전체 메뉴를 만들고 바인딩 할 수 있습니다.

놀고 싶다면 여기 my source code입니다.

맨 위에있는 "TestMenu"클래스는 buildMenus 메서드를 사용하는 방법을 보여주는 테스트 클래스입니다.

이것은 수년 전에 행해졌 다. 지금은 다르게 할 수도 있지만 작동한다. 나는 실제로 메뉴를 생성하는 것을 좋아하지 않는다. 그리고 나는 문자열 파서가 각 항목에 대해 문자열로 나누는 대신 하나의 문자열을 사용하도록 만들 것이라고 생각한다. - 각 항목이 공백으로 구분되도록 보장해야한다. 이 (당신이 전달 또는 다른 어떤 객체)에서 호출되는 저장 방법을 야기 저장 버튼을 눌러 곳

bind(this, new JButton("Save"), "save", this); 

..

더 좋은 API는이 같은 바인드 방법이 될 수 있습니다. "save"매개 변수를 선택적으로 만들 수도 있고 매개 변수가없는 경우 JButton.getText(). toLower()를 호출하는 메서드로 사용할 수도 있습니다. (구성 전의 규칙이라고 생각합니다)

나는하지 않았습니다. 메뉴 작성 및 메뉴 관계를 내 데이터로 추상화하기를 원했기 때문에이 방법으로 메뉴를 만들었습니다.

이렇게 코딩하면 Java에서 MVC 분리를 얻을 수 있습니다. 모든 컨트롤러 코드는보기에서 제거 할 수 있습니다.

0
package hEvil; 

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.FlowLayout; 
import java.awt.Font; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.JButton; 
import javax.swing.JDialog; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextArea; 
import javax.swing.border.EmptyBorder; 

public class JDial extends JDialog { 

private static final long serialVersionUID = -26565050431585019L; 
private final JPanel contentPanel = new JPanel(); 

public static void main(String[] args) { 
    try { 

     JDial dialog = new JDial(); 
     dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 
     dialog.setVisible(true); 
     dialog.setTitle("Heavy Evil"); 
     dialog.setBackground(Color.WHITE); 

    } catch (final Exception e) { 
     e.printStackTrace(); 
    } 
} 

public JDial() { 
    setBounds(0, 0, 1300, 800); 
    getContentPane().setLayout(new BorderLayout()); 
    contentPanel.setLayout(new FlowLayout()); 
    contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); 
    getContentPane().add(contentPanel, BorderLayout.CENTER); 

    JPanel windowPane = new JPanel(); 
    windowPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); 
    getContentPane().add(windowPane, BorderLayout.SOUTH); 

    { 
     JButton cancelButton = new JButton("Exit"); 
     cancelButton.setActionCommand("Exit"); 
     windowPane.add(cancelButton); 
     cancelButton.setBounds(0, 0, 1200, 700); 

    } 

    { 
     JPanel textPane = new JPanel(); 
     textPane.setLayout(new FlowLayout(FlowLayout.LEFT)); 
     getContentPane().add(textPane, BorderLayout.NORTH); 
     textPane.setVisible(true); 

     { 
      JTextArea textArea = new JTextArea("Username", 2, 15); 
      textPane.add(textArea); 
      textArea.setWrapStyleWord(true); 
      textArea.setEditable(true); 
      textArea.setFont(Font.getFont(Font.SANS_SERIF)); 
      textArea.setVisible(true); 
      textArea.enableInputMethods(isEnabled()); 
      textArea.computeVisibleRect(getBounds()); 
      textArea.setBackground(Color.GRAY); 

      JTextArea textArea2 = new JTextArea("Password", 2, 15); 
      textPane.add(textArea2); 
      textArea2.setWrapStyleWord(true); 
      textArea2.setEditable(true); 
      textArea2.setFont(Font.getFont(Font.SANS_SERIF)); 
      textArea2.setVisible(true); 
      textArea2.enableInputMethods(isEnabled()); 
      textArea2.computeVisibleRect(getBounds()); 
      textArea2.setBackground(Color.GRAY); 

     } 
     { 

      JButton registerButton = new JButton("Register"); 
      textPane.add(registerButton); 

     } 
     { 
      JButton newButton = new JButton("Submit"); 
      textPane.add(newButton); 
      newButton.setEnabled(true); 
      getRootPane().setDefaultButton(newButton); 
      newButton.addActionListener(new ActionListener() { 
       public void actionPerformed(ActionEvent evt) { 

        JFrame newFrame = new JFrame("Welcome"); 
        newFrame.setVisible(true); 
        newFrame.setBackground(Color.BLACK); 
        newFrame.setBounds(0, 0, 580, 200); 

        JPanel newPanel = new JPanel(); 
        newFrame.add(newPanel); 
        dispose(); 

        JButton nuButton = new JButton("Mario"); 
        newPanel.add(nuButton); 

        JButton nuButton2 = new JButton("Kirby"); 
        newPanel.add(nuButton2); 

        JButton nuButton3 = new JButton("Mew Two"); 
        newPanel.add(nuButton3); 

        JButton nuButton4 = new JButton("Vegeta"); 
        newPanel.add(nuButton4); 

        JButton nuButton5 = new JButton("Tidus"); 
        newPanel.add(nuButton5); 

        JButton nuButton6 = new JButton("Link"); 
        newPanel.add(nuButton6); 

        JButton nuButton7 = new JButton("Master Chief"); 
        newPanel.add(nuButton7); 

        JButton nuButton8 = new JButton("Snake"); 
        newPanel.add(nuButton8); 

        JButton nuButton9 = new JButton("Cash"); 
        newPanel.add(nuButton9); 

        JButton nuButton10 = new JButton("Lara"); 
        newPanel.add(nuButton10); 

        JButton nuButton11 = new JButton("Max"); 
        newPanel.add(nuButton11); 

        JButton nuButton12 = new JButton("Spyro"); 
        newPanel.add(nuButton12); 

        JButton nuButton13 = new JButton("Sephiroth"); 
        newPanel.add(nuButton13); 

        JButton nuButton14 = new JButton("Scorpion"); 
        newPanel.add(nuButton14); 

       } 
      }); 

     } 

    } 

} 

} 
//AND I WANT TO BE ABLE TO IMPLEMENT EACH BUTTON FROM ANOTHER CLASS 
//FROM ACTIONEVENT WITH SOMETHINGS SIMILAR TO nuButtonX.actionImplemented... 
//CALLING THE SAME ACTIONLISTENER IF I CAN AND THEN CREATING A CLASS FOR 
//MODIFICATIONS AT EACH INSTANCE. 
enter code here 
package hEvil; 

import java.awt.event.ActionEvent; 

import javax.swing.JFrame; 

public class ActoEve extends ActionEvent { 

/** 
* 
*/ 
private static final long serialVersionUID = -2354901917888497068L; 
public ActoEve(Object arg0, int arg1, String arg2) { 
    super(ActionEvent.ACTION_PERFORMED, arg1, "flame_001.jpg"); 
    // TODO Auto-generated constructor stub 
} 
public void actionImplemented(ActionEvent evt1) { 


    JFrame nuFrame = new JFrame(); 
    nuFrame.setVisible(true); 
    nuFrame.setBounds(0, 0, 300, 200); 



    // TODO Auto-generated constructor stub 
} 

}