2014-04-29 29 views
5

Ví dụ về Strategy Pattern từ sách, Head First Design Patterns, được viết bằng C++ tại [here]. Tôi đang thực hành để chuyển đổi nó thành phong cách C++ 11 theo Effective GoF Patterns with C++11 and Boost như hiển thị dưới đây.Mẫu chiến lược C + 11 có trạng thái

Các Quack hành vi:

struct Quack { 
    static void quack() 
    { 
     std::cout << __FUNCTION__ << std::endl; 
    } 
}; 

struct MuteQuack { 
    static void quack() 
    { 
     std::cout << __FUNCTION__ << std::endl; 
    } 
}; 

Các Fly hành vi:

struct FlyWithWings { 
public: 
    static void fly() 
    { 
     std::cout << __FUNCTION__ << std::endl; 
    } 
}; 

struct FlyNoWay { 
public: 
    static void fly() 
    { 
     std::cout << __FUNCTION__ << std::endl; 
    } 
}; 

Các Duck hệ thống cấp bậc:

class Duck 
{ 
public: 
    typedef std::function<void(void)> QUACK; 
    typedef std::function<void(void)> FLY; 

public: 
    Duck(const QUACK &q, const FLY &f) 
     : m_Quack(q), m_Fly(f) {} 

    virtual ~Duck() 
    { 
    } 

    void perform_quack() 
    { 
     m_Quack(); 
    } 
    void perform_fly() 
    { 
     m_Fly(); 
    } 

protected: 
    QUACK m_Quack; 
    FLY  m_Fly; 

private: 
    Duck(const Duck&) = delete; 
    Duck& operator=(const Duck&) = delete; 
}; 

class MallardDuck 
    : public Duck 
{ 
public: 
    MallardDuck() 
     : Duck(&Quack::quack, &FlyWithWings::fly) 
    { 
    } 
}; 

class PaintedDuck 
    : public Duck 
{ 
public: 
    PaintedDuck() 
     : Duck(&MuteQuack::quack, &FlyNoWay::fly) 
    { 
    } 
}; 

Cho đến nay rất tốt, khách hàng hoạt động tốt.

int main() 
{ 
    MallardDuck x1; 
    x1.perform_quack(); 
    x1.perform_fly(); 

    PaintedDuck x2; 
    x2.perform_quack(); 
    x2.perform_fly(); 

    return 0; 
} 

Bây giờ tôi muốn mở rộng đến một lớp học mới RubberDuck để Duck hệ thống cấp bậc, và RubberDuck sử dụng một hành vi bay mới FlyWithRocket trong đó có một trạng thái đối tượng. Như sau:

Một mới Fly hành vi:

class FlyWithRocket { 
public: 
    FlyWithRocket() : m_Energy(3) {} 
    void fly() 
    { 
     if(m_Energy > 0) 
     { 
      fly_with_rocket(); 
      --m_Energy; 
     } 
     else 
     { 
      fly_out_of_energy(); 
     } 
    } 

private: 
    void fly_with_rocket() 
    { 
     std::cout << __FUNCTION__ << std::endl; 
    } 
    void fly_out_of_energy() 
    { 
     std::cout << __FUNCTION__ << std::endl; 
    } 

    unsigned int m_Energy; 
}; 

Một mới Duck loại:

class RubberDuck 
    : public Duck 
{ 
public: 
    RubberDuck() 
     : Duck(&MuteQuack::quack, std::bind(&FlyWithRocket::fly, std::ref(m_flyrocket))) 
     , m_flyrocket() 
    { 
    } 
private: 
    FlyWithRocket m_flyrocket; 
}; 

Từ bây giờ tôi đang tự hỏi rằng các quy tắc của trật tự khởi tạo thành viên . Các cơ sở Duck khởi tạo trước khi các thành viên m_flyrocket, nhưng lưu ý rằng các cơ sở Duck được khởi tạo với ràng buộc m_flyrocket mà chưa được khởi tạo. Kết quả là tôi chạy nó trong VS2013, điều này làm việc mà không có một cái gì đó sai tại thời gian chạy.

Nhưng mã có thực sự không an toàn không? Nếu không, làm thế nào tôi có thể sửa đổi một thiết kế tốt hơn?

+1

Câu hỏi lớn là "đang đi qua một đối tượng không khởi tạo một hành vi undefined constructor? ".Tôi đoán rằng trong hầu hết các trường hợp, điều này sẽ hoạt động miễn là bạn không truy cập nó trong khi xây dựng, nhưng câu trả lời này cho thấy rằng đó là hành vi không xác định, vì vậy không an toàn: http://stackoverflow.com/a/22203006/104774 – stefaanv

Trả lời

6

Nó không an toàn, nhưng nó không có khả năng phá vỡ trừ khi bạn gọi m_Fly() từ hàm tạo lớp cơ sở.

Bạn có thể dễ dàng tránh điều này tuy nhiên, bởi một trong hai:

  1. cho các nhà xây dựng lớp cơ sở một giả hoặc mặc định-xây dựng std::function, và tái phân m_Fly để functor ràng buộc của bạn trong constructor lớp có nguồn gốc

    RubberDuck() 
        : Duck(&MuteQuack::quack, std::function<void()>()) 
    { 
        m_Fly = std::bind(&FlyWithRocket::fly, std::ref(m_flyrocket)); 
    } 
    
  2. làm FlyWithRocket một functor bản thân (chỉ cần đổi tên void fly-void operator()) và đi qua nó theo giá trị thay vì giữ một memb tin er (nó sẽ được sở hữu bởi các đối tượng m_Fly chức năng, và bạn có thể truy cập nó qua std::function::target<FlyWithRocket>() nếu bạn cần)

    class FlyWithRocket { 
    public: 
        FlyWithRocket() : m_Energy(3) {} 
        void operator()() { 
    // ... 
    
    RubberDuck() 
        : Duck(&MuteQuack::quack, FlyWithRocket()) {} 
    
+0

Cả hai đều tuyệt vời! – cbel

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