CHƯƠNG 1 : PHÁT TRIỂN PHẦN MỀM HƯỚNG MÔ HÌNH
3.3. Sinh mã sử dụng ngôn ngữ ATL
3.3.1.1. Tạo phần Model
Trong phần này, luận án phát triển các quy tắc và giải thuật sinh mã nguồn tựđộng từ các mơ hình theo phương pháp UWE (UML-based Web Engineering) để tạo ứng dụng web chẩn đoán bệnh thủy sản (benhthuysan.vn). Các quy tắc được thực nghiệm bằng ngôn ngữ chuyển đổi ATL, đảm bảo hiệu quả chuyển đổi mơ hình cho phát triển ứng dụng Web hướng mơ hình theo kiến trúc MVC. Luận án đưa phương pháp tiếp cận MDE để tạo ra các ứng dụng Web từ các mơ hình theo phương pháp UWE. Luận án đã cải tiến xây dựng một số quy tắc, giải thuật sinh code tựđộng nhằm cải tiến, nâng cao hiệu quả chuyển đổi từ mơ hình UWE sang code và xây dựng công cụ CODEGER-UWE để xây dựng các ứng dựng Web. Đây là một phương pháp với quy trình phát triển rõ ràng và tiện dụng.
Trong bài báo công bố năm 2018, tác giảđã đề xuấtbổ sung một số quy tắc chuyển đổi sang mơ hình xử lý và mơ hình trình bày, nhằm cải tiến, nâng cao hiệu quả chuyển đổi mơ hình UWE từ cơng cụ MagicUWE. Nghiên cứu đã phát triển 03 quy tắc sinh mã từ mơ hình PSM: Quy tắc CM2M sinh mã thành phần Model từ mơ hình nội dung; Quy tắc PRES2V sinh mã thành phần View từ mơ hình trình bày và Quy tắc NPROM2C sinh mã thành phần Controller từ mơ hình điều hướng và xử lý. Tương ứng với 03 quy tắc này, đã xây dựng 03 giải thuật sinh mã: CM2M, NPROM2C, PRES2V. Từ các quy tắc, giải thuật xây dựng, tác giả đã xây dựng công cụ CODEGER-UWE để hỗ trợ sinh mã nguồn cho ví dụ “Address Book”. Công cụ ODEGER-UWE đã cho phép sinh mã nguồn từ bốn mơ hình UWE tương ứng với các thành phần trong mơ hình Web MVC. Kết quả cho thấy, sinh mã tự động tốt hơn, đây là một vấn đề thiết thực, nhằm tăng năng suất, tiết kiệm thời gian, chi phí cho nhà phát triển.
Kế thừa các kết quả nghiên cứu đã được cơng bố và được trình bày tại mục 3.2 của luận án.Trong nội dung này, nghiên cứu tập trung hoàn thiện các quy tắc và giải thuật sinh code từ các mơ hình UWE bằng ATL. Như thế, chúng ta sẽ có một tập các quy tắc sinh code đầy đủ. Cụ thể, các mơ hình mức PIM trong UWE sẽ được chuyển đổi sang thành phần tươngứng trong kiến trúc MVC (hình 3.18):
- Mơ hình nội dung và mơ hình xử lý được chuyển đổi sang thành phần Model, Trong đó, mơ hình nội dung dùng để định nghĩa cấu trúc cơ sở dữ liệu, mơ hình xử lý định nghĩa các hàm, phương thức mà Model cần cung cấp cho Controller (các Service trong Spring).
- Mơ hình điều hướng được chuyển đổi sang thành phần Controller. - Mơ hình trình bày được chuyển đổi sang thành phần View.
Hình 3.18. Sơ đồ chuyển đổi mơ hình và sinh code
Module sinh code gồm có 2 thành phần, phần model-to-model cho phép chuyển đổi từ các mơ hình mức PIM sang các mơ hình mức PSM (mơ hình Java và mơ hình JSP), phần model-to-code cho phép sinh code từ các mơ hình mức PSM thu được thành các file mã nguồn .java và .jsp.
a. Chuyển đổi mơ hình nội dung
Mơ hình nội dung chứa biểu đồ lớp của ứng dụng web được mơ hình hố bằng các phần tử mơ hình UML 2.0 tiêu chuẩn. Mỗi class trong mơ hình nội dung được ánh xạ với một class trong phần model, các thuộc tính được để private và được truy cập thông qua các phương thức getter, setter.
Quy trình thực hiện gồm các bước như sau: Bước 1: Đọc mơ hình nội dung, tìm gói Content
Bước 2: Với mỗi class trong gói Content, tạo class Java với tên tương ứng, sau đó lấy ra danh sách các thuộc tính của class
Bước 3: Với mỗi thuộc tính của class, tạo thuộc tính tương ứng và các phương thức getter, setter trong class đó.
Trong kiến trúc MVC thành phần Model chứa các lớp dùng để định nghĩa cấu trúc cơ sở dữ liệu (mỗi lớp tương ứng với một bảng, mỗi trường tương ứng với một cột của bảng trong cơ sở dữ liệu). Việc chuyển đổi từ mơ hình nội dung sang mơ hình Java được thực hiện dựa trên 08 quy tắc được thể hiện chi tiết tại bảng 3.3.
Bảng 3.3. Các quy tắc chuyển đổi từ mơ hình nội dung sang mơ hình java
Tên quy tắc Phần tử mơ hình nội dung
Phần tửJava tương ứng
Package2Package Package Package
Class2Class Class Class
Tên quy tắc Phần tử mơ hình nội dung
Phần tửJava tương ứng
Property2GetterMethod Property Method (get)
Property2SetterMethod Property Method (set)
CollectionProperty2Field Property (số nhiều) Field (Set hoặc List) CollectionProperty2
GetterMethod
Property (số nhiều) Method (get) CollectionProperty2
SetterMethod
Property (số nhiều) Method (set) Cụ thể, quy tắc Package2Package được thực hiện như sau:
1 rule Package2Package { 2 from 3 p: UWE!Package ( 4 p.name = 'Content' 5 ) 6 to 7 jp: JAVA!Package ( 8 name <- thisModule.getModelName().toLower() + '.model',
9 classes <- p.packagedElement -> select(e |
10 e.oclIsTypeOf(UWE!Class))
11 -> collect(e | thisModule.Class2Class(e))
12 )
13 }
b. Chuyển đổi mơ hình xử lý
Mơ hình xử lý thể hiện các hành vi trong ứng dụng web mà controller sẽ thực hiện. Model cần cung cấp phương thức tương ứng để controller gọi khi muốn thực hiện hành vi nào đó. Phần tử ProcessClass trong mơ hình xử lý chứa thuộc tính contentClass tham chiếu đến class trong mơ hình nội dung cho biết hành vi đó ứng với model nào, mỗi hành vi ứng với một phương thức của service trong kiến trúc MVC.
Quy trình thực hiện gồm các bước như sau: - Bước 1: Đọc mơ hình, tìm gói Content.
- Bước 2: Với mỗi class trong gói Content, tạo class Service tương ứng trong Java, tìm phần tử ProcessClass có thuộc tính contentClass tham chiếu đến.
- Bước 3: Với mỗi hành vi ứng với ProcessClass, tạo phương thức tương ứng trong class Service.
Các quy tắc chuyển đổi được thực hiện bằng ngôn ngữ ATL cụ thểnhư sau:
1 module Process2Java;
2 create OUT: JAVA from IN: UWE; 3 helper def: getModelName(): String =
UWE!Model.allInstances().first().name; 4 rule Package2Package { 5 from 6 p: UWE!Package ( 7 p.name = 'Content' 8 ) 9 to 10 jp: JAVA!Package ( 11 name <- thisModule.getModelName().toLower() + '.service', 12 classes <- p.packagedElement 13 -> select(e | e.oclIsTypeOf(UWE!Class)) 14 -> select(e | UWE!ProcessClass.allInstances() 15 -> select(pc | pc.contentClass = e).size() > 0) 16 -> collect(e | thisModule.Class2Class(e))
17 )
18 }
19 lazy rule Class2Class {
20 from
21 c: UWE!Class
22 to
23 jc: JAVA!JavaClass (
24 annotation <- '@Service', 25 name <- c.name + 'Service', 26 isAbstract <- c.isAbstract, 27 isPublic <- true, 28 isInterface <- false, 29 members <- UWE!ProcessClass.allInstances() 30 -> select(pc | pc.contentClass = c) 31 -> collect(pc | pc.baseClass) 32 -> collect(bc | bc.ownedOperation 33 -> select(e | true)).flatten()
34 -> collect(o | thisModule.Operation2Method(o))
35 )
36 }
37 lazy rule Operation2Method {
38 from 39 o: UWE!Operation 40 to 41 m: JAVA!Method ( 42 name <- o.name, 43 isPublic <- true, 44 isStatic <- false, 45 parameters <- o.ownedParameter 46 -> collect(p | thisModule.Parameter2MethodParameter(p)), 47 body <- '' 48 ) 49 }
50 lazy rule Parameter2MethodParameter {
51 from 52 p: UWE!Parameter 53 to 54 mp: JAVA!MethodParameter ( 55 name <- p.name, 56 type <- p.type.name 57 ) 58 }
Gói service thuộc thành phần Model, chứa các phương thức liên quan đến dữ liệu mà ứng dụng cần gọi đến (truy xuất cơ sở dữ liệu, chuyển đổi, kiểm tra dữ liệu hợp lệ,…). Do đó, các hành vi trong mơ hình xửlý được dùng đểđịnh nghĩa các phương thức cho các service. Tác giảđã phát triển 04 quy tắc để thực hiện việc chuyển đổi từ mơ hình xử lý sang mơ hình java (Bảng 3.4)
Bảng 3.4. Quy tắc và các thành phần của mơ hình xử lý và Java
Tên quy tắc Phần tử mơ hình xử lý Phần tửJava tương ứng
Package2Package Package Package
Class2Class Class (Content class được ProcessClass tham chiếu)
Tên quy tắc Phần tử mơ hình xử lý Phần tửJava tương ứng Operation2Method Operation (của ProcessClass) Method (của Service class) Parameter2MethodParameter Parameter (của các Operation) MethodParameter 3.3.1.2. Tạo thành phần Controller
Mơ hình điều hướng thể hiện luồng chuyển hướng giữa các trang web, mỗi phần tử NavigationClass trong mơ hình điều hướng ứng với một nút trong luồng điều hướng và từđó ứng với một u cầu từ phía client mà controller cần xử lý. Thuộc tính contentClass tham chiếu đến class mơ hình nội dung và cho biết yêu cầu tương ứng được xử lý bởi lớp Controller nào.
Quy trình thực hiện gồm các bước như sau: - Bước 1: Đọc mơ hình, tìm gói Content.
- Bước 2: Với mỗi class trong gói Content, tạo class Controller tương ứng trong Java, tìm phần tử NavigationClass có thuộc tính contentClass tham chiếu đến.
- Bước 3: Với mỗi NavigationClass, tạo phương thức trong class Controller với annotation @RequestMapping ứng với yêu cầu từ client.
Các quy tắc chuyển đổi từmơ hình điều hướng sang mơ hình Java sử dụng ngơn ngữ ATL:
1 module Navigation2Java;
2 create OUT : JAVA from IN : UWE; 3 helper def: getModelName(): String =
UWE!Model.allInstances().first().name; 4 rule Package2Package { 5 from 6 p: UWE!Package ( 7 p.name = 'Content' 8 ) 9 to 10 jp: JAVA!Package ( 11 name <- thisModule.getModelName().toLower() + '.controller', 12 classes <- p.packagedElement 13 -> select(e | e.oclIsTypeOf(UWE!Class)) 14 -> select(e | UWE!NavigationClass.allInstances()
15 -> select(nc | nc. contentClass = e).size() > 0) 16 -> collect(e | thisModule. Class2Class(e))
17 )
18 }
19 lazy rule Class2Class {
20 from
21 c: UWE!Class
22 to
23 jc: JAVA!JavaClass (
24 annotation <- '@Controller',
25 name <- c.name + 'Controller',
26 isAbstract <- c.isAbstract, 27 isPublic <- true, 28 isInterface <- false, 29 members <- UWE!NavigationClass.allInstances() 30 -> select(nc | nc.contentClass = c) 31 -> collect(pc | pc.baseClass) 32 -> collect(bc | thisModule.Class2Method(bc)) 33 ) 34 }
35 lazy rule Class2Method {
36 from 37 c: UWE!Class 38 to 39 m: JAVA!Method ( 40 annotation <- '@GetMapping("/' + c.name.toLower() + '")', 41 type <- 'String', 42 name <- c.name.toLower(), 43 isPublic <- true, 44 isStatic <- false, 45 parameters <- Sequence, 46 body <- 'return "";' 47 ), 48 param: JAVA!MethodParameter ( 49 name <- 'model', 50 type <- 'Model' 51 ) 52 }
Trong kiến trúc MVC thành phần Controller, nhận các yêu cầu từ phía client và gọi các phương thức cần thiết (thường do các service trong thành phần Model cung cấp) để thực hiện yêu cầu sử dụng chú thích @RequestMapping đểxác định khi nhận được một yêu cầu, phương thức nào của controller sẽđược thực thi. Trong mơ hình điều hướng, mỗi NavigationClass thể hiện một nút trong luồng điều hướng giữa các trang web tương ứng với một yêu cầu từphía client nên được dùng đểđịnh nghĩa các phương thức cho controller. Danh sách các quy tắc chuyển đổi từmơ hình điều hướng sang mơ hình Java được thể hiện tại Bảng 3.5.
Bảng 3.5. Các quy tắc chuyển đổi mơ hình điều hướng sang mơ hình Java
Tên quy tắc Phần tửmơ hình điều hướng Phần tửJava tương ứng
Package2Package Package Package
Class2Class Class (Content class được NavigationClass tham chiếu)
Class (Controller class) Class2Method NavigationClass Method (của Controller
class) 3.3.1.3. Tạo thành phần View
Mơ hình trình bày cung cấp một tập các phần tử giao diện dùng để mơ hình hố giao diện người dùng được hiển thị trên ứng dụng web. Phần tử PresentationPage đại diện cho một trang web, trong đó có thể chứa các phần tử giao diện khác: Text,
Image, Button, Anchor, TextInput, FileUpload,…
Quy trình thực hiện gồm các bước như sau:
- Bước 1: Tìm các phần tử PresentationPage, tạo phần tử Root tương ứng trong
JSP.
- Bước 2: Với mỗi phần tử PresentationPage, tìm các phần tử giao diện chứa trong
đó và chuyển thành phần tử giao diện tương ứng trong JSP. Các quy tắc chuyển mơ hình trình bày thành mơ hình JSP, cụ thểnhư sau:
1 module Presentation2JSPNew;
2 create OUT: JSP from IN: UWE;
3 rule PresentationPage2JSP { 4 from 5 pp: UWE!PresentationPage 6 to 7 root: JSP!Root ( 8 documentName <- pp.name + '.jsp', 9 children <- Sequence{htmlNode}
10 ), 11 htmlNode: JSP!Element ( 12 name <- 'html', 13 children <- Sequence{headNode} 14 .append(UWE!PresentationElement.allInstances() 15 -> select(pe | not pe.oclIsTypeOf(UWE!PresentationPage))
16 -> collect(pe | if pe.oclIsTypeOf(UWE!PresentationGroup) then
17 thisModule.PresentationGroup2JSP(pe) 18 else if 19 thisModule.PresentationAlternative2JSP(pe) 20 else if pe.oclIsTypeOf(UWE!Text) 21 then 22 thisModule.Text2JSP(pe) 23 else if 24 pe.oclIsTypeOf(UWE!Image) 25 then 26 thisModule.Image2JSP(pe) 27 else if 28 pe.oclIsTypeOf(UWE!Button) 29 then 30 thisModule.Button2JSP(pe) 31 else if 32 pe.oclIsTypeOf(UWE!Anchor) 33 then 34 thisModule.Anchor2JSP(pe) 35 else if 36 pe.oclIsTypeOf(UWE!TextInput) 37 then 38 thisModule.TextInput2JSP(pe) 39 else 40 thisModule.FileUpload2JSP(pe) 41 endif 42 endif 43 endif 44 endif 45 endif 46 endif))
47 ), 48 headNode: JSP!Element ( 49 name <- 'head', 50 children <- Sequence{titleNode} 51 ), 52 titleNode: JSP!Element ( 53 name <- 'title', 54 children <- Sequence{textNode} 55 ), 56 textNode: JSP!TextNode ( 57 value <- pp.name 58 ) 59 }
Có 08 quy tắc được xây dựng để chuyển đổi mơ hình trình bày sang mơ hình JSP được thể hiện ở bảng 3.6
Bảng 3.6. Các quy tắc chuyển đổi mơ hình trình bày sang JSP:
Tên quy tắc Phần tử mơ hình trình
bày ThẻJSP tương ứng
PresentationPage2JSP PresentationPage html và head PresentationGroup2JSP PresentationGroup div
Text2JSP Text span
Image2JSP Image img
Button2JSP Button button
Anchor2JSP Anchor a
TextInput2JSP TextInput input (type=”text”)
FileUpload2JSP FileUpload input (type=”file”)
3.3.2. Cấu trúc mơ hình PSM theo kiến trúc MVC
3.3.2.1. Thành phần model
Mơ hình nội dung biểu diễn các lớp cơ bản trong ứng dụng bệnh thủy sản, sau khi áp dụng các quy tắc chuyển đổi được tác giả xây dựng, mơ hình Java được sinh ra với cấu trúc mô tả trong bảng 3.7. Trong kiến trúc MVC, gói model thuộc thành phần Model, chứa các lớp dùng để định nghĩa cấu trúc cơ sở dữ liệu (mỗi lớp tương ứng với một bảng, mỗi trường tương ứng với một cột của bảng trong cơ sở dữ liệu).
Bảng 3.7. Cấu trúc thành phần model
<?xml version="1.0" encoding="ISO-8859-1"?>
<java:Package xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:java="http:///java.ecore" name="FishDisease.model"> <classes name="User" isPublic="true"/>
<classes name="Guess" isPublic="true"/> <classes name="Disease" isPublic="true">
<members xsi:type="java:Field" name="name" type="String"/> <members xsi:type="java:Method" name="getName" isPublic="true" type="String" body="return name;"/>
<members xsi:type="java:Method" name="setName" isPublic="true" body="this.name = name;">
<parameters name="name" type="String"/> </members>
</classes>
<classes name="Diagnosis" isPublic="true">
<members xsi:type="java:Field" name="sympton" type="String"/> <members xsi:type="java:Field" name="name" type="String"/> <members xsi:type="java:Field" name="diseaseInfo" type="String"/> <members xsi:type="java:Method" name="getSympton" isPublic="true" type="String" body="return sympton;"/>
<members xsi:type="java:Method" name="getName" isPublic="true" type="String" body="return name;"/>
<members xsi:type="java:Method" name="getDiseaseInfo" isPublic="true" type="String" body="return diseaseInfo;"/>
<members xsi:type="java:Method" name="setSympton" isPublic="true" body="this.sympton = sympton;">
<parameters name="sympton" type="String"/> </members>
<members xsi:type="java:Method" name="setName" isPublic="true" body="this.name = name;">
<parameters name="name" type="String"/> </members>
<members xsi:type="java:Method" name="setDiseaseInfo" isPublic="true" body="this.diseaseInfo = diseaseInfo;">
</members> </classes>
<classes name="Review" isPublic="true">
<members xsi:type="java:Field" name="rating" type="Float"/> <members xsi:type="java:Field" name="comment" type="String"/> <members xsi:type="java:Method" name="getRating" isPublic="true" type="Float" body="return rating;"/>
<members xsi:type="java:Method" name="getComment" isPublic="true" type="String" body="return comment;"/>
<members xsi:type="java:Method" name="setRating" isPublic="true" body="this.rating = rating;">
<parameters name="rating" type="Float"/> </members>
<members xsi:type="java:Method" name="setComment" isPublic="true" body="this.comment = comment;">
<parameters name="comment" type="String"/> </members>
</classes> </java:Package>
Trong kiến trúc MVC, gói service cũng thuộc thành phần Model, chứa các phương thức liên quan đến dữ liệu mà ứng dụng cần gọi đến (truy xuất cơ sở dữ liệu, chuyển đổi, kiểm tra dữ liệu hợp lệ,…). Do đó, các hành vi trong mơ hình xử lý được dùng để định nghĩa các phương thức cho các service, nội dung các phương thức được để trống và cần viết code đểphương thức hiện chức năng mong muốn. Kết quả chuyển đổi mơ hình xử lý thành mơ hình Java thu được có cấu trúc như trong bảng 3.8.
Bảng 3.8. Cấu trúc thành phần service
<?xml version="1.0" encoding="ISO-8859-1"?>
<java:Package xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:java="http:///java.ecore" name="FishDisease.service">
<classes name="DiseaseService" annotation="@Service" isPublic="true"/> <classes name="DiagnosisService" annotation="@Service" isPublic="true"> <members xsi:type="java:Method" name="DataValidation" isPublic="true" body="">
<parameters name="name" type="String"/> <parameters name="sympton" type="String"/>
</members>
<members xsi:type="java:Method" name="SaveDiagnosis" isPublic="true" body=""/>
</classes>
<classes name="ReviewService" annotation="@Service" isPublic="true"> <members xsi:type="java:Method" name="SaveReview" isPublic="true" body=""/>
</classes> </java:Package>
3.3.2.3. Thành phần Controler
Thành phần controller nhận các yêu cầu từ phía client và gọi các phương thức cần thiết (thường do các service trong thành phần Model cung cấp) để thực hiện yêu cầu. Spring sử dụng chú thích @RequestMapping để xác định khi nhận được một yêu cầu, phương thức nào của controller sẽ được thực thi. Trong mơ hình điều hướng, mỗi NavigationClass thể hiện một nút trong luồng điều hướng giữa các trang web tương ứng với một yêu cầu từphía client nên được dùng đểđịnh nghĩa các phương thức cho controller. Áp dụng các quy tắc chuyển vào mơ hình điều hướng của ứng dụng bệnh thủy sản, mơ hình Java thu được có cấu trúc như trong bảng 3.9.