惠子青,劉曉燕,朱匯龍
(昆明理工大學 信息工程與自動化學院,云南 昆明 650500)
敏捷開發方法是目前非常流行也是非常成功的開發方式[1]。敏捷開發方法要求“在全面的文檔中使用文件”.綜合的uml圖認為代碼是次要的,uml的維護成本似乎也說明了代碼是次要的。uml圖與代碼是分開的它只能在文檔中體現,當uml圖用于增量模型設計時它與源代碼之間的協調是非常困難的,增量模型因此也變得非常的復雜,由于uml的類模型很少允許遞歸,因此幾乎沒有伸縮性(相反,數據流圖允許遞歸,因為流程就是一個數據流圖).另一方面,uml提供了很好的可視化效果,它的好處我們不應該丟棄[6]。一般來說,uml除了成本因素,是非常有利于開發的。
本文描述了一種在代碼注釋中逐步嵌入圖形化UML類模型的嚴格方法。它是和快速開發的代碼一起進行的,最后的結果由簡單易懂的類模塊組成。該方法從確定我們所需調用的函數開始,每個函數對應一個需求。例如:如果用戶需要訪問一個購物網站所需的函數可能包括display_product_image()、verify_password()和process_payment(),當一個函數被識別并且驅動非平凡的main(),我們可以稱它為-跟設計函數。跟設計函數存在的前提條件是所需要的功能函數存在,而它的后置條件是將跟設計函數需放在開發人員所創建的類中,(后置條件也有可能包括設計約束)。跟設計函數的高階規范是這篇文章主要討論的問題。
一般來說,除了成本之外,UML已經被發現對軟件開發有利。例如,Vargas發現[3],“在系統層面上,使用類圖建模的代碼的變化傾向性低于根本沒有建模的代碼的變化傾向性”
UML在敏捷開發中的應用已經讓研究人員關注了一段時間(例如Wei等人[2]),但是還沒有出現廣泛使用的方法。模型驅動分析指定了用 XML描述 UML模型和元模型的廣泛方法,但是這些對于僅僅處理代碼的開發者來說并不是立即可見的。Pitk?nen和Selonen[10]對增量建模的研究就是一個例子。Marovac在[1]中也將 UML表示為“被標記的句子”而不是數字。Ambler[7]在敏捷建模方面做了非常有用的工作,但是他并沒有像在這里展示的那樣討論嵌入UML。他在[8]中指出:“在AM(敏捷建模)的范圍內,它(源代碼)不會被認為是一個模型,因為我想區分這兩個概念。”一些敏捷建模工作的形式(例如Rumpe[5])。一個具體的例子是Karagiannis[4],它描述了敏捷建模方法工程(AMME)。
Weiser顯示,開發人員在調試相關的代碼片段時使用片段,而這些片段不一定是連續的。在不同的上下文中,本文描述的技術使用切片[11]。
首先,我們現在假設需求是給定的,沒有執行已經設計的uml可視化,并且注釋源代碼是我們唯一了解軟件結構的途徑。其次,假設代碼設計的功能是可用的。在這里我們不假設uml必須以完整的形式顯示,如:顯示所有類或全部類關系或類的所有方法。
快速開發規范從需求開始當前的輸出。在示例中,“用戶”可以指定廚房的裝修風格,以及收到相應的反饋。例如,這就要求存在一個將櫥柜置于某個位置的功能,如place_s_wall cabinet(a_position)。這種功能的識別取決于開發者,在這種情況下,我們將把它們稱為“需求”。
根設計功能是執行滿足當前所有要求(即通過授權)的功能。所需功能的存在是明確的前置條件。根設計功能依靠這些功能來完成其后置條件,其中包括明確的創建適用于所需功能的類,并將這些類關聯起來。這是 UML可視化變得有用的地方。對于快速開發來說,我們所描述的技術就是創建這些UML片段-不需要放置所需的功能。由于這些片段通常只有 1-4個類的組合,所以它們及其相互關系可以作為簡單的ASCII數字在評論內提供。 就像每個代碼塊依賴于前面的代碼一樣,每個新的 UML片段都應該和已經給出的代碼相關聯。換句話說,一個連接類可以使模型變得簡單。在本質上,根設計函數所要求的功能可以是設計功能遞歸地重復本文描述的過程。
在本文中,每個函數被分解組織成一個有序的積累的子目標。它們的連接必須滿足所有的后置條件。在最簡單的情況下,一個子目標可能與后置條件相同。“積累”屬性意味著一旦子目標被實現,就通過功能代碼的其余部分來維護。這種漸進方式與快速開發是一致的。子目標在代碼中用“-”表示。為了方便起見,對于不是必需子目標的邏輯充分性的推理用方括號表示。
開發人員需要確定滿足后置條件的子目標,并且選擇執行子目標的順序。另一個是將所需的功能放在合適的位置。
我們選擇本文的例子的目標是:它們應該足夠小,足以被充分描述和實施,足夠復雜以說明技術,并且足夠熟悉,以便讀者可以將結果與傳統起草的UML文檔進行比較。因此,我們選擇了設計模式的應用程序:抽象工廠和調解器,一般在Gamma等[9]中描述。
例1:廚房的可視化
第一個例子取自[6],使得用戶能夠以“古董”或“現代”等各種風格可視化給定的廚房布局(在下面的代碼中基本上是櫥柜布局)。布局由5乘以 5格的櫥柜(“f”)和墻柜(“w”),如下例所示:

arrange_kitchen_with(“ANTIQUE”)的執行
產生以下內容:

執行 arrange_kitchen_wit(“MODERN”)
相同的布局產生以下內容:

設計和實施必須很容易適應新的櫥柜和新的風格。
(1)選擇根設計功能
這個過程是由根設計功能驅動的,執行產生所需的應用程序,并依賴于它在附屬(“必需”)功能。根設計函數的如下。
def arrange_kitchen_with(a_style):
前置條件明確地引用了所需的功能——實際繪制或放置各種櫥柜的方法風格各異,如下:
Preconditions
1. a_style = 'MODERN' or 'ANTIQUE'
2. place_y_x_cabinet(a_position) are defined,each placing an x cabinet in style y at a_position on the console, where x = floor or wall and y = modern or antique
3. arrange_kitchen() is defined, using place_x_cabinet() with x = floor or wall, to produce a picture of a kitchen on the console.
4. cabinet_arrangement specifies where the flooror wall cabinets should be located on a two-dimensional grid
Postconditions
1.arrange_kitchen()and place_x_cabinet() are allocated
2. place_y_x_cabinet() are allocated for x =floor/wall and y = modern/antique
3. (place_x_cabinet() delegates):
Kitchen.place_x_cabinet() delegates to
place_y_x_cabinet()where y corresponds to a_style
4. A kitchen is displayed on the console as per
cabinet_arrangement and a_style
這些規范依賴于 place_y_x_cabinet()等詳細規范的存在。
(2)分解
根設計函數被分解成一系列代碼塊,每個代碼塊實現一個子目標(在代碼中用“ - ”表示)。 總體來說,子目標聲明暗示了整個設計功能的后置條件。當一個子目標標簽是有用的,它顯示在括號內。
第一個子目標放置函數 arrange_kitchen(),place_floor_cabinet()和 place_wall_cabinet()。 這個子目標的實現由這里描述的UML和Kitchen的代碼組成(通常在一個單獨的文件中)。
--(Postcondition 1): arrange_kitchen() and
place_x_cabinet()with x = floor or wall are allocated AND
the_kitchen is Kitchen instance with cabinet_arrangement
|_______Kitchen_______|
| arrange_kitchen() |
|place_floor_cabinet()|
| place_wall_cabinet()|
from … import Kitchen
the_kitchen = Kitchen()
the_kitchen.set_arrangement(cabinet_arrangement)
我們接下來找到place_y_x_cabinet(),注意將新的UML片段連接到已經引入的UML。
--(Postcondition 2): place_y_x_cabinet() allocated with x
= floor or wall and y = antique or modern
Kitchen<>---->|_XCabinet_|
_________________________ ^
|____AntiqueXCabinet______|
|____ModernXCabinet______|
|place_antique_x_cabinet()|
|place_modern_x_cabinet()|
現在可以使用place_x_cabinet()函數實施
--(Postcondition 3): place_x_cabinet() delegates
Kitchen<>-x_cabinet---> XCabinet|place()_|
Kitchen.place_x_cabinet() delegates to x_cabinet.place(),
and YXCabinet.place() delegates to place_y_x_cabinet()
where x = floor or wall and y = antique or modern
接下來,開發者可以將Style引入為對象。 方括號表示一個子目標,雖然需要執行,但是在檢查子目標的意味著聯合后置條件時是不需要的。
--[Style]: the_kitchen.the_style is a Style instance
corresponding to a_style
Kitchen<>-the_style--->|_Style_|
_____________________ ^
|_AntiqueKitchenStyle_||_ModernKitchenStyle_|
’’’
from … import AntiqueKitchenStyle, ModernKitchenStyle
if a_style == "ANTIQUE":
the_style = AntiqueKitchenStyle()
else:
the_style = ModernKitchenStyle()
the_kitchen.set_kitchen_style(the_style)
下一個子目標確保the_kitchen.x_cabinet是設置為適當樣式的對象。
--[x_cabinet set]: the_kitchen.x_cabinet is an YXCabinet,
where x/X = wall or floor, and Y corresponds to a_style
Style ---->XCabinet
|get_cabinets()|
YKitchenStyle.get_cabinet() sets the_kitchen.x_cabinet
to a YXCabinet instance
現在的設計足以實現后置條件4,如下所示:
# --(Postcondition 4): A kitchen is displayed
# on the console as per a_style and arrange_kitchen()
the_kitchen.arrange_kitchen()
例2:對接模擬
第二個例子模擬了一艘船的對接,并附有一艘拖船。一個簡單的5×5網格就足以證明該方法。我們將指定船舶傾向于1空間的對角線運動,拖船最多可以移動2個空間,但只能垂直和水平移動。例如,下面顯示了初始條件,其中d是船的目的地,“0”和s分別表示拖船和船的初始位置。

輸出顯示如下,每當有重疊時,拖輪位置(數字)占主導地位:

讀者將認識到中介設計模式的出現。
(1)選擇根設計功能
由于對接是主要的用戶故事,因此以下是一個合適的根設計功能:
def dock(a_ship_position, a_tugboat_position,
a_dock_position):
dock()的規范主要涉及所需函數的位置,但是它們也包含一個設計約束(后置條件1)。
Intent: Simulate docking of a ship with tugboat support
Preconditions
1: The parameters are instances of Position
2: a_ship_position is the ship's initial position
3: a_tugboat_position is the tugboat's initial position
4: a_dock_position is the ship's destination
5: move_ship() is defined--the effects of a single move
with specified constraints
6: move_tugboat() is defined–the effects single move with
specified constraints
Postconditions
1. (Independence): The code defining ship and tugboat is
independent of each other
2. move_ship() is allocated
3. move_tugboat() is allocated
4. (Trajectories shown): The ship and tugboat's
trajectories are on the console for the ship traveling in
a minimal path from a_ship_position to a_dock_position
and the tugboat staying as close as possible but out of
the way.
(2)分解
前兩個子目標與前兩個后置條件相同,并且以避免獨立所禁止的相互引用的方式分配所需函數move_ship()和 move_tugboat()。這意味著船舶和拖船之間不應存在隨后的依賴關系。
# --(Postconditions 1 & 2): "Independence" observed
# AND move_ship() is allocated
# _________ ___________
# |_Docking_|<>-ship--->|___Ship____|
# |move_ship()|
# --(Postcondition 3): move_tugboat() allocated
# ______________
# Docking<>-tugboat--->|____Tugboat___|
# |move_tugboat()|
下一個子目標履行滿足超過相應的后置條件。
# --[Vessel instances have position and heading]
# ________
# |_Vessel_|
# |position|
# |_heading|
# ^
# Ship Tugboat
下一個子目標可以協調。
# --[Each Vessel is aware of its peer]
# ____________________
#
|_VesselCoordination_|<---coordination-<>Vessel
# |_____get_peer()_____|
# ^
# Docking
一旦滿足其主要需求,根設計函數通常會通過控制來構建應用程序的設計。
# --(Postcondition 4): Trajectories shown
#
# This is executed by Docking.execute()
from mediator.docking_class import Docking
Docking().execute(a_ship_position,a_tugboat_position,
a_dock_position)
UML的可視化屬性可以在敏捷項目中通過建立放置所需函數的設計函數以及將 UML嵌入到有意義的片段中來利用。這不僅僅是一個權宜之計:它減輕了復雜和龐大的UML類模型的不可讀性。
這項研究已經預見到了實驗和規模的使用,在不斷的發展。實際上,大小適中的應用程序需要封裝,同樣的設計功能驅動的過程應該能夠驅動創建包及其使用關系,就像驅動創建類及其關系一樣。
不斷發展的應用程序開發的主要障礙是可能存在不一致,效率低下以及難以看清整體。本文描述的所需 UML片段允許驗證已建立的系統符合嵌入式 UML規范。工具支持是必需的。一個工具將檢查一個程序的 UML片段的一致性,并產生一個XML UML描述(以及圖)。至少有兩種一致性要檢查。第一個驗證片段集內的自我一致性(例如,使得類A未被顯示為從B繼承,反之亦然)。第二個檢查代碼是與 UML片段一致(例如,使得承諾的類A實際上被構造)。
檢查一個已建立的系統是否符合其公布的UML的能力并不是特定于本文描述的零碎方法;然而,在持續進化的背景下,開發人員實際上更容易表達“這里是我需要實現當前沖刺的額外的UML”,而不是通過一個大的 UML類模型尋找合適的地方來編輯。
未來的工作將涉及規模項目的設計功能樹和碎片化UML的影響。根設計函數假定存在一組函數,其中一些函數本身就是設計函數等等。由此產生的設計函數層次結構可以改善在簡介中提到的 UML類模型中遞歸“伸縮”的缺乏。
[1] N. Marovac. “UML based embedded documentation for semiautomatic software development,” SIGSOFT Softw. Eng.Notes 32.5 (2007), pages 1–3, doi: 10.1145/1290993.1290997.
[2] Q. Wei, G. Danwei, X. Yaohong, F. Jingtao, H. Cheng, and J.Zhengang. “Research on software development process conjunction of scrum and UML modeling,” Proceedings - 2014 4th International Conference on Instrumentation and Measurement, Computer, Communication and Control (IMCCC 2014), 2014, pages 978–982, doi: 10.1109/IMCCC. 2014.206.
[3] R. Vargas, A. Nugroho, M. Chaudron, and J. Visser. “The use of UML class diagrams and its effect on code changeproneness,” Proceedings of the Second Edition of the International Workshop on Experiences and Empirical Studies in Software Modelling (EESSMod '12), Article No. 2, 2012, doi:10.1145/2424563.2424566.
[4] D. Karagiannis. Agile modeling method engineering. 2015,doi: 10.1145/ 2801948.2802040.
[5] B. Rumpe. “Agile Modeling with the UML,” Rissef (2002),pages 297– 309, doi: 10.1007/978-3-540-24626-8_21.
[6] E. Braude. Software design: from programming to architecture.Hoboken, NJ: J. Wiley, 2003, pp133-135.
[7] S. Ambler. Agile Modeling: Effective Practices for eXtreme Programming and the Unified Process. 2002, page 400, doi:10.1017/CBO9780511817533.018.
[8] S. Ambler. Agile/Lean Documentation: Strategies for Agile Software Development. url: http://agilemodeling.com/essays/agileDocumentation.htm# ModelsDocumentsSourceCode.
[9] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design patterns: elements of reusable object-oriented software. Reading,Mass.: Addison-Wesley, 1995.
[10] R. Pitk?nen and P. Selonen “A UML Profile for Executable and Incremental Specification-Level Modeling,” ?UML?2004 — The Unified Modeling Language. Modeling Languages and Applications Vol 3273, Lecture Notes in Computer Science, pages 158-172, doi 10.1007/978-3-540-30187-5_12.
[11] M. Weiser, "Programmers use slices when debugging,"Communications of the ACM (1982), pages 446-452, doi 10.1145/358557.358577.