Sau đó kiểm tra câu trả lời củabạn đối với các cột bên tay trái IComponent ComponentCompositeOperation Một đơn vị có thể nhìn thấy trongiPhoto Một bức ảnh đơnMột album ảnh Mở và xem Thực
Trang 1CHƯƠNG III: CÁC MẪU CẤU TRÚC: COMPOSITE VÀ FLYWEIGHT
Các mẫu cấu trúc Composite1 và Flyweight áp dụng cho hệ thống cónhiều đối tượng dữ liệu Mẫu Composite có ứng dụng rộng rãi, và danh sáchtổng hợp của nó cũng có thể sử dụng mẫu Flyweight Mẫu Flyweight chia sẻcác đối tượng giống hệt nhau ở hậu trường để tiết kiệm bộ nhớ Trong việctriển khai của chúng, các mẫu này sử dụng các tính năng mới của C# sau đây:
Minh hoạ
Ứng dụng máy tính chuyên về nhóm dữ liệu có rất nhiều những ngàynày Xem xét danh sách nhạc trong iTunes hoặc một album ảnh số trongFlickr hoặc iPhoto (Hình 3-1) Các mục được đặt trong một danh sách lớn,sau đó được đưa vào cấu trúc riêng biệt
1 Tổng hợp
Trang 2Hình 2-1 Hình minh họa mẫu Composite —iPhoto
Nhìn vào ảnh chụp màn hình từ iPhoto, chúng ta có thể thấy rằng cónhiều cách khác nhau để xem hình ảnh đã được nhập: thứ tự thời gian hoặctên sự kiện Một bức ảnh duy nhất có thể xuất hiện trong nhiều album (Ví dụ:
"Blog", "HaLong", "2014") Tạo ra một album dưới hình thức một đối tượnghỗn hợp nhưng không đòi hỏi sao chép thực sự các bức ảnh Trong ngữ cảnhnày, điểm quan trọng về mẫu Composite là các hoạt động thực hiện trên ảnh
và album ảnh phải có tên và hiệu ứng tương tự, mặc dù việc triển khai khácnhau Ví dụ, người sử dụng sẽ có thể hiển thị một hình ảnh hoặc một album(có chứa hình ảnh) Tương tự như vậy, xóa một ảnh hay một album sẽ hành
xử theo cùng một cách
Thiết kế
Mẫu composite là một trong những mẫu đơn giản nhất, trên giao diệncủa nó Nó phải ứng phó với hai thứ: thành phần (Component) và tổng hợp(Composites) của những thành phần đó Cả hai loại được thống nhất chophù hợp với một giao diện của các hoạt động chung Đối tượng Compositechứa các Component, và trong nhiều trường hợp, các hoạt động trên mộtComposite được thực hiện bằng cách gọi các hoạt động tương đương chocác đối tượng Component của nó Xem hình 3-2 với sơ đồ UML cho mẫunày
Các yếu tố cần thiết trong sơ đồ UML mẫu Composite là:
IComponent
Xác định các thao tác cho các đối tượng trong cơ cấu, và bất kỳ hành vimặc định nào sẽ được áp dụng trên các đối tượng của cả hai loại
Trang 3Hình 3-2 Sơ đồ UML mẫu Composite
Client giao tiếp chỉ với giao diện IComponent, đơn giản hoá nhiệm
vụ của nó
Câu hỏi
Khớp các yếu tố mẫu Composite với minh họa iPhoto
Để kiểm tra xem bạn hiểu mẫu Composite, che cột trái của bảng dưới đây vàxem liệu bạn có thể xác định các đối tượng trong các mục từ ví dụ minh họa(Hình 3-1), như thể hiện trong cột bên tay phải Sau đó kiểm tra câu trả lời củabạn đối với các cột bên tay trái
IComponent
ComponentCompositeOperation
Một đơn vị có thể nhìn thấy trongiPhoto
Một bức ảnh đơnMột album ảnh
Mở và xem
Thực hiện
Mặc dù minh họa của chúng tôi đề cập vào ảnh và album, thiết kế vàtriển khai mẫu Composite là hoàn toàn độc lập với kiểu thành phần cơ bảnđược xử lý Chúng ta có thể thực hiện tốt như nhau với các nhóm người hoặc
Trang 4danh mục đầu tư tài khoản ngân hàng Các hoạt động tổng hợp, Ngược lại,phải lặp qua một cấu trúc của các thành phần này Để thực hiện cả sự linhhoạt của các thành phần của bất kỳ dạng nào và một kết nối giữa các thành
phần và yếu tố tổng hợp, chúng ta có thể sử dụng tính năng C# Generics.
Tính năng C#—Generics
Generics là một phần mở rộng với hệ thống kiểu nào đó theo đó cấu trúc, lớp,giao diện, các delegate (đại biểu) và các phương thức có thể được tham số vớicác kiểu này Ví dụ, một kiểu chung như List<T> là một bộ tạo với nhiềuloại như xây dựng List<T> và List<Person> Tất cả các lớp chứa(collection) trong thư viện NET Framework sử dụng bởi C # có sẵn ở dạnggeneric Chúng bao gồm Dictionary, Stack, Queue, và List, cộng vớicác biến thể trên chúng Ngoài ra còn có các phương pháp chung như Sort
và BinarySearch Những yêu cầu dạng này cần phải thực hiện giao diệnIComparer sao cho sự so sánh giữa các thành phần có thể được thực hiệnmột cách chính xác
Để khai báo một kiểu hoặc phương thức generic, sử dụng các tham số generictrong dấu ngoặc nhọn, như <T> hoặc <T, P> Kiểu generic có thể đượcdùng để khai báo thành phần generic, một lần nữa bằng cách sử dụng địnhdạng <T>, hoặc nó có thể được sử dụng bình thường như là một kiểu, như T
Để xây dựng một kiểu thực tế hoặc phương thức từ một generic, khai báo cáckiểu thực tế cho mỗi thông số chung chung, như <string>
Theo C# Language Specification phiên bản 3.0, Tháng chín 2007, mục 10.1.3
Bằng cách khai báo IComponent, Component, và Compositenhư các kiểu chung chung, chúng tôi tạo ra một thực thi mẫu Composite cóthể được khởi tạo trong một ví dụ thực tế
Đối với mẫu Composite, chúng ta sẽ không tạo một chương trình,
mà tạo một không gian tên (namespace) có chứa hai lớp,
Composite và Component, và một giao diện, IComponent.Bắt đầu với giao diện IComponent, chúng ta khai báo nó như là mộtgeneric cuả kiểu T Sau đó, mỗi hoạt động dự kiến như sau:
public interface IComponent<T>
{
void Add(IComponent<T> c);
IComponent<T> Remove(T s);
IComponent<T> Find(T s);
string Display(int depth);
T Item { get; set; }
}
Trang 5Đối với mẫu Composite, chúng ta sẽ không tạo một chương trình,
mà tạo một không gian tên (namespace) có chứa hai lớp,
Composite và Component, và một giao diện, IComponent.Tại sao Add, Remove, và Find đề cập đến IComponent<T> vàRemove và Find đề cập đến T như vậy? Sự khác biệt giữa <T> và T là gì?Nhớ lại rằng một đối tượng kiểu IComponent hoặc là một Componenthoặc một Composite và sẽ có trạng thái liên quan là một IComponent,ngoài giá trị thực tế của phần tử đó được lưu trữ Các phần tử là kiểu T, vì vậytrong trường hợp của Find và Remove, chúng ta truyền tham số là yếu tốgiá trị thực tế của kiểu T Ví dụ, nếu giao diện được khởi tạo với T là mộtImage, chúng tôi sẽ truyền một đối tượng Image tới Find và ngược lại cóđược một tham chiếu đến một đối tượng IComponent Đối tượng trả về sẽchứa đối tượng Image nếu tìm kiếm đã thành công
Dòng cuối cùng của giao diện thú vị bởi vì nó giới thiệu một cú phápmới cho các thuộc tính trong lớp Trước C# 3.0, cú pháp mới này chỉ xuấthiện trong giao diện Nó đã được mở rộng đến các lớp và làm cho chươngtrình ngắn hơn và dễ đọc hơn
Tính năng C#—Thuộc tính (Properties) và Bộ truy xuất (Accessors)
Một thuộc tính (property) được kiểm soát cho phép truy cập đến các trạng thái
cục bộ riêng của một đối tượng, trực tiếp đến các trường của nó hoặc qua một
số tính toán Thuộc tính xác định một hoặc hai bộ truy cập: get và/hoặc set.Hình thức phổ biến nhất của một thuộc tính là:
public type Field {get; set;}
nơi mà Field là một định danh Khai báo này tạo ra một trường riêng tư vàlàm cho nó có thể truy cập để đọc và ghi công khai thông qua tên Field.Bằng cách đặt thuộc tính như vậy trong một giao diện, chúng tôi yêu cầu mộtcách hiệu quả các lớp phải cung cấp bộ truy cập tài sản như những phươngthức thông thường khi thực hiện giao diện
Thuộc tính có thể bỏ qua bộ truy xuất get hoặc set; thường là bỏ qua set,làm cho thuộc tính chỉ có thể đọc
Thuộc tính cũng có thể tính toán kết quả của get hoặc set, trong trường hợpnày, cú pháp mở rộng là:
public type Field
{
get { statements; return expression; }
set { statements including reference to value; }
}
Một thuộc tính thông thường, nhưng không nhất thiết, có thể truy cập tới mộtbiến riêng kiểu Field value là tham số tiềm ẩn của một lời gọi để thiết lậpmột thuộc tính
Trang 6Theo C# Language Specification phiên bản 3.0, Tháng chín 2007, mục 10.7
Bây giờ, hãy xem xét làm thế nào các lớp Component sẽ được thựcthi phù hợp với giao diện IComponent, như trong không gian tên hiển thịtrong Ví dụ 3-1
Ví dụ 3-1 Code không gian tên - mẫu Composite
18 public T Name { get; set; }
19 public Component(T name)
48 public T Name { get; set; }
49 public Composite(T name)
50 {
51 Name = name; list = new List<IComponent<T>>();
52 }
Trang 753 public void Add(IComponent<T> c)
54 {
55 list.Add(c);
56 }
57 IComponent<T> holder = null;
58 // Finds the item from a particular point in the structure
59 // and returns the composite from which it was removed
60 // If not found, return the point as given
61 public IComponent<T> Remove(T s)
73 // Recursively looks for an item
74 // Returns its reference or else null
75 public IComponent<T> Find(T s)
76 {
77 holder = this;
78 if (Name.Equals(s)) return this;
79 IComponent<T> found = null;
80 foreach (IComponent<T> c in list)
do đó, một thông báo lỗi đơn giản được viết ra (xem phần "bài tập" sắp tới cómột mở rộng ở điểm này) Trong phương thức Find của Component (dòng
Trang 836-42), các lớp dựa trên các tham số kiểu thực cho T cần có một phương thứcEquals Nếu không, thực thể generic sẽ lỗi tại thời gian biên dịch.
Phương thức Display (dòng 32-35) cũng giả định rằng trường truycập bởi các thuộc tính Name có một phương thức ToString xác định.Trong thực tế, chỉ các kiểu "string-like" sẽ hoạt động ở đây, vì vậy có khảnăng là phương thức Display cần được định nghĩa sau (xem phần "Bài tập"sắp tới.)
Lớp Composite cũng thực thi giao diện IComponent (dòng 45).Phương thức Find và Remove của Composite có nhiều công phu hơn sovới những thứ khác trong Component để chúng có thể xử lý các cấu trúc tùy
ý của tổng hợp và các thành phần Chúng ta hãy xem xét một số điều thú vịhơn:
Composite giữ dưới dạng danh sách một cấu trúc cục bộ bao gồmcác Component và Composite (dòng 47) Khi nội dung không cócác Composite, một đối tượng mới được tạo ra, cũng như một danhsách mới Danh sách được khai báo là:
List <IComponent <T>> list;
Điều này cho thấy một kiểu generic mở có thể được sử dụng như mộttham số đến một kiểu generic
Logic của Remove (dòng 61-72) là chúng ta đầu tiên tìm đối tượngtrong cấu trúc và sau đó, nếu nó có, chúng ta loại bỏ nó khỏi cấu trúcdanh sách được tổ chức cục bộ trong Composite (dòng 67):
Đây mới chỉ là việc thực hiện mẫu Composite lý thuyết Ngoài mốiquan tâm với phương thức Display, ba kiểu trước có thể được sử dụngchung với bất kỳ thành phần nào Do đó, chúng ta có thể đặt chúng trong mộtkhông gian tên gọi là CompositePattern để sử dụng trong các ví dụ tiếptheo Ví dụ: Photo Library
Trang 9Trong ví dụ này, chúng ta đang quan tâm đến việc thu thập tên tập tinhình ảnh số vào tập hợp đã đặt tên Chúng ta sẽ không sử dụng hình ảnh thật
sự trong ví dụ này, chỉ tên tập tin là chuỗi Client đưa ra một tập miền cụ thểcủa lệnh nào đó để tạo ra và thao tác với thư viện Trung tâm các thao tác củathư viện, từ quan điểm của người sử dụng, là "nơi chúng đang có" Chúng tabắt đầu với một tập hợp rỗng được gọi là "Album" Một số lệnh rời khỏi hệthống ở các thành phần vừa được điều chỉnh, trong khi những thứ khác dichuyển nó trở lại thành phần đầu tiên trong tập hợp này Những lệnh này là:AddSet
Thêm một tập rỗng mới với một cái tên và để tại album đó
AddPhoto
Thêm một bức ảnh mới được đặt tên sau con trỏ và để tại album đó Find
Tìm các thành phần đã đặt tên (tập hoặc hình ảnh), hoặc trả về null nếu
nó không được tìm thấy
Các tập tin đầu vào có chứa tất cả các lệnh ở giữa Chương trình
sẽ mong đợi tập tin này phải được gọi là Composite.dat
Ví dụ 3-2 Mẫu Composite—Chương trình Photo Library xuất ra
Trang 10AddPhoto Spring.jpg
AddPhoto Summer.jpg
AddPhoto Flowers.jpg
AddPhoto Trees.jpg
Display Returns to start of Album
Set Album length :2
Set Home length :2
Lớp Client mà thực thi các lệnh này được hiển thị trong ví dụ 3-3 Nó
là một trình thông dịch lệnh đơn giản mà làm tốt việc sử dụng C # 's chuyểnđổi trên tính năng chuỗi
Ví dụ 3-3 Ví dụ code mẫu Composite—Photo Library
IComponent<string> album = new Composite<string>("Album");
IComponent<string> point = album;
string[] s;
string command, parameter;
// Create and manipulate a structure
StreamReader instream = new StreamReader("Composite.dat");
Trang 11Sử dụng mẫu Composite khi
Để xử lý tất cả các đối tượng trong một sự kết hợp thống nhất
Nhưng xem xét việc sử dụng cũng như vậy:
1 Mẫu Decorator để cung cấp các thao tác như Add, loại bỏ, và Find
2 Mẫu Flyweight để chia sẻ các thành phần, quy định các khái niệm của
"nơi tôi ở" có thể được bỏ qua và tất cả các thao tác bắt đầu từ gốc củahợp
3 Mẫu Visitor cho phép bản địa hoá các hoạt động hiện tại được phânphối giữa các lớp Composite và Component
Bài tập
1 Một người quản lý là một nhân viên dẫn đầu các kỹ sư, kỹ thuật viên,
và nhân viên hỗ trợ, tất cả họ cũng là nhân viên Tập trung vào các hoạtđộng sắp nghỉ phép, mô hình hoá trường hợp này với mẫu Composite
2 Trong ví dụ mẫu Composite, lớp Component có hai điều kiện lỗi Thựchiện chúng bằng cách sử dụng các ngoại lệ, và quyết định các ngoại lệphải là một phần của giao diện IComponent Nói chung, làm thế nàobạn suy nghĩ các mẫu phải xử lý ngoại lệ?
Trang 123 Các phương thức Display trong Component và Composite giảđịnh rằng kiểu T là string-like Tạo ra một phiên bản của mẫuComposite trong đó T không phải là một kiểu có thể chuyển đổi thànhcác chuỗi (ví dụ như dùng hình ảnh) và điều tra xem liệu nhưngphương thưc Display có thể được ghi đè bằng các phương thức mởrộng trong không gian tên của Client.
4 Trong lớp Composite, thay đổi List thành một Dictionary.Như thế nào mà điều này sẽ ảnh hưởng đến thao tác Find?
5 Mặc dù chúng tôi vẫn cố gắng để có một giao diện cho các thành phần
và tổng hợp, một cách tiếp cận khác là xem xét các hoạt động thực sựphổ biến và tách riêng những thứ chỉ áp dụng, ví dụ, tổng hợp Sau đóchúng ta sẽ có hai cấp độ của các giao diện, như sau:
interface IComponent {
// Name, Display }
interface IComposite<T> where T : IComponent {
// Add, Remove, Find } // 1
Bây giờ, không có nhu cầu cho IComponent thành generic—mộtcomponent chỉ là một cái gì đó mà có một tên và có thể được hiển thị.Những căng thẳng khi sử dụng T và khi sử dụng IComponent<T>mất đi, quá Lập trình lại ví dụ theo cách này (Gợi ý: giao diện thứ hai
ở trên sử dụng các ràng buộc generic, được dùng nhiều trong Chương6.)
Trạng thái nội tại có thể được chia sẻ trên một quy mô rộng, giảm thiểuyêu cầu lưu trữ
1 Nguyên bản của tác giả
interface IComponent {
// Name, Display
}
interface IComposite<T> IComponent where T : IComponent {
// Add, Remove, Find
}
Nhưng mình tra cứu từ khoá where ( http://msdn.microsoft.com/en-us/library/bb384067.aspx ) trên MSDN thì không được có IComponent ở chỗ gạch chân, gõ trong VS thì phải bỏ cái chỗ đó.
Trang 13 Trạng thái ngoại sinh có thể tính được, nhằm tính toán giao dịch để lưutrữ.
Minh hoạ
Xem xét tại phương diện hình ảnh của ứng dụng Thư viện ảnh, nhưthảo luận trong mẫu Composite Tại một thời điểm, chúng tôi muốn có mộttrang đầy đủ của hình ảnh được hiển thị, và không có thời gian trễ nhận thấyđược, chúng tôi muốn có thể cuộn lên và xuống thông qua thư viện Điều nàyngụ ý phải có nhiều hình ảnh nhất có thể nên được cài đặt sẵn vào bộ nhớ vàgiữ ở đó trong khi ứng dụng hình ảnh đang chạy Với một ứng dụng PhotoGroup, chức năng chính là sắp xếp các hình ảnh theo nhóm Các hình ảnh cóthể thuộc nhiều nhóm khác nhau, vì vậy số lượng hình ảnh hiển thị có thể tăngrất nhiều Nếu tất cả các hình ảnh không đủ trong bộ nhớ, thực tế là chúng cóthể thuộc các nhóm khác nhau có nghĩa là bức ảnh bất kì cụ thể có thể đượcgọi lên để hiển thị tại thời gian không đều, dẫn đến rất nhiều các quá trình đọcđĩa
Xem xét các minh họa trong hình 3-3 Với một cửa sổ nhỏ hơn, hainhóm đầu tiên có thể cuộn qua cửa sổ vào thời điểm nhóm Food xuất hiện,đòi hỏi phải có ít nhất ba trong số những hình ảnh được tái nạp và hiển thị
Trạng thái không chia sẻ của một đối tượng là tập hợp của các nhóm
mà nó thuộc về Trạng thái bên ngoài của nó là hình ảnh thực tế, nó lớn chiếmkhoảng 2 MB Tuy nhiên, có những phương thức trong namespaceSystem.Drawing để chuyển đổi hình ảnh vào một hình ảnh thu nhỏ trongkhoảng 8 KB Điều này sẽ thành trạng thái nội tại của đối tượng, đủ nhỏ đểcho phép tất cả các hình ảnh duy nhất ở lại trong bộ nhớ tại một thời điểm.Thông qua các thông tin nhóm, các ứng dụng có thể hiển thị những hình ảnhtrong các kết hợp khác nhau Bằng cách sử dụng khi nạp đĩa, nó cũng có thểhiển thị những hình ảnh với kích thước hoàn chỉnh ban đầu
Thiết kế
Xem xét các sơ đồ UML trong hình 3-4 Như đã giải thích ở phầntrước, các mô hình Flyweight dựa vào khả năng phân chia trạng thái của ứngdụng thành ba loại Các intrinsicState nằm trong đối tượngFlyweight Lớp Flyweight thực thi một giao diện IFlyweight, xácđịnh các hoạt động trên đó phần còn lại của hệ thống dựa Khách hàng duy trìunSharedState cũng như một từ điển của tất cả các Flyweights, mà nónhận được từ một FlyweightFactory có công việc là để đảm bảo rằngchỉ một trong mỗi giá trị được tạo ra Cuối cùng, extrinsicState khôngxuất hiện trong các hệ thống như vậy; nó có nghĩa là phải được tính trong thờigian chạy cho mỗi trường hợp instrinsicState, theo yêu cầu
Các yếu tố của mẫu Flyweight gồm:
Client