Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

HIỂU COMPOSITE PATTERN QUA MỘT VÍ DỤ VỀ XML

UNDERSTANDING THE COMPOSITE PATTERN THROUGH A XML EXAMPLE


Nguyễn Công Vũ, Phạm Đại Xuân
Khoa Công nghệ thông tin, Đại học Nông Lâm TP.HCM
Email: ncv@hcmuaf.edu.vn hoặc ncv@hcm.fpt.vn

SUMMARY

Design patterns are writings of software designs that have worked well in particular situations.
These designs can be applied again in similar situations in the future by both the experienced and
the novice designer. Although documenting design patterns for reuse is time-consuming, a
considerable effort is still needed to understand and to appreciate their usefulness. In our point of
view, working examples are vital to clarify ambiguity in the description of design patterns. This
paper explains a structural design pattern known as the Composite pattern via a simple but
interesting example on processing of XML (Extensible Markup Language) files.

ĐẶT VẤN ĐỀ

Design patterns đã được đưa vào chương trình đào tạo Công nghệ thông tin tại nhiều trường đại
học trên thế giới, thậm chí vào cả những học kỳ đầu tiên (Bergin). Trong thời gian công tác tại
đại học Calgary – Canada vào quí tư năm 2003, chúng tôi nhận thấy rằng việc giới thiệu design
patterns vào chương trình học tại đây chưa thật sự hiệu quả vì nội dung chỉ tập trung vào các chi
tiết đã được đề cập trong sách giáo khoa, mà không chú trọng vào việc minh họa qua các ví dụ
cụ thể, khiến nhiều người học cảm thấy mơ hồ khó hiểu. Là người làm công tác đào tạo và
nghiên cứu, chúng tôi cũng gặp không ít khó khăn khi tiếp cận tài liệu nổi tiếng về design
patterns của Gamma và cộng sự. Điều này có thể được lý giải là do design patterns được giới
công nghiệp phần mềm đúc kết lại trong quá trình phát triển nhiều phần mềm qui mô lớn, vì vậy
giới đào tạo và nghiên cứu ở các trường đại học đi sau trong lĩnh vực này.

Bài viết này dựa trên một chương trình ví dụ về XML của Baldwin để bổ sung thêm một ví dụ
minh họa việc áp dụng Composite pattern. Đây là một design pattern thuộc nhóm cấu trúc.
Chương trình nguồn của Baldwin đã được hiệu chỉnh cho phù hợp với logic của pattern và mục
đích của bài viết. Đây là một ví dụ không phức tạp và lý thú vì XML hiện nay rất thông dụng
trong các ứng dụng Web.

COMPOSITE PATTERN

Theo Gamma và cộng sự, Composite pattern được dùng thiết kế các đối tượng thành cấu trúc
cây để biểu diễn quan hệ bộ phận – tổng thể. Composite cho phép mã khách hàng (Client) xem
các đối tượng đơn lẻ và đối tượng phức hợp từ các đối tượng đơn lẻ này đều như nhau.

Việc không phân biệt đối tượng đơn lẻ và đối tượng phức hợp làm chương trình xử lý trở nên
đơn giản hơn. Mục tiêu này đạt được bằng cách lợi dụng tính đa hình trong lập trình hướng đối
tượng: Một lớp trừu tượng (abstract class) hoặc giao diện (interface) sẽ được dùng làm đại diện
cho cả các đối tượng đơn lẻ lẫn đối tượng phức hợp. Hình 1 minh họa sơ đồ lớp UML tổng quát
của một Composite pattern. Ở đây Leaf là một lớp đơn lẻ thông thường, nhưng lớp Composite có
tính đệ qui: Nó vừa dẫn xuất từ lớp trừu tượng Component, vừa chứa một hay nhiều đối tượng
cũng thuộc lớp Component. Để hiểu rõ hơn, ta cần tìm hiểu pattern này qua các ví dụ minh họa.

Client Component

Leaf Composite

Hình 1. Sơ đồ lớp tổng quát của Composite pattern

VÍ DỤ MINH HỌA

Đã có nhiều ví dụ hay về Composite pattern, đặc biệt là của Felleisen và các cộng sự. Mặc dù
không dùng thuật ngữ Composite, các tác giả đã đưa ra nhiều ví dụ bằng ngôn ngữ lập trình
Scheme liên quan đến pattern này. Nếu bạn đọc không quen với ngôn ngữ Scheme thì có thể gặp
một ít khó khăn, nhưng đây là một giáo trình thiết kế chương trình hay, rất đáng tham khảo.
Dung đã chuyển một vài ví dụ Scheme sang Java trong bài giảng của mình tại Đại học Rice.

Composite pattern có thể được dùng để mô tả cấu trúc của một văn bản XML. Lưu ý rằng ở đây
chúng tôi không có ý định trình bày chi tiết về XML, mà chỉ chú trọng đến khía cạnh ứng dụng
của Composite pattern trong XML. Bạn đọc có thể tham khảo thêm về XML trong bài viết của
Baldwin. Dưới đây là ví dụ về một file XML (Hình 2):

<?xml version="1.0" encoding="UTF-8"?>


<bookOfPoems>
<poem PoemNumber="1" DumAtr="dum val">
<line>Roses are red,</line>
<line>Violets are blue.</line>
<line>Sugar is sweet,</line>
<line>and so are you.</line>
</poem>
<?processor ProcInstr="Dummy"?>
<!--Comment-->
<poem PoemNumber="2" DumAtr="dum val">
<line>Roses are pink,</line>
<line>Dandelions are yellow,</line>
<line>If you like Java,</line>
<line>You are a good fellow.</line>
</poem>
</bookOfPoems>

Hình 2. Một tập thơ tiếng Anh được biểu diễn dưới dạng XML

Vấn đề đặt ra là làm thế nào biểu diễn được cấu trúc XML này trong bộ nhớ máy tính để thuận
tiện cho việc xử lý. Nhận xét rằng cấu trúc này tương tự với cấu trúc của một thư mục, vì vậy nó
có thể được biểu diễn bằng một cấu trúc đệ qui dạng cây. Trong Mô Hình Đối Tượng Văn Bản
(Document Object Model – DOM) của Java, cấu trúc cây XML trong bộ nhớ được thiết kế là
một đối tượng có kiểu giao diện Document, và bản thân Document là một giao diện con của giao
diện Node, trong đó Node là kiểu dữ liệu nguyên thủy của DOM. Mọi kiểu nút trong cây, kể cả
Document, đều được dẫn xuất từ kiểu giao diện nguyên thủy này (Hình 3).

<<Interface>>
Node
0..*

<<Interface>> <<Interface>> <<Interface>> 1 <<Interface>>


CharacterData Attr Document Element

0..*

- header : String - attrs: list of Attr


<<Interface>> - root : Element - nodes : list of Nodes
Text

Hình 3. Sơ đồ minh họa Composite Element

Trong XML, Node có thể được định nghĩa đầy đủ như sau:

Một Node hoặc


• rỗng (null), hoặc
• không rỗng.
Nếu không rỗng thì Node là đối tượng thuộc một trong các kiểu dưới đây
o Attr
o CDATASection
o Comment
o Document
o DocumentFragment
o DocumentType
o Element
o Entity
o EntityReference
o Notation
o ProcessingInstruction
o Text

Trong ví dụ ở Hình 2, toàn bộ nội dung file XML được biểu diễn trong bộ nhớ bằng một Node
gốc thuộc kiểu Document. Đối tượng Document này chứa một Node duy nhất là bookOfPoems
thuộc kiểu Element. Bên trong Node bookOfPoems có hai Node poem cũng thuộc kiểu Element,
một Node processor thuộc kiểu ProcessingInstruction, và một Node chú thích thuộc kiểu
Comment. Thêm vào đó, mỗi Node poem lại chứa hai Node thuộc tính (Attr) là PoemNumber,
DumAtr, và bốn Node line thuộc kiểu Element. Bên trong mỗi Node line lại chứa một Node Text.
Các kiểu Node khác không có mặt trong ví dụ ở Hình 2.

Ở định nghĩa trên, bốn kiểu thành phần cơ bản của một cấu trúc XML là Attr, Document,
Element, và Text., trong đó Element là kiểu phức hợp. Định nghĩa của Element cụ thể như sau:

Một Node Element gồm


• tên Node,
• một danh sách gồm các Node thuộc tính Attr, và
• một danh sách các Node con

Ví dụ,

<poem PoemNumber="1" DumAtr="dum val">


<line>Roses are red,</line>
<line>Violets are blue.</line>
<line>Sugar is sweet,</line>
<line>and so are you.</line>
</poem>

Ví dụ trên minh họa một Node Element chứa hai Node Attr và bốn Node Element khác. Bên
trong mỗi Node Element line là một Node Text. Cũng cần lưu ý ở Hình 3 rằng Document không
phải là một Composite mặc dù Document chứa một Element. Lý do là Document bắt buộc phải
chứa một đối tượng Element và không được phép chứa bất kỳ đối tượng thuộc kiểu khác, vì vậy
Document không được thiết kế để chứa một Node tổng quát để tránh việc phải xử lý các tình
huống ngoại lệ. Đối với đối tượng Element thì ta cũng phải xử lý một trường hợp ngoại lệ khi áp
dụng Composite pattern vào Node này. Đó là trường hợp đối tượng Element không được phép
chứa một đối tượng thuộc kiểu Document. Điều này thể hiện tính linh hoạt khi áp dụng design
patterns vào những tình huống cụ thể.

Hình 4 là một ví dụ minh họa việc ghi nội dung của một đối tượng Document vào file. Lưu ý
rằng mã nguồn này chỉ có tính minh họa và chưa được phát triển đầy đủ để có thể xử lý tất cả các
kiểu Node trong XML. Hình 5 là mã kiểm thử tính năng của lớp DOMWriter ở Hình 4. Bạn đọc
có thể dùng Internet Explorer để kiểm tra file kết quả.

import java.io.*;
import org.w3c.dom.*;

public class DOMWriter {


private PrintWriter out;

public DOMWriter(String xmlFileName) {


try {
out = new PrintWriter(new FileOutputStream(xmlFileName));
} catch (FileNotFoundException e) {
throw new RuntimeException("Cannot open file " + xmlFileName);
}
}
public void writeXMLFile(Document document) { // Adapter pattern
writeNode(document);
}

private void writeNode(Node node) {


if (node == null)
return;

int type = node.getNodeType();


switch (type) {
case Node.DOCUMENT_NODE:
out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
writeNode(((Document)node).getDocumentElement());
out.flush();
break;

case Node.ELEMENT_NODE:
out.print('<');
out.print(node.getNodeName());

Attr[] attrs = getAttrArray(node.getAttributes());


for (int i = 0; i < attrs.length; i++) {
Attr attr = attrs[i];
out.print(' ');
out.print(attr.getNodeName());
out.print("=\"");
out.print(strToXML(attr.getNodeValue()));
out.print('"');
}
out.print('>');

NodeList children = node.getChildNodes();


for (int i = 0; i < children.getLength(); i++)
writeNode(children.item(i));

out.print("</");
out.print(node.getNodeName());
out.print('>');
break;

case Node.TEXT_NODE:
out.print(strToXML(node.getNodeValue()));
break;

case Node.PROCESSING_INSTRUCTION_NODE:
out.print("<?");
out.print(node.getNodeName());
String data = node.getNodeValue();
if (data != null && data.length() > 0) {
out.print(' ');
out.print(data);
}
out.print("?>");
break;
}
}
private String strToXML(String s) {
if (s == null)
return "";
StringBuffer str = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
switch (ch) {
case '<':
str.append("&lt;");
break;
case '>':
str.append("&gt;");
break;
case '&':
str.append("&amp;");
break;
case '"':
str.append("&quot;");
break;
default:
str.append(ch);
}
}
return str.toString();
}

private Attr[] getAttrArray(NamedNodeMap attrs) {


if (attrs == null)
return new Attr[0];
else {
int len = attrs.getLength();
Attr[] array = new Attr[len];
for (int i = 0; i < len; i++)
array[i] = (Attr)attrs.item(i);
return array;
}
}
}
Hình 4. Lớp Java dùng để ghi một đối tượng Document vào file

import javax.xml.parsers.*;
import org.w3c.dom.*;

public class DOMTest {


public static void main(String[] args) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

DocumentBuilder builder = null;


Document document = null;
try {
builder = factory.newDocumentBuilder();
document = builder.parse("BookOfPoemsInput.xml");
} catch (Exception e) {
e.printStackTrace(System.err);
}
new DOMWriter("BookOfPoemsOutput.xml").writeXMLFile(document);
}
}
Hình 5. Lớp Java dùng để kiểm thử Lớp DOMWriter ở Hình 4

KẾT LUẬN

Bài viết đã giới thiệu Composite pattern qua một ví dụ đơn giản nhưng thực tế và lý thú về việc
xử lý cấu trúc DOM của một văn bản XML bên trong bộ nhớ máy tính. Composite pattern
thường được dùng để thiết kế các cấu trúc dữ liệu phức hợp có tính đệ qui. Bài viết này là một
trong những nỗ lực nhằm diễn giải các design patterns theo một cách rõ ràng và dễ hiểu, giúp
người đọc có thể dễ dàng ứng dụng chúng trong các đề án thiết kế và phát triển phần mềm. Tuy
nhiên, ví dụ minh họa chưa thể hiện rõ nét tính đa hình trong lập trình hướng đối tượng vì đã sử
dụng lệnh điều kiện để kiểm tra kiểu cụ thể của một Node. Hơn nữa, để ghi một đối tượng
Document vào file, ta có thể dùng lệnh document.getDocumentElement().toString() để nhận biểu
diễn String của đối tượng document rồi ghi kết quả đó vào file. Một thiết kế chi tiết hơn của
DOM sẽ được trình bày trong một bài viết khác.

CẢM TẠ

Chúng tôi chân thành cảm ơn Viện Công nghệ thông tin và Công nghệ phần mềm – Đại học Liên
hiệp quốc (UNU/IIST), Macao và Khoa Kỹ nghệ Điện và Máy tính – Đại học Calgary, Canada
đã hỗ trợ chúng tôi thực hiện một số nghiên cứu liên quan đến design patterns.

TÀI LIỆU THAM KHẢO

BALDWIN, R.D., 2003. Java API for XML Processing (JAXP), Getting Started.
www.developer.com/java/other/article.php/3099751

BALDWIN, R.D., 2003. Getting Started with Java JAXP and XSL Transformations (XSLT).
www.developer.com/java/ent/article.php/3113351

BALDWIN, R.D., 2003. Java JAXP, Exposing a DOM Tree.


www.developer.com/java/ent/article.php/3292751

BERGIN, J., 2003. Karel J. Robot. A Gentle Introduction to the Art of Object-Oriented
Programming in Java. csis.pace.edu/~bergin/KarelJava2ed/Karel++JavaEdition.html

DUNG, N.X., 2003. Comp212. Intermediate Programming. www.owlnet.rice.edu/~comp212

FELLEISEN, M., FINDLER, R.B., FLATT, M., KRISHNAMURTHI, S., 2003. How To Design
Programs. The MIT Press. www.htdp.org

GAMMA, E., HELM, R., JOHNSON, R., VLISSIDES, J., 1995. Design Patterns. Elements of
Reusable Object-Oriented Software. Addison-Wesley.

You might also like