熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

采用敏捷方法進行用戶界面開發

2013-11-23 19:54:36  來源: Java高級技術 

  摘要
  
  年秋在美國勒海大學亞科卡學院的一份研究報告《世紀美國制造業的戰略一個工業主導的觀點》中首次提出了敏捷競爭的概念而今天我們似乎已經看到敏捷已經在我們身邊形影不離
  
  之前Matrix一則討論激烈的新聞(l) 也表達了敏捷在今天的熱度下面通過一個簡單的例子 告訴你如何起步敏捷開發
  
  概述
  
  如果你沒有采用敏捷的方式那你就落後了這是最近SD Best Practices 會議上的標語敏捷的方法就象XP和Scrum正在世界的軟件發展中變得越來越普遍了敏捷是一個巨大的改變讓軟件開發者把重心轉移到質量和速度上這對已經被比喻成面向對象設計的軟件開發有很大的影響但是這種影響的一些方面已經放慢了GUI的開發自從大多數軟件包含了一些類型的GUI而且比較多的軟件開發的百分比是去完成以GUI為中心的應用敏捷的優勢去開發GUI就是關鍵問題了
  
  什麼在阻止人們用敏捷的方法來開發GUI呢?不論他們的應用是基於web的或是桌面應用大多數開發者不做用戶界面user interface的測試驅動開發testdriven development (TDD)這都因為一個簡單的原因單元測試GUI是很困難的測試GUI是很乏味沉悶而且容易出錯的包含了模擬用戶事件的復雜代碼在事件傳播和控制重繪的時候等待然後在他顯示給用戶之前嘗試著檢測狀態敏捷依賴於測試驅動開發但是為GUI的特定行為來寫有效的測試代碼是困難的在cube farm(辦公農莊用隔斷間隔成很多小工作間的辦公室? 商務英語)的GUI方面上質量和設計從敏捷方法中受益已經被完全認識到
  
  敏捷實踐正在滲透進這個領域單元測試GUI元素的工具激增JFCUnit 框架測試GUI是用java Swing基於Web的GUI能被HTMLUnit HTTPUnit jWebUnit和類似的工具測試許多GUI構造器和工具包和單元測試工具有關系比如VBUnit是為了Visual BasicQtUnit是給Qt用的
  
  工具已經存在了但是處理仍是不確定的在測試驅動開發(TDD)中每個代碼的改變都在新行為的單元測試前在開發GUI時許多變化不過是視覺顯示上的調整比如改變元素的位置文本或者顏色你可以加一個按鈕建一個菜單項或者構造一個對話框但是怎樣和為什麼你要測試這種變化呢?測試每個標簽或者顏色值是很愚蠢的同樣的對於標准的元素象按鈕和域測試他們通常的行為是沒有意義的象對鼠標移動的響應鍵的按下點擊和諸如此類的這些事情是不可能被中斷的怎樣去測試他們的問題只是徒勞的增添了做GUI測試的難度
  
  一個關鍵的問題怎樣做測試先行的開發?答案就在於GUI的編碼是怎樣組織的敏捷方式的領袖例如Kent Beck 和 David Astels建議在構造GUI的時候要保持視圖對象盡可能是輕量的而且在表面下( below the surface)測試視圖層這個 敏捷對象/瘦視圖 模型和我們熟悉的 文檔視圖 及 客戶端服務器模式類似但是被應用於個別的GUI元素內容和表現的分離改善了代碼的設計使他更模塊化和更利於測試每個用戶界面的組件被實現為一個敏捷對象包括將要被測試的應用的行為但不包括GUI表現的代碼每個敏捷對象有一個相應的瘦視圖類只包括普通的GUI行為采用這種設計模式GUI構造變得可以被應用於 測試驅動開發(TDD) 處理了
  
  例子構造一個登錄對話框
  
  讓我們進入一個例子看看怎樣使用TDD和 敏捷對象/瘦視圖 代碼設計模式去開發一個GUI對話框起初讓我們考慮對話框的圖形敏捷開發提倡預先最小化設計讓軟件構架在多次循環開發中重構但是這個方法對GUI設計不是很合適設計一個用戶界面是一個創造的過程應該規范地處理畫草圖做原型和可用性測試然後盡管在GUI下的代碼可以用TDD迭代地設計一個形象的設計草圖是明智的第一步這個對話框的基本的設計在圖中勾畫出來
  
  
 
  Figure GUI design sketch for login dialog

  
  這個對話框很簡單包括用戶名和密碼域相應的靜態文本框和標簽登錄和取消按鈕做為一個他行為的初始輪廓我們決定登錄成功的話對話框關閉登錄失敗的話對話框仍然開著取消按鈕也關閉對話框
  
  基本的 敏捷對象/瘦視圖 代碼類設計的對話框實現在圖中表示
  
  
 
  Figure The classes LoginDialog and LoginDialogView

  
  敏捷對象類LoginDialog 將包含一個方法對應對話框的每個功能行為瘦視圖類LoginDialogView 將只包含簡單的和顯示相關的代碼還有get/set 方法去讀取和設置顯示的信息在這個過程裡只有LoginDialog裡復雜的功能需要被單元測試我們可以十分自信在LoginDialogView 裡的簡單行為可以正常工作
  
  第一個構造的組件是敏捷對象LoginDialog 他需要一個相應的測試類LoginDialogTest 第一個測試方法將要驗證登錄方法如圖所示
  
  
 
  Figure The smart object LoginDialog and its test class LoginDialogTest
  

  作為測試先行的開發方法規定首先要寫單元測試測試預期和定義了要被測試的功能設計我們需要獲得一個用戶名和密碼然後返回一個登錄成功或者登錄失敗一個用來判斷的接口方法來做剛才所述的
  
  boolean login(String username String password);
  
  測試類LoginDialogTest 將測試這個功能展示了在LoginDialogTestjava 文件中他的初始實現
  
  LoginDialogTestjava
  
  import junitframework*;
  public class LoginDialogTest extends TestCase {
  public void testLogin() {
  LoginDialog dialog = new LoginDialog();
  assertTrue( dialoglogin(user passwd) );
  }}
  
  這個測試是基於JUnit基礎測試類TestCase的測試方法testLogin()創建了一個LoginDialog 的實例調用了他的login()方法然後判定結果是真這段代碼將不會編譯因為LoginDialog 不存在在TDD過程後LoginDialog
  
  將生成和保存代碼編譯後測試運行驗證將象預期的那樣失敗(因為方法沒有實現)然後 LoginDialog 為了通過單元測試給出最小的實現遵照敏捷的聖條 做 可能工作的最簡單的事情(the simplest thing that could possibly work)展示了最初的LoginDialog 版本用最少的代碼通過了單元測試實現在LoginDialogjava 文件裡
  
  LoginDialogjava
  
  public class LoginDialog {
  LoginDialog() {}
  public boolean login(String username String password) {
  return true;  } }
  
  使用下面的命令來運行代碼
  javac classpath ;junitjar LoginDialogTestjava
  javac classpath LoginDialogjava
  
  classpath 必須包括junitjar 來運行單元測試因為他使用了JUnit在LinuxMac OSX還有其他的UNIX系統上classpath將包含一個冒號(:)而不是想下面那樣用一個分號
  
  測試將如下運行
  
  java classpath ;junitjar junittextuiTestRunner LoginDialogTest
  
  單元測試通過了真好!不幸的是這個編碼只是模擬一下Login()方法將總是批准登錄毋庸置疑客戶將不會欣賞這種水平的安全機制顯然要寫的下一個測試是驗證如果給的條件不正確的話將失敗展示了LoginDialogTest 的第二個測試方法去實現這個目的testLoginFail() 既然兩個測試都使用一個LoginDialog 的實例測試類被重構為在他的setUp() 方法裡創建一個固定的測試用的LoginDialog
  
  LoginDialogTestjava
  
  import junitframework*;
  public class LoginDialogTest extends TestCase {
  private LoginDialog dialog;
  public void setUp() {
  dialog = new LoginDialog();
  }
  public void testLogin() {
  assertTrue( dialoglogin(user passwd) );
  }
  public void testLoginFail() {
  assertFalse( dialoglogin( ) );
  }}
  
  LoginDialog 必須得通過新的測試不能在第一次測試的時候有失敗TDD過程引導我們構造我們需要的真正的功能在用正確的用戶名和密碼登錄的時候能成功登錄如果不是就失敗展示了按此修改的LoginDialog
  
  LoginDialogjava
  
  public class LoginDialog {
  private String user = user;
  private String passwd = passwd;
  LoginDialog() {}
  public boolean login(String username String password) {
  if (userequals(username) && passwdequals(password))
  return true;
  else
  return false;
  } }
  
  LoginDialog 現在能通過所有的測試為此他包括了符合成功登錄條件的用戶名和密碼域顯然這只是比第一個版本的安全性能稍微好一些登錄代碼不應該包含認證的硬編碼!基於這點我們應該引入一個單獨的類來包含LoginDialog 用的驗證用戶的登錄信息然而這個例子是關於GUI構造的那讓我們暫停這個不安全的登錄代碼繼續GUI方面
  
  現在我們已經建立了登錄功能並用單元測試覆蓋了他但沒有可視的GUI來顯示它那下一步該做什麼呢?對於已經作的和測試的實際功能在GUI方面做的是創建和顯示圖像元素然後在適當的時候調用login()方法這個功能是普通和容易建立的所以他不包含能中斷和需要單元測試的復雜行為因此當建立GUI元素時我們不需要去做測試先行的開發展示了創建對話框窗口的Swing類LoginDialogView 他的實現在LoginDialogViewjava文件
  
  LoginDialogViewjava
  
  import javaawt*;
  import javaawtevent*;
  import javaxswing*;
  public class LoginDialogView extends JFrame
  implements ActionListener {
  protected JTextField usernameField;
  protected JTextField passwordField;
  protected JButton loginButton;
  protected JButton cancelButton;
  private LoginDialog dialog;
  LoginDialogView(LoginDialog dlg) {
  super(Login);
  setSize( );
  dialog = dlg;
  addControls();
  loginButtonaddActionListener( this );
  cancelButtonaddActionListener( this );
  }
  public void actionPerformed(ActionEvent e) {
  String cmd = egetActionCommand();
  if (cmdequals(Login)
  && dialoglogin(usernameFieldgetText()
  passwordFieldgetText())) {
  hide();
  }
  }
  private void addControls() {
  Container contentPane = thisgetContentPane();
  contentPanesetLayout(new GridBagLayout());
  GridBagConstraints c = new GridBagConstraints();
  JLabel label = new JLabel(Username: LabelRIGHT);
  cinsets = new Insets( );
  cgridx = ;
  cgridy = ;
  contentPaneadd(label c);
  usernameField = new JTextField( );
  usernameFieldsetMinimumSize(new Dimension( ));
  cgridx = ;
  contentPaneadd(usernameField c);
  JLabel label = new JLabel(Password: LabelRIGHT);
  cgridx = ;
  cgridy = ;
  contentPaneadd(label c);
  passwordField = new JTextField( );
  passwordFieldsetMinimumSize(new Dimension( ));
  cgridx = ;
  contentPaneadd(passwordField c);
  loginButton = new JButton(Login);
  cgridx = ;
  cgridy = ;
  contentPaneadd(loginButton c);
  cancelButton = new JButton(Cancel);
  cgridx = ;
  contentPaneadd(cancelButton c);
  }}
  
  LoginDialogView 包含了文本域標簽和按鈕元素除了普通的GUI行為外他只是有一個簡單的行為被actionPerformed() 方法實現這個行為就是當登錄按鈕被點擊後login()方法被調用如果登錄成功對話框就被所調用的hide()方法所關閉
  
  為了調用login()函數在LoginDialogView 構造器裡需要接收一個LoginDialog實例另外他組裝了完整的GUI設置和事件處理代碼大部分代碼在addControls() 裡他簡單的創建和排版了窗體上的GUI元素
  
  LoginDialogView 代碼示范了一個GUI瘦視圖元素怎樣被設計使它只包含普通的GUI代碼而把重要的需要測試應用的行為放到一個單獨可測試的敏捷對象中
  
  LoginDialogView 只需要通過創建它來測試察看他從用戶的角度確認它看起來和運行起來象期望的那樣展示了可執行的類APPMain它創建了對話窗體來傳遞可用性測試(指的是傳遞loginDialog的實例)
  
  AppMainjava
  
  public class AppMain {
  public static void main(String[] args) {
  AppMain app = new AppMain();
  }  public AppMain() {
  LoginDialog dialog = new LoginDialog();
  LoginDialogView view = new LoginDialogView(dialog);
  viewshow();
  while (viewisVisible()) {
  try {
  ThreadcurrentThread()sleep();
  } catch(Exception x) {}
  }
  Systemexit();
  }}
  
  AppMain 類簡單的創建一個LoginDialog 和LoginDialogView 顯示視圖休眠直到視圖關閉然後退出
  
  AppMain 象下面一樣運行
  
  java –classpath AppMain
  
  運行它創建登錄對話框如圖所示
  
  
 
  Figure The login dialog window

  
  和登錄對話框交互驗證了用圖所示的值登錄會登錄成功然後窗體關閉試著用其它的值登錄窗體將保持打開因為登錄失敗了取消按鈕關閉窗體就象窗體的關閉按鈕一樣這個登錄對話框就如同設計的那樣運行
  
  解決方案
  
  我們已經根據TDD創建了登錄對話框和一個敏捷對象/瘦視圖設計模式得到了一個有很好構架和功能的程序有功能的應用行為被單元測試所覆蓋普通的用來顯示的代碼不需要復雜的GUI測試展示了我們所開發的這個軟件的構架
  
  
 
  Figure The classes LoginDialog LoginDialogView and LoginDialogTest
  

  基於此其他的特性可以被加入登錄對話框可以有一個消息域去提醒用戶登錄失敗其他的登陸參數域也可以被加入一個單獨的驗證對象可以被創建硬編碼的登錄值可以被刪掉不管怎麼變化TDD和敏捷對象/瘦視圖模式提供了一個設計和實現上的清晰的方向重要的應用功能是在於可以測試的敏捷的對象和在瘦視圖中普通的顯示用的代碼的
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27653.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.