2009-04-22 34 views
5

tôi có mã này để đại diện cho ngân hàng:
Thao tác với con trỏ đến đối tượng lớp được thừa kế thông qua con trỏ tới lớp cơ sở vật

class Bank { 
    friend class InvestmentMethod; 
    std::vector<BaseBankAccount*> accounts; 
public: 
//... 

BaseBankAccount là một lớp trừu tượng cho tất cả các tài khoản trong ngân hàng:

class BaseBankAccount { 
    public: 
     BaseBankAccount() {} 
     virtual int getInterest() const = 0; 
     virtual int getInvestedSum() const = 0; 
     virtual void increaseDepositSum(int additionalSum) = 0; 
     virtual void close(std::string& reason) = 0; 
     virtual ~BaseBankAccount() {} 
}; 

Vấn đề là, khi tôi thao tác với con trỏ đến các đối tượng lớp dẫn xuất thông qua con trỏ tới đối tượng lớp cơ sở, tập hợp các phương thức tôi có thể gọi bị hạn chế bởi giao diện công cộng BaseBankAccount - bất kể đối tượng REAL là gì.

Ví dụ, không phải mọi tài khoản có một tùy chọn để tăng tổng vốn đầu tư đã được - vì vậy, tôi didn`t bao gồm phương pháp này trong một lớp cơ sở:

class BankAccount: public BaseBankAccount { 
protected: 
    BaseDeposit* deposit; 
    double sumInvested; 
public: 
    BankAccount(int sum, int term, int inter): sumInvested(sum), depositTerm(term), interest(inter) {} 
    int getInterest() const { return interest; } 
    int getInvestedSum() const { return sumInvested; } 
    void prolong(int increaseTerm) { 
     depositTerm += increaseTerm; 
    } 
    void increaseInvestment(double addition) { 
      sumInvested += addition; 
    } 
    virtual ~BankAccount() {} 
}; 

sau đó, tôi muốn gọi nó là:

Bank bank1(...); 
bank1.accounts[i]->increaseInvestment(1000.0); 

Vì vậy, tôi có thể làm gì để truy cập vào giao diện của các đối tượng lớp dẫn xuất trong trường hợp này? Theo như tôi biết, downcasting đến loại bê tông mỗi lần tôi cần gọi chức năng cụ thể không tốt.
Tạo thêm một lớp trừu tượng bắt nguồn từ điều này để mở rộng giao diện?
Tạo cấu trúc phân cấp song song cho từng loại cụ thể mà tôi cần thực hiện (trông hơi nặng để làm)?

+0

phương pháp là ảo, tất nhiên - I 'm xin lỗi – chester89

+0

có vẻ như với tôi rằng cách dễ nhất để giải quyết này là để đẩy mạnh lĩnh vực dữ liệu từ BankAccount tới lớp cơ sở của nó – chester89

Trả lời

7

Một giải pháp để truy cập nhiều tính năng lớp có nguồn gốc từ lớp cơ sở là mẫu khách truy cập.

class BaseBankAccount { 
public: 
    ... 
    virtual void AcceptVisitor(IVisitor& v) = 0; 
}; 


class AccountTypeA : public BaseBankAccount { 
public: 
    void TypeAFeature() {...} 

    void AcceptVisitor(IVisitor& v) 
    { 
     v.VisitAccountTypeA(*this); 
    } 
}; 

class AccountTypeB : public BaseBankAccount { 
public: 
    void TypeBFeature() {...} 

    void AcceptVisitor(IVisitor& v) 
    { 
     v.VisitAccountTypeB(*this); 
    } 
}; 

class IVisitor { 
public: 
    virtual void VisitAccountTypeA(AccountTypeA& account) = 0; 
    virtual void VisitAccountTypeB(AccountTypeB& account) = 0; 
}; 

class ConcreteVisitor : public IVisitor{ 
public: 
    void VisitAccountTypeA(AccountTypeA& account) 
    { 
     account.TypeAFeature(); //Can call TypeA features 
    } 

    void VisitAccountTypeB(AccountTypeB& account) 
    { 
     account.TypeBFeature(); //Can call TypeB Features 
    } 
}; 

Tương tác không rõ ràng ngay lập tức. Bạn định nghĩa một phương thức ảo thuần túy AcceptVisitor trong lớp cơ sở của bạn, lấy đối tượng kiểu IVisitor làm tham số. IVisitor có một phương thức cho mỗi lớp dẫn xuất trong hệ thống phân cấp. Mỗi lớp dẫn xuất thực hiện AcceptVisitor một cách khác nhau và gọi phương thức tương ứng với kiểu cụ thể của nó (AccountTypeA & AccountTypeB), và chuyển một tham chiếu cụ thể tới chính nó đến phương thức. Bạn thực hiện các chức năng sử dụng các loại có nguồn gốc nhiều hơn của hierachy của bạn trong các đối tượng xuất phát từ IVisitor. Wikipedia: Visitor Pattern

+0

giải pháp là rất mạnh mẽ, nhưng còn tình huống khi tôi muốn thực hiện mote hơn 1 phương thức của lớp dẫn xuất là lớp cơ sở missimg thì sao? – chester89

+0

Tôi không chắc mình hiểu nhận xét này. ConcreteVisitor :: VisitAccountTypeA có quyền truy cập vào tất cả AccountTypeA và có thể làm bất cứ điều gì với nó, không chỉ gọi một phương thức. Thông thường, bạn sẽ nhận được nhiều hơn một "Khách truy cập" cụ thể từ IVisitor và gọi cho họ sau Chiến dịch họ thực hiện. – TheFogger

0

Phải xuống dốc, trong khi luôn rủi ro, đôi khi không thể tránh khỏi. Ví dụ: trước khi các mẫu được đưa vào Java, bạn phải luôn luôn downcast khi sử dụng các bộ sưu tập tiêu chuẩn vì chúng chỉ làm việc với các đối tượng.

Tôi giả định rằng khi bạn đang downcasting, bạn đang làm cho nó an toàn với dynamic_cast. Tôi cũng giả định rằng tất cả mọi thứ là ảo trong lớp cơ sở của bạn và bạn chỉ cần bỏ qua nó trong mã này.

Cụ thể cho câu hỏi của bạn, không có câu trả lời đúng nào. Nói chung, khi bạn gộp lại một loạt các lớp dẫn xuất thành một bộ sưu tập không đồng nhất, bạn đang sử dụng bộ sưu tập dưới dạng một "nhóm bit" hoặc có ý định làm việc trên mẫu số chung thấp nhất (giao diện của lớp cơ sở).

Nếu trường hợp cũ, sau đó downcasting, trong khi xấu xí, có thể là cần thiết. Nếu trường hợp là sau, thì bạn phải tự hỏi tại sao bạn muốn xử lý các kiểu con cụ thể khác nhau và liệu bạn có thể thay đổi việc triển khai lớp cơ sở bằng cách nào đó gọi các công cụ có liên quan trong lớp dẫn xuất (ví dụ, thông qua một phương thức dẫn xuất).

Trong cả hai trường hợp, bạn có thể muốn xem xét lại liệu một bộ sưu tập không đồng nhất có ý nghĩa ở nơi đầu tiên hay không, trong một số trường hợp nó thực sự không có. Ví dụ: có thể có ý nghĩa khi duy trì riêng các bộ sưu tập cho từng loại tài khoản chính và có phương thức "getAllAccounts" sẽ trả về tổng hợp?

1

Hạn chế đối với giao diện công khai lớp cơ sở (và bằng cách này, không có một số 'ảo bị thiếu trong những gì bạn đăng) là những gì C++ làm. Nếu bạn cần truy cập các hàm cụ thể chỉ thuộc về một lớp dẫn xuất, thì bạn cần phải đưa con trỏ đến lớp đó bằng cách sử dụng dynamic_cast.

Nếu bạn thấy cần phải sử dụng dynamic_cast rất nhiều, thì thiết kế của bạn có thể mong muốn, nhưng rất khó để bình luận về điều này mà không cần biết chi tiết chính xác miền doanh nghiệp bạn đang xử lý.

Một cách có thể làm tròn vấn đề là cung cấp các phương thức truy cập các thành phần của tài khoản. Ví dụ, một phương thức lớp cơ sở GetPortfolio() có thể làm lại một con trỏ đối tượng danh mục đầu tư nhưng chỉ cho các lớp tài khoản có danh mục đầu tư. Đối với các lớp khác, bạn định nghĩa phương thức GetPortfolio() của chúng như trả về NULL. Một khi bạn có con trỏ danh mục đầu tư, bạn làm việc trên giao diện danh mục đầu tư (mà chính nó có thể đại diện cho một chế độ thừa kế lớp) thay vì thean BankAccount.

+0

Tôi chưa bao giờ kiểm tra, nhưng sẽ biên dịch này? (Có = 0 nhưng không có ảo?) – Uri

+0

Không, nó không nên, nhưng các nhà văn biên dịch thường coi = 0 là tài sản đặc biệt của riêng họ và làm những điều kỳ lạ với nó. –

+0

Tôi đã nghĩ rằng đây sẽ là một phần của ngữ pháp ... – Uri

1

Tại sao bạn cần "truy cập vào giao diện của các đối tượng lớp dẫn xuất"?

Nó có thể giúp cho mục đích thảo luận nếu bạn cung cấp ví dụ về lớp con của BaseBankAccount với phương thức bạn muốn gọi.

Tôi cho rằng các phương thức thuần túy trong lớp BaseBankAccount của bạn cũng có nghĩa là ảo?

Nếu bạn muốn gọi các phương thức của các lớp con của BaseBankAccount, bạn thường sẽ cần thêm các phương thức đó (như ảo) vào lớp cơ sở BaseBankAccount. Nếu các phương thức không có ý nghĩa đối với tất cả các lớp con của BaseBankAccount thì bạn có thể cần phải suy nghĩ lại về thiết kế lớp của bạn.

Ví dụ:

class BaseBankAccount { 
    public: 
     BaseBankAccount() {} 

     virtual void someNewMethod() = 0; 

     // ... 
}; 

class SavingsBankAccount : public BaseBankAccount { 
    public: 
     virtual void someNewMethod() { 
      // ... 
     } 

     // ... 
}; 
+0

bạn đang đúng - chúng có nghĩa là ảo – chester89

Các vấn đề liên quan