Sơ đồ lớp mẫu Role Object đệ quy

Một phần của tài liệu (LUẬN VĂN THẠC SĨ) Phát triển mẫu thiết kế phần mềm và ứng dụng (Trang 88)

Tại lúc thực thi, điều này dẫn đến một chuỗi các đối tƣợng vai trò và lõi. Hình sau mô tả tình huống:

Hình 4.6. Sơ đồ tương tác giữa các vai trò và lõi

Mã nguồn ví dụ: class CustomerRole; class Customer { public:

// customer specific operations

virtual list<Account *> getAccounts() = 0; // Role management

virtual CustomerRole * getRole(String aSpec) = 0; virtual CustomerRole * addRole(String aSpec) = 0;

public:

CustomerRole * getRole(String aSpec){ return roles[aSpec];

};

CustomerRole * addRole(String aSpec){ CustomerRole * role = NULL;

if ((role = getRole(aSpec)) == NULL){

if(role == CustomerRole :: createFor(aSpec, this)) roles[Spec] = role; } return role; }; list<Account *> getAccounts() { ... }; private:

map<String, CustomerRole *> roles; };

Việc xác định một vai trò đƣợc thực thi nhƣ là một xâu tƣơng đƣơng tên lớp của lớp ConcreteRole. Việc ánh xạ giữa vai trò xác định và đối tƣợng vai trò bản thân nó đƣợc thực thi bởi một từ điển. Tiếp theo, chúng ta định nghĩa một lớp con của lớp Customer gọi là CustomerRole mà chúng ta sẽ phân lớp để thu đƣợc các vai trò cụ thể khác nhau. CustomerRole trang trí CustomerCore đƣợc tham chiếu bởi biến thực thể lõi. Cho mỗi phép toán trong giao diện của Customer, CustomerRole chuyển tiếp yêu cầu tới đối tƣợng lõi.

class CustomerRole : public Customer { public:

list<Account *> getAccounts(){ return core.getAccounts() };

CustomerRole * addRole(String aSpec){ return core.addRole(aSpec); };

static CustomerRole * createFor(String aSpec,

CustomerCore * aCore){

CustomerRole * newRole = NULL;

if (newRole == lookup(aSpec.create()) newRole.core = aCore; return newRole; }; private: CustomerCore * core; };

public:

list<Security *> getSecurities() { return securities; }; private:

list<Security *> securities; };

Chú ý rằng các client phải down-cat tham chiếu vai trò đƣợc trả về bởi thành phần lõi trƣớc khi chúng có thể gọi các phép toán xác định vai trò trên bản thể vai trò: Customer * aCoustomer = new Customer(“Tom Jones”);

Borrower * aBorrower = NULL;

if (aBorrower == dynamic_cast<Borrower *> aCustomer.getRole( “Borrower”))

// access securities

list<Security *> securities = aBorrower.getSecurities(); Các chuỗi GEBOS của các dự án ngân hàng hƣớng đối tƣợng sử dụng mở rộng mẫu Role Objec. Nó cung cấp sự hỗ trợ phần mềm cho một số các hạng mục nghiệp vụ ngân hàng bao gồm teller, loan và phòng investment cũng nhƣ là việc quản lý tài khoản và tự dịch vụ. Hệ thống GEBOS đƣợc dựa trên một tầng miền nghiệp vụ phổ biến mô hình các khái niệm lõi của ngân hàng. Các ứng dụng cụ thể mở rộng các khái niệm lõi này sử dụng mẫu Role Object.

Hệ thống Geo hiện tại dƣới sự phát triển tại Ubilab, phòng nghiên cứu công nghệ thông tin của hiệp hội ngân hàng của Thụy Sĩ đang sử dụng mẫu Role Object nhƣ là một sự biến đổi thực thi của các vai trò nhƣ là các thực thể lớp đầu tiên của lập trình.

Mẫu Extension Object chú tâm cùng một vấn đề: một thành phần đƣợc mở rộng bởi phƣơng tiện các đổi tƣợng mở rộng theo cách chúng thỏa mãn các yêu cầu xác định ngữ cảnh. Mẫu này dẫu sao không chỉ ra làm thế nào các đổi tƣợng Component và ComponentRole có thể đƣợc đối xử một cách trong suốt, mà chúng ta xem xét một khía cạnh chính của việc áp dụng mẫu Role Object. Thêm vào đó, mẫu Extension Object chỉ đề cập đến vấn đề của việc tạo và quản lý đối tƣợng mở rộng (role object). Chúng ta xem xét sự tích hợp của mẫu Decorator với mẫu Product Trader là một yếu tố chính của mẫu Role Object.

Mẫu Post mô tả một sự biến đổi thú vị của mẫu này. Tƣơng tự nhƣ mẫu Extension Object, nó mô tả các trách nhiệm của đối tƣợng lõi trong ngữ cảnh của ứng dụng nhất định. Dẫu sao một đối tƣợng Post tồn tại độc lập với đối tƣợng lõi và có thể sống mà không đƣợc gán cho đối tƣợng lõi.

Với một số sự trừu tƣợng, khó có thể đoán trƣớc giao diện hoàn thành của chúng bởi vì các client khác nhau yêu cầu các tầm nhìn khác nhau về sự trừu tƣợng. Việc kết hợp tất cả các phép toán và trạng thái mà các client khác nhau cần vào một giao diện đơn sẽ làm giao diện bị phồng lên. Các giao diện nhƣ vậy khó hiểu và khó bảo trì. Thêm vào đó, một thay đổi đối với phần xác định client của một giao diện có thể ảnh hƣởng tới các client khác sử dụng cùng sự trừu tƣợng.

Một ví dụ là xem xét các kiến trúc tài liệu phức hợp nhƣ là OLE 2 hay OpenDoc. Một tài liệu phức hợp đƣợc cấu tạo bởi các thành phần nhƣ là text, graphics, spreadsheets hay movies. Do đó sự trừu tƣợng chính của tài liệu phức hợp là những thứ nhƣ là một Component. Để gắn kết các thành phần theo các cách quan tâm khác nhau, cần có một giao diện chung. Giả sử rằng giao diện này đƣợc định nghĩa bởi một lớp trừu tƣợng Component. Giao diện này cung cấp các phép toán để quản lý và sắp xếp các thành phần trong một tài liệu.

Bây giờ hãy xem xét một bộ kiểm tra chính tả cho một tài liệu phức hợp. Nó yêu cầu một giao diện để đếm các từ của các thành phần mà lƣu trữ text. Một giải pháp là thêm giao diện này vào Component và có thực thi trống cho các thành phần mà không có text. Dẫu sao, giao diện này là phụ thuộc client và sẽ làm phồng lên giao diện Component. Nó sẽ tốt hơn nếu các giao diện mới hoặc không đoán trƣớc sẽ đƣợc thêm vào một cách riêng rẽ. Mỗi thành phần sẽ có thể cung cấp một giao diện cho bộ kiểm tra chính tả mà không phải mở rộng giao diện Component.

Ý tƣởng của mẫu Extension Object là đoán trƣớc đƣợc sự mở rộng nhƣ vậy. Nó đề xuất đóng gói giao diện kiểm tra chính tả trong một đối tƣợng riêng rẽ. Các client mà muốn sử dụng giao diện đƣợc mở rộng này có thể yêu cầu Component hỗ trợ nó.

ComponentExtension là lớp cơ sở chung cho các sự mở rộng. Nó cung cấp chỉ một giao diện tối thiểu đƣợc sử dụng để quản lý sự mở rộng bản thân nó. ComponentExtension phân lớp TextAccessor xác định giao diện cho truy cập text đƣợc sử dụng bởi client kiểm tra chính tả. Các phép toán của nó là FirstWord, NextWord cho việc đếm các từ và ChangeWord để thay thế các từ sai chính tả. Để cho phép các thực thi khác nhau của giao diện bộ kiểm tra chính tả, nó đƣợc xác định nhƣ là lớp trừu tƣợng.

Hình 4.7. Sơ đồ lớp minh họa

Các sự mở rộng bản thân nó là không có ích. Cần có một cách để xác định xem là một thành phần có hỗ trợ một sự mở rộng xác định. Cho mục đích của ví dụ này, hãy giả sử rằng chúng ta đặt tên một sự mở rộng với một xâu đơn giản. Để tránh xung đột, sẽ phải đăng ký cho các sự mở rộng nhƣ vậy. Bộ kiểm tra chính tả có thể truy vấn xem là một Component cung cấp một giao diện nhất định bởi gọi GetExtension (extensionName). Nếu Component đó cung cấp một sự mở rộng với tên đƣợc đƣa ra, nó trả về tƣơng ứng đối tƣợng mở rộng, nếu không nó trả về null.

TextComponent nạp chồng GetExtension để trả về một đối tƣợng TextAccessor khi nó đƣợc yêu cầu cho một giao diện với tên là TextAccessor. Dựa vào kiến trúc này, một bộ kiểm tra chính tả cho một tài liệu phức hợp đƣợc thực thi nhƣ theo sau. Xem xét kỹ các thành phần trong tài liệu. Hỏi mỗi Component cho sự mở rộng TextAccessor của nó. Nếu Component trả về đối tƣợng mở rộng TextAccessor tƣơng ứng, sử dụng nó (trong C++ sau khi down casting nó thành một TextAccessor) để kiểm tra chính tả tài liệu. Nếu không bỏ qua thành phần và di chuyển tới tiếp theo.

Sử dụng mẫu Extension Object khi:

Bạn cần sự hỗ trợ sự thêm vào của các giao diện mới hay không đoán trƣớc vào các lớp đang tồn tại và bạn không muốn tác động đến các client mà không cần đến giao diện mới này. Extension Object cho phép bạn giữ các phép toán liên quan với

thiết để bảo vệ sự trừu tƣợng chính bản thân nó. Cho ví dụ, một đối tƣợng khách hàng vẫn là một đối tƣợng khách hàng thậm chí nếu các hệ thống con khác nhau xem nó là khác nhau.

Một lớp sẽ có thể mở rộng với cách cƣ xử mới mà không phân lớp từ nó

Hình 4.8. Sơ đồ lớp mẫu Extension Object

 Subject (Component): xác định định danh của một sự trừu tƣợng. Nó khai báo giao diện để truy vấn xem là một đối tƣợng có một sự mở rộng đặc biệt. Trong trƣờng hợp đơn giản nhất, một giao diện đƣợc xác định bởi một xâu.

 Extension (ComponentExtension): lớp cơ sở cho tất cả các sự mở rộng. Nó xác định sự hỗ trợ cho quản lý bản thân các sự mở rộng. Các sự mở rộng biết các đối tƣợng sở hữu của nó.

 ConcreteSubject (StandardTextComponent): thực thi phép toán GetExtension để trả về một đối tƣợng mở rộng tƣơng ứng khi client yêu cầu nó.

 AbstractExtension (TextAccessor): khai báo giao diện cho một sự mở rộng xác định.

 ConcreteExtension (StandardTextAccessor): thực thi giao diện mở rộng cho một thành phần nhất định. Lƣu trữ trạng thái kết hợp với một sự mở rộng xác định. Một client yêu cầu một Subject cho một sự mở rộng xác định.

Khi sự mở rộng tồn tại Subject trả về một đối tƣợng mở rộng tƣơng ứng. Client sau đó sử dụng đối tƣợng mở rộng để truy cập các chức năng mở rộng

Nếu Subject không hỗ trợ một sự mở rộng, nó trả về null để báo hiệu rằng nó không hỗ trợ điều đó.

Các đối tƣợng mở rộng làm cho dễ dàng các giao diện thêm vào. Việc thêm vào một giao diện mới vào một Subject đƣợc đơn giản hóa bởi vì điều này không yêu cầu

bất kỳ sự thay đổi đối với giao diện subject đang tồn tại. Các đối tƣợng mở rộng cung cấp các chức năng mở rộng này trong khi bản thân nó bảo vệ sự trừu tƣợng chính.

Giao diện lớp không bị phồng lên bởi các sự trừu tƣợng chính. Một sự trừu tƣợng chính không trở thành bị ô nhiễm bởi các phép toán xác định client. Điều này có thể đạt đƣợc bởi sự phân lớp sự trừu tƣợng chính. Đó là các phép toán xác định client đƣợc định nghĩa trong các lớp con. Dẫu sao các kết quả này trong hệ thống phân lớp có thể khó quản lý. Sự kế thừa là tĩnh và yêu cầu tạo ra một lớp mới cho mỗi giao diện thêm vào.

Hỗ trợ cho mô hình hóa các vai trò khác nhau của một sự trừu tƣợng chính trong các hệ thống con khác nhau. Khi một sự trừu tƣợng đƣợc sử dụng qua các hệ thống con, nó thƣờng đóng các vai trò khác nhau. Mỗi vai trò yêu cầu yêu cầu các trạng thái và cách cƣ xử. Các đối tƣợng mở rộng hỗ trợ điều này bởi việc mô hình một vai trò nhƣ một đối tƣợng mở rộng. Bởi việc giữ chia tách các vai trò, một hệ thống con không phải biết các vai trò đƣợc sử dụng trong các hệ thống con khác.

Các client trở thành phức tạp hơn. Việc sử dụng giao diện mở rộng sẽ phức tạp hơn là giao diện đƣợc cung cấp bởi bản thân subject. Một client phải truy vấn giao diện và kiểm tra xem là nó có tồn tại hay không. Điều này đƣa ra các sự kiểm thử mở rộng và các đƣờng dẫn điều khiển trong chƣơng trình.

Thực thi

Sự mở rộng trong hay ngoài. Một vấn đề chính là làm thế nào, các sự mở rộng đƣợc tạo ra và quản lý. Một giải pháp là để lƣu trữ một đối tƣợng mở rộng trong một biến thực thể của ConcreteSubject. Khi một sự mở rộng đƣợc yêu cầu, nó có thể đƣợc trả về cho client. Giải pháp này thừa nhận rằng ConcreteSubject biết các sự mở rộng của nó và do đó chúng đƣợc xem xét nhƣ là bên trong. Trong giải pháp này, có thể gắn kết các đối tƣợng mới từ bên ngoài. Dẫu sao, sự thực thi này chỉ thêm phép toán truy cập GetExtension và không yêu cầu bất kỳ trạng thái nào trong Subject.

Một sự thay thế mà cho phép các client gắn kết các sự mở rộng đƣợc thể hiện theo hình dƣới đây

Hình 4.9. Sơ đồ lớp mẫu Extension Object rút gọn

Trong cách tiếp cận này, Subject duy trì một từ điển ánh xạ các tên mở rộng tới các sự mở rộng của nó. Client có thể đăng ký một sự mở rộng trong Subject bởi việc gọi AddExtension (extensionName, extension). Cách tiếp cận từ điển cho phép client thêm vào sự mở rộng bên ngoài mới theo yêu cầu và không yêu cầu ConcreteSubject biết trƣớc tất cả các sự mở rộng của nó. Một đối tƣợng đƣợc cƣ trú với các sự mở rộng khác nhau khi cần thiết.

Việc xác định các sự mở rộng. Các sự mở rộng cần một cách duy nhất để xác định chúng. Một cách tiếp cận đơn giản là việc sử dụng string. Các string yêu cầu một vài kiến trúc để tránh việc lặp. Một cách để ép buộc tính duy nhất trong C++ là sử dụng RTTI và phép toán typeid để xác định một cách duy nhất một giao diện. Trong trƣờng hợp này GetExtension lấy một type_info nhƣ là một tham số. Để yêu cầu cho sự mở rộng nhất định, client chuyển tiếp type_info của lớp AbstractExtension. Phép toán typeid đƣợc sử dụng để thu đƣợc một type_info tƣơng ứng.

AbstracsExtension*extension; Subject* subject;

extension = subject.GetExtension(typeid((AbstractExtension)); Xác định sự mở rộng trừu tƣợng. Trong C++ tất cả các thành viên của AbstractExtension đƣợc khai báo là thuần ảo. Trong Java, nó đƣợc xác định nhƣ là một giao diện.

Việc giải phóng các đối tƣợng mở rộng. Một vấn đề thực thi khác trong các môi trƣờng đƣợc thu thập không vô nghĩa là sự giải phóng của các đối tƣợng mở rộng. Subject kiểm soát một tham chiếu tới một đối tƣợng mở rộng và do đó không có điều khiển lên trên thời gian tồn tại của sự mở rộng. Một cách tiếp cận là xem xét các sự mở rộng đƣợc sở hữu bởi Subject và phá hủy chúng khi đối tƣợng Subject bị phá hủy. Một sự thay thế là sự sử dụng việc đếm tham chiếu. Client phải tăng một sự đếm tham chiếu khi chúng truy cập một sự mở rộng và giảm nó ngay khi chúng đƣợc thực hiện với nó.

Mã ví dụ: class Subject{ public:

virtual Extension* GetExtension(const char* name); };

Extension* Subject::GetExtension(const char* name){ return 0;

}

class ConcreteSubject: public Subject { public:

private:

SpecificExtension* specificExtension; };

Extension* ConcreteSubject::GetExtension(const char* name){ if (strcmp(name, "SpecificExtension" == 0) {

if (specificExtension == 0)

specificExtension = new SpecificExtension(this); return specificExtension; } return Subject::GetExtension(name); } //Mã Client để truy cập sự mở rộng SpecificExtension* extension; Subject* subject; extension = dynamic_cast<SpecificExtension*>( subject->GetExtension("SpecificExtension") ); if (extension){

// use the extension interface }

Hỗ trợ cho các giao diện có thể mở rộng là phổ biến trong các kiến trúc tài liệu phức hợp. Cả OpenDoc và OLE đều cung cấp kỹ thuật tƣơng ứng. Trong OpenDoc, ODObjec cung cấp giao diện cho việc truy cập các sự mở rộng. OLE xây dựng trên Component Object Model (COM). Trong COM tất cả các giao diện của một đối tƣợng đƣợc truy cập bởi kỹ thuật QueryInterface.

Trong framework giao diện ngƣời dùng của Taligent’s CommonPoint, lớp Tview chịu trách nhiệm quản lý phần trực quan của tình trạng thực màn hình. Tview là một sự trừu tƣợng chính của framework giao diện ngƣời dùng đồ họa. Nó có thể đƣợc mở rộng với các giao diện thêm vào và các cƣ xử mà không phải phân lớp. Tview có các thuộc tính. Một thuộc tính có thể đƣợc gắn kết vào một view và một client có thể truy vấn một view cho một thuộc tính xác định. Để mở rộng một view với cách cƣ xử thêm vào, client thực thi một lớp con Attribute và gắn một thực thể vào một view. Chẳng hạn một TnameAttribute có thể đƣợc sử dụng để gắn một giao diện cho việc gán một tên vào một view. Tview sử dụng cách tiếp cận từ điển để quản lý tập các sự mở rộng.

Visitor tập trung cách cƣ xử và cho phép thêm vào các cƣ xử mới vào một phân cấp lớp mà không phải thay đổi nó. Visitor có ích giống nhƣ mẫu Extension Object. Điểm khác nhau là mẫu Extension Object không yêu cầu một phân cấp lớp ổn định và

Decorator sử dụng tốt nhất trong các tình huống khi giao diện eo hẹp và một số thao tác đã có cần đƣợc tăng thêm.

Adapter hỗ trợ việc tƣơng thích một giao diện đang tồn tại. Mẫu Extension

Một phần của tài liệu (LUẬN VĂN THẠC SĨ) Phát triển mẫu thiết kế phần mềm và ứng dụng (Trang 88)

Tải bản đầy đủ (PDF)

(113 trang)