Culture Name Language-Country/Region
"" (empty string)- invariant culture
af-Afrikaans
af-ZA-
Afrikaans - South Africa
sq-
Albanian
sq-AL-
Albanian - Albania
ar-
Arabic
ar-DZ-
Arabic - Algeria
ar-BH-
Arabic - Bahrain
ar-EG-
Arabic - Egypt
ar-IQ-
Arabic - Iraq
ar-JO-
Arabic - Jordan
ar-KW-
Arabic - Kuwait
ar-LB-
Arabic - Lebanon
ar-LY-
Arabic - Libya
ar-MA-
Arabic - Morocco
ar-OM-
Arabic - Oman
ar-QA-
Arabic - Qatar
ar-SA-
Arabic - Saudi Arabia
ar-SY-
Arabic - Syria
ar-TN-
Arabic - Tunisia
ar-AE-
Arabic - United Arab Emirates
ar-YE-
Arabic - Yemen
hy-
Armenian
hy-AM-
Armenian - Armenia
az-
Azeri
az-AZ-Cyrl-
Azeri (Cyrillic) - Azerbaijan
az-AZ-Latn-
Azeri (Latin) - Azerbaijan
eu-
Basque
eu-ES-
Basque - Basque
be-
Belarusian
be-BY-
Belarusian - Belarus
bg-
Bulgarian
bg-BG-
Bulgarian - Bulgaria
ca-
Catalan
ca-ES-
Catalan - Catalan
zh-HK-
Chinese - Hong Kong SAR
zh-MO-
Chinese - Macao SAR
zh-CN-
Chinese - China
zh-CHS-
Chinese (Simplified)
zh-SG-
Chinese - Singapore
zh-TW-
Chinese - Taiwan
zh-CHT-
Chinese (Traditional)
hr-
Croatian
hr-HR-
Croatian - Croatia
cs-
Czech
cs-CZ-
Czech - Czech Republic
da-
Danish
da-DK-
Danish - Denmark
div-
Dhivehi
div-MV-
Dhivehi - Maldives
nl-
Dutch
nl-BE-
Dutch - Belgium
nl-NL
Dutch - The Netherlands
en
English
en-AU-
English - Australia
en-BZ
English - Belize
en-CA
English - Canada
en-CB
English - Caribbean
en-IE
English - Ireland
en-JM
English - Jamaica
en-NZ
English - New Zealand
en-PH
English - Philippines
en-ZA
English - South Africa
en-TT
English - Trinidad and Tobago
en-GB
English - United Kingdom
en-US
English - United States
en-ZW
English - Zimbabwe
et
Estonian
et-EE
Estonian - Estonia
fo
Faroese
fo-FO
Faroese - Faroe Islands
fa
Farsi
fa-IR
Farsi - Iran
fi
Finnish
fi-FI
Finnish - Finland
fr
French
fr-BE
French - Belgium
fr-CA
French - Canada
fr-FR
French - France
fr-LU
French - Luxembourg
fr-MC
French - Monaco
fr-CH
French - Switzerland
gl
Galician
gl-ES
Galician - Galician
ka
Georgian
ka-GE
Georgian - Georgia
de
German
de-AT
German - Austria
de-DE
German - Germany
de-LI
German - Liechtenstein
de-LU
German - Luxembourg
de-CH
German - Switzerland
el
Greek
el-GR
Greek - Greece
gu
Gujarati
gu-IN
Gujarati - India
he
Hebrew
he-IL
Hebrew - Israel
hi
Hindi
hi-IN
Hindi - India
hu
Hungarian
hu-HU
Hungarian - Hungary
is
Icelandic
is-IS
Icelandic - Iceland
id
Indonesian
id-ID
Indonesian - Indonesia
it
Italian
it-IT
Italian - Italy
it-CH
Italian - Switzerland
ja
Japanese
ja-JP
Japanese - Japan
kn
Kannada
kn-IN
Kannada - India
kk
Kazakh
kk-KZ
Kazakh - Kazakhstan
kok
Konkani
kok-IN
Konkani - India
ko
Korean
ko-KR
Korean - Korea
ky
Kyrgyz
ky-KG
Kyrgyz - Kyrgyzstan
lv
Latvian
lv-LV
Latvian - Latvia
lt
Lithuanian
lt-LT
Lithuanian - Lithuania
mk
Macedonian
mk-MK
Macedonian - Former Yugoslav Republic of Macedonia
ms
Malay
ms-BN
Malay - Brunei
ms-MY
Malay - Malaysia
mr
Marathi
mr-IN
Marathi - India
mn
Mongolian
mn-MN
Mongolian - Mongolia
no
Norwegian
nb-NO
Norwegian (Bokmål) - Norway
nn-NO
Norwegian (Nynorsk) - Norway
pl
Polish
pl-PL
Polish - Poland
pt
Portuguese
pt-BR
Portuguese - Brazil
pt-PT
Portuguese - Portugal
pa
Punjabi
pa-IN
Punjabi - India
ro
Romanian
ro-RO
Romanian - Romania
ru
Russian
ru-RU
Russian - Russia
sa
Sanskrit
sa-IN
Sanskrit - India
sr-SP-Cyrl
Serbian (Cyrillic) - Serbia
sr-SP-Latn
Serbian (Latin) - Serbia
sk
Slovak
sk-SK
Slovak - Slovakia
sl
Slovenian
sl-SI
Slovenian - Slovenia
es
Spanish
es-AR
Spanish - Argentina
es-BO
Spanish - Bolivia
es-CL
Spanish - Chile
es-CO
Spanish - Colombia
es-CR
Spanish - Costa Rica
es-DO
Spanish - Dominican Republic
es-EC
Spanish - Ecuador
es-SV
Spanish - El Salvador
es-GT
Spanish - Guatemala
es-HN
Spanish - Honduras
es-MX
Spanish - Mexico
es-NI
Spanish - Nicaragua
es-PA
Spanish - Panama
es-PY
Spanish - Paraguay
es-PE
Spanish - Peru
es-PR
Spanish - Puerto Rico
es-ES
Spanish - Spain
es-UY
Spanish - Uruguay
es-VE
Spanish - Venezuela
sw
Swahili
sw-KE
Swahili - Kenya
sv
Swedish
sv-FI
Swedish - Finland
sv-SE
Swedish - Sweden
syr
Syriac
syr-SY
Syriac - Syria
ta
Tamil
ta-IN
Tamil - India
tt
Tatar
tt-RU
Tatar - Russia
te
Telugu
te-IN
Telugu - India
th
Thai
th-TH
Thai - Thailand
tr
Turkish
tr-TR
Turkish - Turkey
uk
Ukrainian
uk-UA
Ukrainian - Ukraine
ur
Urdu
ur-PK
Urdu - Pakistan
uz
Uzbek
uz-UZ-Cyrl
Uzbek (Cyrillic) - Uzbekistan
uz-UZ-Latn
Uzbek (Latin) - Uzbekistan
vi
Vietnamese
vi-VN
Vietnamese - Vietnam
------*%*%##$@$##%*%*----*%*%##$@$##%*%*----*%*%##$@$##%*%*----*%*%##$@$##%*%*------
Thuốc để giải ưu phiền là công việc, thú vui chỉ là liệu thuốc tạm thời..
---------------------------------------------------------------------------------------------------------------
Thứ Năm, 10 tháng 6, 2010
Thứ Tư, 17 tháng 3, 2010
RegExp Validation
// returns true if the string is empty
function isEmpty(str){
return (str == null) || (str.length == 0);
}
// returns true if the string is a valid email
function isEmail(str){
if(isEmpty(str)) return false;
var re = /^[^\s()<>@,;:\/]+@\w[\w\.-]+\.[a-z]{2,}$/i
return re.test(str);
}
// returns true if the string only contains characters A-Z or a-z
function isAlpha(str){
var re = /[^a-zA-Z]/g
if (re.test(str)) return false;
return true;
}
// returns true if the string only contains characters 0-9
function isNumeric(str){
var re = /[\D]/g
if (re.test(str)) return false;
return true;
}
// returns true if the string only contains characters A-Z, a-z or 0-9
function isAlphaNumeric(str){
var re = /[^a-zA-Z0-9]/g
if (re.test(str)) return false;
return true;
}
// returns true if the string's length equals "len"
function isLength(str, len){
return str.length == len;
}
// returns true if the string's length is between "min" and "max"
function isLengthBetween(str, min, max){
return (str.length >= min)&&(str.length <= max);
}
// returns true if the string is a US phone number formatted as...
// (000)000-0000, (000) 000-0000, 000-000-0000, 000.000.0000, 000 000 0000, 0000000000
function isPhoneNumber(str){
var re = /^\(?[2-9]\d{2}[\)\.-]?\s?\d{3}[\s\.-]?\d{4}$/
return re.test(str);
}
// returns true if the string is a valid date formatted as...
// mm dd yyyy, mm/dd/yyyy, mm.dd.yyyy, mm-dd-yyyy
function isDate(str){
var re = /^(\d{1,2})[\s\.\/-](\d{1,2})[\s\.\/-](\d{4})$/
if (!re.test(str)) return false;
var result = str.match(re);
var m = parseInt(result[1]);
var d = parseInt(result[2]);
var y = parseInt(result[3]);
if(m < 1 || m > 12 || y < 1900 || y > 2100) return false;
if(m == 2){
var days = ((y % 4) == 0) ? 29 : 28;
}else if(m == 4 || m == 6 || m == 9 || m == 11){
var days = 30;
}else{
var days = 31;
}
return (d >= 1 && d <= days);
}
// returns true if "str1" is the same as the "str2"
function isMatch(str1, str2){
return str1 == str2;
}
// returns true if the string contains only whitespace
// cannot check a password type input for whitespace
function isWhitespace(str){ // NOT USED IN FORM VALIDATION
var re = /[\S]/g
if (re.test(str)) return false;
return true;
}
// removes any whitespace from the string and returns the result
// the value of "replacement" will be used to replace the whitespace (optional)
function stripWhitespace(str, replacement){// NOT USED IN FORM VALIDATION
if (replacement == null) replacement = '';
var result = str;
var re = /\s/g
if(str.search(re) != -1){
result = str.replace(re, replacement);
}
return result;
}
// validate the form
function validateForm(f, preCheck){
var errors = '';
if(preCheck != null) errors += preCheck;
var i,e,t,n,v;
for(i=0; i < f.elements.length; i++){
e = f.elements[i];
if(e.optional) continue;
t = e.type;
n = e.name;
v = e.value;
if(t == 'text' || t == 'password' || t == 'textarea'){
if(isEmpty(v)){
errors += n+' cannot be empty.\n'; continue;
}
if(v == e.defaultValue){
errors += n+' cannot use the default value.\n'; continue;
}
if(e.isAlpha){
if(!isAlpha(v)){
errors += n+' can only contain characters A-Z a-z.\n'; continue;
}
}
if(e.isNumeric){
if(!isNumeric(v)){
errors += n+' can only contain characters 0-9.\n'; continue;
}
}
if(e.isAlphaNumeric){
if(!isAlphaNumeric(v)){
errors += n+' can only contain characters A-Z a-z 0-9.\n'; continue;
}
}
if(e.isEmail){
if(!isEmail(v)){
errors += v+' is not a valid email.\n'; continue;
}
}
if(e.isLength != null){
var len = e.isLength;
if(!isLength(v,len)){
errors += n+' must contain only '+len+' characters.\n'; continue;
}
}
if(e.isLengthBetween != null){
var min = e.isLengthBetween[0];
var max = e.isLengthBetween[1];
if(!isLengthBetween(v,min,max)){
errors += n+' cannot contain less than '+min+' or more than '+max+' characters.\n'; continue;
}
}
if(e.isPhoneNumber){
if(!isPhoneNumber(v)){
errors += v+' is not a valid US phone number.\n'; continue;
}
}
if(e.isDate){
if(!isDate(v)){
errors += v+' is not a valid date.\n'; continue;
}
}
if(e.isMatch != null){
if(!isMatch(v, e.isMatch)){
errors += n+' does not match.\n'; continue;
}
}
}
if(t.indexOf('select') != -1){
if(isEmpty(e.options[e.selectedIndex].value)){
errors += n+' needs an option selected.\n'; continue;
}
}
if(t == 'file'){
if(isEmpty(v)){
errors += n+' needs a file to upload.\n'; continue;
}
}
}
if(errors != '') alert(errors);
return errors == '';
}
/*
The following elements are not validated...
button type="button"
checkbox type="checkbox"
hidden type="hidden"
radio type="radio"
reset type="reset"
submit type="submit"
All elements are assumed required and will only be validated for an
empty value or defaultValue unless specified by the following properties.
isEmail = true; // valid email address
isAlpha = true; // A-Z a-z characters only
isNumeric = true; // 0-9 characters only
isAlphaNumeric = true; // A-Z a-z 0-9 characters only
isLength = number; // must be exact length
isLengthBetween = array; // [lowNumber, highNumber] must be between lowNumber and highNumber
isPhoneNumber = true; // valid US phone number. See "isPhoneNumber()" comments for the formatting rules
isDate = true; // valid date. See "isDate()" comments for the formatting rules
isMatch = string; // must match string
optional = true; // element will not be validated
*/
// ||||||||||||||||||||||||||||||||||||||||||||||||||
// --------------------------------------------------
// ||||||||||||||||||||||||||||||||||||||||||||||||||
// All of the previous JavaScript is coded to process
// any form and should be kept in an external file if
// multiple forms are being processed.
// This function configures the previous
// form validation code for this form.
function configureValidation(f){
f.firstname.isAlphaNumeric = true;
f.lastname.isAlphaNumeric = true;
f.email.isEmail = true;
f.phone.isPhoneNumber = true;
f.birthday.isDate = true;
f.password1.isLengthBetween = [4,255];
f.password2.isMatch = f.password1.value;
f.comments.optional = true;
var preCheck = (!f.infohtml.checked && !f.infocss.checked && !f.infojs.checked) ? 'select at least one checkbox.\n' : null;
return validateForm(f, preCheck);
}
function isEmpty(str){
return (str == null) || (str.length == 0);
}
// returns true if the string is a valid email
function isEmail(str){
if(isEmpty(str)) return false;
var re = /^[^\s()<>@,;:\/]+@\w[\w\.-]+\.[a-z]{2,}$/i
return re.test(str);
}
// returns true if the string only contains characters A-Z or a-z
function isAlpha(str){
var re = /[^a-zA-Z]/g
if (re.test(str)) return false;
return true;
}
// returns true if the string only contains characters 0-9
function isNumeric(str){
var re = /[\D]/g
if (re.test(str)) return false;
return true;
}
// returns true if the string only contains characters A-Z, a-z or 0-9
function isAlphaNumeric(str){
var re = /[^a-zA-Z0-9]/g
if (re.test(str)) return false;
return true;
}
// returns true if the string's length equals "len"
function isLength(str, len){
return str.length == len;
}
// returns true if the string's length is between "min" and "max"
function isLengthBetween(str, min, max){
return (str.length >= min)&&(str.length <= max);
}
// returns true if the string is a US phone number formatted as...
// (000)000-0000, (000) 000-0000, 000-000-0000, 000.000.0000, 000 000 0000, 0000000000
function isPhoneNumber(str){
var re = /^\(?[2-9]\d{2}[\)\.-]?\s?\d{3}[\s\.-]?\d{4}$/
return re.test(str);
}
// returns true if the string is a valid date formatted as...
// mm dd yyyy, mm/dd/yyyy, mm.dd.yyyy, mm-dd-yyyy
function isDate(str){
var re = /^(\d{1,2})[\s\.\/-](\d{1,2})[\s\.\/-](\d{4})$/
if (!re.test(str)) return false;
var result = str.match(re);
var m = parseInt(result[1]);
var d = parseInt(result[2]);
var y = parseInt(result[3]);
if(m < 1 || m > 12 || y < 1900 || y > 2100) return false;
if(m == 2){
var days = ((y % 4) == 0) ? 29 : 28;
}else if(m == 4 || m == 6 || m == 9 || m == 11){
var days = 30;
}else{
var days = 31;
}
return (d >= 1 && d <= days);
}
// returns true if "str1" is the same as the "str2"
function isMatch(str1, str2){
return str1 == str2;
}
// returns true if the string contains only whitespace
// cannot check a password type input for whitespace
function isWhitespace(str){ // NOT USED IN FORM VALIDATION
var re = /[\S]/g
if (re.test(str)) return false;
return true;
}
// removes any whitespace from the string and returns the result
// the value of "replacement" will be used to replace the whitespace (optional)
function stripWhitespace(str, replacement){// NOT USED IN FORM VALIDATION
if (replacement == null) replacement = '';
var result = str;
var re = /\s/g
if(str.search(re) != -1){
result = str.replace(re, replacement);
}
return result;
}
// validate the form
function validateForm(f, preCheck){
var errors = '';
if(preCheck != null) errors += preCheck;
var i,e,t,n,v;
for(i=0; i < f.elements.length; i++){
e = f.elements[i];
if(e.optional) continue;
t = e.type;
n = e.name;
v = e.value;
if(t == 'text' || t == 'password' || t == 'textarea'){
if(isEmpty(v)){
errors += n+' cannot be empty.\n'; continue;
}
if(v == e.defaultValue){
errors += n+' cannot use the default value.\n'; continue;
}
if(e.isAlpha){
if(!isAlpha(v)){
errors += n+' can only contain characters A-Z a-z.\n'; continue;
}
}
if(e.isNumeric){
if(!isNumeric(v)){
errors += n+' can only contain characters 0-9.\n'; continue;
}
}
if(e.isAlphaNumeric){
if(!isAlphaNumeric(v)){
errors += n+' can only contain characters A-Z a-z 0-9.\n'; continue;
}
}
if(e.isEmail){
if(!isEmail(v)){
errors += v+' is not a valid email.\n'; continue;
}
}
if(e.isLength != null){
var len = e.isLength;
if(!isLength(v,len)){
errors += n+' must contain only '+len+' characters.\n'; continue;
}
}
if(e.isLengthBetween != null){
var min = e.isLengthBetween[0];
var max = e.isLengthBetween[1];
if(!isLengthBetween(v,min,max)){
errors += n+' cannot contain less than '+min+' or more than '+max+' characters.\n'; continue;
}
}
if(e.isPhoneNumber){
if(!isPhoneNumber(v)){
errors += v+' is not a valid US phone number.\n'; continue;
}
}
if(e.isDate){
if(!isDate(v)){
errors += v+' is not a valid date.\n'; continue;
}
}
if(e.isMatch != null){
if(!isMatch(v, e.isMatch)){
errors += n+' does not match.\n'; continue;
}
}
}
if(t.indexOf('select') != -1){
if(isEmpty(e.options[e.selectedIndex].value)){
errors += n+' needs an option selected.\n'; continue;
}
}
if(t == 'file'){
if(isEmpty(v)){
errors += n+' needs a file to upload.\n'; continue;
}
}
}
if(errors != '') alert(errors);
return errors == '';
}
/*
The following elements are not validated...
button type="button"
checkbox type="checkbox"
hidden type="hidden"
radio type="radio"
reset type="reset"
submit type="submit"
All elements are assumed required and will only be validated for an
empty value or defaultValue unless specified by the following properties.
isEmail = true; // valid email address
isAlpha = true; // A-Z a-z characters only
isNumeric = true; // 0-9 characters only
isAlphaNumeric = true; // A-Z a-z 0-9 characters only
isLength = number; // must be exact length
isLengthBetween = array; // [lowNumber, highNumber] must be between lowNumber and highNumber
isPhoneNumber = true; // valid US phone number. See "isPhoneNumber()" comments for the formatting rules
isDate = true; // valid date. See "isDate()" comments for the formatting rules
isMatch = string; // must match string
optional = true; // element will not be validated
*/
// ||||||||||||||||||||||||||||||||||||||||||||||||||
// --------------------------------------------------
// ||||||||||||||||||||||||||||||||||||||||||||||||||
// All of the previous JavaScript is coded to process
// any form and should be kept in an external file if
// multiple forms are being processed.
// This function configures the previous
// form validation code for this form.
function configureValidation(f){
f.firstname.isAlphaNumeric = true;
f.lastname.isAlphaNumeric = true;
f.email.isEmail = true;
f.phone.isPhoneNumber = true;
f.birthday.isDate = true;
f.password1.isLengthBetween = [4,255];
f.password2.isMatch = f.password1.value;
f.comments.optional = true;
var preCheck = (!f.infohtml.checked && !f.infocss.checked && !f.infojs.checked) ? 'select at least one checkbox.\n' : null;
return validateForm(f, preCheck);
}
Thứ Hai, 15 tháng 3, 2010
Thứ Năm, 4 tháng 3, 2010
Giới thiệu về XML-RPC
XML-RPC là gì?
XML-RPC là một cài đặt RPC (Remote Procedure Call) trên nền giao thức HTTP, sử dụng XML để mã hoá và trao đổi dữ liệu.
Bạn có thể tìm hiểu thêm chi tiết về XML-RPC ở các địa chỉ:
- Trang chủ XML-RPC: http://www.xmlrpc.com
- XML-RPC cho người mới nhập môn: http://davenet.scripting.com/1998/07/14/xmlRpcForNewbies
Vậy RPC là gì?
Khi bạn viết chương trình, ắt hẳn bạn cũng đã quen với các khái niệm thủ tục và hàm. Các đoạn chương trình lặp đi lặp lại bạn viết lại thành 1 hàm (hay thủ tục) và sau đó khi dùng chỉ cần gọi thủ tục hoặc hàm đó với các tham số thích hợp. Các thủ tục hoặc hàm đó bạn có thể:
- Để chung trong 1 file nguồn cùng với chương trình chính,
- Để trong 1 file nguồn khác và được include vào file nguồn chính khi cần,
- Được biên dịch sẵn và để trong 1 thư viện hoặc unit để các chương trình khác của bạn (cùng được viết bằng 1 ngôn ngữ) sử dụng,
- Được biên dịch sẵn để trong file DLL để các chương trình (được viết bằng các ngôn ngữ khác nhau) sử dụng.
Điểm chung của tất cả các phương thức trên là các hàm và thủ tục cần gọi đều nằm trên cùng 1 máy với nơi gọi chúng. Tuy nhiên bạn có thể thấy là nơi gọi và hàm cần gọi có thể được tách rời nhau ra: từ chung file, đến khác file, rồi đến khác ngôn ngữ.
RPC - Remote Procedure Call (lời gọi hàm xa) - là một mở rộng của lời gọi hàm (procedure call) theo cách cổ điển. Giờ đây hàm cần gọi và nơi gọi hàm có thể nằm ở 2 máy tính khác nhau; thậm chí có thể cách xa nhau nửa vòng trái đất, nối với nhau qua mạng internet.
Một địa chỉ khá hay để tham khảo cho người mới bắt đầu với XML-RPC là XML-RPC for newbies, các khái niệm về XML-RPC đựoc giải thích khá cặn kẽ và trình bày rất dễ hiểu cho người mới làm quen với XML-RPC.
XML-RPC là một cài đặt của RPC, vậy còn RPC "nào khác" nữa không?
Dĩ nhiên là còn chứ. RPC là nền tảng của lập trình phân tán (distributed programming). Các giao thức xây dựng trên RPC đã xuất hiện từ rất lâu và có thể đã trở nên quen thuộc với bạn mà bạn không để ý tới. Ví dụ:
CORBA: CORBA là một trong những giao thức được sử dụng khá phổ biến để phát triển các ứng dụng phân tán (distributed) hướng đối tượng (object-oriented). Bạn sẽ hay gặp CORBA trong các chương trình đa tầng với qui mô "xí nghiệp" (multi-tier enterprise application). Các địa chỉ tham khảo hữu ích về CORBA là trang chủ của OMG (Object Management Group) và OMG CORBA.
DCOM: Nếu bạn là tín đồ của Microsoft thì chắc hẳn là bạn không xa lạ gì với thuật ngữ này. DCOM là cú "đáp trả" của Micrsoft đối với CORBA. Bạn có thể ghé thăm trang chủ của DCOM để tìm hiểu thêm chi tiết.
SOAP: Rất có thể bạn đã nghe nhiều đến SOAP trong thời gian gần đây. SOAP cũng là một cài đặt RPC trên HTTP. Bạn có thể tìm hiểu thêm về SOAP ở trang chủ của SOAP.
Tại sao sử dụng XML-RPC?
XML-RPC khá đơn giản so với CORBA, DCOM và SOAP. Và hơn thế, XMl-RPC hoạt động khá tốt và cài đặt tương đối đơn giản với các ngôn ngữ lập trình web phổ biến như PHP, Perl, Python. Với lý do đó, XML-RPC là sự lựa chọn khá lý tưởng để phát triển các ứng dụng web qui mô nhỏ.
Và dĩ nhiên, XML-RPC vẫn có thể được cài đặt với các ngôn ngữ lập trình desktop như C/C++, Java, và thậm chí với Microsoft .NET
XML-RPC là một cài đặt RPC (Remote Procedure Call) trên nền giao thức HTTP, sử dụng XML để mã hoá và trao đổi dữ liệu.
Bạn có thể tìm hiểu thêm chi tiết về XML-RPC ở các địa chỉ:
- Trang chủ XML-RPC: http://www.xmlrpc.com
- XML-RPC cho người mới nhập môn: http://davenet.scripting.com/1998/07/14/xmlRpcForNewbies
Vậy RPC là gì?
Khi bạn viết chương trình, ắt hẳn bạn cũng đã quen với các khái niệm thủ tục và hàm. Các đoạn chương trình lặp đi lặp lại bạn viết lại thành 1 hàm (hay thủ tục) và sau đó khi dùng chỉ cần gọi thủ tục hoặc hàm đó với các tham số thích hợp. Các thủ tục hoặc hàm đó bạn có thể:
- Để chung trong 1 file nguồn cùng với chương trình chính,
- Để trong 1 file nguồn khác và được include vào file nguồn chính khi cần,
- Được biên dịch sẵn và để trong 1 thư viện hoặc unit để các chương trình khác của bạn (cùng được viết bằng 1 ngôn ngữ) sử dụng,
- Được biên dịch sẵn để trong file DLL để các chương trình (được viết bằng các ngôn ngữ khác nhau) sử dụng.
Điểm chung của tất cả các phương thức trên là các hàm và thủ tục cần gọi đều nằm trên cùng 1 máy với nơi gọi chúng. Tuy nhiên bạn có thể thấy là nơi gọi và hàm cần gọi có thể được tách rời nhau ra: từ chung file, đến khác file, rồi đến khác ngôn ngữ.
RPC - Remote Procedure Call (lời gọi hàm xa) - là một mở rộng của lời gọi hàm (procedure call) theo cách cổ điển. Giờ đây hàm cần gọi và nơi gọi hàm có thể nằm ở 2 máy tính khác nhau; thậm chí có thể cách xa nhau nửa vòng trái đất, nối với nhau qua mạng internet.
Một địa chỉ khá hay để tham khảo cho người mới bắt đầu với XML-RPC là XML-RPC for newbies, các khái niệm về XML-RPC đựoc giải thích khá cặn kẽ và trình bày rất dễ hiểu cho người mới làm quen với XML-RPC.
XML-RPC là một cài đặt của RPC, vậy còn RPC "nào khác" nữa không?
Dĩ nhiên là còn chứ. RPC là nền tảng của lập trình phân tán (distributed programming). Các giao thức xây dựng trên RPC đã xuất hiện từ rất lâu và có thể đã trở nên quen thuộc với bạn mà bạn không để ý tới. Ví dụ:
CORBA: CORBA là một trong những giao thức được sử dụng khá phổ biến để phát triển các ứng dụng phân tán (distributed) hướng đối tượng (object-oriented). Bạn sẽ hay gặp CORBA trong các chương trình đa tầng với qui mô "xí nghiệp" (multi-tier enterprise application). Các địa chỉ tham khảo hữu ích về CORBA là trang chủ của OMG (Object Management Group) và OMG CORBA.
DCOM: Nếu bạn là tín đồ của Microsoft thì chắc hẳn là bạn không xa lạ gì với thuật ngữ này. DCOM là cú "đáp trả" của Micrsoft đối với CORBA. Bạn có thể ghé thăm trang chủ của DCOM để tìm hiểu thêm chi tiết.
SOAP: Rất có thể bạn đã nghe nhiều đến SOAP trong thời gian gần đây. SOAP cũng là một cài đặt RPC trên HTTP. Bạn có thể tìm hiểu thêm về SOAP ở trang chủ của SOAP.
Tại sao sử dụng XML-RPC?
XML-RPC khá đơn giản so với CORBA, DCOM và SOAP. Và hơn thế, XMl-RPC hoạt động khá tốt và cài đặt tương đối đơn giản với các ngôn ngữ lập trình web phổ biến như PHP, Perl, Python. Với lý do đó, XML-RPC là sự lựa chọn khá lý tưởng để phát triển các ứng dụng web qui mô nhỏ.
Và dĩ nhiên, XML-RPC vẫn có thể được cài đặt với các ngôn ngữ lập trình desktop như C/C++, Java, và thậm chí với Microsoft .NET
Sự khác biệt chính giữa GET và POST
Sự khác biệt chính giữa GET và POST request là với HTTP GET, tất cả các tham số hay dữ liệu được chuyển đến server được chứa trong chính địa chỉ URL. Điều này có nghĩa là ta có thể gọi trực tiếp các thủ tục của server từ xa thông qua URL và các tham số của nó. Tuy nhiên, để chuyển các tham số, ta bị giới hạn bằng định dạng văn bản đơn giản với độ dài các tham số bị giới hạn bởi kích thước lớn nhất của chiều dài dòng request của máy chủ Web. Ví dụ, trên server Web Tomcat, kích thước tối đa mặc định của dòng request được đặt là 8190 bytes và có thể được thay đổi.
HTTP POST thích hợp hơn đối với việc truyền lượng dữ liệu lớn hay thậm chí dữ liệu nhị phân từ thiết bị di động đến server, bởi vì dữ liệu được gởi đến server độc lập với URL. Việc này có ưu điểm là lượng dữ liệu không bị hạn chế, như trong trường hợp của phương thức GET. Trên Java, việc này có thể thực hiện bằng cách mở một luồng (stream) tách biệt, trên đó có các phương thức cần thiết của Java cho streamù. Do đó ta có thể truyền dữ liệu nhị phân đến server mà không gặp vấn đề gì. Dữ liệu trao đổi giữa client và server có thể được tổ chức hoàn toàn bằng các luồng.
Ưu điểm chính của việc liên lạc dùng định dạng dữ liệu nhị phân trên HTTP là hiệu suất truyền cao và kích thước phần tải (payload) cô đọng. Mặt khác, khuyết điểm của nó là nó không có tính tự mô tả (self-descriptive), và điều kiện tiên quyết là phải biết trước định dạng, ở cả hai phía client và server, trước khi có thể bắt đầu phát triển ứng dụng. Điều này dẫn đến vấn đề là bất kỳ thay đổi nào đến định dạng thông điệp phải được nhất quán giữa client và server. Ngoài ra, với sự gia tăng số lượng các thông điệp không tương tự nhau mà server cần phải xử lý, mã chương trình trở nên phức tạp.
Sự tuần tự hóa đối tượng
Một cách tiếp cận hay để khắc phục các khó khăn này là sử dụng các đối tượng được tuần tự hóa. Ngoài các kiểu dữ liệu chuẩn được hỗ trợ bởi các phương thức đọc và ghi trong các lớp luồng, ta có khả năng thực hiện tuần tự hóa các đối tượng bất kỳ để việc truyền dữ liệu giữa client và server được dễ dàng. Vấn đề là MIDP 1.0 không giống như môi trường hoàn chỉnh J2SE, mặc định MIDP không hỗ trợ tuần tự hóa (serialization) và phản ánh (reflection) đối tượng. Do thiếu một cơ chế tổng quát, mỗi lớp phải đưa ra sự thực hiện tính năng này theo cách của riêng mình.
Tuần tự hóa có được thông qua một giao diện phù hợp đòi hỏi đối phải cung cấp các phương thức cần thiết cho việc tuần tự và giải tuần tự nó. Cần chú ý là các lớp với cơ chế tuần tự độc quyền của nó phải hiện diện với các phiên bản giống nhau ở cả hai phía client và server.
Trao đổi thông điệp bằng XML
XML thể hiện định dạng thông điệp tự mô tả. XML không yêu cầu bắt buộc phải triển khai một giao thức RPC giống như SOAP dựa trên XML. Nhà phát triển có thể chỉ xây dựng định dạng thông điệp phù hợp dựa theo cơ sở của XML để thực hiện việc trao đổi dữ liệu giữa client và server. Ưu điểm có tính quyết định của XML là nó đã được chuẩn hóa và do đó có tính khả chuyển cao. Hơn nữa, nó dựa vào văn bản, dữ liệu có cấu trúc có thể được mô tả theo phương cách có tính tự giải thích (self-explanatory). Trong lĩnh vực doanh nghiệp, XML đã chiếm được ưu thế so với các giao thức thông điệp khác chủ yếu là do sự hỗ trợ tốt của nền tảng J2EE.
Ngoài việc truyền lượng dữ liệu lớn hơn khi dùng XML cũng như tăng thêm phần dư thừa của XML, ta còn gặp vấn đề khác với các thiết bị di động. Trong khi nền tảng J2EE hỗ trợ XML, thì môi trường MIDP 1.0 không cung cấp hỗ trợ tích hợp nào cho việc phân tích XML. Các giải pháp dựa trên XML cần phải bao gồm bộ phân tích XML trên MIDP client. Mặc dù có nhiều bộ phân tích mã nguồn mở, chẳng hạn như NanoXML, TinyXML, hay kXML, có thể được dùng cho mục đích này và đã được thiết kế để sử dụng tối thiểu tài nguyên, tuy nhiên dù sao thì vẫn yêu cầu không gian lưu trữ trên thiết bị, vốn thường rất ít để chia sẻ.
Ở đây ta sử dụng KXML của Enhydra, bởi vì nó chỉ chiếm 21 KB bộ nhớ.
Để phân tích tài liệu XML, cần phải có thêm bộ nhớ và khả năng tính toán tương ứng. Trong hầu hết trường hợp, thông điệp XML lớn hơn nhiều so với thông điệp nhị phân – chủ yếu là do tính dài dòng của định dạng XML.
Nén nhị phân XML
Định dạng WBXML giúp giảm kích thước tài liệu XML một cách đáng kể, trong đó định dạng văn bản của tài liệu XML được chuyển sang một dạng nhị phân. Định dạng này cũng được dùng để chuyển các trang WML, kích thước được giảm xuống rất nhiều bằng cách thay các thẻ, thuộc tính, và các giá trị thông dụng bằng một tập các thẻ bài (token) có thể cấu hình được. Cũng hoàn toàn giống như việc mã hóa và giải mã các thông điệp trong thiết bị WAP được thực hiện thông qua một WAP gateway, việc liên lạc giữa thiết bị MIDP và server J2EE cũng có thể tương tự. Bộ phân tích đảm nhận việc mã hóa và giải mã thông điệp một cách phù hợp, bộ phân tích phải hỗ trợ WBXML. Ví dụ, bộ phân tích KXML của Endydra hỗ trợ giao thức này và cho phép truyền dữ liệu một cách hiệu quả giữa client và server. Server đương nhiên cũng cần phải hiểu định dạng WBXML. Một cách thay thế là, việc liên lạc phải được xử lý thông qua một proxy hay một WBXML gateway.
XML-RPC
XML-RPC là một giao thức truyền thông điệp cực nhẹ cho phép thực thi các thủ tục từ xa trên mạng thông qua HTTP. Client gởi một thông điệp XML thông qua HTTP POST đến server để phân tích. Một thủ tục nội của server trả về kết quả là response, cũng ở dạng một thông điệp XML, trả về cho client.
Do đó XML-RPC có được các ưu điểm của XML. Nó xây dựng rất ít tính năng, cho phép xác định và truyền các kiểu dữ liệu giống như các tham số để triệu gọi các thủ tục từ xa theo cách tiếp cận trung lập nền (platform-neutral). Phạm vi của định dạng chủ ý càng nhỏ càng tốt bằng cách giới hạn sáu loại kiểu dữ liệu gốc (primitive): int, boolean, string, double, datetime và base64 và hai kiểu liên hợp (complex): struct và array. Điều này làm cho giao thức đặc biệt thích hợp với các ứng dụng J2ME và kết nối mạng điện thoại di động băng thông hẹp.
Nhược điểm ở đây, cũng giống như với XML, là nền tảng J2ME không cung cấp bất kỳ hỗ trợ tích hợp nào cho XML-RPC, và nó cần phải dựa vào các gói bổ sung như KXM-RPC của Enhydra để triển khai ứng dụng trên thiết bị. Gói KXML-RPC được xây dựng trên nền của KXML và chạy khá tốt với chỉ 24KB tài nguyên thiết bị bao gồm KXML. Các mã nguồn liên quan của XML-RPC cũng cung cấp cho việc cài đặt điểm liên lạc tương ứng tại server. Một ví dụ của nó là Apache XML-RPC dựa trên nền tảng Java của Tổ chức phần mềm Apache (Apache Software Foundation), có thể được áp dụng với Tomca t Servlet engine.
SOAP
Simple Object Access Protocol (SOAP). Đầu tiên Microsoft phát triển giao thức này cùng với Useland để đáp ứng nhu cầu của các nhà phát triển muốn phát triển các ứng dụng phân tán với các công nghệ của Microsoft. SOAP và các phiên bản trước của XML-RPC có cùng nguồn gốc. Tuy nhiên, không giống như SOAP, XML-RPC đã được cải tiến và không gia tăng tính phức tạp. Việc định nghĩa phức tạp giúp cho các giao thức mở rất linh động, điều này giải thích vì sao nó là chuẩn trong thực tế cho việc gọi hàm từ xa bằng XML trên HTTP.
Đáng tiếc là, với các nhược điểm vốn có của lĩnh vực di động, nó dẫn tới nhiều dữ liệu thừa mà không luôn luôn cần đến, nhưng chiếm nhiều tài nguyên. Đặc tả giao thức hiện tại là 1.2 và ngoài tập tính năng của XML-RPC, nó cung cấp các tính năng thêm như tính quan tâm không gian tên (namespace awareness), cơ chế định kiểu dữ liệu, và việc truyền dữ liệu header bổ sung. SOAP cũng có cùng mặt hạn chế, J2ME không cung cấp bất kỳ hỗ trợ nào cho nó. Các client cần hỗ trợ SOAP cần phải tích hợp trong các chức năng của ứng dụng. Trên đầu cuối client, một giải pháp là dùng Enhydra ME project-kSOAP. Tuy nhiên, gói kSOAP yêu cầu ít nhất 41 KB bộ nhớ, là một gánh nặng cho ứng dụng trên thiết bị.
Trong trường hợp này, ta phải bảo đảm bộ nhớ và băng thông yêu cầu có thể thỏa mãn. Đương nhiên server cũng cần phải hỗ trợ SOAP. Phía server thì có nhiều giải pháp, có thể dùng dự án mã nguồn mở Apache SOAP (cũ) hoặc dự án Axis mới hơn, Axis nên dùng cho các dự án mới. Axis cũng thuộc Apache.
Tối ưu hóa bằng Proxy
Ta đã khảo sát nhiều tùy chọn khác nhau cho việc trao đổi dữ liệu client/server thông qua HTTP. Tùy thuộc vào mục đích của ứng dụng, ta có thể quyết định sử dụng giữa giao thức nén theo ý mình với các giao thức linh động nhưng dài dòng đã trở thành chuẩn được sử dụng rộng rãi. Nếu ta muốn kết hợp các ưu điểm của các phương pháp đối lập này, thì có một giải pháp khá thú vị – sử dụng một proxy server hoặc một gateway giữa MIDP client và server J2EE. Như ta đã đề cập ở trên, thông điệp XML có thể được nén thông qua WBXML gateway.
Tương tự như vậy, ta cũng có thể thực hiện một proxy cho giao thức độc quyền, chuyển nó thành giao thức chuẩn như XML-RPC hay SOAP. Nhờ vậy, dữ liệu được chuyển từ MIDP client đến proxy bằng một giao thức nhẹ chẳng hạn như một giao thức nhị phân hay WBXML, để tối thiểu hóa kích thước thông điệp và tận dụng tốt hơn băng thông hạn chế. Proxy, đến lượt nó, đóng vai trò như một client đến server J2EE và dịch request của giao thức nhẹ đó thành giao thức dựa trên XML của server và ngược lại.
Dùng proxy, ta có khả năng kết nối ứng dụng đến một server đang tồn tại với sự giúp đỡ của giao thức được tối ưu hóa, ví dụ server đó có thể đang hỗ trợ SOAP hay XML-RPC. Điều này có ưu điểm là khả năng liên kết và hiệu suất đều được nâng cao. Mặt khác, cần phải xem xét một vấn đề là sự gia tăng độ phức tạp của toàn bộ hệ thống sẽ gây khó khăn cho quá trình phát triển ứng dụng.
Thứ Ba, 2 tháng 3, 2010
AJAX Client Life-Cycle Events
Introduction
A Microsoft ASP.NET AJAX page raises the same server life-cycle events as an ASP.NET 2.0 Web page and also raises client life-cycle events. The client events enable you to customize the UI for both postbacks and for asynchronous postbacks (partial-page updates). The client events also help you manage custom script components during the lifetime of the page in the browser.
The client events are raised by classes in the Microsoft AJAX Library. These classes are automatically instantiated when a page contains ASP.NET AJAX server controls. The client classes provide APIs that enable you to bind to events and to provide handlers for those events. Because the Microsoft AJAX Library is browser independent, the code you write for your handlers works the same in all supported browsers.
The key event for initial requests (GET requests) and synchronous postbacks is the load event of the Application instance. When script in a load event handler runs, all scripts and components have been loaded and are available. When partial-page rendering with UpdatePanel controls is enabled, the key client events are the events of the PageRequestManager class. These events enable you to handle many common scenarios. These include the ability to cancel postbacks, to give precedence to one postback over another, and to animate UpdatePanel controls when their content is refreshed.
Client events are useful whether you are creating pages or writing components. If you are a page developer, you can provide custom script that is called when the page loads and unloads in the browser.
For more information about the server life-cycle events, see ASP.NET Page Life Cycle Overview.
Client Classes
The two main Microsoft AJAX Library classes that raise events during the client life cycle of an ASP.NET AJAX Web page are the Application and PageRequestManager classes.
The Application class is instantiated in the browser when the page contains a ScriptManager control. The Application class resembles the Page server control, which derives from the Control class, but provides additional functionality for raising server events. Similarly, the Application class derives from the Sys.Component class, but raises client life-cycle events that you can handle.
If a page contains a ScriptManager control and one or more UpdatePanel controls, the page can perform partial-page updates (if partial-page rendering is enabled and supported in the browser). In that case, an instance of the PageRequestManager class is automatically available in the browser. The PageRequestManager class raises client events that are specific to asynchronous postbacks. For details about partial-page rendering, see Partial-Page Rendering Overview.
Adding Handlers for Client Events
To add or remove handlers for events raised by the Application and PageRequestManager classes, use the add_eventname and remove_eventname methods of those classes. The following example shows how to add a handler named MyLoad to the init event of the Application object.
CS
Sys.Application.add_init(MyInit);
function MyInit(sender) {
}
Sys.Appplication.remove_init(MyInit);
VB
Sys.Application.add_init(MyInit);
function MyInit(sender) {
}
Sys.Appplication.remove_init(MyInit);
note
This example shows just the syntax of the add_eventname and remove_eventname methods. Details about what you can do with specific events are provided later in this topic.
Handling the Application Load and Unload Events
To handle the load and unload events of the Application object, you do not have to explicitly bind a handler to the event. Instead, you can create functions that use the reserved names pageLoad and pageUnload. The following example shows how to add a handler for the load event of the Application object by using this approach.
CS
function pageLoad(sender, args) {
}
VB
function pageLoad(sender, args) {
}
Events for Other Client Classes
This topic describes only the events that are raised by the Application and PageRequestManager classes. The Microsoft AJAX Library also contains classes for adding, clearing, and removing handlers for DOM element events. These classes include the following:
The Sys.UI.DomEvent.addHandler method or the shortcut $addHandler.
The Sys.UI.DomEvent.clearHandlers method or the shortcut $clearHandlers.
The Sys.UI.DomEvent.removeHandler method or the shortcut $removeHandler.
Events raised by DOM elements are not discussed in this topic.
Client Events of the Application and PageRequestManager Classes
The following table lists client events of the Application and PageRequestManager classes that you can handle in AJAX ASP.NET-enabled pages. The order in which the events are raised is described later in this topic.
init Event
Raised after all scripts have been loaded but before any objects are created. If you are writing a component, the init event gives you a point in the life cycle to add your component to the page. The component can then be used by other components or by script later in the page life cycle. If you are a page developer, you should use the load event instead of the init event for most scenarios.
The init event is raised only one time when the page is first rendered. Subsequent partial-page updates do not raise the init event.
load Event
Raised after all scripts have been loaded and all objects in the application that are created by using $create are initialized. The load event is raised for all postbacks to the server, which includes asynchronous postbacks.
If you are a page developer, you can create a function that has the name pageLoad, which automatically provides a handler for the load event. The pageLoad handler is called after any handlers that have been added to the load event by the add_load method.
The load event takes an eventargs parameter, which is an Sys.ApplicationLoadEventArgs object. You can use the event arguments to determine whether the page is being refreshed as a result of a partial-page update and what components were created since the previous load event was raised.
unload Event
Raised before all objects are disposed and before the browser window's window.unload event occurs.
If you are a page developer, you can create a function that has the name pageUnload, which automatically provides a handler for the unload event. The pageUnload event is called just before the page is unloaded from the browser. During this event, you should free any resources that your code is holding.
propertyChanged Event
Potentially raised when a property of a component changes. The Application object inherits this event from the Component class. This event is raised only if a component developer has called the Sys.Component.raisePropertyChange method in a property set accessor. For more information, see Defining Custom Component Properties and Raising PropertyChanged Events.
The propertyChanged event takes an eventargs parameter, which is a Sys.applicationLoadEventArgs object.
disposing Event
Raised when the Application instance is disposed. The Application object inherits this event from the Component class.
initializeRequest Event
Raised before an asynchronous request starts. You can use this event to cancel a postback, such as to give precedence to another asynchronous postback.
The initializeRequest event takes an eventargs parameter, which is a Sys.WebForms.InitializeRequestEventArgs object. This object makes available the element that caused the postback and the underlying request object. InitializeRequestEventArgs also exposes a cancel property. If you set cancel to true, the new postback is canceled.
beginRequest Event
Raised before an asynchronous postback starts and the postback is sent to the server. If there is a postback already processing, it is stopped (by using the abortPostBack method). You can use this event to set request headers or to begin an animation on the page to indicate that the request is in process.
The beginRequest event takes an eventargs parameter, which is a Sys.WebForms.BeginRequestEventArgs object. This object makes available the element that caused the postback and the underlying request object.
pageLoading Event
Raised after the response from the server to an asynchronous postback is received, but before any content on the page is updated. You can use this event to provide a custom transition effect for updated content.
The pageLoading event takes an eventargs parameter, which is an Sys.WebForms.PageLoadingEventArgs object. This object makes available information about what panels will be deleted and updated as a result of the most recent asynchronous postback.
pageLoaded Event
Raised after all content on the page is refreshed, as a result of either a synchronous or an asynchronous postback. For synchronous postbacks, panels can only be created, but for asynchronous postbacks, panels can be both created and updated. You can use this event to manage a custom transition effect for updated content.
The pageLoaded event takes an eventargs parameter, which is an Sys.WebForms.PageLoadedEventArgs object. This object makes available information about which panels were updated and created in the most recent postback.
endRequest Event
Raised after the response for an asynchronous postback is processed and the page is updated, or during the processing of the response if there is an error. If an error occurs, the page is not updated. Use this event to provide customized error notification to users or to log errors.
The endRequest event takes an eventargs parameter, which is a Sys.WebForms.EndRequestEventArgs object. This object makes available information about errors that have occurred and whether the error was handled. It also makes available the response object.
A Microsoft ASP.NET AJAX page raises the same server life-cycle events as an ASP.NET 2.0 Web page and also raises client life-cycle events. The client events enable you to customize the UI for both postbacks and for asynchronous postbacks (partial-page updates). The client events also help you manage custom script components during the lifetime of the page in the browser.
The client events are raised by classes in the Microsoft AJAX Library. These classes are automatically instantiated when a page contains ASP.NET AJAX server controls. The client classes provide APIs that enable you to bind to events and to provide handlers for those events. Because the Microsoft AJAX Library is browser independent, the code you write for your handlers works the same in all supported browsers.
The key event for initial requests (GET requests) and synchronous postbacks is the load event of the Application instance. When script in a load event handler runs, all scripts and components have been loaded and are available. When partial-page rendering with UpdatePanel controls is enabled, the key client events are the events of the PageRequestManager class. These events enable you to handle many common scenarios. These include the ability to cancel postbacks, to give precedence to one postback over another, and to animate UpdatePanel controls when their content is refreshed.
Client events are useful whether you are creating pages or writing components. If you are a page developer, you can provide custom script that is called when the page loads and unloads in the browser.
For more information about the server life-cycle events, see ASP.NET Page Life Cycle Overview.
Client Classes
The two main Microsoft AJAX Library classes that raise events during the client life cycle of an ASP.NET AJAX Web page are the Application and PageRequestManager classes.
The Application class is instantiated in the browser when the page contains a ScriptManager control. The Application class resembles the Page server control, which derives from the Control class, but provides additional functionality for raising server events. Similarly, the Application class derives from the Sys.Component class, but raises client life-cycle events that you can handle.
If a page contains a ScriptManager control and one or more UpdatePanel controls, the page can perform partial-page updates (if partial-page rendering is enabled and supported in the browser). In that case, an instance of the PageRequestManager class is automatically available in the browser. The PageRequestManager class raises client events that are specific to asynchronous postbacks. For details about partial-page rendering, see Partial-Page Rendering Overview.
Adding Handlers for Client Events
To add or remove handlers for events raised by the Application and PageRequestManager classes, use the add_eventname and remove_eventname methods of those classes. The following example shows how to add a handler named MyLoad to the init event of the Application object.
CS
Sys.Application.add_init(MyInit);
function MyInit(sender) {
}
Sys.Appplication.remove_init(MyInit);
VB
Sys.Application.add_init(MyInit);
function MyInit(sender) {
}
Sys.Appplication.remove_init(MyInit);
note
This example shows just the syntax of the add_eventname and remove_eventname methods. Details about what you can do with specific events are provided later in this topic.
Handling the Application Load and Unload Events
To handle the load and unload events of the Application object, you do not have to explicitly bind a handler to the event. Instead, you can create functions that use the reserved names pageLoad and pageUnload. The following example shows how to add a handler for the load event of the Application object by using this approach.
CS
function pageLoad(sender, args) {
}
VB
function pageLoad(sender, args) {
}
Events for Other Client Classes
This topic describes only the events that are raised by the Application and PageRequestManager classes. The Microsoft AJAX Library also contains classes for adding, clearing, and removing handlers for DOM element events. These classes include the following:
The Sys.UI.DomEvent.addHandler method or the shortcut $addHandler.
The Sys.UI.DomEvent.clearHandlers method or the shortcut $clearHandlers.
The Sys.UI.DomEvent.removeHandler method or the shortcut $removeHandler.
Events raised by DOM elements are not discussed in this topic.
Client Events of the Application and PageRequestManager Classes
The following table lists client events of the Application and PageRequestManager classes that you can handle in AJAX ASP.NET-enabled pages. The order in which the events are raised is described later in this topic.
init Event
Raised after all scripts have been loaded but before any objects are created. If you are writing a component, the init event gives you a point in the life cycle to add your component to the page. The component can then be used by other components or by script later in the page life cycle. If you are a page developer, you should use the load event instead of the init event for most scenarios.
The init event is raised only one time when the page is first rendered. Subsequent partial-page updates do not raise the init event.
load Event
Raised after all scripts have been loaded and all objects in the application that are created by using $create are initialized. The load event is raised for all postbacks to the server, which includes asynchronous postbacks.
If you are a page developer, you can create a function that has the name pageLoad, which automatically provides a handler for the load event. The pageLoad handler is called after any handlers that have been added to the load event by the add_load method.
The load event takes an eventargs parameter, which is an Sys.ApplicationLoadEventArgs object. You can use the event arguments to determine whether the page is being refreshed as a result of a partial-page update and what components were created since the previous load event was raised.
unload Event
Raised before all objects are disposed and before the browser window's window.unload event occurs.
If you are a page developer, you can create a function that has the name pageUnload, which automatically provides a handler for the unload event. The pageUnload event is called just before the page is unloaded from the browser. During this event, you should free any resources that your code is holding.
propertyChanged Event
Potentially raised when a property of a component changes. The Application object inherits this event from the Component class. This event is raised only if a component developer has called the Sys.Component.raisePropertyChange method in a property set accessor. For more information, see Defining Custom Component Properties and Raising PropertyChanged Events.
The propertyChanged event takes an eventargs parameter, which is a Sys.applicationLoadEventArgs object.
disposing Event
Raised when the Application instance is disposed. The Application object inherits this event from the Component class.
initializeRequest Event
Raised before an asynchronous request starts. You can use this event to cancel a postback, such as to give precedence to another asynchronous postback.
The initializeRequest event takes an eventargs parameter, which is a Sys.WebForms.InitializeRequestEventArgs object. This object makes available the element that caused the postback and the underlying request object. InitializeRequestEventArgs also exposes a cancel property. If you set cancel to true, the new postback is canceled.
beginRequest Event
Raised before an asynchronous postback starts and the postback is sent to the server. If there is a postback already processing, it is stopped (by using the abortPostBack method). You can use this event to set request headers or to begin an animation on the page to indicate that the request is in process.
The beginRequest event takes an eventargs parameter, which is a Sys.WebForms.BeginRequestEventArgs object. This object makes available the element that caused the postback and the underlying request object.
pageLoading Event
Raised after the response from the server to an asynchronous postback is received, but before any content on the page is updated. You can use this event to provide a custom transition effect for updated content.
The pageLoading event takes an eventargs parameter, which is an Sys.WebForms.PageLoadingEventArgs object. This object makes available information about what panels will be deleted and updated as a result of the most recent asynchronous postback.
pageLoaded Event
Raised after all content on the page is refreshed, as a result of either a synchronous or an asynchronous postback. For synchronous postbacks, panels can only be created, but for asynchronous postbacks, panels can be both created and updated. You can use this event to manage a custom transition effect for updated content.
The pageLoaded event takes an eventargs parameter, which is an Sys.WebForms.PageLoadedEventArgs object. This object makes available information about which panels were updated and created in the most recent postback.
endRequest Event
Raised after the response for an asynchronous postback is processed and the page is updated, or during the processing of the response if there is an error. If an error occurs, the page is not updated. Use this event to provide customized error notification to users or to log errors.
The endRequest event takes an eventargs parameter, which is a Sys.WebForms.EndRequestEventArgs object. This object makes available information about errors that have occurred and whether the error was handled. It also makes available the response object.
Understand page life circle in asp.net
Page Request
The page request occurs before the page life cycle begins. When a user requests the page, ASP.NET determines whether the page needs to be parsed and compiled (therefore beginning the life of a page), or whether a cached version of the page can be sent in response without running the page.
Start
In the start step, page properties such as Request and Response are set. At this stage, the page also determines whether the request is a postback or a new request and sets the IsPostBack property. Additionally, during the start step, the page's UICulture property is set.
Page Initialization
During page initialization, controls on the page are available and each control's UniqueID are generated but not their properties. Any themes are also applied to the page.
Developers have access to the Init, InitComplete and PreLoad methods in this stage. The methods are as follows:
Init: This event is raised after all controls have been initialized and any skin settings have been applied. This event is used to read or initialize control properties.
InitComplete: The Page object raises this event. This event is used for processing tasks that require completion of all initialization.
PreLoad: Use this event if you need to perform processing on your page or control before the Load event. After the Page raises this event, it loads view state for itself and all controls, and then processes any postback data included with the Request instance.
Load
During load, if the current request is a postback, control properties are loaded with information recovered from view state and control state. The OnLoad event method is fired during this stage.
This is where you will want to set properties for all of the server controls on your page, request query strings, and establish database connections.
Validation
During validation, the Validate method of all validator controls is called, which sets the IsValid property of individual validator controls and of the page.
PostBack Event Handling
If the request is a postback, any event handlers are called. The event handling for server controls occurs during this stage.
Render
During rendering, view state is saved to the page and then the page calls on each control to contribute its rendered output to the OutputStream of the page's Response property. Render is not really an event. The HTML of the page and all controls are sent to the browser for rendering.
Unload
Unload is called when the page has been fully rendered, sent to the client, and is ready to be discarded. At this point, page properties such as Response and Request are unloaded and any cleanup is performed. The cleanup includes routines such as closing database connections and file streams, or, event logging and other tasks.
Conclusion
When a Web page is requested, the server creates objects associated with the page and all of its child controls objects and uses these to render the page to the browser. Once the final stage is complete, the web server destroys these objects, to free up resource to handle additional request.
Delegate và Event
1. Delegate là gì ?
Delegate là khái niệm trong C#, nó giống như con trỏ hàm của C++. Delegate là con trỏ trỏ vào các hàm có cùng đối(số lượng đối và kiểu đối giống nhau)
Tại sao phải dùng ?
- Delegate là cơ sở của Event do đó khi ta muốn sử dụng event ta phải sử dụng Delegate
Cách sử dụng
public delegate void LearningDelegate(string s);
class A
{
public void MethodA()
{
Console.WriteLine("Method A");
}
public void MethodB()
{
Console.WriteLine("Method B");
}
}
class Program
{
public static void Main()
{
A a=new A();
LearningDelegate deg;
deg=new LearningDeleage(a.MethodA);
deg();
deg=new LearningDeleage(a.MethodB);
deg();
}
}
2. Event là gì ?
Event là các sự kiện xảy ra khi chạy chương trình (sự kiện click của button, sự kiện giá trị của comboBox thay đổi,...)
Tại sao phải dùng Event ?
Dùng Event giúp chúng ta xử lý code lịnh hoạt và đơn giản hơn. Khi sử dụng Event thì chúng ta không cần quan tâm đến việc khai nào thì đặt hàm xử lý vì khi event phát sinh nó sẽ tự động gọi hàm xử lý ra để thực hiện.
Ví dụ : Khi bạn Add/Remove 1 item vào mảng thì chương trình hiện ra thông báo "Add" hoặc "Remove". Với cách xử lý truyền thống khi Add hoặc Remove sẽ có 1 hàm để xử lý đoạn lệnh thông báo, nhưng với Event thì mỗi khi sự kiện Add/Remove xảy ra thì tự động chương trình sẽ gọi hàm xử lý. Điều này làm giảm công sức của coder
Cách sử dung : tạo ra 3 lớp
- Lớp 1 : khai báo đối tượng có event và các event của đối tượng đó
- Lớp 2 : đăng ký event của với chương trình
- Lớp 3 : chạy chương trình, tạo đối tượng có event và thực thi
namespace Practice_Console
{
public delegate void AddItemHandler(object sender, EventArgs e);
public delegate void RemoveItemHandler(object sender, EventArgs e);
class ListWithEventChanged
{
public event AddItemHandler AddItem;
public event RemoveItemHandler RemoveItem;
protected virtual void OnAddItem(EventArgs e)
{
if (AddItem != null)
AddItem(this, e);
}
protected virtual void OnRemoveItem(EventArgs e)
{
if (RemoveItem != null)
RemoveItem(this, e);
}
public void Add(string s)
{
Console.WriteLine(s);
OnAddItem(EventArgs.Empty);
}
public void Remove(string s)
{
Console.WriteLine(s);
OnRemoveItem(EventArgs.Empty);
}
}
class EventListener
{
public EventListener(ListWithEventChanged list)
{
ListWithEventChanged List = new ListWithEventChanged();
List = list;
List.AddItem+=new AddItemHandler(List_AddItem);
List.RemoveItem+=new RemoveItemHandler(List_RemoveItem);
}
private void List_AddItem(object sender, EventArgs e)
{
Console.WriteLine("Phat sinh su kien Add");
}
private void List_RemoveItem(object sender, EventArgs e)
{
Console.WriteLine("Phat sinh su kien Remove");
}
}
class Program
{
public static void Main(string[] args)
{
ListWithEventChanged list = new ListWithEventChanged();
EventListener listener = new EventListener(list);
list.Add("Add");
Console.ReadLine();
list.Remove("Remove");
Console.ReadLine();
}
}
}
Delegate là khái niệm trong C#, nó giống như con trỏ hàm của C++. Delegate là con trỏ trỏ vào các hàm có cùng đối(số lượng đối và kiểu đối giống nhau)
Tại sao phải dùng ?
- Delegate là cơ sở của Event do đó khi ta muốn sử dụng event ta phải sử dụng Delegate
Cách sử dụng
public delegate void LearningDelegate(string s);
class A
{
public void MethodA()
{
Console.WriteLine("Method A");
}
public void MethodB()
{
Console.WriteLine("Method B");
}
}
class Program
{
public static void Main()
{
A a=new A();
LearningDelegate deg;
deg=new LearningDeleage(a.MethodA);
deg();
deg=new LearningDeleage(a.MethodB);
deg();
}
}
2. Event là gì ?
Event là các sự kiện xảy ra khi chạy chương trình (sự kiện click của button, sự kiện giá trị của comboBox thay đổi,...)
Tại sao phải dùng Event ?
Dùng Event giúp chúng ta xử lý code lịnh hoạt và đơn giản hơn. Khi sử dụng Event thì chúng ta không cần quan tâm đến việc khai nào thì đặt hàm xử lý vì khi event phát sinh nó sẽ tự động gọi hàm xử lý ra để thực hiện.
Ví dụ : Khi bạn Add/Remove 1 item vào mảng thì chương trình hiện ra thông báo "Add" hoặc "Remove". Với cách xử lý truyền thống khi Add hoặc Remove sẽ có 1 hàm để xử lý đoạn lệnh thông báo, nhưng với Event thì mỗi khi sự kiện Add/Remove xảy ra thì tự động chương trình sẽ gọi hàm xử lý. Điều này làm giảm công sức của coder
Cách sử dung : tạo ra 3 lớp
- Lớp 1 : khai báo đối tượng có event và các event của đối tượng đó
- Lớp 2 : đăng ký event của với chương trình
- Lớp 3 : chạy chương trình, tạo đối tượng có event và thực thi
namespace Practice_Console
{
public delegate void AddItemHandler(object sender, EventArgs e);
public delegate void RemoveItemHandler(object sender, EventArgs e);
class ListWithEventChanged
{
public event AddItemHandler AddItem;
public event RemoveItemHandler RemoveItem;
protected virtual void OnAddItem(EventArgs e)
{
if (AddItem != null)
AddItem(this, e);
}
protected virtual void OnRemoveItem(EventArgs e)
{
if (RemoveItem != null)
RemoveItem(this, e);
}
public void Add(string s)
{
Console.WriteLine(s);
OnAddItem(EventArgs.Empty);
}
public void Remove(string s)
{
Console.WriteLine(s);
OnRemoveItem(EventArgs.Empty);
}
}
class EventListener
{
public EventListener(ListWithEventChanged list)
{
ListWithEventChanged List = new ListWithEventChanged();
List = list;
List.AddItem+=new AddItemHandler(List_AddItem);
List.RemoveItem+=new RemoveItemHandler(List_RemoveItem);
}
private void List_AddItem(object sender, EventArgs e)
{
Console.WriteLine("Phat sinh su kien Add");
}
private void List_RemoveItem(object sender, EventArgs e)
{
Console.WriteLine("Phat sinh su kien Remove");
}
}
class Program
{
public static void Main(string[] args)
{
ListWithEventChanged list = new ListWithEventChanged();
EventListener listener = new EventListener(list);
list.Add("Add");
Console.ReadLine();
list.Remove("Remove");
Console.ReadLine();
}
}
}
Thứ Năm, 25 tháng 2, 2010
Design Pattern
PATTERN là gì?
Pattern mô tả một giải pháp chung đối với một vấn đề nào đó trong thiết kế thường được “lặp lại” trong nhiều dự án. Nói một cách khác, một pattern có thể được xem như một “khuôn mẫu” có sẵn áp dụng được cho nhiều tình huống khác nhau để giải quyết một vấn đề cụ thể. Trong bất kỳ hệ thống phần mềm hướng đối tượng nào chúng ta cũng có thể bắt gặp các vấn đề lặp lại.
Đặc điểm chung:
• Pattern được hiểu theo nghĩa tái sử dụng ý tưởng hơn là mã lệnh. Pattern cho phép các nhà thiết kế có thể cùng ngồi lại với nhau và cùng giải quyết một vấn đề nào đó mà không phải mất nhiều thời gian tranh cãi. Trong rất nhiều trường hợp, dự án phần mềm thất bại là do các nhà phát triển không có được sự hiểu biết chung trong các vấn đề về kiến trúc phần mềm. Ngoài ra, pattern cũng cung cấp những thuật ngữ và khái niệm chung trong thiết kế. Nói một cách đơn giản, khi đề cập đến một pattern nào đấy, bất kỳ ai biết pattern đó đều có thể nhanh chóng hình dung ra “bức tranh” của giải pháp. Và cuối cùng, nếu áp dụng pattern hiệu quả thì việc bảo trì phần mềm cũng được tiến hành thuận lợi hơn, nắm bắt kiến trúc hệ thống nhanh hơn.
• Pattern hỗ trợ tái sử dụng kiến trúc và mô hình thiết kế phần mềm theo quy mô lớn. Cần phân biệt design pattern với framework. Framework hỗ trợ tái sử dụng mô hình thiết kế và mã nguồn ở mức chi tiết hơn. Trong khi đó, design pattern được vận dụng ở mức tổng quát hơn, giúp các nhà phát triển hình dung và ghi nhận các cấu trúc tĩnh và động cũng như quan hệ tương tác giữa các giải pháp trong quá trình thiết kế ứng dụng đối với một chuyên khu riêng biệt.
• Pattern đa tương thích. Pattern không phụ thuộc vào ngôn ngữ lập trình, công nghệ hoặc các nền tảng lớn như J2EE của Sun hay Microsoft .NET Framework.
Tiềm năng ứng dụng của pattern là rất lớn. Các thiết kế dựa trên pattern được sử dụng khá nhiều ở các phần mềm mã nguồn mở, trong nền tảng J2EE hoặc .NET... Trong các dạng ứng dụng này, có thể dễ dàng nhận ra một số tên lớp chứa các tiền tố hoặc hậu tố như Factory, Proxy, Adapter...
PHÂN LOẠI PATTERN
Pattern được phân loại ra làm 3 nhóm chính sau đây:
• Nhóm cấu thành (Creational Pattern): Gồm Factory, Abstract Factory, Singleton, Prototype, Builder... Liên quan đến quá trình khởi tạo đối tượng cụ thể từ một định nghĩa trừu tượng (abstract class, interface).
• Nhóm cấu trúc tĩnh (Structural Pattern): Gồm Proxy, Adapter, Wrapper, Bridge, Facade, Flyweight, Visitor... Liên quan đến vấn đề làm thế nào để các lớp và đối tượng kết hợp với nhau tạo thành các cấu trúc lớn hơn.
• Nhóm tương tác động (Behavioral Pattern): Gồm Observer, State, Command, Iterator... Mô tả cách thức để các lớp hoặc đối tượng có thể giao tiếp với nhau.
Dưới đây chúng ta sẽ tìm hiểu chi tiết một số pattern tiêu biểu nhất: Factory, Abstract Factory, Singleton, Proxy, Adapter và Wrapper. Chúng ta quy ước với nhau rằng “giao diện lớp” được hiểu như interface hoặc abstract class vì đây đơn thuần là các định nghĩa lớp.
FACTORY PATTERN
Định nghĩa
Factory Pattern định nghĩa một lớp (interface, abstract, class) đóng vai trò như một “nhà xưởng” có nhiệm vụ khởi tạo đối tượng “cụ thể” khi ứng dụng chạy. Tại thời điểm thiết kế đối tượng này được định nghĩa trừu tượng.
Đặc điểm
Kiểm soát được các hoạt động trong suốt chu kỳ sống của đối tượng, như khởi tạo đối tượng, huỷ đối tượng... Đảm bảo cho các đối tượng được thực thi an toàn. Nắm được thông tin về những đối tượng nào được tạo ra và được khởi tạo ra sao. Nói cách khác, các đối tượng được quản lý tốt hơn và an toàn hơn với Factory Pattern.
Đối tượng Factory thường được đặt tên theo những chuẩn khác nhau nhưng vẫn có thể dễ dàng nhận ra thiết kế Factory Pattern ẩn chứa trong đó. Ví dụ: BankFactory,...
Tính chất đóng gói (encapsulation) thể hiện rõ trong Factory Pattern; các thông tin liên quan đến truy cập đối tượng được che giấu trong Factory. Thiết kế Factory luôn có một thủ tục khởi tạo đối tượng, ví dụ createObject().
Factory Pattern tuân thủ nguyên tắc thiết kế DIP (Dependency Inversion Principle): không nên phụ thuộc vào những thứ quá cụ thể.
Phân loại
Factory Pattern được thiết kế theo một trong hai cách sau đây:
Based-class Factory Pattern: Mẫu này sử dụng tính chất thừa kế để phân loại các đối tượng được tạo ra.
Based-object Factory Pattern: Sử dụng mối quan hệ kết hợp để tham chiếu tới một đối tượng sẽ được tạo ra. Đối tượng được tạo ra sẽ trở thành một phần hay thuộc tính của lớp Factory. Chúng ta thường hay gặp loại này trong Abstract Factory Pattern được trình bày ở phần tiếp theo.
ABSTRACT FACTORY PATTERN
Định nghĩa
Abstract Factory cung cấp một giao diện lớp có chức năng tạo ra một tập hợp các đối tượng liên quan hoặc phụ thuộc lẫn nhau mà không chỉ ra đó là những lớp cụ thể nào tại thời điểm thiết kế.
Về bản chất, Abstract Factory Pattern chỉ khác Factory Pattern ở chỗ bản thân đối tượng Factory không được chỉ ra cụ thể tại thời điểm thiết kế, tức nó là một giao diện hoặc lớp trừu tượng (interface, abstract). Nếu như Factory Patttern phân loại đối tượng dựa trên tham số đầu vào thì đối với Abstract Factory Pattern, thủ tục createObject() còn phụ thuộc thêm vào các yếu tố phụ khác như môi trường hệ điều hành chẳng hạn. Ứng với mỗi yếu tố phụ thứ hai ta có một lớp Factory cụ thể.
Thiết kế động với Abstract Factory
Một trong những vấn đề gặp phải là khung giao diện Abstract Factory thường hay bị sửa đổi, thí dụ như bổ sung thủ tục chẳng hạn, khi đó các lớp cụ thể thực thi giao diện này sẽ phải được dịch và triển khai lại. Để giảm nhẹ vấn đề này người ta thường thiết kế giao diện Abstract Factory một cách linh động.
SINGLETON PATTERN
(Static Factory Pattern)
Định nghĩa
Singleton Pattern đảm bảo một lớp chỉ có một thực thể (instance) duy nhất được tạo ra và đồng thời cung cấp một truy cập toàn cục đến đối tượng được tạo ra.
Chúng ta xét trường hợp có nhiều đối tượng có cùng chung một số tính chất nào đó được tạo ra ứng với mỗi một yêu cầu từ các đối tượng khách (client), lúc này độ phức tạp sẽ tăng lên và ứng dụng sẽ chiếm dụng nhiều vùng nhớ hơn. Singleton Pattern là một giải pháp đặc biệt của Factory Pattern ở chỗ đối tượng sinh ra là điểm truy cập toàn cục “duy nhất” đối với mọi chương trình gọi đến, hay nói một cách khác tất cả các đối tượng khách gọi đến đều chia sẻ đối tượng được tạo ra.
Ứng dụng rõ rệt nhất của Singleton Pattern có thể thấy trong dịch vụ web khi triệu gọi các đối tượng từ xa, ở đó đối tượng nằm trên server hoặc sẽ phục vụ chung cho tất cả các ứng dụng khách (singleton) hoặc sẽ chỉ đáp ứng một ứng dụng khách riêng lẻ nào đó rồi tự bị phá huỷ sau đó (single call).
Về các mẫu thiết kế tiêu biểu trong nhóm cấu thành: Factory, Abstract Factory và Singleton, các bạn có thể tham khảo thêm tài liệu về phương pháp xây dựng cụ thể cũng như mã nguồn chương trình viết bằng C#.NET tại địa chỉ:
http://www.codeproject.com/gen/desig...assFactory.asp
PROXY PATTERN
Định nghĩa
Proxy Pattern là mẫu thiết kế mà ở đó tất cả các truy cập trực tiếp một đối tượng nào đó sẽ được chuyển hướng vào một đối tượng trung gian (Proxy Class).
Nếu như Factory Pattern giúp quản lý đối tượng tốt hơn thì Proxy Pattern lại có nhiệm vụ bảo vệ việc truy cập một đối tượng thông qua Proxy, hay còn gọi là truy cập gián tiếp. Proxy được ủy quyền về phía ứng dụng khách cho phép tương tác với đối tượng đích theo những cách khác nhau; như gửi yêu cầu một dịch vụ nào đó, theo dõi trạng thái và vòng đời đối tượng, xây dựng lớp vỏ bảo vệ đối tượng... Thí dụ chúng ta phát hiện ra một đối tượng trong một thư viện DLL có thể bị khai thác truy cập vào một số trường quan trọng, khi đó chúng ta không thể mở mã nguồn thư viện đã được dịch để vá lỗ hổng, giải pháp lúc này là xây dựng một proxy ngăn chặn truy cập các trường đó và cuối cùng biên dịch lại thành một DLL mới.
Phân loại
Độ phức tạp của giải pháp sử dụng Proxy Pattern phụ thuộc vào tình huống bài toán đưa ra, chúng ta sẽ lần lượt tìm hiểu nguyên tắc làm việc của các proxy dưới đây:
Remote Proxy: Client truy cập qua remote proxy để tham chiếu tới một đối tượng được bảo vệ nằm bên ngoài ứng dụng (trên cùng máy hoặc máy khác) như dịch vụ Windows, dịch vụ web, ứng dụng ở xa... Mô hình này "che giấu" đối tượng được triệu gọi đang nằm ở rất xa đâu đó và client có vẻ như truy cập vào đối tượng nằm trên cùng một chuyên khu làm việc (domain).
Virtual Proxy: Virtual Proxy tạo ra một đối tượng trung gian mỗi khi có yêu cầu tại thời điểm thực thi ứng dụng, nhờ đó làm tăng hiệu suất của ứng dụng.
Monitor Proxy: Monitor Proxy sẽ thiết lập các ràng buộc bảo mật trên đối tượng cần bảo vệ, ngăn không cho client truy cập một số trường quan trọng của đối tượng.
Protection Proxy: Đối với proxy này thì phạm vi truy cập của các client khác nhau sẽ khác nhau. Protection Proxy sẽ kiểm tra các quyền truy cập của client khi có một dịch vụ được yêu cầu.
Cache Proxy: Cung cấp không gian lưu trữ tạm thời cho các kết quả trả về từ đối tượng nào đó, kết quả này sẽ được tái sử dụng cho các client chia sẻ chung một yêu cầu gửi đến và do đó làm tăng đáng kể hiệu suất chương trình.
Firewall Proxy: Bảo vệ đối tượng từ chối các yêu cầu xuất xứ từ các client không tín nhiệm.
Smart Reference Proxy: Là nơi kiểm soát các hoạt động bổ sung mỗi khi đối tượng được tham chiếu, ví dụ như kiểm soát vòng đời của đối tượng, lưu lại số lần tham chiếu vào đối tượng...
Synchronization Proxy: Đảm bảo nhiều client có thể truy cập vào cùng một đối tượng mà không gây ra xung đột. Thực tế có rất nhiều tình huống khiến chúng ta phải nghĩ đến thiết kế này. Một synchronization proxy được thiết lập có thể kiểm soát được nhiều yêu cầu cập nhật dữ liệu một cách đồng thời, tại thời điểm bắt đầu cập nhật chỉ có một client với mức ưu tiên cao nhất giành được khoá để đánh dấu rằng các client khác cần phải chờ đến lượt.
Synchronization proxy hoạt động rất hiệu quả và phổ biến trong thiết kế các bài toán đa tuyến. Một hiện tượng hay xảy ra với thiết kế này là khi một client nào đó chiếm dụng khoá khá lâu (và thậm chí là mãi mãi) khiến cho số lượng các client trong danh sách hàng đợi cứ tăng lên, và do đó hoạt động của hệ thống bị ngừng trệ, có thể dẫn đến hiện tượng “tắt nghẽn” là hiện tượng khoá được giữ vô thời hạn bởi một đối tượng nào đó. Trong trường hợp này người ta cải tiến thành mẫu thiết kế phức tạp hơn, đó là Copy-On-Write Proxy.
Copy-On-Write Proxy: Cope-On-Write Proxy đảm bảo rằng sẽ không có client nào phải chờ vô thời hạn. Thiết kế này rất phức tạp do đó chỉ nên ứng dụng Copy-On-Write Proxy thay thế Synchronization Proxy khi hệ thống được dự đoán sẽ thường xuyên bị ngừng trệ hoặc có hiện tượng “tắt nghẽn” xảy ra.
Đặc điểm chung
Proxy Pattern có những đặc điểm chung sau đây:
• Cung cấp mức truy cập gián tiếp vào một đối tượng.
• Tham chiếu vào đối tượng đích và chuyển tiếp các yêu cầu đến đối tượng đó.
• Cả proxy và đối tượng đích đều kế thừa hoặc thực thi chung một lớp giao diện. Mã máy dịch cho lớp giao diện thường “nhẹ” hơn các lớp cụ thể và do đó có thể giảm được thời gian tải dữ liệu giữa server và client.
ADAPTER PATTERN
Định nghĩa
Adapter Pattern biến đổi giao diện của một lớp thành một giao diện khác mà các đối tượng client có thể hiểu được. Lớp với giao diện được tạo ra đó gọi là Adapter. Nguyên tắc cơ bản của Adapter Pattern nằm ở chỗ làm thế nào để các lớp với các giao diện không tương thích có thể làm việc được với nhau.
Nguyên lý xây dựng Adapter Pattern khá đơn giản: chúng ta xây dựng một lớp với một giao diện mong muốn sao cho lớp đó giao tiếp được với một lớp cho trước ứng với một giao diện khác.
Adapter Pattern không quản lý tập trung các đối tượng gần giống nhau như Factory Pattern, mà kết nối với nhiều lớp không có liên quan gì với nhau. Ví dụ lớp A sau khi thực thi giao diện của nó và vẫn muốn bổ sung các phương thức từ một lớp B nào đó, chúng ta có thể kết nối A với B thông qua hình thức kế thừa hoặc liên kết đối tượng như một thành phần. Adapter Pattern có sự giống nhau một chút với Proxy Pattern ở chỗ nó tận dụng tối đa tính chất “uỷ quyền” (delegation); lớp Adapter sẽ kết nối với một đối tượng nào đó gọi là Adaptee và Adapter sẽ được uỷ quyền truy cập vào Adaptee, lớp Adapter đóng vai trò như một kênh trung gian để client truy cập vào một số các thành phần quan trọng của lớp Adaptee.
Đặc điểm
• Adapter Pattern hướng tập trung vào giải quyết sự tương thích giữa hai giao diện đang tồn tại, giảm công sức viết lại mã lệnh xuống mức tối thiểu có thể được.
• Tái sử dụng giao diện cũ và Adapter Pattern chỉ thực sự cần thiết khi mọi thứ đã được thiết kế từ trước.
Phạm vi ứng dụng
Adapter Pattern được ứng dụng trong các trường hợp:
• Cần tích hợp một vài module vào chương trình.
• Không thể sát nhập trực tiếp module vào chương trình (ví dụ như module thư viện đã được dịch ra .DLL, .CLASS...).
• Module đang tồn tại không có giao diện mong muốn như:
- Cần nhiều hơn phương thức cho module đó.
- Một số phương thức có thể được nạp chồng.
WRAPPER PATTERN
Wrapper Pattern là một trường hợp đặc biệt của Adapter Pattern. Nếu một Adapter chỉ đơn thuần là “nhúng” (wrap) các lớp với các giao diện không tương thích với nhau để chúng có thể hoạt động cùng nhau thì có thể được gọi bằng tên riêng Wrapper Pattern. Khi đó lớp Adapter còn được gọi là lớp Wrapper. Đây là quan hệ “có một”, tức là một giao diện không tương thích có thể được nhúng vào thành một phần của một giao diện khác.
Đặc điểm
Đối tượng Wrapper mô phỏng tất cả các hành vi (hàm, thủ tục) của giao diện được nhúng bởi các hành vi với tên y hệt. Thí dụ nếu lớp được nhúng A có thủ tục SpecificRequest() thì lớp Wrapper cũng phải có thủ tục SpecificRequest() tham chiếu đến thủ tục cùng tên của A. (Ngoài ra đối tượng Wraper có thể được bổ sung các phương thức khác nếu cần thiết). Đặc điểm này được đưa ra dựa trên nguyên tắc thiết kế “Law of Demeter” nói rằng không nên tham chiếu một đối tượng sâu hơn một lớp.
Các phương thức trong Adaptee được “nhúng” trong Wrapper bằng cách truyền lời gọi cùng với các tham số tới phương thức tương ứng trong Adaptee, và trả về kết quả giống như vậy. Các thành viên (thuộc tính, trường, sự kiện) được nhúng trong Wrapper có tính chất giống hệt như trong các lớp được nhúng (tên, kiểu dữ liệu, phạm vi truy cập...).
Từ các đặc điểm ở trên, có thể thấy rằng Wrapper Pattern cho phép một module chương trình tương tác được trong một môi trường khác biệt với môi trường phát triển của module đó (ví dụ C++ và Java).
Khác biệt giữa Wrapper Pattern và Adapter Pattern
Sự khác biệt giữa Wrapper và Adapter nằm ở mục đích sử dụng: Adapter Pattern định hướng cho một đối tượng đang tồn tại có thể làm việc được với các đối tượng khác và biến đổi logic theo một cách thức nào đó, trong khi Wrapper Pattern chỉ đơn thuần cung cấp một giao diện kết hợp các đối tượng được xây dựng từ cùng một ngôn ngữ hoặc khác ngôn ngữ, trên cùng một hệ điều hành hoặc trên những hệ điều hành khác nhau.
Proxy Adapter Pattern
Nếu một lớp Adapter đóng thêm vai trò như một proxy bảo vệ cho Adaptee thì ta có mô hình Proxy Adapter Pattern, trong trường hợp này chúng ta có thể đặt tên lớp với nghĩa kết hợp, ví dụ BankProxyAdapter.
Lời kết
Bài viết này đưa ra một số mẫu thiết kế tiêu biểu giúp các bạn thấy được tầm quan trọng của design pattern trong việc nâng cao chất lượng phần mềm ở các yếu tố: hiệu suất ứng dụng, độ ổn định, tái sử dụng, tính bảo mật... Các bạn có thể tìm hiểu thêm về các mẫu thiết kế cao cấp khác ở rất nhiều tài liệu thiết kế phần mềm.
Pattern mô tả một giải pháp chung đối với một vấn đề nào đó trong thiết kế thường được “lặp lại” trong nhiều dự án. Nói một cách khác, một pattern có thể được xem như một “khuôn mẫu” có sẵn áp dụng được cho nhiều tình huống khác nhau để giải quyết một vấn đề cụ thể. Trong bất kỳ hệ thống phần mềm hướng đối tượng nào chúng ta cũng có thể bắt gặp các vấn đề lặp lại.
Đặc điểm chung:
• Pattern được hiểu theo nghĩa tái sử dụng ý tưởng hơn là mã lệnh. Pattern cho phép các nhà thiết kế có thể cùng ngồi lại với nhau và cùng giải quyết một vấn đề nào đó mà không phải mất nhiều thời gian tranh cãi. Trong rất nhiều trường hợp, dự án phần mềm thất bại là do các nhà phát triển không có được sự hiểu biết chung trong các vấn đề về kiến trúc phần mềm. Ngoài ra, pattern cũng cung cấp những thuật ngữ và khái niệm chung trong thiết kế. Nói một cách đơn giản, khi đề cập đến một pattern nào đấy, bất kỳ ai biết pattern đó đều có thể nhanh chóng hình dung ra “bức tranh” của giải pháp. Và cuối cùng, nếu áp dụng pattern hiệu quả thì việc bảo trì phần mềm cũng được tiến hành thuận lợi hơn, nắm bắt kiến trúc hệ thống nhanh hơn.
• Pattern hỗ trợ tái sử dụng kiến trúc và mô hình thiết kế phần mềm theo quy mô lớn. Cần phân biệt design pattern với framework. Framework hỗ trợ tái sử dụng mô hình thiết kế và mã nguồn ở mức chi tiết hơn. Trong khi đó, design pattern được vận dụng ở mức tổng quát hơn, giúp các nhà phát triển hình dung và ghi nhận các cấu trúc tĩnh và động cũng như quan hệ tương tác giữa các giải pháp trong quá trình thiết kế ứng dụng đối với một chuyên khu riêng biệt.
• Pattern đa tương thích. Pattern không phụ thuộc vào ngôn ngữ lập trình, công nghệ hoặc các nền tảng lớn như J2EE của Sun hay Microsoft .NET Framework.
Tiềm năng ứng dụng của pattern là rất lớn. Các thiết kế dựa trên pattern được sử dụng khá nhiều ở các phần mềm mã nguồn mở, trong nền tảng J2EE hoặc .NET... Trong các dạng ứng dụng này, có thể dễ dàng nhận ra một số tên lớp chứa các tiền tố hoặc hậu tố như Factory, Proxy, Adapter...
PHÂN LOẠI PATTERN
Pattern được phân loại ra làm 3 nhóm chính sau đây:
• Nhóm cấu thành (Creational Pattern): Gồm Factory, Abstract Factory, Singleton, Prototype, Builder... Liên quan đến quá trình khởi tạo đối tượng cụ thể từ một định nghĩa trừu tượng (abstract class, interface).
• Nhóm cấu trúc tĩnh (Structural Pattern): Gồm Proxy, Adapter, Wrapper, Bridge, Facade, Flyweight, Visitor... Liên quan đến vấn đề làm thế nào để các lớp và đối tượng kết hợp với nhau tạo thành các cấu trúc lớn hơn.
• Nhóm tương tác động (Behavioral Pattern): Gồm Observer, State, Command, Iterator... Mô tả cách thức để các lớp hoặc đối tượng có thể giao tiếp với nhau.
Dưới đây chúng ta sẽ tìm hiểu chi tiết một số pattern tiêu biểu nhất: Factory, Abstract Factory, Singleton, Proxy, Adapter và Wrapper. Chúng ta quy ước với nhau rằng “giao diện lớp” được hiểu như interface hoặc abstract class vì đây đơn thuần là các định nghĩa lớp.
FACTORY PATTERN
Định nghĩa
Factory Pattern định nghĩa một lớp (interface, abstract, class) đóng vai trò như một “nhà xưởng” có nhiệm vụ khởi tạo đối tượng “cụ thể” khi ứng dụng chạy. Tại thời điểm thiết kế đối tượng này được định nghĩa trừu tượng.
Đặc điểm
Kiểm soát được các hoạt động trong suốt chu kỳ sống của đối tượng, như khởi tạo đối tượng, huỷ đối tượng... Đảm bảo cho các đối tượng được thực thi an toàn. Nắm được thông tin về những đối tượng nào được tạo ra và được khởi tạo ra sao. Nói cách khác, các đối tượng được quản lý tốt hơn và an toàn hơn với Factory Pattern.
Đối tượng Factory thường được đặt tên theo những chuẩn khác nhau nhưng vẫn có thể dễ dàng nhận ra thiết kế Factory Pattern ẩn chứa trong đó. Ví dụ: BankFactory,...
Tính chất đóng gói (encapsulation) thể hiện rõ trong Factory Pattern; các thông tin liên quan đến truy cập đối tượng được che giấu trong Factory. Thiết kế Factory luôn có một thủ tục khởi tạo đối tượng, ví dụ createObject().
Factory Pattern tuân thủ nguyên tắc thiết kế DIP (Dependency Inversion Principle): không nên phụ thuộc vào những thứ quá cụ thể.
Phân loại
Factory Pattern được thiết kế theo một trong hai cách sau đây:
Based-class Factory Pattern: Mẫu này sử dụng tính chất thừa kế để phân loại các đối tượng được tạo ra.
Based-object Factory Pattern: Sử dụng mối quan hệ kết hợp để tham chiếu tới một đối tượng sẽ được tạo ra. Đối tượng được tạo ra sẽ trở thành một phần hay thuộc tính của lớp Factory. Chúng ta thường hay gặp loại này trong Abstract Factory Pattern được trình bày ở phần tiếp theo.
ABSTRACT FACTORY PATTERN
Định nghĩa
Abstract Factory cung cấp một giao diện lớp có chức năng tạo ra một tập hợp các đối tượng liên quan hoặc phụ thuộc lẫn nhau mà không chỉ ra đó là những lớp cụ thể nào tại thời điểm thiết kế.
Về bản chất, Abstract Factory Pattern chỉ khác Factory Pattern ở chỗ bản thân đối tượng Factory không được chỉ ra cụ thể tại thời điểm thiết kế, tức nó là một giao diện hoặc lớp trừu tượng (interface, abstract). Nếu như Factory Patttern phân loại đối tượng dựa trên tham số đầu vào thì đối với Abstract Factory Pattern, thủ tục createObject() còn phụ thuộc thêm vào các yếu tố phụ khác như môi trường hệ điều hành chẳng hạn. Ứng với mỗi yếu tố phụ thứ hai ta có một lớp Factory cụ thể.
Thiết kế động với Abstract Factory
Một trong những vấn đề gặp phải là khung giao diện Abstract Factory thường hay bị sửa đổi, thí dụ như bổ sung thủ tục chẳng hạn, khi đó các lớp cụ thể thực thi giao diện này sẽ phải được dịch và triển khai lại. Để giảm nhẹ vấn đề này người ta thường thiết kế giao diện Abstract Factory một cách linh động.
SINGLETON PATTERN
(Static Factory Pattern)
Định nghĩa
Singleton Pattern đảm bảo một lớp chỉ có một thực thể (instance) duy nhất được tạo ra và đồng thời cung cấp một truy cập toàn cục đến đối tượng được tạo ra.
Chúng ta xét trường hợp có nhiều đối tượng có cùng chung một số tính chất nào đó được tạo ra ứng với mỗi một yêu cầu từ các đối tượng khách (client), lúc này độ phức tạp sẽ tăng lên và ứng dụng sẽ chiếm dụng nhiều vùng nhớ hơn. Singleton Pattern là một giải pháp đặc biệt của Factory Pattern ở chỗ đối tượng sinh ra là điểm truy cập toàn cục “duy nhất” đối với mọi chương trình gọi đến, hay nói một cách khác tất cả các đối tượng khách gọi đến đều chia sẻ đối tượng được tạo ra.
Ứng dụng rõ rệt nhất của Singleton Pattern có thể thấy trong dịch vụ web khi triệu gọi các đối tượng từ xa, ở đó đối tượng nằm trên server hoặc sẽ phục vụ chung cho tất cả các ứng dụng khách (singleton) hoặc sẽ chỉ đáp ứng một ứng dụng khách riêng lẻ nào đó rồi tự bị phá huỷ sau đó (single call).
Về các mẫu thiết kế tiêu biểu trong nhóm cấu thành: Factory, Abstract Factory và Singleton, các bạn có thể tham khảo thêm tài liệu về phương pháp xây dựng cụ thể cũng như mã nguồn chương trình viết bằng C#.NET tại địa chỉ:
http://www.codeproject.com/gen/desig...assFactory.asp
PROXY PATTERN
Định nghĩa
Proxy Pattern là mẫu thiết kế mà ở đó tất cả các truy cập trực tiếp một đối tượng nào đó sẽ được chuyển hướng vào một đối tượng trung gian (Proxy Class).
Nếu như Factory Pattern giúp quản lý đối tượng tốt hơn thì Proxy Pattern lại có nhiệm vụ bảo vệ việc truy cập một đối tượng thông qua Proxy, hay còn gọi là truy cập gián tiếp. Proxy được ủy quyền về phía ứng dụng khách cho phép tương tác với đối tượng đích theo những cách khác nhau; như gửi yêu cầu một dịch vụ nào đó, theo dõi trạng thái và vòng đời đối tượng, xây dựng lớp vỏ bảo vệ đối tượng... Thí dụ chúng ta phát hiện ra một đối tượng trong một thư viện DLL có thể bị khai thác truy cập vào một số trường quan trọng, khi đó chúng ta không thể mở mã nguồn thư viện đã được dịch để vá lỗ hổng, giải pháp lúc này là xây dựng một proxy ngăn chặn truy cập các trường đó và cuối cùng biên dịch lại thành một DLL mới.
Phân loại
Độ phức tạp của giải pháp sử dụng Proxy Pattern phụ thuộc vào tình huống bài toán đưa ra, chúng ta sẽ lần lượt tìm hiểu nguyên tắc làm việc của các proxy dưới đây:
Remote Proxy: Client truy cập qua remote proxy để tham chiếu tới một đối tượng được bảo vệ nằm bên ngoài ứng dụng (trên cùng máy hoặc máy khác) như dịch vụ Windows, dịch vụ web, ứng dụng ở xa... Mô hình này "che giấu" đối tượng được triệu gọi đang nằm ở rất xa đâu đó và client có vẻ như truy cập vào đối tượng nằm trên cùng một chuyên khu làm việc (domain).
Virtual Proxy: Virtual Proxy tạo ra một đối tượng trung gian mỗi khi có yêu cầu tại thời điểm thực thi ứng dụng, nhờ đó làm tăng hiệu suất của ứng dụng.
Monitor Proxy: Monitor Proxy sẽ thiết lập các ràng buộc bảo mật trên đối tượng cần bảo vệ, ngăn không cho client truy cập một số trường quan trọng của đối tượng.
Protection Proxy: Đối với proxy này thì phạm vi truy cập của các client khác nhau sẽ khác nhau. Protection Proxy sẽ kiểm tra các quyền truy cập của client khi có một dịch vụ được yêu cầu.
Cache Proxy: Cung cấp không gian lưu trữ tạm thời cho các kết quả trả về từ đối tượng nào đó, kết quả này sẽ được tái sử dụng cho các client chia sẻ chung một yêu cầu gửi đến và do đó làm tăng đáng kể hiệu suất chương trình.
Firewall Proxy: Bảo vệ đối tượng từ chối các yêu cầu xuất xứ từ các client không tín nhiệm.
Smart Reference Proxy: Là nơi kiểm soát các hoạt động bổ sung mỗi khi đối tượng được tham chiếu, ví dụ như kiểm soát vòng đời của đối tượng, lưu lại số lần tham chiếu vào đối tượng...
Synchronization Proxy: Đảm bảo nhiều client có thể truy cập vào cùng một đối tượng mà không gây ra xung đột. Thực tế có rất nhiều tình huống khiến chúng ta phải nghĩ đến thiết kế này. Một synchronization proxy được thiết lập có thể kiểm soát được nhiều yêu cầu cập nhật dữ liệu một cách đồng thời, tại thời điểm bắt đầu cập nhật chỉ có một client với mức ưu tiên cao nhất giành được khoá để đánh dấu rằng các client khác cần phải chờ đến lượt.
Synchronization proxy hoạt động rất hiệu quả và phổ biến trong thiết kế các bài toán đa tuyến. Một hiện tượng hay xảy ra với thiết kế này là khi một client nào đó chiếm dụng khoá khá lâu (và thậm chí là mãi mãi) khiến cho số lượng các client trong danh sách hàng đợi cứ tăng lên, và do đó hoạt động của hệ thống bị ngừng trệ, có thể dẫn đến hiện tượng “tắt nghẽn” là hiện tượng khoá được giữ vô thời hạn bởi một đối tượng nào đó. Trong trường hợp này người ta cải tiến thành mẫu thiết kế phức tạp hơn, đó là Copy-On-Write Proxy.
Copy-On-Write Proxy: Cope-On-Write Proxy đảm bảo rằng sẽ không có client nào phải chờ vô thời hạn. Thiết kế này rất phức tạp do đó chỉ nên ứng dụng Copy-On-Write Proxy thay thế Synchronization Proxy khi hệ thống được dự đoán sẽ thường xuyên bị ngừng trệ hoặc có hiện tượng “tắt nghẽn” xảy ra.
Đặc điểm chung
Proxy Pattern có những đặc điểm chung sau đây:
• Cung cấp mức truy cập gián tiếp vào một đối tượng.
• Tham chiếu vào đối tượng đích và chuyển tiếp các yêu cầu đến đối tượng đó.
• Cả proxy và đối tượng đích đều kế thừa hoặc thực thi chung một lớp giao diện. Mã máy dịch cho lớp giao diện thường “nhẹ” hơn các lớp cụ thể và do đó có thể giảm được thời gian tải dữ liệu giữa server và client.
ADAPTER PATTERN
Định nghĩa
Adapter Pattern biến đổi giao diện của một lớp thành một giao diện khác mà các đối tượng client có thể hiểu được. Lớp với giao diện được tạo ra đó gọi là Adapter. Nguyên tắc cơ bản của Adapter Pattern nằm ở chỗ làm thế nào để các lớp với các giao diện không tương thích có thể làm việc được với nhau.
Nguyên lý xây dựng Adapter Pattern khá đơn giản: chúng ta xây dựng một lớp với một giao diện mong muốn sao cho lớp đó giao tiếp được với một lớp cho trước ứng với một giao diện khác.
Adapter Pattern không quản lý tập trung các đối tượng gần giống nhau như Factory Pattern, mà kết nối với nhiều lớp không có liên quan gì với nhau. Ví dụ lớp A sau khi thực thi giao diện của nó và vẫn muốn bổ sung các phương thức từ một lớp B nào đó, chúng ta có thể kết nối A với B thông qua hình thức kế thừa hoặc liên kết đối tượng như một thành phần. Adapter Pattern có sự giống nhau một chút với Proxy Pattern ở chỗ nó tận dụng tối đa tính chất “uỷ quyền” (delegation); lớp Adapter sẽ kết nối với một đối tượng nào đó gọi là Adaptee và Adapter sẽ được uỷ quyền truy cập vào Adaptee, lớp Adapter đóng vai trò như một kênh trung gian để client truy cập vào một số các thành phần quan trọng của lớp Adaptee.
Đặc điểm
• Adapter Pattern hướng tập trung vào giải quyết sự tương thích giữa hai giao diện đang tồn tại, giảm công sức viết lại mã lệnh xuống mức tối thiểu có thể được.
• Tái sử dụng giao diện cũ và Adapter Pattern chỉ thực sự cần thiết khi mọi thứ đã được thiết kế từ trước.
Phạm vi ứng dụng
Adapter Pattern được ứng dụng trong các trường hợp:
• Cần tích hợp một vài module vào chương trình.
• Không thể sát nhập trực tiếp module vào chương trình (ví dụ như module thư viện đã được dịch ra .DLL, .CLASS...).
• Module đang tồn tại không có giao diện mong muốn như:
- Cần nhiều hơn phương thức cho module đó.
- Một số phương thức có thể được nạp chồng.
WRAPPER PATTERN
Wrapper Pattern là một trường hợp đặc biệt của Adapter Pattern. Nếu một Adapter chỉ đơn thuần là “nhúng” (wrap) các lớp với các giao diện không tương thích với nhau để chúng có thể hoạt động cùng nhau thì có thể được gọi bằng tên riêng Wrapper Pattern. Khi đó lớp Adapter còn được gọi là lớp Wrapper. Đây là quan hệ “có một”, tức là một giao diện không tương thích có thể được nhúng vào thành một phần của một giao diện khác.
Đặc điểm
Đối tượng Wrapper mô phỏng tất cả các hành vi (hàm, thủ tục) của giao diện được nhúng bởi các hành vi với tên y hệt. Thí dụ nếu lớp được nhúng A có thủ tục SpecificRequest() thì lớp Wrapper cũng phải có thủ tục SpecificRequest() tham chiếu đến thủ tục cùng tên của A. (Ngoài ra đối tượng Wraper có thể được bổ sung các phương thức khác nếu cần thiết). Đặc điểm này được đưa ra dựa trên nguyên tắc thiết kế “Law of Demeter” nói rằng không nên tham chiếu một đối tượng sâu hơn một lớp.
Các phương thức trong Adaptee được “nhúng” trong Wrapper bằng cách truyền lời gọi cùng với các tham số tới phương thức tương ứng trong Adaptee, và trả về kết quả giống như vậy. Các thành viên (thuộc tính, trường, sự kiện) được nhúng trong Wrapper có tính chất giống hệt như trong các lớp được nhúng (tên, kiểu dữ liệu, phạm vi truy cập...).
Từ các đặc điểm ở trên, có thể thấy rằng Wrapper Pattern cho phép một module chương trình tương tác được trong một môi trường khác biệt với môi trường phát triển của module đó (ví dụ C++ và Java).
Khác biệt giữa Wrapper Pattern và Adapter Pattern
Sự khác biệt giữa Wrapper và Adapter nằm ở mục đích sử dụng: Adapter Pattern định hướng cho một đối tượng đang tồn tại có thể làm việc được với các đối tượng khác và biến đổi logic theo một cách thức nào đó, trong khi Wrapper Pattern chỉ đơn thuần cung cấp một giao diện kết hợp các đối tượng được xây dựng từ cùng một ngôn ngữ hoặc khác ngôn ngữ, trên cùng một hệ điều hành hoặc trên những hệ điều hành khác nhau.
Proxy Adapter Pattern
Nếu một lớp Adapter đóng thêm vai trò như một proxy bảo vệ cho Adaptee thì ta có mô hình Proxy Adapter Pattern, trong trường hợp này chúng ta có thể đặt tên lớp với nghĩa kết hợp, ví dụ BankProxyAdapter.
Lời kết
Bài viết này đưa ra một số mẫu thiết kế tiêu biểu giúp các bạn thấy được tầm quan trọng của design pattern trong việc nâng cao chất lượng phần mềm ở các yếu tố: hiệu suất ứng dụng, độ ổn định, tái sử dụng, tính bảo mật... Các bạn có thể tìm hiểu thêm về các mẫu thiết kế cao cấp khác ở rất nhiều tài liệu thiết kế phần mềm.
Tính chất nổi bật trong c#
C# có 4 đặc điểm nổi bật chính là Abstraction (trừu tượng hóa), Encapsulation (đóng gói), Inheritance (kế thừa) và Polymorphism (đa hình).
Tuy nhiên, bản thân mình thấy cách phân chia này không hay. Nó không hay vì 1 lớp Abstract (trừu tượng) vốn được tạo ra chỉ để kế thừa. Vì thế nên gộp Abstraction và Inheritance làm một.
Trong C++ thì 3 đặc điểm này (đóng gói, kế thừa, đa hình) chính là 3 pillar (trụ cột chính) không thể đánh đổ.
Bây giờ mình sẽ giải thích về từng đặc điểm một.
Encapsulation (tính đóng gói): Tính đóng gói là một cách mô phỏng đời thật của lập trình hướng đối tượng. Trong cuộc sống thật, khi bạn có một cái gameboy, bạn biết cách khởi động nó, bạn biết cách nhấn nút để điều khiển trò chơi nhưng bạn không cần quan tâm xem bên trong cái máy gameboy ấy có những linh kiện gì và cũng không biết các linh kiện ấy hoạt động, tương tác với nhau như thế nào. Trong lập trình cũng vậy. Giả như bạn tạo ra một lớp trong C# tên là GameBoy. Bạn khai báo các biến đối tượng, nhưng bạn không để lộ thông tin đó ra bên ngoài. Người dùng lớp GameBoy mà bạn tạo, không biết được là bạn tạo cái lớp GameBoy đó thế nào (và thực sự là người ta cũng không cần biết). Cái người ta cần biết là khi tạo ra một lớp, bạn phải cung cấp một tài liệu về cách sử dụng cái lớp đó (cách tạo đối tượng từ lớp đó, cách gọi phương thức từ lớp đó ...v...v...). Sau đó người ta chỉ việc gọi phương thức từ lớp đó thôi chứ không cần biết lớp đó đã được tạo thế nào. Gọi phương phương thức mà không cần biết nó được tạo ra như thế nào cũng giống như việc chơi gameboy mà không cần biết bên trong hoạt động ra sao. --> tính đóng gói giúp bảo mật thông tin, tránh những can thiệp không cần thiết, giảm hỏng hóc, kiểm soát được trạng thái của đối tượng.
Inheritance (tính kế thừa): Một lớp con được thừa hưởng tất cả các thuộc tính và phương thức của lớp cha, như thế gọi là kế thừa. Tại sao phải kế thừa? Vì không có kế thừa thì không có đa hình (polymorphism). Kế thừa giúp tiết kiệm code, giúp chương trình dễ dàng bảo trì, nâng cấp, nhờ đó mà tiết kiệm được chi phí. Nếu chúng ta có các lớp khác nhau, và các lớp này có chung các trường dữ liệu cùng các phương thức nào đó, chúng ta sẽ abstract (trừu tượng hóa) các phương thức và trường dữ liệu đó ra một lớp riêng gọi là lớp cha. Các lớp còn lại, chúng ta cho kế thừa cái lớp mà chúng ta vừa mới tạo. Như vậy chúng ta không cần viết đi viết lại các đoạn code giống nhau nữa, mà chỉ cần viết những đoạn code khác nhau ở các lớp con thôi. Còn những đoạn code giống nhau thì lớp con nào cũng có, vì nó đã được kế thừa từ lớp cha. --> đây là một biểu hiện của việc dùng lại code.
Polymorphism (đa hình): Đa hình chính là mục tiêu lớn nhất của kế thừa (inheritance). Việc override hoặc overload một phương thức là biểu hiện của đa hình. Khi bạn viết một lớp có tên là Animal, bạn viết tiếp một lớp con của lớp Animal là lớp Cat, sau đó trong một lớp khác, bạn viết một phương thức mà tham số của phương thức đó là Animal: program.Kill(Animal a), đến khi sử dụng phương thức này, bạn không truyền vào một một đối tượng của lớp Animal, mà bạn truyền vào một đối tượng của lớp Cat: program.Kill(Cat c), đây là một ví dụ nữa của đa hình.
Tuy nhiên, bản thân mình thấy cách phân chia này không hay. Nó không hay vì 1 lớp Abstract (trừu tượng) vốn được tạo ra chỉ để kế thừa. Vì thế nên gộp Abstraction và Inheritance làm một.
Trong C++ thì 3 đặc điểm này (đóng gói, kế thừa, đa hình) chính là 3 pillar (trụ cột chính) không thể đánh đổ.
Bây giờ mình sẽ giải thích về từng đặc điểm một.
Encapsulation (tính đóng gói): Tính đóng gói là một cách mô phỏng đời thật của lập trình hướng đối tượng. Trong cuộc sống thật, khi bạn có một cái gameboy, bạn biết cách khởi động nó, bạn biết cách nhấn nút để điều khiển trò chơi nhưng bạn không cần quan tâm xem bên trong cái máy gameboy ấy có những linh kiện gì và cũng không biết các linh kiện ấy hoạt động, tương tác với nhau như thế nào. Trong lập trình cũng vậy. Giả như bạn tạo ra một lớp trong C# tên là GameBoy. Bạn khai báo các biến đối tượng, nhưng bạn không để lộ thông tin đó ra bên ngoài. Người dùng lớp GameBoy mà bạn tạo, không biết được là bạn tạo cái lớp GameBoy đó thế nào (và thực sự là người ta cũng không cần biết). Cái người ta cần biết là khi tạo ra một lớp, bạn phải cung cấp một tài liệu về cách sử dụng cái lớp đó (cách tạo đối tượng từ lớp đó, cách gọi phương thức từ lớp đó ...v...v...). Sau đó người ta chỉ việc gọi phương thức từ lớp đó thôi chứ không cần biết lớp đó đã được tạo thế nào. Gọi phương phương thức mà không cần biết nó được tạo ra như thế nào cũng giống như việc chơi gameboy mà không cần biết bên trong hoạt động ra sao. --> tính đóng gói giúp bảo mật thông tin, tránh những can thiệp không cần thiết, giảm hỏng hóc, kiểm soát được trạng thái của đối tượng.
Inheritance (tính kế thừa): Một lớp con được thừa hưởng tất cả các thuộc tính và phương thức của lớp cha, như thế gọi là kế thừa. Tại sao phải kế thừa? Vì không có kế thừa thì không có đa hình (polymorphism). Kế thừa giúp tiết kiệm code, giúp chương trình dễ dàng bảo trì, nâng cấp, nhờ đó mà tiết kiệm được chi phí. Nếu chúng ta có các lớp khác nhau, và các lớp này có chung các trường dữ liệu cùng các phương thức nào đó, chúng ta sẽ abstract (trừu tượng hóa) các phương thức và trường dữ liệu đó ra một lớp riêng gọi là lớp cha. Các lớp còn lại, chúng ta cho kế thừa cái lớp mà chúng ta vừa mới tạo. Như vậy chúng ta không cần viết đi viết lại các đoạn code giống nhau nữa, mà chỉ cần viết những đoạn code khác nhau ở các lớp con thôi. Còn những đoạn code giống nhau thì lớp con nào cũng có, vì nó đã được kế thừa từ lớp cha. --> đây là một biểu hiện của việc dùng lại code.
Polymorphism (đa hình): Đa hình chính là mục tiêu lớn nhất của kế thừa (inheritance). Việc override hoặc overload một phương thức là biểu hiện của đa hình. Khi bạn viết một lớp có tên là Animal, bạn viết tiếp một lớp con của lớp Animal là lớp Cat, sau đó trong một lớp khác, bạn viết một phương thức mà tham số của phương thức đó là Animal: program.Kill(Animal a), đến khi sử dụng phương thức này, bạn không truyền vào một một đối tượng của lớp Animal, mà bạn truyền vào một đối tượng của lớp Cat: program.Kill(Cat c), đây là một ví dụ nữa của đa hình.
Đăng ký:
Bài đăng (Atom)