Trong phát triển phần mềm, phương phát tiếp cận phát triển hướng miền được sử
dụng cho những nhu cầu phức tạp, kết nối cài đặt với một mô hình đang phát triển của
các khái niệm nghiệp vụ. DDD bao gồm một ngôn ngữ chung, các kỹ thuật và mô hình
cũng như một kiến trúc; tập trung vào mô hình hóa các vấn đề cần giải quyết. Với
DDD, các nhà phát triển có được những kỹ thuật giúp tối thiểu hóa sự phức tạp và thúc
đẩy sự cộng tác với các chuyên gia miền. Với những ưu điểm vượt trội của mình,
DDD thực sự là một phương pháp phát triển phần mềm hiện đại và hữu ích.
Công cụ hỗ trợ phát triển phần mềm hướng miền được thiết kế dựa trên các
nguyên tắc DDD, giúp tự động sinh ra phần mềm từ các mô hình lớp miền. Nhờ đó,
mà người dùng chỉ cần tập trung vào miền vấn đề và mô hình miền, toàn bộ phần mềm
(bao gồm giao diện và lưu trữ đối tượng) sẽ được sinh ra một cách tự động và nâng
cao hiệu xuất sản xuất phần mềm. Mặc dù, chức năng các phần mềm được tạo ra bởi
công cụ, còn đơn giản nhưng công cụ đã giúp cho thiết kế hướng miền DDD lại gần
hơn với các nhà phát triển phần mềm. Một trong những hạn chế của công cụ là không
có giao diện trực quan, dẫn đến những khó khăn cho người dùng cũng như tốn nhiều
thời gian trong quá trình sử dụng. Luận văn này đã giải quyết được hạn chế này bằng
việc xây dựng một ứng dụng Eclipse plug-in. Về mặt lý thuyết, ứng dụng này được
xây dựng từ ba thuật toán là thuật toán sinh phương thức cho phần mềm, thuật toán
sinh cấu hình mô-đun phần mềm và cuối cùng là thuật toán sinh cấu hình phần mềm.
Ứng dụng này là một phần của Eclipse và được tích hợp trực tiếp vào Eclipse. Qua đó,
bất kì người dùng nào sử dụng công cụ có thể tải về và cài đặt vào Eclipse của mình để
sử dụng. Điều này có ý nghĩa quan trọng giúp cho công cụ hỗ trợ phát triển phần mềm
hướng miền được sử dụng rộng rãi hơn.
Tuy nhiên, trong luận văn mới chỉ dừng lại ở việc xây dựng được ứng dụng
Eclipse plug-in giúp trực quan hóa việc sử dụng công cụ hỗ trợ phát triển phần mềm
hướng miền. Khi miền vấn đề lớn lên, mô hình miền trở nên phức tạp hơn, làm sao
biết được mô hình miền được sinh ra bởi công cụ là đúng. Vì vậy, hướng phát triển
tiếp theo của luận văn là xây dựng phương pháp kiểm tra tính đúng đắn của mô hình
miền sử dụng các ánh xạ cấu trúc lớp miền được định nghĩa cho DCSL. Hy vọng,
trong thời gian tới, tôi có thể phát triển và hoàn thiện nội dung này.
Qua việc thực hiện luận văn, tôi đã thu được rất nhiều kiến thức bổ ích về một
kiến thức phát triển phần mềm hiện đại là thiết kế hướng miền cũng như kiến thức về
xây dựng ứng dụng một ứng dụng Eclipse plug-in. Tuy nhiên, do kiến thức có hạn nên
trong luận văn không thể tránh khỏi những sai sót, khuyết điểm, tôi rất mong thầy
nhận được sự đóng góp của quý thầy cô để luận văn được hoàn thiện hơn.
73 trang |
Chia sẻ: yenxoi77 | Lượt xem: 540 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Luận văn Nghiên cứu và cài đặt một công cụ trên nền tảng Eclipse để hỗ trợ phát triển các ứng dụng Java - Vũ Thanh Hà, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
u không có lỗi xảy ra thì lớp miền đầy đủ và kết quả
thành công sẽ được trả về lần lượt cho “Plug-in Handler”, “Plug-in UI” để hiển thị
thông báo lên cho người dùng. Trường hợp có lỗi thì thông báo thất bại sẽ được trả về.
35
Quá trình được thực hiện tương tự cho việc sinh cấu hình mô-đun phần mềm,
sinh cấu hình phần mềm.
Biểu đồ lớp
Hình 2.3: Biểu đồ lớp cho plug-in
Có ba lớp chính được sử dụng trong plug-in là lớp quản lý và xử lý các sự kiện
“Plug-in Handler”. Khi nhận được một sự kiện nào đó, lớp này sẽ phân tích và gọi đến
các hệ thống con tương ứng để xử lý. Có ba hệ thống con được đóng gói trong ba thư
viện được sử dụng là sinh phương thức cho lớp miền (BspaceGenToo), sinh cấu hình
mô-đun phần mềm (MCCGenTool) và sinh cấu hình phần mềm (SWCGenTool).
Biểu đồ triển khai
Hình 2.4: Biểu đồ triển khai cho plug-in
Máy chủ update-site của Eclipse quản lý tất cả các update-site của người dùng.
Plug-in sau khi hoàn thành, nếu muốn trở thành một phần của Eclipse và người dùng
khác có thể tải về và sử dụng, thì plug-in phải được đóng gói dưới dạng một
“deployable feature” và một trang cập nhật “update-site”. Người dùng đăng ký trang
cập nhật plug-in của mình với máy chủ update-site của Eclipse. Người dùng khác
muốn cài đặt và cập nhật plug-in đó, có thể vào Help > Install New Software > điền
tên plug-in > Install.
36
2.3.2. Thuật toán sinh phương thức và Thuật toán sinh cấu hình mô-đun
Các thuật toán được đóng gói trong các thư viện tương ứng và được plug-in sử dụng.
Thuật toán sinh phương thức
Mô hình lớp miền được xây dựng sử dụng các annotation đính kèm trên lớp và
các thành phần của lớp. Ban đầu, lớp miền chưa đầy đủ, nó chỉ chứa các thuộc tính
phản ánh miền vấn đề và cần phải xây dựng các phương thức cho nó. Công việc này
chính là ánh xạ cấu trúc giữa không gian trạng thái và hành vi của một lớp miền.
Về mặt lý thuyết, các quy tắc ánh xạ cấu trúc được sử dụng [6] là:
TT
Thành phần không gian trạng thái SSEP
(thuộc tính)
Thành phần không gian hành vi
BSEP (phương thức)
DAtrr DAssoc DOpt
Property Stateful func. Property
Stateful
func.
Property Stateful func.
1
auto isNotAuto - -
type isObjectFormContructorType
type
isNotCollection
Type
- -
2
auto isNotAuto - - type
isRequiredContructorType type
isNotCollection
Type
- - type
optional isNotOptional - - type
3 mutable isMutable - - type isSetterType
4 auto isAuto - unassign type isAutoValueGenType
5 type isCollectionType
ascType
isOneMany
Asc
type isLinkAdderNewType
type isLinkAdderType
type isLinkUpdaterType
endType isOneEnd
type isLinkRemoverType
type isLinkGetterType
type isLinkSetterType
6 type isDomainType ascType
isOneOne
Asc
type isLinkAdderNewType
Mỗi dòng của bảng định nghĩa một quy tắc ánh xạ cấu trúc. Không gian trạng
thái liên quan đến hai meta-attribute là DAtrr và DAssoc. Mỗi meta-attribute có các
thuộc tính con trong cột “property” và các hàm trạng thái “stateful func.” kết hợp với
nó, giá trị của thuộc tính con bị rằng buộc bởi hàm trạng thái tương ứng. Cụ thể, tên
của mỗi hàm trạng thái mới trong không gian hành vi là một giá trị OptType. Ví dụ,
hàm isAuto có nghĩa là kiểm tra xem DAttr.auto=true; tương tự, hàm isSetterType có
nghĩa là kiểm tra DOpt.type=Setter.
Các quy tắc này quy định các phương thức gì sẽ được sinh ra từ các mô tả trong
lớp miền. Quy tắc 3 ánh xạ SSEP(DAttr, isMutable) thành BSEP(DOpt, isSetterType).
37
Quy tắc 2 ánh xạ SSEP(DAttr, isMutable ∩ isNotOptional ∩ isNotCollectionType)
thành BSEP(DOpt, isRequiredConstructorType), không gian trạng thái liên quan đến
ba thuộc tính con và hàm trạng thái của nó được cấu thành bởi phép toán logic AND
ba hàm trạng thái của ba thuộc tính con. Quy tắc 4 ánh xạ SSEP(DAttr, isAuto),
(DAssoc, unassign) thành BSEP(DOpt, isAutoValueGenType). Quy tắc 5 ánh xạ 1
SSEP(DAttr, isCollectionType), (DAssoc, isOneManyAsc ∩ isOneEnd) thành 6 BSEP:
(DOpt, isLinkAdderNewType), (DOpt, isLinkAdderType), (DOpt, isLinkUpdateType),
(DOpt, isLinkRemoverType), (DOpt, isLinkGetterType), (DOpt, isLinkSetterType).
Về mặt kỹ thuật lập trình, thuật toán Bspace được sử dụng để ánh xạ cấu trúc để
tự động sinh ra đặc tả hành vi của một lớp miền [6].
Đầu vào: c là một lớp miền có không gian trạng thái xác định
Đầu ra: c được cập nhật các phương thức
//tạo các hàm khởi tạo
1. Đặt FU = { f | f ∈ cF , ¬ isAuto (DAttr ( f )), ¬ isCollectionType (DAttr ( f ))}
2. Đặt FR = { f | f ∈ FU , ¬ isOptional (DAttr ( f ))}
3. if FU ≠ ∅ then
4. Tạo trong c hàm khởi tạo đối tượng c1 (u1,, um ) (uj ∈ FU ) //quy tắc 1
5. if FR ≠ ∅ then
6. Tạo/cập nhật trong c hàm khởi tạo được yêu cầu c2(r1,, rm ) (rk ∈ FR ) //quy tắc 2
//tạo các hàm khác
7. for all f ∈ cF do
8. Tạo trong c hàm getter cho f
9. if isMutable (DAttr ( f )) then
10. Tạo trong c hàm setter cho f //quy tắc 3
11. if def(DAssoc ( f )) then
12. if isOneManyAsc (DAssoc ( f )) AND isOneEnd (DAssoc ( f )) then
13. Tạo trong c các hàm liên quan đến liên kết cho f //quy tắc 5
14. else if isOneOneAsc (DAssoc ( f )) then
15. Tạo trong c hàm thêm liên kết mới cho f //quy tắc 6
16. if isAuto (DAttr ( f )) AND undef (DAssoc ( f )) then
17. Tạo trong c hàm tự động sinh giá trị cho thuộc tính cho f //quy tắc 4
Thuật toán lấy đầu vào là một lớp miền c có không gian trạng thái xác định, đầu
ra tự động sinh trong c tập các định nghĩa phương thức. Giả sử mỗi loại phương thức
có một mẫu có sẵn trong một ngôn ngữ lập trình hướng đối tượng đích. Hai tập FU và
FR là các tập thuộc tính thỏa mãn không gian trạng thái của hai quy tắc 1 và 2. Mỗi tập
hình thành các tham số của một hàm khởi tạo tương ứng, các hàm khởi tạo được tạo ra
ở dòng 3-4 và 5-6. Câu lệnh tạo/ cập nhật trong dòng 6 cho phép trường hợp tập thuộc
tính FU = FR thì hàm khởi tạo đối tượng ObjectFormContructor (c1) giống hàm khởi
tạo được yêu cầu RequiredContructor (c2) và (c2) không cần tạo nữa. Thay vào đó,
(c1) được gán bổ sung thêm một DOpt có kiểu RequiredContructor.
38
Vòng lặp for tại dòng 7-17 thực hiện các quy tắc ánh xạ 3-6, được áp dụng cho
từng thuộc tính riêng của lớp. Quy tắc 3 được áp dụng tại dòng 10 để tạo một hàm
setter cho thuộc tính miền f nếu nó là mutable. Quy tắc 4 được áp dụng tại dòng 17 để
tạo một hàm tự động sinh giá trị cho thuộc tính f nếu nó là một trường tự động và
không liên kết. Nếu f là một trường liên kết thì quy tắc 5 và 6 được sử dụng tại dòng
13-15 để tạo các hàm bắt buộc liên quan đến liên kết đó cho hai trường hợp là liên kết
một-một và liên kết một-nhiều.
Thuật toán sinh cấu hình mô-đun phần mềm
Mô hình kiến trúc của phần mềm COURSMAN, được sinh ra từ
DomainAppTool như sau:
Hình 2.5: Mô hình kiến trúc phần mềm COURSEMAN
Mô hình kiến trúc trên định vị mô hình miền tại lõi và hiện đang tập trung vào
việc xây dựng giao diện người dùng trong một lớp xung quanh lõi này. Nó được xây
dựng trên hai khái niệm chính [9]: lớp mô-đun dựa trên MVC (các đặc tả về mô-đun
phần mềm) và lớp chứa các mô-đun (các đặc tả về phần mềm) . Một lớp mô-đun là
một lớp có cấu trúc dựa trên MVC được sử dụng để tạo ra mô-đun. Lớp này bao gồm 3
thành phần: 1 mô hình (một lớp miền), 1 lớp View và 1 lớp Controller. Với một lớp
miền C, các lớp View và Controller là các lớp tham số hóa nghĩa là chúng là hai lớp
mẫu, có sẵn; lớp miền C có thể coi chúng như một tham số cấu hình. Tập trung chính
của một lớp mô-đun là cấu hình ba thành phần của nó. Cấu hình này được gọi là lớp
cấu hình mô-đun (MCC), cấu trúc MCC gồm 4 khái niệm: lớp mô-đun ModuleClass,
trường giao diện ViewField, mô tả về mô-đun ModuleDesc và mô tả về thuộc tính
AttributeDesc. ModuleClass được dẫn xuất từ lớp có tên Class và phản ánh lớp
miền. ViewField được dẫn xuất từ các trường trong lớp miền và đại diện cho các
trường trên giao diện hiển thị. ModuleDesc và AttributeDesc là các annotation được
gắn cho ModuleClass và ViewField. Ví dụ về một ModuleStudent của lớp Student.
39
@ModuleDescriptor(name = "ModuleStudent",
modelDesc = @ModelDesc( model = Student.class),
viewDesc = @ViewDesc(formTitle = "Form: Student", imageIcon = "Student.png",
domainClassLabel = "Student",parent=RegionName.Tools,
viewType=Region.Type.Data, view = View.class),
controllerDesc = @ ControllerDesc())
public class ModuleStudent {
@AttributeDesc(label = "title")
private String title;
@AttributeDesc(label = "id")
private int id;
@AttributeDesc(label = "name")
private String name;
@AttributeDesc(label = "address")
private Address address;
@AttributeDesc(label = "enrolments")
private Collection enrolments;
}
Thuật toán MCC [9] được sử dụng để sinh cấu hình mô-đun phần mềm trên:
Đầu vào: c: Lớp miền
Đầu ra: m: lớp cấu hình mô đun phần mềm MCC
//tạo tiêu đề cho m
1. Đặt nc = c.name, nm = “Module” + nc
2. Đặt m = Class (visibility = “public”, name = nm )
// tạo ModuleDesc cho m
3. Đặt do = ModelDesc (model = c)
4. Đặt dv = ViewDesc(formTitle= “Form:” + nc, domainClassLabel = nc)
5. Đặt dc = ControllerDesc ()
6. Đặt dm = ModuleDesc(m): modelDesc = do, viewDesc = dv, controllerDesc= dc
// tạo các ViewField cho m
7. Đặt fd = Field( class = m, visibility = “private”, name = “title”, type = String)
8. Tạo AttributeDesc ( fd ): label = nc
9. Đặt cF = { f | f : Domain Field, f ∈ c.fields} // các trường của lớp miền c
// tạo một ViewField để phản ánh mỗi trường của lớp miền c
10. AddViewFields (m, cF )
11. return m
12. Function AddViewFields (Class m, Set F):
13. for all f in F do
14. Đặt nf = f.name, tf = f.type
15. Đặt fd = Field (class = m, visibility = “private”, name = nf, type = tf )
16. Tạo AttributeDesc (fd ): label = nf
40
MCC được tạo ra hoàn toàn tự động. Tuy nhiên, một số phần (dòng 4, 6, 8,16) chứa
các thuộc tính có thể tùy chỉnh được. Chúng bao gồm các thuộc tính liên quan đến
khung nhìn như tiêu đề, nhãn, icon và các thuộc tính liên quan đến các quy tắc cấu
hình, ví dụ như nếu MCC là không có giao diện người dùng thì thuộc tính ViewDesc
(view = View ) cần được thay đổi sang ViewDesc (view = null).
Thuật toán này giúp sinh ra cấu hình mô-đun phần mềm theo cách biểu diễn các
thông tin đặc tả về mô hình, giao diện, controller dưới dạng các annotation. Nhờ đó,
công cụ hỗ trợ phát triển phần mềm DomainAppTool có thể giải mã được, và tạo ra
mô-đun phần mềm tương ứng.
2.3.3 Thuật toán sinh cấu hình phần mềm SWC
Nếu như lớp cấu hình mô-đun MCC tập trung vào mô tả cấu hình ba thành phần
chính MVC của nó thì cấu hình phần mềm SWC tập trung vào mô tả cấu hình toàn hệ
thống. Cấu trúc SWC chứa 3 khái niệm: lớp cấu hình SWC, lớp cấu hình mô-đun
ModuleClass và mô tả về hệ thống SystemDesc. Lớp cấu hình SWC được dẫn xuất từ
thứ tự vòng lặp phát triển do trong mỗi vòng lặp, một lớp cấu hình SWC cần được tạo
thành để sinh ra nguyên mẫu phần mềm. ModuleClass được dẫn xuất từ lớp có tên
Class và phản ánh lớp miền. Trong mỗi vòng lặp phát triển, SWC có thể chứa một số
lớp cấu hình mô-đun khác nhau. Các mô tả SystemDesc bao gồm : thông tin chung về
hệ thống (tên ứng dụng, logo, ngôn ngữ sử dụng), thông tin về tổ chức OrgDesc (tên
tổ chức, logo, trang web), thông tin về cơ sở dữ liệu của hệ hệ thống DSDesc (loại cơ
sở dữ liệu, url, user/password, loại kết nối,), thông tin các MCC được sử dụng
Modules, mô tả về cài đặt SetUpDesc, mô tả về bảo mật SecurityDesc. ModuleDesc
là annotation được gắn cho lớp SWC . Ví dụ về một SWC trong vòng lặp phát triển
thứ nhất:
@SystemDesc(
appName = "Courseman",
splashScreenLogo = "coursemanapplogo.jpg",
language = Language.English,
orgDesc = @OrgDesc(name = "Faculty of IT",
address = "K1m9 Nguyen Trai Street, Thanh Xuan District",
logo = "hanu.gif",
url = ""),
dsDesc = @DSDesc(type = "postgresql",
dsUrl = "//localhost:5432/domainds",
user = "admin", password = "password",
dsmType = DSM.class, domType = DOM.class,
osmType = PostgreSQLOSM.class, connType = ConnectionType.Client),
modules = { ModuleMain.class, ModuleAddress.class, ModuleStudent.class,
ModuleEnrollment.class },
sysModules = {},
setUpDesc = @SysSetUpDesc(setUpConfigType = SetUpConfig.class),
securityDesc = @SecurityDesc(isEnabled = false))
public class SWC1 {
}
Thuật toán SWC được sử dụng để sinh cấu hình phần mềm:
41
Đầu vào: c1,cN: các lớp cấu hình mô-đun (MCCs), m: ModuleMain.class,
p: tên dự án, i: số thứ tự vòng lặp phát triển
Đầu ra: s: lớp cấu hình phần mềm
//tạo tiêu đề cho s
1. Đặt ns= “SWC” + i
2. Đặt s = Class (visibility = “public”, name = ns )
3. Đặt da = AppName(p)
4. Đặt dlo = ScreenLogo(p + “logo.png” )
5. Đặt dla = Language(Language.English)
6. Đặt dm = Modules (c1,c2,cN, m)
7. Đặt do = OrgDesc(name = “name”, address = “address”, logo = “logo”, url = “url”)
8. Đặt dd = DSDesc(type = “postgresql”, dsUrl = “”, user = “”, password = “”,
dsmType = DSM.class, domType = DOM.class, omsType =
postgreSQL.class, connType = ConnectionType.client
9. Đặt dsp = SysSetUpDesc( setUpConfigType = setUpConfig.class)
10. Đặt dsc = SecurityDesc( isEnable = false)
11. Đặt ds = SystemDesc(s) : appName = da , screenLogo = dlo , language = dla ,
orgDesc = do , dsDesc = dd , setUpDesc = dsp , securityDesc = dsc
Đầu vào là các lớp cấu hình mô-đun MCC đã chọn trong vòng lặp phát triển thứ
i, tên dự án, và một ModuleMain.class là mô-đun cấu hình cho lớp main khi chạy
phần mềm. Đầu tiên, dòng : đặt tên của cấu hình phần mềm được đặt theo cú pháp
“SWC” + số thứ tự vòng lặp phát triển. Các dòng từ 3-5 thiết lập các mô tả chung của
hệ thống. Dòng 6 là các lớp mô-đun được sử dụng trong vòng lặp thứ i để tạo ra cấu
hình phần mềm. Trong khi, dòng 7 mô tả về tổ chức thì dòng 8 mô tả về cơ sở dữ liệu
của hệ thống. Dòng cuối cùng chuyển tất cả các mô tả về hệ thống ở trên thành các
thuộc tính con cho metta-attribute SystemDesc của lớp SWCi. Như vậy, lớp SWCi là
một lớp rỗng chỉ chứa các cấu hình mô tả về hệ thống. Trong các mô tả đó, một số
phần (dòng 4- 8) chứa các thuộc tính có thể tùy chỉnh được. Chúng bao gồm các thuộc
tính liên quan thông tin về tổ chức, về cơ sở dữ liệu sử dụng và các thuộc tính liên
quan đến các quy tắc cấu hình, ví dụ nếu hệ thống không sử dụng chính sách bảo mật
thì thuộc tính isEnable trong SecurityDesc sẽ có giá trị là true.
Tương tự như thuật toán MCC, thuật toán SWC giúp sinh ra cấu hình phần mềm
theo cách biểu diễn các thông tin mô tả hệ thống dưới dạng annotation gắn với lớp.
Sau đó, công cụ hỗ trợ phát triển phần mềm DomainAppTool sử dụng cấu hình này để
tự động sinh ra nguyên mẫu phần mềm tương ứng. Hiểu một cách đơn giản, đây là quá
trình mã hóa và giải mã. Quá trình mã hóa là cấu hình phần mềm phải được xây dựng
dựa trên các quy tắc nhất định để công cụ DomainAppTool có thể hiểu được, các mô
tả được biểu diễn dưới dạng thuộc tính con của các meta-attribute. Quá trình giải mã:
công cụ DomainAppTool tạo ra phần mềm tương ứng dựa trên cấu hình phần mềm.
Việc sinh cấu hình phần mềm từ cấu hình mô-đun phần mềm làm tăng tính mô-đun
hóa cũng như khả năng tái sử dụng mô-đun của hệ thống.
42
2.4. Cài đặt chi tiết thiết kế plug-in
Các bước thực hiện xây dựng plug-in như sau:
Hình 2.6: Các bước xây dựng Plug-in
Bước 1: Tạo dự án plug-in mới
Vào File > New > Other > Plug-in Development > Plug-in Project để tạo tạo
plug-in mới. Nhập tên dự án “DomainAppEclipsePlugin” .
Màn hình tiếp theo hiển thị các thông tin được yêu cầu để tạo ra plug-in:
Hình 2.7: Màn hình nhập thông tin ban đầu Plug-in mới
43
Tiếp theo chọn loại Template cho plug-in. Eclipse đã cung cấp rất nhiều loại
Template nhưng đối với dự án này, “Hello, World Command” sẽ được sử dụng.
Mẫu này giúp tạo ra các item mới trên Menu hoặc Context Menu, thêm các button
mới vào thanh công cụ,
Hình 2.8: Màn hình chọn Template cho Plug-in mới
Cuối cùng, hoàn thành dự án mới được tạo ra sử dụng Template đã chọn. Đối với mẫu
“Hello, World Command”, eclipse sẽ mặc định import bốn plug-in khác được yêu
cầu cho Template và mặc định tạo package có tên theo cú pháp “ID + handlers”.
Giao diện dự án mới như sau:
Hình 2.9: Màn hình dự án Plug-in mới đã tạo
Một dự án Eclipse plug-in sẽ chứa một vài tệp được sinh một cách tự động dựa trên
nội dung đã điền khi tạo dự án. Các tệp chính trong một Eclipse plug-in là:
44
META-INF/MANIFEST.MF: mô tả tên, phiên bản và các dependencies của
plug-in. Nếu dự án, có sử dụng thư viện bên ngoài thì cần phải được khai báo
trong tệp này.
plugin.xml: khai báo những thành phần mở rộng mà plug-in cung cấp cho
Eclipse runtime. Các nhãn biểu diễn cho lệnh/hành động/menu đều được khai
báo trong tệp plugin.xml chứ không phải trong mã nguồn lập trình cho phép
Eclipse hiển thị các menu trước khi cần tải hoặc thực thi bất kì mã nguồn nào.
Đây cũng là lý do Eclipse khởi tạo rất nhất nhờ không cần tải hay thực thi các
lớp. Tại thời điểm chạy, tùy vào hành động của người dùng mà các lớp yêu cầu
sẽ được tải.
build.properties: được sử dụng khi build dự án. Nói chung, nó có thể được bỏ
qua nhưng nếu plug-in cần thêm vào các nguồn tài nguyên mới như ảnh, tệp
thuộc tính, nội dung HTML, thì cần được khai báo ở đây, nếu không nó sẽ
không được tìm thấy.
Bước 2: Thêm vào các thư viện cần thiết cho plug-in
Tạo thư mục /lib và import các thư viện .jar : dcsl.jar, mccl.jar, swcl.jar
domainapp.jar và javaparser-core-3.2.5.jar.
Khai báo các thư viện ngoài trong tệp META-INF/MANIFEST.MF:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: MCCsGenerator
Bundle-SymbolicName: DomainAppEclipsePlugin;singleton:=true
Bundle-Version: 1.0.0.qualifier
Require-Bundle: org.eclipse.ui,
org.eclipse.jdt.core;bundle-version="3.13.50",
org.eclipse.core.runtime;bundle-version="3.13.0",
org.eclipse.core.resources;bundle-version="3.12.0",
org.eclipse.core.expressions;bundle-version="3.6.0",
org.eclipse.e4.core.di.annotations;bundle-version="1.6.0",
org.eclipse.e4.core.services;bundle-version="2.1.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .,
lib/domainapp.jar,
lib/javaparser-core-3.2.5.jar,
lib/dcsl.jar,
lib/mccl.jar,
lib/swcl.jar
Bước 3: Tạo ba lớp handlers tương ứng với ba item trên Context Menu “Source”
Plug-in này sẽ bổ sung ba item mới cho Menu “Source” là:
Generate Bspace: Khi người dùng chọn các lớp miền > Source > Generate
Bspace. Handler “GenerateBspace” sẽ sinh ra các phương thức cho các lớp
miền đã chọn.
45
Generate MCC: Khi người dùng chọn các lớp miền > Source > Generate
MCC. Handler “GenerateMCC” sẽ sinh ra các cấu hình mô-đun phần mềm
chứa các lớp miền đã chọn.
Generate SWC: Khi người dùng chọn các mô đun cấu hình phần mềm >
Source > Generate SWC. Handler “GenerateSWC” sẽ sinh ra cấu hình phần
mềm chứa các mô đun cấu hình phần mềm đã chọn.
Ba lớp handers “GenerateBspace”, “GenerateMCC” và “GenerateSWC” có cấu
tạo tương tự nhau, chỉ khác gọi đến các thư viện khác nhau khi sinh ra các tệp đâu ra.
Ví dụ, “GenerateBspace” gọi đến thư viện sinh phương thức dcsl.jar,
“GenerateMCC” gọi đến thư viện sinh cấu hình mô-đun phần mềm mccl.jar, còn
“GenerateSWC” gọi đến thư viện sinh cấu hình phần mềm swcl.jar.
Mã nguồn handlers “GenerateBspace” như sau:
public class GenerateBSpace extends AbstractHandler{
private String rootSrcPath="";
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
Shell shell = HandlerUtil.getActiveShell(event);
IStructuredSelection selections =
HandlerUtil.getCurrentStructuredSelection(event);
ICompilationUnit firstCu = (ICompilationUnit) selections.getFirstElement();
Iterator selectedElements = selections.iterator();
Object selectedElement;
while (selectedElements.hasNext()) {
selectedElement= selectedElements.next();
generateBSpaces(shell, selectedElement);
}
try {
firstCu.getJavaProject().getProject().refreshLocal(IResource.FOLDER, null);
} catch (CoreException e) {
e.printStackTrace();
}
MessageDialog.openInformation(shell, "Info", "Finish !!!");
return null;
}
private void generateBSpaces(Shell shell, Object firstElement) {
ICompilationUnit cu = (ICompilationUnit) firstElement;
String[] domainClsNameStr = new String[]{cu.getElementName().split("\\.")[0]};
try {
String pkgName = cu.getPackageDeclarations()[0].getElementName();
BSpaceGenTool bSpaceGenTool = new BSpaceGenTool(rootSrcPath, pkgName,
domainClsNameStr) ;
bSpaceGenTool.exec();
} catch (JavaModelException e) {
e.printStackTrace();
MessageDialog.openInformation(shell, "Error", e.getMessage());
}
}
46
Khi người dùng nhấn vào “Generate Bspace”, phương thức execute của handler
này được kích hoạt. Đầu tiên, lớp HandlerUtil sẽ xác định các lớp được lựa chọn
(chính là các lớp miền) cho sự kiện nhấn chuột vừa xảy ra là gì. Sau đó, vòng lặp sinh
phương thức gọi đến hàm generateBSpaces cho mỗi lớp miền được thực hiện. Hàm
generateBSpaces sẽ đọc đường dẫn đến các lớp miền và sử dụng thư viện dcsl.jar
để thực thi sinh ra các phương thức cho các lớp miền.
Các handler còn lại được xây dựng một cách tương tự.
Bước 4:Tạo các điểm mở rộng trong plug-in.xml
Mở tệp plug-in.xml > chọn tab Extensions > hiện thị mặc định bốn điểm mở rộng
cần được thiết lập :
Hình 2.10: Màn hình Extensions
Tạo các command: Chọn org.eclipse.ui.commands > New > Generic > Sửa các
thông tin: catagoryId, id, name.
Hình 2.11: Màn hình tạo các command
Khai báo các lớp hander: Chọn org.eclipse.ui.handlers > New > Generic > Sửa
các thông tin: class,commandId. Trong đó, thuộc tính class phải được trỏ đến
47
lớp handler đã tạo ở bước 3 còn commandId thì trỏ đến command tương ứng
trong org.eclipse.ui.commands.
Hình 2.12: Màn hình khai báo các handlers
Tạo các bindings: Chọn org.eclipse.ui.bindings > New > Generic > Sửa các
thông tin: commandId, contextId và sequence. Trong đó, commandId được khai
báo với command tương ứng trong org.eclipse.ui.commands; contextId thể
hiện loại component nào được sử dụng (context menu, menu, button,), mỗi
component có id mặc định khác nhau và được tra cứu trên các hướng dẫn của
PDE; cuối cùng sequence hiển thị phím tắt cho component đã tạo.
Hình 2.13: Màn hình khai báo các bindings
Thiết lập vị trí cho component mới: Chọn org.eclipse.ui.menu > New >
Generic > Sửa các thông tin locationURI bằng
“popup:org.eclipse.jdt.ui.source.menu?after=DPSeparator”: thêm thành
phần mới này vào contextmenu “Source” sau một dòng ngăn cách. Tiếp tục,
chọn “popup:org.eclipse.jdt.ui.source.menu?after=DPSeparator” vừa tạo >
New > Generic > Sửa các thông tin: commandId, id, label. Trong đó,
commandId chỉ đến command tương ứng đã tạo trong
org.eclipse.ui.commands và label sẽ hiển thị tên item mới.
48
Hình 2.14: Màn hình khai báo vị trí của các item mới trên menu
Bước 5: Chạy dự án
Chọn dự án PDE đã tạo > Run As > Eclipse Plugin
2.5. Tổng kết chương
Chương này đã mô tả chi tiết các bước thực hiện xây dựng plug-in từ thuật toán
sử dụng, mô hình thiết kế plug-in cho đến cài đặt mã nguồn. Plug-in được tạo ra đáp
ứng các yêu cầu đầu vào, hứa hẹn sẽ giúp cho công cụ hỗ trợ phát triển phần mềm
hướng miền của nhóm tác giả [5] được sử dụng rộng rãi. Ở chương tiếp theo, plug-in
sẽ được cài đặt và chạy thử nghiệm trên một ví dụ thực nghiệm. Từ đó, đánh giá được
những điểm hạn chế và đề xuất các hướng phát triển.
49
CHƯƠNG 3. CÀI ĐẶT VÀ THỰC NGHIỆM
3.1. Giới thiệu chương
Tính thực tiễn và hiệu quả của plug-in sẽ được đánh giá thông qua việc sử dụng
một ví dụ phần mềm thực tế. Cụ thể, nội dung chính của chương bao gồm đưa ra một
mô hình miền điển hình của một phần mềm thực tế để vận dụng plug-in vào việc sinh
cấu hình phần mềm, kết quả thu được là phần mềm tự động được sinh ra theo cấu hình
phần mềm đó.
3.2. Môi trường cài đặt
Plug-in tương thích với Java phiên bản 1.8 và Eclipse phiên bản Oxygen.1a
Release 4.7.1a .
3.3. Bài toán quản lý khóa học
Như đã giới thiệu trong 1.4.3, CourseMan là ví dụ điển hình được sử dụng xuyên
suốt để minh họa phương pháp thiết kế phần mềm hướng miền DDSDM cũng như ứng
dụng của công cụ DomainAppTool. Trước đây, phần mềm được tự động sinh ra nhờ
việc thực hiện một loạt các dòng lệnh command-line, gây khó khăn cho người dùng
trong việc sử dụng công cụ DomainAppTool để thiết kế mô hình miền hoàn chỉnh.
Trong chương này, luận văn cũng xin sử dụng CourseMan làm ví dụ thực nghiệm
nhưng nhờ có plug-in, việc tạo ra CourseMan trở nên dễ dàng hơn.
CourseMan là một phần mềm quản lý khóa học đơn giản bao gồm các chức năng
quản lý sinh viên, quản lý lớp, quản lý mô-đun học, quản lý sinh viên đăng ký mô-đun
học và báo cáo sinh viên theo tên. Sơ đồ ca sử dụng của CourseMan như sau:
Hình 3.1. Mô hình ca sử dụng của CourseMan
Áp dụng phương pháp phát triển phần mềm hướng miền DDSDM để xây dựng
phần mềm CourseMan:
50
Phát triển một mô hình miền khái niệm
Hình 3.2: Mô hình miền khái niệm và các mô hình con của CourseMan
Bên phải hình 3.2 là mô hình miền khái niệm của CourseMan. Mô hình này được
tạo nên bởi năm mô hình con. Các mô hình con được biểu diễn bởi hình ô-van điền
đầy và được kết nối tới một ca sử dụng ở phía bên trái. Hai mô hình con đầu tiên chứa
các lớp miền Student và Module tương ứng, mô hình con thứ ba chưa cả hai lớp miền
Student, Module và liên kết giữa chúng. Liên kết này đại diện cho sự đăng ký của
sinh viên vào các môn học.
Định nghĩa các vòng lặp phát triển
CourseMan có năm ca sử dụng tương ứng năm vòng lặp phát triển là:
STT Ca sử dụng Các mô hình con
1 Manage students Student
2 Manage course modules CourseModule
3 Manage enrollments Student, CourseModule
4 Manage student classes SClass, Student
5 Manage student-by-name reports StudentByNameReport, Student
Thực hiện các vòng lặp phát triển
Các hoạt động được thực hiện trong vòng lặp thứ nhất :
Hoạt động Mô tả Kết quả
Phân tích Phân tích ca sử dụng “Manage
students” để phát hiện ra một lớp
mới tên là Address, đại diện cho
Mô hình con với hai lớp miền Student
và Address .
51
địa chỉ của mỗi Student .
Thiết kế Áp dụng các quy luật thiết kế để
thiết kế lớp miền Student và
Address.
Một mô hình con thiết kế được thiết kế
chi tiết.
Lập trình Tự động sinh các phương thức
cho lớp miền Student và
Address sử dụng BspaceGen.
Tự động sinh cấu hình mô-
đun phần mềm từ lớp miền sử
dụng MCC
Tự động sinh cấu hình phần
mềm từ mô-đun ModuleMain
và các mô-đun vừa sinh sử
dụng SWC.
Mã nguồn hoàn chỉnh của lớp miền
Student và Address: đầy đủ các
thuộc tính và phương thức.
Mã nguồn cho cấu hình mô-đun
phần mềm là ModuleStudent và
ModuleAddress.
Mã nguồn cấu hình nguyên mẫu
phần mềm cho vòng lặp thứ nhất bao
gồm ModuleMain, ModuleStudent
và ModuleAddress
Kiểm thử Sử dụng thư viện của công cụ
DomainAppTool với cấu
hình phần mềm đã sinh để tạo
ra nguyên mẫu phần mềm
Tạo mới/chỉnh sửa/xóa các
đối tượng miền của mỗi lớp
miền để kiểm tra thiết kế của
lớp đó.
Kiểm tra bảng dữ liệu cho mỗi lớp
bao gồm: test-case, kết quả mong đợi
và kết quả thực tế.
Thực hiện một cách tương tự cho các vòng lặp sau để thu được các nguyên mẫu
phần mềm.
Tích hợp các nguyên mẫu phần mềm
Mặc dù các mô hình con của các vòng lặp có thể liên quan đến các tập con khác
nhau của các lớp miền nhưng các lớp miền cùng với nhau tạo thành cây mã nguồn
chung của mô hình miền CourseMan. Khi làm việc trên cùng một lớp miền trong các
vòng lặp khác nhau thì lớp miền cần nhận ra yêu cầu của từng lần lặp. Giả các lần lặp
không xung đột với nhau hoặc xung đột có thể giải quyết được thì một mô hình miền
hoàn chỉnh của CourseMan sẽ được xây dựng. Tuy nhiên trong phạm vi của mình,
luận văn chỉ tập trung vào việc xây dựng thuật toán tự động sinh cấu hình phần mềm
cho các vòng lặp phát triển và cài đặt các thuật toán đó trên thành phần mở rộng
Eclipse Plug-in.
Phần sau sẽ mô tả chi tiết các bước thực hiện sinh nguyên mẫu phần mềm
CourseMan sử dụng plug-in và đánh giá kết quả đạt được.
52
3.4. Kết quả thực nghiệm
Bước 1: Chuẩn bị đầu vào
Đầu vào của thực nghiệm là mô hình miền của CourseMan và lớp ModuleMain.
Mô hình miền có thể là một mô hình miền chưa hoàn chỉnh, thông qua việc thực hiện
các vòng lặp phát triển, mô hình miền sẽ được làm phong phú và hoàn thiện.
Hình 3.3. Đầu vào của thực nghiệm
Lớp ModuleMain là một lớp cấu hình mô-đun, chứa các cấu hình chung về lớp
điều khiển, giao diện hiển thị, môi trường cài đặt, của phần mềm được khởi tạo.
@ModuleDescriptor(
name = "ModuleMain",
viewDesc = @ViewDesc(
formTitle = "Course Management App: CourseMan",
imageIcon = "courseman.jpg",
view = View.class,
viewType = Type.Main,
topX = 0.5, topY = 0.0, widthRatio = 0.75f, heightRatio = 1f,
children = {
RegionName.Desktop,
RegionName.MenuBar,
RegionName.ToolBar,
RegionName.StatusBar
},
excludeComponents = { Add },
props = {
@PropertyDesc(name = PropertyName.view_toolBar_buttonIconDisplay,
valueAsString = "true", valueType = Boolean.class),
@PropertyDesc(name = PropertyName.view_toolBar_buttonTextDisplay,
valueAsString = "false", valueType = Boolean.class),
@PropertyDesc(name=PropertyName.view_searchToolBar_buttonIconDisplay,
valueAsString = "true", valueType = Boolean.class),
@PropertyDesc(name=PropertyName.view_searchToolBar_buttonTextDisplay,
valueAsString = "false", valueType = Boolean.class),
@PropertyDesc(name = PropertyName.view_lang_international,
valueAsString = "true", valueType = Boolean.class),
}
),
controllerDesc = @ControllerDesc(controller = Controller.class),
type = ModuleType.DomainMain,
setUpDesc = @SetUpDesc(postSetUp = CopyResourceFilesCommand.class)
)
public class ModuleMain { }
53
Mô hình miền của của CourseMain chứa bốn lớp miền. Mã nguồn lớp miền
Student :
@DClass(schema=”courseman”)
public class Student{
/*** STATE SPACE **/
@DAttr(name = “id”, type = Type.Integer, id = true, auto = true,
mutable = false, optional = false, min = 1.0)
private int id;
@DAttr(name = “name”, type = Type.String, length =30 , optional = false)
private String name;
@DAttr(name = “address”, type = Type.Domain, length = 20, optional = true)
@DAssoc(ascName = “student-has-address”, role = “student”,
ascType = AssocType.One2One, endType = AssocEndType.One,
associate = @Associate(type= Address.class, cardMin = 1, cardMax= 1))
private Address address;
@DAttr(name = “enrolments”, type = Type.Collection, optional = false,
serialisable = false, filter = @Select(clazz = Enrolment.class))
@DAssoc(ascName = “std-has-enrols”, role = “student”,
ascType = AssocType.One2Many, endType = AssocEndType.One,
associate= @Associate(type= Enrolment.class, cardMin=0, cardMax= 30))
private Collection enrolments;
/*** BEHAVIOUR SPACE **/
}
Và mã nguồn lớp Address :
@DClass(schema = "courseman")
public class Address {
/*** STATE SPACE **/
@DAttr(name= "id", id= true, auto= true, length= 3, mutable = false,
optional = false, type = Type.Integer)
private int id;
@DAttr(name = "cityName", type = Type.String, length = 20, optional = false)
private String cityName;
@DAttr(name="student", type=Type.Domain, optional=true, serialisable=false)
@DAssoc(ascName = "student-has-address", role = "address",
ascType = AssocType.One2One, endType = AssocEndType.One,
associate = @Associate(type= Student.class, cardMin = 1, cardMax = 1,
determinant = true))
private Student student;
/*** BEHAVIOUR SPACE **/
}
Các lớp miền được thiết kế chứa các thông tin hướng miền và được mô hình hóa
dưới dạng tập các meta-attribute gắn vào lớp và các thành viên của lớp. Ví dụ đối với
lớp Address, DClass xác định schema lưu trữ các đối tượng miền là “courseman”, các
thuộc tính khác có giá trị mặc định: serialisable = true : các đối tượng miền có thể
được lưu trữ trong không gian lưu trữ có tên schema là “courseman”; mutable = true :
54
các đối tượng miền được hiển thị dưới dạng chỉ xem nên người dùng không được phép
thay đổi trạng thái của chúng; singleton = false : có một vài đối tượng miền được tạo
ra trong thời gian chạy của phần mềm
Các lớp miền đầu vào có cấu trúc chưa hoàn thiện, thiếu các phương thức hoạt
động. Các phương thức này sẽ được tự động sinh ra trong bước tiếp theo.
Bước 2: Sinh phương thức cho lớp miền nhờ BspaceGen
Các phương thức của lớp miền được sinh ra bằng cách thực hiện: Chọn các lớp
miền > chuột phải, chọn Source > chọn Generate BSpace
Hình 3.4: Giao diện menu sinh phương thức cho lớp miền
Kết quả thu được hai lớp miền hoàn chỉnh. Mã nguồn lớp Address thu được như sau:
@DClass(schema = “courseman”)
public class Address {
/*** STATE SPACE **/
@DAttr(name = "id", id = true, auto = true, length = 3, mutable = false,
optional = false, type = Type.Integer)
private int id;
@DAttr(name = "cityName", type = Type.String, length = 20, optional = false)
private String cityName;
@DAttr(name= "student", type= Type.Domain, optional=true, serialisable= false)
@DAssoc(ascName = "student-has-address", role = "address",
ascType = AssocType.One2One, endType = AssocEndType.One,
associate = @Associate(type = Student.class, cardMin = 1, cardMax = 1,
determinant = true))
private Student student;
55
/*** BEHAVIOUR SPACE **/
private static int idCounter;
@DOpt(type = DOpt.Type.Getter)
@AttrRef(value = "id")
public int getId() {}
@DOpt(type = DOpt.Type.AutoAttributeValueGen)
@AttrRef(value = "id")
private static int genId(Integer id) {...}
@DOpt(type = DOpt.Type.Getter)
@AttrRef(value = "cityName")
public String getCityName() {...}
@DOpt(type = DOpt.Type.Setter)
@AttrRef(value = "cityName")
public void setCityName(String cityName) {...}
@DOpt(type = DOpt.Type.Getter)
@AttrRef(value = "student")
public Student getStudent() {...}
@DOpt(type = DOpt.Type.Setter)
@AttrRef(value = "student")
public void setStudent(Student student) {...}
@DOpt(type = DOpt.Type.LinkAdderNew)
@AttrRef(value = "student")
public boolean setNewStudent(Student obj) {
setStudent(obj);
return false;
}
@DOpt(type = DOpt.Type.DataSourceConstructor)
public Address(Integer id, String cityName) throws
ConstraintViolationException {
this.id = genId(id);
this.cityName = cityName;
this.student = null;
}
@DOpt(type = DOpt.Type.ObjectFormConstructor)
public Address(String cityName, Student student) throws
ConstraintViolationException {
this.id = genId(null);
this.cityName = cityName;
this.student = student;
}
@DOpt(type = DOpt.Type.RequiredConstructor)
public Address(String cityName) throws ConstraintViolationException {
this.id = genId(null);
this.cityName = cityName;
this.student = null;
}
}
Lớp miền Address mới có ba hàm khởi tạo, một hàm getter và hàm tự động sinh
giá trị cho thuộc tính id; các hàm getter và setter cho thuộc tính cityName; và ba
phương thức liên quan đến thuộc tính student. Trong ba phương thức này, hai phương
56
thức đầu là hàm getter và setter, phương thức cuối liên quan đến việc thêm mới các
đối tượng Student kết hợp với Address. Các phương thức tạo có chứa meta-attribute
tên là Dopt xác định loại phương thức.
Loại tham số được sử dụng trong mỗi hàm khởi tạo phải là kiểu đối tượng. Hàm
khởi tạo sử dụng hàm gentID, tự động tạo ra giá trị tiếp theo cho thuộc tính id. Hàm
genID này sử dụng một thuộc tính tĩnh khác là idCounter để giữ giá trị lớn nhất đến
thời điểm hiện tại của id. Thuộc tính id, không có hàm setter bởi vì nó có
DAttr.mutable = false nghĩa là giá trị của nó không được thay đổi bởi người dùng.
Phương thức setNewStudent thuộc loại Dopt.Type.LinkAdderNew đặc tả cho
liên kết 1:1 giữa Student và Address nghĩa là thêm một liên kết mới từ đối tượng
Student tới đối tượng Address đã tồn tại hoặc vừa tạo.
Bước 3: Sinh tập cấu hình mô-đun phần mềm, được xây dựng từ mô hình lớp miền.
Cách thực hiện: chọn các lớp miền sinh ra từ bước 2 > chuột phải, chọn Source >
chọn Generate MCC :
Hình 3.5: Giao diện menu sinh cấu hình mô-đun phần mềm
Các cấu hình mô-đun phần mềm được sinh ra sẽ nằm trong gói modules và có
tên được đặt theo nguyên tắc Module + Tên miền :
57
Hình 3.6: Cấu hình mô-đun phần mềm được sinh ra
Mã nguồn cấu hình mô-đun phần mềm ModuleAddress, được xây dựng từ lớp miền
Address. ModuleAddress là một lớp có kiến trúc dựa trên mô hình MVC bao gồm ba
thành phần: một mô hình (lớp miền), một khung nhìn hiển thị giao diện người dùng và
một lớp điều khiển. Mỗi lớp mô-đun có phạm vi trạng thái là một tập con của các
thuộc tính của lớp miền được định nghĩa trong đặc tả mô-đun, ở đây là lớp Address.
@ModuleDescriptor(
name = "ModuleAddress",
modelDesc = @ModelDesc(model = Address.class),
viewDesc = @ViewDesc(
formTitle = "Form: Address",
imageIcon = "Address.png",
domainClassLabel = "Address",
parent=RegionName.Tools,
view = View.class, viewType=Region.Type.Data),
controllerDesc = @ControllerDesc())
public class ModuleAddress {
@AttributeDesc(label = "title")
private String title;
@AttributeDesc(label = "id")
private int id;
@AttributeDesc(label = "cityName")
private String cityName;
@AttributeDesc(label = "student")
private Student student;
}
Bước 4: Sinh tập cấu hình phần mềm từ tập cấu hình mô-đun phần mềm.
Cách thực hiện: chọn ModuleMain và các cấu hình mô-đun phần mềm sinh ra từ
bước 3 > chuột phải, chọn Source > chọn Generate SWC. Đối với vòng lặp thứ nhất
58
sử dụng 3 cấu hình mô-đun: ModuleAddress, ModuleEnrollment, ModuleStudent.
Hình 3.7: Giao diện menu sinh cấu hình phần mềm
Cấu hình phần mềm được sinh ra sẽ nằm trong gói software.config và có tên được đặt
theo nguyên tắc SWC + Số thứ tự. Mỗi vòng lặp phát triển sử dụng các lớp cấu hình
mô-đun phần mềm khác nhau để tạo ra cấu hình riêng cho mỗi nguyên mẫu phần
mềm. Một biến tự động tăng sinh số thứ tự cho các cấu hình phần mềm. Ví du, trong
vòng lặp thứ nhất, cấu hình phần mềm có tên là SWC1, vòng lặp thứ hai là SWC2.
Hình 3.8: Cấu hình phần mềm SWC1 được sinh ra
59
Mã nguồn cấu hình phần mềm SWC1 là một lớp rỗng, có chứa các meta-attriubute mô
tả chi tiết thông tin nguyên mẫu phần mềm được tạo ra như tên ứng dụng, ngôn ngữ sử
dụng, thông tin về tổ chức, Lớp cấu hình phần mềm SWC1 cũng có kiến trúc dựa
trên mô hình MVC với ba thành phần chính là một mô hình (các lớp mô-đun chứa lớp
miền), một khung nhìn mô tả giao diện người dùng và một lớp điều khiển. Phần mô
hình chứa các lớp mô-đun sử dụng: ModuleMain, ModuleAddress, ModuleStudent,
ModuleEnrollment.
@SystemDesc(
appName = "Courseman",
splashScreenLogo = "coursemanapplogo.jpg",
language = Language.English,
orgDesc = @OrgDesc(name = "Faculty of IT",
address = "K1m9 Nguyen Trai Street, Thanh Xuan District",
logo = "hanu.gif",
url = ""),
dsDesc = @DSDesc(type = "postgresql",
dsUrl = "//localhost:5432/domainds",
user = "admin", password = "password",
dsmType = DSM.class, domType = DOM.class,
osmType = PostgreSQLOSM.class, connType = ConnectionType.Client),
modules = { ModuleMain.class, ModuleAddress.class, ModuleStudent.class,
ModuleEnrollment.class },
sysModules = {},
setUpDesc = @SysSetUpDesc(setUpConfigType = SetUpConfig.class),
securityDesc = @SecurityDesc(isEnabled = false))
public class SWC1 {}
Làm tương tự đối với vòng lặp phát triển thứ hai, sử dụng cấu hình mô-đun phần mềm
ModuleAddress, thu được cấu hình phần mềm SWC2:
Hình 3.9: Cấu hình phần mềm SWC2 được sinh ra
Cấu hình phần mềm SWC2 có mã nguồn chỉ sử dụng cấu hình mô-đun ModuleMain
và ModuleAddress :
60
@SystemDesc(
appName = "Courseman",
splashScreenLogo = "coursemanapplogo.jpg",
language = Language.English,
orgDesc = @OrgDesc(
name = "Faculty of IT",
address = "K1m9 Nguyen Trai Street, Thanh Xuan District",
logo = "hanu.gif",
url = ""),
dsDesc = @DSDesc(
type = "postgresql",
dsUrl = "//localhost:5432/domains",
user = "admin", password = "password",
dsmType = DSM.class, domType = DOM.class,
osmType = PostgreSQLOSM.class, connType = ConnectionType.Client),
modules = { ModuleMain.class, ModuleAddress.class },
sysModules = {},
setUpDesc = @SysSetUpDesc(setUpConfigType = SetUpConfig.class),
securityDesc = @SecurityDesc(isEnabled = false))
public class SWC2 {
}
Thực hiện một cách tương tự cho các vòng lặp phát triển khác, sẽ thu được các
cấu hình phần mềm riêng cho các nguyên mẫu phần mềm. Bước tiếp theo sẽ là sử
dụng công cụ DomainAppTool để sinh ra nguyên mẫu phần mềm.
Bước 5: Sinh phần mềm từ tập cấu hình phần mềm
Tạo lớp CourseMan có hàm main khai báo lớp cấu hình phần mềm SWC1.class
đã sinh và lớp thiết lập môi trường cài đặt mặc định SetupGen.class.
public class CourseMan {
public static void main(String[] args) {
final Class SwCfgCls = SWC1.class;
final Class SetUpCls = SetUpGen.class;
SoftwareAio sw = new SoftwareStandardAio(SetUpCls, SwCfgCls);
try {
sw.exec(args);
} catch (NotPossibleException e) {
e.printStackTrace();
System.exit(1);
}
}
}
Tạo cấu hình JVM mặc định khi chạy mã nguồn: chuột phải CourseMan >
RunAs > Run Configurations. Màn hình Run Configurations hiện ra. Trong panel
bên trái, nhấn chuột vào Java Application > chọn biểu tượng thêm cấu hình mới :
61
Hình 3.10: Cấu hình Run Configurations để chạy mã nguồn
Trong Arguments của panel chính, khai báo hai tham số: -Dlogging=true kích hoạt
chức năng log hệ thống, tham số thứ hai thiết lập đối tượng miền sinh ra sẽ được lưu
trong bộ nhớ chạy thay vì lưu trong cơ sở dữ liệu.
Chạy chương trình: chuột phải CourseMan > Run As > Run Configurations.
Màn hình Run Configurations hiện ra > chọn cấu hình CourseMan vừa tạo > cuối
cùng chọn Run. Giao diện phần mềm CourseMan được sinh ra như sau:
Hình 3.11: Giao diện phần mềm CourseMan
62
Vòng lặp phát triển đầu tiên sử dụng ba mô-đun cấu hình phần mềm, tương ứng với nó
là ba form giao diện được sinh ra. Ba form này nằm ở trên menu Tools. Nhấn chuột
vào từng form, giao diện hiển thị các thông tin về lớp miền tương ứng hiện ra. Trong
mỗi form, các thuộc tính được cấu hình mặc định, ví dụ tiêu đề của form vẫn để là
“title” hay tên các thuộc tính vẫn giữ nguyên như trong lớp miền. Nguyên nhân là do
hiện tại các cấu hình mô-đun chứa các meta-attribute đính kèm với lớp hoặc thành
viên của lớp dưới dạng các annotation, nếu không được thay đổi thủ công bằng tay thì
phần mềm sẽ lấy các giá trị mặc định của cấu hình mô-đun.
Hình 3.12: Giao diện các form của CourseMan trong vòng lặp đầu tiên
Thay đổi cấu hình mô-đun của lớp Address bằng cách sửa giá thuộc tính label trong
mã nguồn ModuleAddress :
@ModuleDescriptor(
name = "ModuleAddress",
modelDesc = @ModelDesc(model = Address.class),
viewDesc = @ViewDesc(
formTitle = "Form: Address",
imageIcon = "Address.png",
domainClassLabel = "Address",
parent=RegionName.Tools,
view = View.class, viewType=Region.Type.Data),
controllerDesc = @ControllerDesc())
public class ModuleAddress {
@AttributeDesc(label = "Địa chỉ")
private String title;
63
@AttributeDesc(label = "Mã thành phố")
private int id;
@AttributeDesc(label = "Tên thành phố")
private String cityName;
@AttributeDesc(label = "Sinh viên")
private Student student;
}
Chạy lại phần mềm, giao diện CourseMan chứa form Address có các nhãn thuộc tính
bằng tiếng Việt:
Hình 3.13: Giao diện các form của CourseMan sau khi sửa cấu hình mô-đun
Làm tương tự đối với các vòng lặp phát triển tiếp theo, các nguyên mẫu phần
mềm CourseMan mới được sinh ra. Kiểm thử trên từng nguyên mẫu phần mềm, nhà
phát triển phát hiện các lỗi xảy ra, các thuộc tính thiếu và quay trở lại thay đổi lớp
miền. Thông qua các vòng lặp, kết quả cuối cùng thu được một lớp miền tối ưu nhất
cho phần mềm.
Trước đây, sau khi chỉnh sửa, các lớp miền phải được đóng gói dưới dạng .jar và
phải sử dụng các lệnh command line trong terminal để thực hiện sinh ra nguyên mẫu
phần mềm. Cứ như thế, mỗi lần thay đổi lớp miền, công việc này lại được lặp lại gây
khó khăn cho các nhà phát triển phần mềm. Như vậy, nhờ có plug-in việc thực hiện
các vòng lặp không những đơn giản hơn mà còn rất hữu ích trong việc xem và thay đổi
cấu hình mô-đun phần mềm.
64
3.5. Tổng kết chương
Kết quả thực nghiệm đã chứng minh công cụ plug-in giúp cho việc phát triển
phần mềm hướng miền trở nên dễ dàng hơn. Các vòng lặp phát triển được thực hiện
đơn giản thông qua nhấn chuột thay vì thực hiện một loạt các lệnh command line. Tuy
nhiên, hệ thống vẫn còn ba hạn chế chính: một là phần mềm được thêm ra mới chỉ
dừng lại ở các chức năng cơ bản là thêm, sửa, xóa; hai là việc thay đổi cấu hình mô-
đun phần mềm hiện tại vẫn phải thực hiện thủ công bằng cách thay đổi trực tiếp mã
nguồn mà số lượng thuộc tính nhiều, sẽ gây không ít khó khăn cho nhà phát triển; ba là
chưa kiểm tra tính đúng đắn của lớp miền, cấu hình mô-đun phần mềm và cấu hình
phần mềm (hiện tại, chủ yếu thực hiện kiểm thử phần mềm sinh ra có lỗi không).
65
KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN
Trong phát triển phần mềm, phương phát tiếp cận phát triển hướng miền được sử
dụng cho những nhu cầu phức tạp, kết nối cài đặt với một mô hình đang phát triển của
các khái niệm nghiệp vụ. DDD bao gồm một ngôn ngữ chung, các kỹ thuật và mô hình
cũng như một kiến trúc; tập trung vào mô hình hóa các vấn đề cần giải quyết. Với
DDD, các nhà phát triển có được những kỹ thuật giúp tối thiểu hóa sự phức tạp và thúc
đẩy sự cộng tác với các chuyên gia miền. Với những ưu điểm vượt trội của mình,
DDD thực sự là một phương pháp phát triển phần mềm hiện đại và hữu ích.
Công cụ hỗ trợ phát triển phần mềm hướng miền được thiết kế dựa trên các
nguyên tắc DDD, giúp tự động sinh ra phần mềm từ các mô hình lớp miền. Nhờ đó,
mà người dùng chỉ cần tập trung vào miền vấn đề và mô hình miền, toàn bộ phần mềm
(bao gồm giao diện và lưu trữ đối tượng) sẽ được sinh ra một cách tự động và nâng
cao hiệu xuất sản xuất phần mềm. Mặc dù, chức năng các phần mềm được tạo ra bởi
công cụ, còn đơn giản nhưng công cụ đã giúp cho thiết kế hướng miền DDD lại gần
hơn với các nhà phát triển phần mềm. Một trong những hạn chế của công cụ là không
có giao diện trực quan, dẫn đến những khó khăn cho người dùng cũng như tốn nhiều
thời gian trong quá trình sử dụng. Luận văn này đã giải quyết được hạn chế này bằng
việc xây dựng một ứng dụng Eclipse plug-in. Về mặt lý thuyết, ứng dụng này được
xây dựng từ ba thuật toán là thuật toán sinh phương thức cho phần mềm, thuật toán
sinh cấu hình mô-đun phần mềm và cuối cùng là thuật toán sinh cấu hình phần mềm.
Ứng dụng này là một phần của Eclipse và được tích hợp trực tiếp vào Eclipse. Qua đó,
bất kì người dùng nào sử dụng công cụ có thể tải về và cài đặt vào Eclipse của mình để
sử dụng. Điều này có ý nghĩa quan trọng giúp cho công cụ hỗ trợ phát triển phần mềm
hướng miền được sử dụng rộng rãi hơn.
Tuy nhiên, trong luận văn mới chỉ dừng lại ở việc xây dựng được ứng dụng
Eclipse plug-in giúp trực quan hóa việc sử dụng công cụ hỗ trợ phát triển phần mềm
hướng miền. Khi miền vấn đề lớn lên, mô hình miền trở nên phức tạp hơn, làm sao
biết được mô hình miền được sinh ra bởi công cụ là đúng. Vì vậy, hướng phát triển
tiếp theo của luận văn là xây dựng phương pháp kiểm tra tính đúng đắn của mô hình
miền sử dụng các ánh xạ cấu trúc lớp miền được định nghĩa cho DCSL. Hy vọng,
trong thời gian tới, tôi có thể phát triển và hoàn thiện nội dung này.
Qua việc thực hiện luận văn, tôi đã thu được rất nhiều kiến thức bổ ích về một
kiến thức phát triển phần mềm hiện đại là thiết kế hướng miền cũng như kiến thức về
xây dựng ứng dụng một ứng dụng Eclipse plug-in. Tuy nhiên, do kiến thức có hạn nên
trong luận văn không thể tránh khỏi những sai sót, khuyết điểm, tôi rất mong thầy
nhận được sự đóng góp của quý thầy cô để luận văn được hoàn thiện hơn.
66
TÀI LIỆU THAM KHẢO
Tiếng Việt
1. Septeni Technology, 2015, “Domain-Driven Design Quickly bản tiếng Việt”
được dịch từ Abel Avram & Floyd Marinescu, (2006), “Domain-Driven Design
Quickly”.
Tiếng Anh
2. Eric Evans, (2013), “Domain-Driven Design: Tackling Complexity in the Heart of
Software”, pp.4-99
3. Alex Blewitt, (2014), “Mastering Eclipse Plug-in Development”, pp. 7-77.
4. Eclipse, (2018), “https://help.eclipse.org/photon/index.jsp”, last visit was on
12/11/2018.
5. Duc Minh Le, Duc-Hanh Dang. (2017), “DomainAppTool: A Domain-Driven
Software Development Tool”, Hanoi University.
6. Duc Minh Le, Duc-Hanh Dang, Viet-Ha Nguyen. (2016), “Domain-Driven
Design Using Meta-Attributes: A DSL-Based Approach”, in KSE 2016, pp.67-72.
7. Duc Minh Le, Duc-Hanh Dang, Viet-Ha Nguyen. (2018), “On Domain Driven
Design Using Annotation-Based Domain Specific Language”,
8. K. Czarnecki, (2004),“Overview of Generative Software Development,” in
Unconventional Programming Paradigms 2004, no. 3566, pp. 326–341.
9. Duc Minh Le, Duc-Hanh Dang, Viet-Ha Nguyen. (2017), “Generative Software
Module Development: A Domain-Driven Design Perspective”, in KSE 2017,
pp.85-90.
10. B.Liskov and J.Guttag, 2000, “Program development in Java: abstraction,
specification, and object-oriented design”, Pearson Education
Các file đính kèm theo tài liệu này:
- luan_van_nghien_cuu_va_cai_dat_mot_cong_cu_tren_nen_tang_ecl.pdf