2009-07-29 27 views
5

Tôi có một lớp giao diện tương tự như:Cách tốt nhất để sử dụng C++ Interface

class IInterface 
{ 
public: 

    virtual ~IInterface() {} 

    virtual methodA() = 0; 

    virtual methodB() = 0; 

}; 

sau đó tôi thực hiện các giao diện:

class AImplementation : public IInterface 
{ 
    // etc... implementation here 
} 

Khi tôi sử dụng giao diện trong một ứng dụng là nó tốt hơn để tạo một thể hiện của lớp AImplementation cụ thể. Ví dụ.

int main() 
{ 
    AImplementation* ai = new AIImplementation(); 
} 

Hoặc là nó tốt hơn để đặt một nhà máy "tạo ra" chức năng thành viên trong giao diện như sau:

class IInterface 
{ 
public: 

    virtual ~IInterface() {} 

    static std::tr1::shared_ptr<IInterface> create(); // implementation in .cpp 
    virtual methodA() = 0; 

    virtual methodB() = 0; 

}; 

Sau đó, tôi sẽ có thể sử dụng giao diện trong chính như sau:

int main() 
{ 
    std::tr1::shared_ptr<IInterface> test(IInterface::create()); 
} 

Tùy chọn thứ nhất có vẻ là thực tế phổ biến (không phải để nói đúng). Tuy nhiên, tùy chọn thứ 2 có nguồn gốc từ "Hiệu quả C++".

+0

Chỉ cần một vài ghi chú cho tính đúng đắn của ngôn ngữ: trong C++ không có phương pháp, có các hàm thành viên. – Artyom

+1

Tôi sẽ viết main() như sau: 'int main() { IInterface * i = new AIImplementation(); } ' –

Trả lời

8

Một trong những lý do phổ biến nhất để sử dụng giao diện là để bạn có thể "lập trình chống lại sự trừu tượng" thay vì sau đó thực hiện cụ thể.

Lợi ích lớn nhất của việc này là nó cho phép thay đổi các phần mã của bạn đồng thời giảm thiểu thay đổi đối với mã còn lại.

Vì vậy, mặc dù chúng tôi không biết toàn bộ nền tảng của những gì bạn đang xây dựng, tôi sẽ đi theo phương pháp Giao diện/nhà máy. Đã nói điều này, trong các ứng dụng nhỏ hơn hoặc nguyên mẫu tôi thường bắt đầu với các lớp cụ thể cho đến khi tôi có được một cảm giác về nơi/nếu một giao diện sẽ được mong muốn. Giao diện có thể giới thiệu mức độ gián tiếp có thể không cần thiết cho quy mô ứng dụng bạn đang xây dựng.

Kết quả là trong các ứng dụng nhỏ hơn, tôi thấy tôi không thực sự cần giao diện tùy chỉnh của riêng mình. Giống như rất nhiều thứ, bạn cần phải cân nhắc chi phí và lợi ích cụ thể cho tình huống của bạn.

+0

Cảm ơn Ash. Tôi đã kết thúc với giải pháp này bởi vì nó cung cấp một dòng rõ ràng giữa khai báo và definitioin. Và tôi chỉ cần bao gồm tệp tiêu đề giao diện khi sử dụng giao diện chứ không phải tệp tiêu đề có chứa triển khai. – Seth

1

"Tốt hơn" theo nghĩa nào?

Phương pháp của nhà máy không mua cho bạn nhiều nếu bạn chỉ định có một lớp cụ thể. (Nhưng sau đó một lần nữa, nếu bạn chỉ có kế hoạch để có một lớp cụ thể, bạn có thực sự cần lớp giao diện ở tất cả? Có lẽ có, nếu bạn đang sử dụng COM.) Trong mọi trường hợp, nếu bạn có thể thấy một giới hạn nhỏ, cố định số lượng các lớp cụ thể, sau đó việc thực hiện đơn giản hơn có thể là một lớp "tốt hơn".

Nhưng nếu có thể có nhiều lớp cụ thể và nếu bạn không muốn lớp cơ sở được ghép chặt với chúng, thì mẫu nhà máy có thể hữu ích.

Và có, điều này có thể giúp giảm khớp nối - nếu lớp cơ sở cung cấp một số phương tiện cho các lớp dẫn xuất để tự đăng ký với lớp cơ sở. Điều này sẽ cho phép nhà máy biết được các lớp dẫn xuất tồn tại, và cách tạo ra chúng, mà không cần thông tin biên dịch về chúng.

+0

Tôi đang sử dụng COM thực sự. Tôi có một giao diện VCL và tôi cần phải kéo và thả từ Windows Explorer.Vì vậy, tôi đang kế thừa IDropTarget trong giao diện của tôi, và tôi đang nhận được các thành phần mà muốn được thả các mục tiêu trong ứng dụng để đăng ký xử lý của họ thông qua giao diện này. Vì vậy, nó có thể sẽ không có giá trị sử dụng phương pháp nhà máy, và tốt hơn để sử dụng gợi ý của Micahel Safyan ở trên. – Seth

0

Tôi sẽ đi với tùy chọn đầu tiên chỉ vì nó phổ biến hơn và dễ hiểu hơn. Nó thực sự tùy thuộc vào bạn, nhưng nếu bạn làm việc trên một ứng dụng thương mại thì tôi sẽ hỏi những gì đồng nghiệp của tôi những gì họ sử dụng.

4

Có là có một sự thay thế mà bạn đã không được đề cập:

 
int main(int argc, char* argv[]) 
{ 
    //... 
    boost::shared_ptr<IInterface> test(new AImplementation); 
    //... 
    return 0; 
} 

Nói cách khác, người ta có thể sử dụng một con trỏ thông minh mà không cần sử dụng một tĩnh "tạo ra" chức năng. Tôi thích phương pháp này, bởi vì một "tạo" chức năng thêm gì, nhưng mã bloat, trong khi lợi ích của con trỏ thông minh là hiển nhiên.

+2

Nếu triển khai được cho là ẩn, sẽ được tải động, phụ thuộc vào nền tảng (ví dụ: AWindowsImplementation, AMacImplementation, ALinuxImplementation) hoặc phụ thuộc vào một số tham số cấu hình, thì bạn nên tạo một nhà máy. Tuy nhiên, IMHO, bạn nên sử dụng một lớp Nhà máy riêng biệt cho điều đó. –

2

Có hai vấn đề riêng biệt trong câu hỏi của bạn: 1. Cách quản lý lưu trữ đối tượng đã tạo. 2. Cách tạo đối tượng.

Phần 1 rất đơn giản - bạn nên sử dụng một con trỏ thông minh như std :: tr1 :: shared_ptr để ngăn chặn rò rỉ bộ nhớ nếu không yêu cầu logic thử/bắt ưa thích.

Phần 2 phức tạp hơn. Bạn không thể chỉ viết create() trong main() như bạn muốn - bạn phải viết IInterface :: create(), bởi vì nếu không thì trình biên dịch sẽ tìm kiếm một hàm toàn cục được gọi là create, cái mà không phải là những gì bạn muốn. Nó có vẻ như có 'std :: tr1 :: shared_ptr test' được khởi tạo với giá trị được trả về bởi create() có vẻ như nó sẽ làm những gì bạn muốn, nhưng đó không phải là cách trình biên dịch C++ hoạt động. Để xem việc sử dụng phương pháp nhà máy trên giao diện là cách tốt hơn để làm điều này hơn là chỉ sử dụng AImplementation mới(), có thể nó sẽ hữu ích trong trường hợp của bạn, nhưng hãy cẩn thận về sự phức tạp đầu cơ - nếu bạn viết giao diện để nó luôn tạo ra một AImplementation và không bao giờ là một BImplementation hoặc một CImplementation, thật khó để xem những gì phức tạp thêm mua cho bạn.

1

Sử dụng phương pháp thứ nhất. Phương thức nhà máy của bạn trong tùy chọn thứ 2 sẽ phải được triển khai thực hiện theo từng lớp cụ thể và điều này không thể thực hiện trong giao diện. Tức là, IInterface :: create() không biết chính xác lớp bê tông nào bạn thực sự muốn tạo ra.

Một phương pháp tĩnh không thể ảo, và thực hiện một phương thức create() không tĩnh trong các lớp cụ thể của bạn đã không thực sự thắng bạn bất cứ điều gì trong trường hợp này.

Phương pháp của nhà máy chắc chắn hữu ích, nhưng đây không phải là cách sử dụng chính xác.

Mục nào trong C++ hiệu quả đề xuất tùy chọn thứ 2? Tôi không thấy nó trong của tôi (mặc dù tôi cũng không có cuốn thứ hai). Điều đó có thể làm sáng tỏ sự hiểu lầm.

+1

Item 31, tôi có ấn bản thứ ba – Seth

+0

Tôi không có hiệu quả C++ tiện dụng, nhưng tôi nghĩ OP là đề cập đến mô hình Abstract Factory (mà tôi luôn nghĩ là quá mức cần thiết, nhưng có thể có lúc thích hợp.) Để biết nhà máy có cần phải được triển khai cho từng lớp cụ thể hay không: Bạn sẽ có một phương thức nhà máy công khai, gọi phương thức tĩnh không công khai để tạo các thể hiện cụ thể. Các lớp học có nguồn gốc đăng ký với nhà máy để làm cho mình có sẵn để xây dựng. Cách nhà máy chọn ** mà ** lớp dẫn xuất để xây dựng tại một thời điểm cụ thể là thực hiện cụ thể. –

+0

nhưng bạn đúng về phương thức create(). trong cuốn sách nó thực sự là thử nghiệm (IInterface :: create()) – Seth

0

Tôi có một câu hỏi rất đơn giản:

Bạn có chắc chắn muốn sử dụng con trỏ không?

Câu hỏi này có vẻ không hợp lý nhưng mọi người đến từ một nền Java sử dụng mới thường xuyên hơn nhiều so với yêu cầu. Trong ví dụ của bạn, việc tạo biến trên ngăn xếp sẽ đủ amply.

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