正方形の周長 #25

SquareParametersPaneを思い切り変更してみる。

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class SquareParametersPane extends JPanel {
    public SquareParametersPane() {
        Square params = new Square();
        TField side = new TField(params, new Accessor() {
            public double get(Square s) { return s.getSide(); }
            public void set(Square s, double v) { s.setSide(v); }
        });
        TField peri = new TField(params, new Accessor() {
            public double get(Square s) { return s.getPerimeter(); }
            public void set(Square s, double v) { s.setPerimeter(v); }
        });
        JLabel lside = new JLabel(SquareParametersRes.get("gui.side.label"));
        JLabel lperi = new JLabel(SquareParametersRes.get("gui.perimeter.label"));
        GroupLayout g = new GroupLayout(this);
        setLayout(g);
        g.setAutoCreateGaps(true);
        g.setAutoCreateContainerGaps(true);
        g.setHorizontalGroup(
            g.createSequentialGroup()
            .addGroup(
                g.createParallelGroup().addComponent(lside).addComponent(lperi)
            ).addGroup(
                g.createParallelGroup().addComponent(side).addComponent(peri)
            ));
        g.setVerticalGroup(
            g.createSequentialGroup()
            .addGroup(
                g.createParallelGroup(Alignment.BASELINE).addComponent(lside).addComponent(side)
            ).addGroup(
                g.createParallelGroup(Alignment.BASELINE).addComponent(lperi).addComponent(peri)
            ));
        side.addActionListener(side);
        side.addFocusListener(side);
        params.addSquareListener(side);
        peri.addActionListener(peri);
        peri.addFocusListener(peri);
        params.addSquareListener(peri);
    }

    private class TField extends JTextField implements ActionListener, FocusListener, SquareListener {
        private Square square;
        private Accessor accessor;

        TField(Square square, Accessor accessor) {
            super(String.valueOf(accessor.get(square)), 10);
            this.square = square;
            this.accessor = accessor;
        }

        public void actionPerformed(ActionEvent e) {
            perform();
        }

        public void focusGained(FocusEvent e) {}

        public void focusLost(FocusEvent e) {
            if (e.isTemporary()) return;
            perform();
        }

        private void perform() {
            try {
                accessor.set(square, Double.parseDouble(getText()));
            } catch (IllegalArgumentException ex) {
                setText(String.valueOf(accessor.get(square)));
            }
        }

        public void squareChanged(SquareEvent e) {
            setText(String.valueOf(accessor.get(square)));
        }
    }

    private interface Accessor {
        double get(Square square);
        void set(Square square, double val);
    }
}

もはやSquareParametersPaneはフィールドに子コンポーネントへの参照を持たず、
コンストラクタ内での設定だけで子コンポーネントは自律的に動作するようになっている。
したがって各イベントの処理もSquareParametersPaneでなく子コンポーネントで行われる。
子コンポーネントはJTextFieldを特殊化したクラスTFieldになっており、
辺長と周長それぞれに固有の処理をこのTFieldで統一的に扱うためにインタフェイスAccessorを定義している。
これはSquare型を引数とし各TFieldで必要な情報を取得したり設定する処理へのインタフェイスとなる。
他のソースの変更はない。かなり適当に変更したのだが、なんだか普通に動いている。残念だ。