Tải bản đầy đủ (.pdf) (10 trang)

Các giải pháp lập trình CSharp- P76 pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.61 MB, 10 trang )

641
Chương 16: Các giao diện và mẫu thông dụng

Hiện thực thêm các thuộc tính và các thành viên dữ liệu để hỗ trợ trạng thái sự kiện mà
bạn cần truyền cho các phương thức thụ lý sự kiện. Tốt nhất là làm cho trạng thái sự
kiện trở nên bất biến (immutable), như vậy bạn nên sử dụng các thành viên dữ liệu
private

readonly
và sử dụng các thuộc tính
public
để cho phép truy xuất chỉ-đọc đến
các thành viên dữ liệu này.

Hiện thực một phương thức khởi dựng
public
hỗ trợ cấu hình ban đầu của trạng thái sự
kiện.

Làm cho lớp đối số sự kiện của bạn trở nên khả-tuần-tự-hóa (serializable) để bộ thực
thi có thể marshal các thể hiện của nó qua các biên miền ứng dụng và biên máy. Áp
dụng đặc tính
System.SerializableAttribute
thường là đã đủ cho các lớp đối số sự
kiện. Tuy nhiên, nếu lớp đối số sự kiện có các yêu cầu tuần tự hóa đặc biệt, bạn phải
hiện thực giao diện
System.Runtime.Serialization.ISerializable
(xem mục 16.1 để
biết cách làm cho một lớp trở nên khả-tuần-tự-hóa).
Đoạn mã dưới đây trình bày một lớp đối số sự kiện tùy biến có tên là
MailReceivedEventArgs


.
Giả sử có một mail-server truyền các thể hiện của lớp
MailReceivedEventArgs
cho các
phương thức thụ lý sự kiện nhận một thông điệp e-mail. Lớp này chứa các thông tin về người
gửi và chủ đề của thông điệp e-mail.
using System;
[Serializable]
public sealed class MailReceivedEventArgs : EventArgs {
// Các thành viên private readonly giữ trạng thái sự kiện
// (được phân bổ cho tất cả các phương thức thụ lý sự kiện).
// Lớp MailReceivedEventArgs sẽ cho biết ai đã gửi mail
// và chủ đề là gì.
private readonly string from;
private readonly string subject;
// Phương thức khởi dựng (khởi tạo trạng thái sự kiện).
public MailReceivedEventArgs(string from, string subject) {
this.from = from;
this.subject = subject;
}
// Các thuộc tính chỉ-đọc cho phép truy xuất
// trạng thái sự kiện.
public string From { get { return from; } }
642
Chương 16: Các giao diện và mẫu thông dụng
public string Subject { get { return subject; } }
}
9.
9.
Hi n th c m u Singletonệ ự ẫ

Hi n th c m u Singletonệ ự ẫ


Bạn cần bảo đảm chỉ có một thể hiện của một kiểu tồn tại ở một thời điểm cho
trước và thể hiện đó là khả-truy-xuất đối với tất cả các phần tử của ứng dụng.


Hiện thực kiểu này theo mẫu
Singleton
như sau:

Hiện thực một thành viên tĩnh
private
để giữ một tham chiếu đến thể hiện
của kiểu.

Hiện thực một thuộc tính tĩnh khả-truy-xuất-công-khai để cho phép truy
xuất chỉ-đọc đến thể hiện.

Hiện thực một phương thức khởi dựng
private
để mã lệnh không thể tạo
thêm các thể hiện của kiểu.
Trong tất cả các mẫu được biết đến, có lẽ mẫu Singleton được biết đến nhiều nhất và thường
được sử dụng nhất. Mục đích của mẫu Singleton là bảo đảm chỉ có một thể hiện của một kiểu
tồn tại ở một thời điểm cho trước và cho phép truy xuất toàn cục đến các chức năng của thể
hiện đó. Đoạn mã dưới đây trình bày một hiện thực của mẫu Singleton cho một lớp có tên là
SingletonExample
:
public class SingletonExample {

// Thành viên tĩnh dùng để giữ một tham chiếu đến thể hiện singleton.
private static SingletonExample instance;
// Phương thức khởi dựng tĩnh dùng để tạo thể hiện singleton.
// Một cách khác là sử dụng "Lazy Initialization" trong
// thuộc tính Instance.
static SingletonExample () {
instance = new SingletonExample();
}

// Phương thức khởi dựng private dùng để ngăn mã lệnh tạo thêm
// các thể hiện của kiểu singleton.
private SingletonExample () {}

// Thuộc tính public cho phép truy xuất đến thể hiện singleton.
public static SingletonExample Instance {
643
Chương 16: Các giao diện và mẫu thông dụng

get { return instance; }
}

// Các phương thức public cung cấp chức năng của singleton.
public void SomeMethod1 () { /* */ }
public void SomeMethod2 () { /* */ }
}
Để gọi các chức năng của lớp
SingletonExample
, bạn có thể lấy về một tham chiếu đến
singleton bằng thuộc tính
Instance

và rồi gọi các phương thức của nó. Bạn cũng có thể trực
tiếp thực thi các thành viên của singleton thông qua thuộc tính
Instance
. Đoạn mã dưới đây
trình bày cả hai cách này:
// Thu lấy tham chiếu đến singleton và gọi một phương thức của nó.
SingletonExample s = SingletonExample.Instance;
s.SomeMethod1();

// Thực thi chức năng của singleton mà không cần tham chiếu.
SingletonExample.Instance.SomeMethod2();
10.
10.
Hi n th c m u Observerệ ự ẫ
Hi n th c m u Observerệ ự ẫ


Bạn cần hiện thực một cơ chế hiệu quả để một đối tượng (
subject
) báo với các đối
tượng khác (
observer
) về những thay đổi trạng thái của nó.


Hiện thực mẫu
Observer
bằng các kiểu ủy nhiệm (đóng vai trò là các con trỏ hàm
an-toàn-về-kiểu-dữ-liệu—
type-safe function pointer

) và các kiểu sự kiện.
Cách truyền thống khi hiện thực mẫu Observer là hiện thực hai giao diện: một để mô tả
observer (
IObserver
) và một để mô tả subject (
ISubject
). Các đối tượng có hiện thực
IObserver
sẽ đăng ký với subject, cho biết chúng muốn được thông báo về các sự kiện quan
trọng (như thay đổi trạng thái) tác động đến subject. Subject chịu trách nhiệm quản lý danh
sách các observer đã đăng ký và thông báo với chúng khi đáp ứng các sự kiện tác động đến
subject. Subject thường thông báo với observer bằng phương thức
Notify
(được khai báo
trong giao diện
IObserver
). Subject có thể truyền dữ liệu cho observer trong phương thức
Notify
, hoặc observer có thể cần gọi một phương thức được khai báo trong giao diện
ISubject
để thu lấy thêm các chi tiết về sự kiện.
Mặc dù bạn có thể hiện thực mẫu Observer bằng C# theo cách vừa được mô tả, nhưng vì mẫu
Observer quá phổ biến trong các giải pháp phần mềm hiện đại nên C# và .NET Framework
cung cấp các kiểu sự kiện và ủy nhiệm để đơn giản hóa hiện thực của nó. Sử dụng sự kiện và
ủy nhiệm nghĩa là bạn không cần khai báo giao diện
IObserver

ISubject
. Ngoài ra, bạn
không cần hiện thực các logic cần thiết để quản lý và thông báo với các observer đã đăng ký

—đây chính là phần dễ xảy ra lỗi nhất khi viết mã.
644
Chương 16: Các giao diện và mẫu thông dụng
.NET Framework sử dụng một hiện thực cho mẫu Observer dựa-trên-sự-kiện và dựa-trên-ủy-
nhiệm thường xuyên đến nỗi nó được đặt một cái tên: mẫu Event. File ObserverExample.cs
chứa một hiện thực cho mẫu Event. Ví dụ này bao gồm:

Lớp
Thermostat
(subject)—theo dõi nhiệt độ hiện tại và thông báo với observer khi có
sự thay đổi nhiệt độ.

Lớp
TemperatureChangeEventArgs
—là một hiện thực tùy biến của lớp
System.EventArgs
, được sử dụng để đóng gói dữ liệu cần phân bổ cho observer.

Ủy nhiệm
TemperatureEventHandler
—định nghĩa chữ ký của phương thức mà tất cả
các observer của đối tượng
Thermostat
phải hiện thực, và đối tượng
Thermostat
sẽ gọi
phương thức này trong sự kiện thay đổi nhiệt độ.

Lớp
TemperatureChangeObserver


TemperatureAverageObserver
—là các observer
của lớp
Thermostat
.
Lớp
TemperatureChangeEventArgs
(được trình bày bên dưới) dẫn xuất từ lớp
System.EventArgs
. Lớp này sẽ chứa tất cả các dữ liệu cần thiết để subject truyền cho các
observer khi nó thông báo với chúng về một sự kiện. Nếu không cần truyền dữ liệu, bạn
không phải định nghĩa một lớp đối số mới (bạn chỉ cần truyền đối số
null
khi dựng nên sự
kiện). Xem mục 16.8 để biết rõ hơn về cách hiện thực lớp đối số sự kiện tùy biến.
// Lớp đối số sự kiện chứa thông tin về sự kiện thay đổi nhiệt độ.
// Một thể hiện của lớp này sẽ được truyền cùng với mỗi sự kiện.
public class TemperatureChangeEventArgs : System.EventArgs {
// Các thành viên dữ liệu chứa nhiệt độ cũ và mới.
private readonly int oldTemperature, newTemperature;

// Phương thức khởi dựng (nhận giá trị nhiệt độ cũ và mới).
public TemperatureChangeEventArgs(int oldTemp, int newTemp) {

oldTemperature = oldTemp;
newTemperature = newTemp;
}

// Các thuộc tính dùng để truy xuất các giá trị nhiệt độ.

public int OldTemperature { get { return oldTemperature; } }
public int NewTemperature { get { return newTemperature; } }
}
Đoạn mã dưới đây trình bày cách khai báo ủy nhiệm
TemperatureEventHandler
. Dựa trên khai
báo này, tất cả các observer phải hiện thực một phương thức (tên không quan trọng), trả về
645
Chương 16: Các giao diện và mẫu thông dụng
void
và nhận hai đối số: một đối tượng
Thermostat
và một đối tượng
TemperatureChangeEventArgs
. Trong quá trình thông báo, đối số
Thermostat
chỉ đến đối
tượng
Thermostat
đã dựng nên sự kiện, và đối số
TemperatureChangeEventArgs
chứa các dữ
liệu về nhiệt độ cũ và mới.
// Ủy nhiệm cho biết chữ ký mà tất cả các phương thức
// thụ lý sự kiện phải hiện thực.
public delegate void TemperatureEventHandler(Thermostat s,
TemperatureChangeEventArgs e);
Lớp
Thermostat
là đối tượng bị giám sát trong mẫu Observer (Event) này. Theo lý thuyết, một

thiết bị giám sát thiết lập nhiệt độ hiện tại bằng cách gọi thuộc tính
Temperature
trên một đối
tượng
Thermostat
. Khi đó, đối tượng
Thermostat
dựng nên sự kiện
TemperatureChange
và gửi
một đối tượng
TemperatureChangeEventArgs
đến mỗi observer. Dưới đây là mã lệnh cho lớp
Thermostat
:
// Lớp mô tả một bộ ổn nhiệt, là nguồn gốc của các sự kiện thay đổi
// nhiệt độ. Trong mẫu Observer, đối tượng Thermostat là subject.
public class Thermostat {

// Trường private dùng để giữ nhiệt độ hiện tại.
private int temperature = 0;

// Sự kiện dùng để duy trì danh sách các ủy nhiệm observer và
// dựng nên sự kiện thay đổi nhiệt độ.
public event TemperatureEventHandler TemperatureChange;
// Phương thức protected dùng để dựng nên sự kiện TemperatureChange.
// Vì sự kiện chỉ có thể được phát sinh bên trong kiểu chứa nó nên
// việc sử dụng phương thức protected cho phép các lớp dẫn xuất
// có cách hành xử tùy biến và vẫn có thể dựng nên sự kiện
// của lớp cơ sở.

virtual protected void RaiseTemperatureEvent
(TemperatureChangeEventArgs e) {

if (TemperatureChange != null) {

TemperatureChange(this, e);
}
}

// Thuộc tính public dùng để lấy và thiết lập nhiệt độ hiện tại.
// Phía "set" chịu trách nhiệm dựng nên sự kiện thay đổi nhiệt độ
646
Chương 16: Các giao diện và mẫu thông dụng
// để thông báo với tất cả các observer về thay đổi này.
public int Temperature {

get { return temperature; }

set {
// Tạo một đối tượng đối số sự kiện mới để chứa
// nhiệt độ cũ và mới.
TemperatureChangeEventArgs e =
new TemperatureChangeEventArgs(temperature, value);

// Cập nhật nhiệt độ hiện tại.
temperature = value;

// Dựng nên sự kiện thay đổi nhiệt độ.
RaiseTemperatureEvent(e);
}

}
}
Với mục đích minh họa mẫu Observer, ví dụ này có hai kiểu observer:
TemperatureAverageObserver
(hiển thị nhiệt độ trung bình mỗi khi một sự kiện thay đổi nhiệt
độ xảy ra) và
TemperatureChangeObserver
(hiển thị thông tin về sự thay đổi mỗi khi một sự
kiện thay đổi nhiệt độ xảy ra). Dưới đây là mã lệnh cho hai lớp này:
// Observer hiển thị thông tin về sự thay đổi nhiệt độ
// khi có một sự kiện thay đổi nhiệt độ xảy ra.
public class TemperatureChangeObserver {

// Phương thức khởi dựng (nhận một tham chiếu đến đối tượng
// Thermostat cần được TemperatureChangeObserver giám sát).
public TemperatureChangeObserver(Thermostat t) {

// Tạo một thể hiện ủy nhiệm TemperatureEventHandler và
// đăng ký nó với Thermostat đã được chỉ định.
t.TemperatureChange +=
new TemperatureEventHandler(this.TemperatureChange);
}
647
Chương 16: Các giao diện và mẫu thông dụng

// Phương thức thụ lý sự kiện thay đổi nhiệt độ.
public void TemperatureChange(Thermostat sender,
TemperatureChangeEventArgs temp) {

System.Console.WriteLine

("ChangeObserver: Old={0}, New={1}, Change={2}",
temp.OldTemperature, temp.NewTemperature,
temp.NewTemperature - temp.OldTemperature);
}
}
// Observer hiển thị thông tin về nhiệt dộ trung bình
// khi có một sự kiện thay đổi nhiệt độ xảy ra.
public class TemperatureAverageObserver {

// Sum chứa tổng các giá trị nhiệt độ.
// Count chứa số lần sự kiện thay đổi nhiệt độ xảy ra.
private int sum = 0, count = 0;

// Phương thức khởi dựng (nhận một tham chiếu đến đối tượng
// Thermostat cần được TemperatureAverageObserver giám sát).
public TemperatureAverageObserver (Thermostat t) {

// Tạo một thể hiện ủy nhiệm TemperatureEventHandler và
// đăng ký nó với Thermostat đã được chỉ định.
t.TemperatureChange +=
new TemperatureEventHandler(this.TemperatureChange);
}

// Phương thức thụ lý sự kiện thay đổi nhiệt độ.
public void TemperatureChange(Thermostat sender,
TemperatureChangeEventArgs temp) {

count++;
sum += temp.NewTemperature;


System.Console.WriteLine
648
Chương 16: Các giao diện và mẫu thông dụng
("AverageObserver: Average={0:F}",
(double)sum/(double)count);
}
}
Lớp
Thermostat
định nghĩa một phương thức
Main
(được trình bày bên dưới) để chạy ví dụ
này. Sau khi tạo một đối tượng
Thermostat
và hai đối tượng observer, phương thức
Main
sẽ
nhắc bạn nhập vào một nhiệt độ. Mỗi khi bạn nhập một nhiệt độ mới, đối tượng
Thermostat
sẽ
báo cho observer hiển thị thông tin ra cửa sổ Console.
public static void Main() {
// Tạo một đối tượng Thermostat.
Thermostat t = new Thermostat();

// Tạo hai observer.
new TemperatureChangeObserver(t);
new TemperatureAverageObserver(t);

// Lặp để lấy nhiệt độ từ người dùng. Bất cứ giá trị

// nào không phải số nguyên sẽ khiến vòng lặp kết thúc.
do {

System.Console.Write("\n\rEnter current temperature: ");

try {
// Chuyển đầu vào của người dùng thành một số
// nguyên và sử dụng nó để thiết lập nhiệt độ
// hiện tại của bộ ổn nhiệt.
t.Temperature =
System.Int32.Parse(System.Console.ReadLine());

} catch (System.Exception) {
// Sử dụng điều kiện ngoại lệ để kết thúc vòng lặp.
System.Console.WriteLine("Terminating ObserverExample.");
return;
}
} while (true);
649
Chương 16: Các giao diện và mẫu thông dụng
}
Dưới đây là kết xuất khi chạy ObserverExample.cs (các giá trị in đậm là do bạn nhập vào):
Enter current temperature: 50
ChangeObserver: Old=0, New=50, Change=50
AverageObserver: Average=50.00
Enter current temperature: 20
ChangeObserver: Old=50, New=20, Change=-30
AverageObserver: Average=35.00
Enter current temperature: 40
ChangeObserver: Old=20, New=40, Change=20

AverageObserver: Average=36.67
650
Chương 16: Các giao diện và mẫu thông dụng

×