2014-06-21 2 views
2

Я реализовал составной шаблон с помощью интеллектуальных указателей, он работает до точки.Композитный с умными указателями без dynamic_pointer_cast

Проблема в том, что я просто могу использовать методы, реализованные в интерфейсе, и я не могу использовать методы, которые определены в производном классе, не используя dynamic_pointer_cast, и я не хочу этого.

Я хочу знать, можно ли это сделать без использования dynamic_pointer_cast.

Я слышал, что мне нужно реализовать шаблон посетителя, но я действительно не знаю, как это сделать, и если он подходит к этой проблеме.

#include <iostream> 
#include <vector> 
#include <memory> 

class Fruit 
{ 
public: 
    virtual void getOld() = 0; 
}; 

class Orange : Fruit 
{ 
public: 
    Orange() {} 

    void add(std::shared_ptr<Fruit> f) 
    { 
     v.push_back(f); 
    } 

    std::shared_ptr<Fruit> get(int k) 
    { 
     return v[k]; 
    } 

    void getOld() 
    { 
     std::cout << "Orange - I'm old." << std::endl; 
    } 
private: 
    std::vector<std::shared_ptr<Fruit>> v; 
}; 

class Bitter : public Fruit 
{ 
public: 
    Bitter() {} 

    void getOld() 
    { 
     std::cout << "Bitter - I'm old." << std::endl; 
    } 

    void getNew() 
    { 
     std::cout << "Bitter - I'm new." << std::endl; 
    } 
}; 

int main(int argc, char ** argv) 
{ 
    auto orange = new Orange; 
    orange->add(std::make_shared<Bitter>()); 
    auto bitter = orange->get(0); 
    bitter->getOld(); 

    return 0; 
} 

Он работает, как вы можете видеть здесь, на live preview, но когда я пытаюсь использовать:

int main(int argc, char ** argv) 
{ 
    auto orange = new Orange; 
    orange->add(std::make_shared<Bitter>()); 
    auto bitter = orange->get(0); 
    bitter->getOld(); 
    bitter->getNew(); 

    return 0; 
} 

Я получил ошибку:

error: 'class Fruit' has no member named 'getNew'

Спасибо заранее.

ответ

2

Проблема здесь, я думаю, что она будет работать с полиморфизмом, но метод getNew не существует в материнском классе, поэтому вам нужно определить его и сделать его виртуальным. Это единственный способ сделать это без использования объекта на объекте. С этой строкой он должен работать.

virtual void getNew() = 0; 
+0

Я знаю это, но что произойдет, если у меня есть десятки классов с разными методами? Интерфейс будет беспорядок с множеством методов, не так ли? - Подождем, чтобы посмотреть, есть ли у кого-то решение или, по крайней мере, способ сделать это лучше. Благодарю. –

+0

Проблема может быть в вашем дизайне. Если вы знаете, какой тип Orange вернется, вы можете делать такие функции, как «getBitter», а не просто «get» – meneldal

+0

Снова та же проблема, что произойдет, если у меня есть десятки классов? Мне нужно будет создавать методы для каждого из них. Если мы посмотрим на сторону реализации, мы сможем это сделать, но как насчет API? –

1

Одним из возможных решений является следующее: Orange.

template <typename T> 
T* get(int k) 
{ 
    return dynamic_cast<T*>(v[k].get()); 
} 

И затем использовать:

auto bitter = orange->get<Bitter>(0); 
bitter->getOld(); 
bitter->getNew(); 

Это выполняет dynamic_cast, но локализуется в Orange.

+0

вы просто скрываете приведение здесь. Вы просто переместите проблему. – meneldal

+1

@meneldal, никаких аргументов нет. Просто предоставление механизма для уменьшения распространения 'dynamic_cast' на другие функции. –

1

После информации можно найти о «композиционного рисунка» из GOF книги. Конечно, это объяснено на основе графического класса.

ключ к композитному шаблон абстрактный класс, что представляетобапримитивы и их контейнеры. Для графической системы этот класс является графическим. Графика объявляет операции, такие как Ничья, которые относятся к графическим объектам. Это также объявляет операции, которые все составные объекты имеют, такие как операции для доступа и управления своими детьми.

На основе приведенного выше объяснения мы должны в идеале объявить все возможные интерфейсы типа листа и нелистового (контейнерного) узла при использовании составного шаблона. Я считаю, что это необходимо для того, чтобы клиент обрабатывал отдельные объекты и композиции объектов равномерно. Поэтому в идеале вы должны объявлять свои классы следующим образом при использовании этого конкретного шаблона. Любая логика, написанная на основе точного типа объекта в клиентском коде, нарушает суть этого шаблона.

//Abstract class which should have all the interface common to 
// Composite and Leaf class. It may also provide the default 
// implementation wherever appropriate. 
class Fruit { 
public: 
    virtual void getOld() = 0; 
    virtual void getNew() = 0; 
    virtual void add(std::shared_ptr<Fruit> f) { } 
    virtual std::shared_ptr<Fruit> get(int index) {return nullptr; } 
    virtual ~Fruit() { } 
}; 


//Composite Node 
class Orange : Fruit { 
public: 
    Orange() {} 
    void add(std::shared_ptr<Fruit> f) { v.push_back(f); } 
    std::shared_ptr<Fruit> get(int k) { return v[k]; } 
    void getOld() { std::cout << "Orange - I'm old." << std::endl; } 
    void getNew() { std::cout << "Orange - I'm new." << std::endl; } 
private: 
    std::vector<std::shared_ptr<Fruit>> v; 
}; 


//Leaf node 
class Bitter : public Fruit { 
public: 
    Bitter() {} 
    void getOld() { std::cout << "Bitter - I'm old." << std::endl; } 
    void getNew() { std::cout << "Bitter - I'm new." << std::endl; } 
}; 
Смежные вопросы