Discussion:
[問題] 多個panel和ActionListener的問題
(时间太久无法回复)
foolish
2006-11-24 13:14:57 UTC
Permalink
※ 引述《neigence (心夜)》之銘言:
: 假如我有一個程式
: public class myFrame extends JFrame{
: public AAA(){
: Container con=getContentPane();
: con.add(myPanelA);
: con.add(myPanelB);
: con.add(myPanelC);
: .....
: }
: }
: 每一個panel都也許具有許多的gui元件 像JButton JTextfield
: 每一個panel都在不同的class
: 比方說 我按下myPanelB上的某個button 會影響讓myPanelA畫出
: 什麼東西 或發生某些行為 或更動變數狀態
: 那..理論上? 這幾個myPanel下所有gui元件的ActionListener
: 都要寫在myFrame這個程式下面嚕?..這樣子光這個檔下面的
: actionPerformed 真的會太多了
++myFrame+++++++++++++++++
| |
| A panel Button |
| |
+-------------------------
| |
| B panel Text |
| |
+-------------------------
| |
| C panel Label |
| |
+-------------------------

你可能在B text填了什麼,
按了A的Button之後,想透過C的Label顯示點什麼訊息

所以直覺想幫A加了一個ActionListener
{
取得ORZ = B.text
經過謎樣的邏輯運算後 XD = cal(ORZ)
顯示結果在C之中 C.label = XD
}

這只是一種初步的想法,當你的Component很多的時候
就會難以控制。因為任何的Component都可能產生關係
要站在某一方(ex. A's Listener)的觀點去操控其他所有的元件
會增加程式設計師的負擔。

for example:
ActionListener
{
t1 = A.xd;
B.label = func1(t1)
C.label = func2(t1)
D.label = func3(t1)
t2 = sum({B, C, D}.label)
............
........
....
.
}
這樣一來計算過還可能有些用處的資料,都散落在各個元件之中。
還要一一再收回? 可能會瘋掉。
===========================================================

Tip 1 :: GUI的資料要獨立且唯一

為了解決資料散落於各元件的問題,我們應該建立新的Class來表現
我們所用來展示、算計、暫存用的資料,有續存的需求更可以實作
Serializable。

所以,先讓資料獨立出來。
public class CheckableData {
private String foo;
public void setData(String f){foo=f;}
}

Tip 2 :: 不要讓運算邏輯散落於Listener之中
讓Data的歸Data的,使用者介面的歸使用者介面的

假設我們要讓資料可序列化,你不可以把序列化的主要部分寫在
Action之中。應該擺在CheckableData這裡才對 (ex. save method)

==========================================================
以提供base64 encode為例:

import sun.misc.BASE64Encoder;

public class CheckableData {
private String foo;

public void setData(String f) {
foo = f;
}

public String getEncodeAsBase64() {
return new BASE64Encoder().encode(foo.getBytes());
}
}


(bad style)

encode_button.addActionListener(){
.........................
xd.label = new BASE64Encoder().encode(foo.getBytes());
}

這樣做的好處是,邏輯運算的部分幾乎都可以回歸資料本身。
達到純粹send message來溝通。

========================================================

即使經過了Tip 1、Tip 2資料獨立了,邏輯計算都歸位了。
你可能還是望著內容不少的Listener而懊惱。
這是怎麼一回事呢? 我們還需要改變些什麼!?

回過頭來看一下Listener
{
取得ORZ = B.text
經過謎樣的邏輯運算後 XD = cal(ORZ)
顯示結果在C之中 C.label = XD
}

這是在按了某個東西,觸發了事件。
最後導致了C.label更新
許多入門的書可能都是這樣寫了
按了foo,計算一下,直接更新了bar
這是個很直覺的構想,也不用覺得寫書的人有什麼不錯
因為他的重點在介面GUI,而我們今天的重點在
怎麼讓自己程式寫起來負擔小一點,
不會把code都擠在一起。

所以,為了我們的睡眠品質與減輕壓力應該想一想
是否有較好的方式,懶人思維: 是不是能讓C.label
自己更新自己
(general case: 讓(..)自己更新自己)
如果成真了這樣一來,就能讓Listener中更新
其他元件的code消失,而Listener只要通知Data object
去計算資料即可

=====================================================
Tip 3 :: 讓需要取得資料者成為觀察者
(提供資料的為被觀察者)

j2se提供了這樣的機制

Interface Observer
void update(Observable o, Object arg)

Class Observable
addObserver(Observer o)
notifyObservers()
setChanged()

這要怎麼用呢!?
Observer --> 需要取用資料者
Observable --> 持有/產生資料者

以我們的例子,CheckableData是產生資料者:

import java.util.Observable;
import sun.misc.BASE64Encoder;

public class CheckableData extends Observable {
private String foo;

public void setData(String f) {
foo = f;
this.setChanged(); // 標示,我"變"了
this.notifyObservers(); // 昭告天下,我真的"變了"
}

public String getEncodeAsBase64() {
return new BASE64Encoder().encode(foo.getBytes());
}
}

======================================================

import java.util.Observable;
import java.util.Observer;

import javax.swing.JLabel;

public class ObLabel extends JLabel implements Observer {
private String label;

public ObLabel(Observable o , String s) {
o.addObserver(this);
label = s;
}

public void setLabel(String s) {
this.label = s;
this.setText(s);
}

public void update(Observable o, Object arg) {
if (o instanceof CheckableData) {
CheckableData cd = (CheckableData) o; // 取得資料
setLabel(cd.getEncodeAsBase64()); // 自己更新自己
}
}
}

如此一來,則所有元件可以透過觀察CheckableData來自動更新。
Listener所負擔的code也就少了,複雜的元件關係也獲得了解脫。



























































--
偷偷講,其實這是簡單的講怎麼做mvc @"@
mvc 是策略模式+觀察著模式與一些互動的規範而成。
此篇稍略了策略模式,但大致上還算可用
--
※ Origin: SayYA 資訊站 <bbs.sayya.org> 
◆ From: pc210-59-94-161.nutn.edu.tw
foolish
2006-11-26 00:25:48 UTC
Permalink
MVC?
Where's your "MODEL" and "CONTROLLER"? (「m」V「c」)
Please describe it, thanks.
MODEL --> CheckableData

Controller --> Listener can send message

to model do something.

====================================================================

文中的最後寫了,稍為乎略了策略模式。

是因為如果直接把Listener的功能對應為Controller

所需要的功能,會使得程式必定要在GUI的情況才能跑

也就是說Controller與View產生了藕合,難以切割


比較能接受的例子大概就是Collections中的Comparator interface

Collections本身定義了一組存放東西的樣版,

但是要如何比較這些東西的順序,以及是否有需要比較

你可以決定是否要實作Comparator來為Collections加上比較的功能


CheckableData之中自己也定義了一些method,

但這些method主要是負責CheckableData必要的邏輯運算用的

如果為了遷就GUI而在base class加上去了一些額外的method

就會使得base class提供的功能過於"雜亂",

對於這樣不一定要出現的method,定義一組配合GUI操作的

interface並實作之。

--
這只是其中一種Controller的"位置"
也有的作者完全不加東西在base class之中,
而是把他包起來,透過中間人去溝通model與view
--
※ Origin: SayYA 資訊站 <bbs.sayya.org> 
◆ From: pc210-59-94-161.nutn.edu.tw

继续阅读narkive:
Loading...