2015-03-13 15 views
5

Tôi đang tìm cách tốt nhất để triển khai mẫu chiến lược trong C++. Cho đến nay, tôi đã luôn luôn sử dụng cách chuẩn, trong đó bối cảnh có một con trỏ tới lớp cơ sở chiến lược như sau:Cách triển khai mẫu chiến lược trong C++ với std :: function

class AbstractStrategy{ 
public: 
    virtual void exec() = 0; 
} 
class ConcreteStrategyA{ 
public: 
    void exec(); 
} 
class ConcreteStrategyB{ 
public: 
    void exec(); 
} 

class Context{ 
public: 
    Context(AbstractStrategy* strategy):strategy_(strategy){} 
    ~Context(){ 
      delete strategy; 
     } 
     void run(){ 
      strategy->exec(); 
     } 
private: 
    AbstractStrategy* strategy_; 

Kể từ khi có con trỏ tới các đối tượng có thể dẫn đến hành vi xấu, tôi đang tìm kiếm để có cách an toàn hơn để triển khai mẫu này và tôi đã tìm thấy this question trong đó std::function được đề xuất là cách tốt hơn để xử lý mẫu này.

Có thể ai đó vui lòng giải thích rõ hơn cách hoạt động của std::function, có thể với ví dụ về mẫu chiến lược?

Trả lời

7

Lưu ý rằng các đối tượng phương pháp đơn là đẳng cấu đối với các hàm và các chiến lược chỉ là các đối tượng phương thức đơn.

Vì vậy, về cơ bản, bạn thoát khỏi tất cả các lớp học của bạn, và bạn chỉ cần sử dụng std::function<void()> thay vì:

class Context { 
public: 
    template<typename F> 
    explicit Context(F strategy) : strategy(std::move(strategy)) { } 

    void run() { strategy(); } 

private: 
    std::function<void()> strategy; 
}; 

Sau đó, bạn có thể vượt qua bất kỳ callable đến constructor của Context:

Context ctx([] { std::cout << "Hello, world!\n"; }); 
ctx.run(); 
+0

Trong trường hợp này, bạn sẽ triển khai F (chiến lược) như thế nào? Và tại sao bạn cần sử dụng std :: move? – gcswoosh

+2

@Gabrielecswoosh Chiến lược này chỉ là bất kỳ con trỏ hàm hoặc đối tượng nào với 'toán tử()' bị quá tải.Ví dụ của tôi chuyển một lambda (định nghĩa 'void operator()() const'). 'std :: move' là để ngăn chặn một bản sao. – rightfold

0

Cái gì đó như điều này ?

#include <functional> 
#include <iostream> 


typedef std::function<int(int)> Strategy; 

void execute_strategy(Strategy strategy, int object) { 
    std::cout << strategy(object) << std::endl; 
}; 

int power2(int i) { 
    return i*i; 
}; 

int main() { 
    execute_strategy(power2, 3); 
} 

Ý tôi là, mẫu chiến lược là giải pháp cho thiếu sót không có lambdas thực tế. Đó là giải quyết, vì vậy bạn chỉ có thể vượt qua các chức năng thích hợp xung quanh.

3

Có một chút thảo luận về chủ đề này herehere. Tôi nghĩ rằng nó phụ thuộc vào trường hợp cụ thể trong tầm tay. Chiến lược của bạn chỉ là một cuộc gọi hàm đơn giản, ví dụ - tôi thường có các mẫu chiến lược trong đó chiến lược của tôi sẽ cần nhiều khả năng, không được xử lý tốt bằng cách chỉ có hàm hoặc hàm functor. Nhưng nếu bạn chỉ cần một hàm hoặc hàm, thì std::function là một cách tiện dụng để cho phép sự linh hoạt tối đa, lưu trữ các con trỏ hàm, lambdas hoặc các hàm. Có thể có các vấn đề về hiệu năng, được thảo luận here để thực hiện tăng cường ban đầu.

+0

Bạn sẽ gợi ý điều gì trong trường hợp chiến lược không đơn giản là gọi hàm? Trong trường hợp đó, một con trỏ tới lớp trừu tượng là cách duy nhất? – gcswoosh

+1

Có. Bạn có thể xử lý kiểm soát tài nguyên thích hợp với 'std :: unique_ptr' hoặc' std :: shared_ptr' nếu thích hợp. – sfjac

1

Làm việc trên câu trả lời của райтфолд

Về cơ bản, bạn thoát khỏi tất cả các lớp học của bạn, và bạn chỉ cần sử dụng std :: chức năng để thay thế.

chức năng tổng quát này cho phép bạn vượt qua chức năng, lambda, functors, và hàm thành viên (sử dụng std :: bind)

class Context { 
public: 
    explicit Context(std::function<void()> input) : strategy(input) { } 

void run() { strategy(); } 

private: 
    std::function<void()> strategy; 
}; 

Sau đó, bạn có thể vượt qua bất kỳ callable đến constructor của Bối cảnh:

Context ctx([] { std::cout << "Hello, world!\n"; }); 
ctx.run(); 

hoặc

void sayHelloWorld(){ 
    std::cout << "Hello, world!\n"; 
} 


int main(){ 
    Context ctx(sayHelloWorld); 
    ctx.run(); 
} 

hoặc

class SayHelloWorld{ 
    operator()(){std::cout << "Hello, world!\n";} 
} 

int main(){ 
    SayHelloWorld hello_world; 
    Context ctx(hello_world); 
    ctx.run(); 
} 
Các vấn đề liên quan