2013-12-16 4 views
1

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

Задача состояла в том, чтобы реализовать фигуры, такие как круг или прямоугольники, сначала определяя абстрактный класс с именем Shape, а затем реализуя различные формы (круг, прямоугольник ..), наследуя от базового класса (Shape).

Два абстрактных метода для каждой формы: read_to_file (который должен был читать форму из файла) и write_to_file, который должен был записать фигуру в файл.

Все было выполнено путем реализации этой виртуальной функции в унаследованной форме (пример: для круга я писал радиус, для квадрата я сохранил сторону квадрата ....).

class Shape { 
public: 
    string Shape_type; 

    virtual void write_into_file()=0; 

    virtual void read_into_files()=0; 

    Shape() { 
    } 
    virtual ~Shape() { 
    }}; 
class Square: public Shape { 
public: 
    int size; 
    Square(int size) { 
     this->size = size; 
    } 
    void write_into_file() { 
     //write this Square into a file 
    } 
    void read_into_files() { 
     //read this Square into a file 
    } 
}; 

Это было сделано, чтобы узнать, знаю ли я полиморфизм.

Но тогда меня попросили реализовать две функции, которые принимают вектор *shape и записывают/читают его в файл.

Сочинение части была легко и выходит что-то вроде этого:

for (Shape sh : Shapes) { 
    s.write_into_file(); 
} 

, как для чтения части я думал о чтении первого слова в тексте (я реализовал сериализуемую файл как текстовый файл, который есть в этом строка: Shape_type: Circle, Radius: 12; Shape_type:Square...., так что первые слова говорят, тип формы). и сохранение его в строку, например, как:

string shape_type; 
shape_type="Circle"; 

Тогда мне нужно, чтобы создать новый экземпляр этой конкретной формы, и я думал о чем-то вроде большого переключателя

<pre><code> 
switch(shape_type): 
{ 
case Circle: return new circle; 
case Square: return new square 
...... 
} 
</pre></code> 

А потом, интервьюер сказал мне кажется, что есть проблема с этой реализацией , о которой я думал, был тот факт, что каждая новая форма, которую мы добавим в будущем, также должна обновить int, что большой swicht. он пытается направить меня в шаблон дизайна, я сказал ему, что, возможно, шаблон дизайна завода поможет, но я не мог найти способ избавиться от этого переключателя. даже если я переведу переключатель из функции в FactoryClass, мне все равно придется использовать переключатель, чтобы проверить тип формы (в соответствии с содержимым строки, полученным из текстового файла).

У меня была строка, которую я читал из файла, которые говорят текущий тип формы. Я хотел сделать что-то вроде:

string shape_type; 
shape_type="Circle"; 
Shape s = new shape_type; //which will be like: Shape s = new Circle 

Но я не могу сделать это на C++.

Любая идея о том, что я должен был сделать?

ответ

1

В вашей фабрике вы можете нанести std::string на номер function<Shape*()>. При запуске регистрации фабричные методы будут завод:

shapeFactory.add("circle", []{new Circle;}); 
shapeFactory.add("square", []{new Square;}); 
shapeFactory.add("triangle", []{new Triangle;}); 

В коде десериализации вы читаете название типа и получить его фабричный метод с завода:

std::string className = // read string from serialization stream 
auto factory = shapeFactory.get(className); 
Shape *shape = factory(); 

Теперь вы получили указатель на конкретный экземпляр формы, который можно использовать для десериализации объекта.

EDIT: Добавлено больше кода по запросу:

class ShapeFactory 
{ 
private: 
    std::map<std::string, std::function<Shape*()> > m_Functions; 

public: 

    void add(const std::string &name, std::function<Share*()> creator) 
    { 
    m_Functions.insert(name, creator) 
    } 

    std::function<Shape*()> get(const std::string &name) const 
    { 
    return m_Functions.at(name); 
    } 
}; 

ПРИМЕЧАНИЕ: Я оставил из проверки ошибок.

+0

как я могу сделать эту карту? является ли регистр резервным словом в C++? – Matoy

+0

Я добавил еще несколько кодов и сменил имя на 'add', чтобы, надеюсь, сделать его более понятным. – Sean

+1

Спасибо! это похоже на ответ моего интервьюера (в 30-минутном интервью .. go figure). в любом случае, ваш ответ привел меня к Google, и я нашел эту статью: http://blog.fourthwoods.com/2011/06/04/factory-design-pattern-in-c/ (которая в основном более подробно освещена на вашем правом ответ. – Matoy

0

В C++ с

for (Shape sh : Shapes) { 
    s.write_into_file(); 
} 

у вас есть object slicing. Объект sh - это Shape и ничего больше, он теряет всю информацию о наследовании.

Вам либо нужно хранить ссылки (не хранить в стандартной коллекции), либо указатели, и использовать их при циклировании.

+0

Неверно. int main() { \t Форма * s = новый Sqaure (20); \t s-> print_type(); // Sqaure } – Matoy

+0

@ user1007665 Да, но тогда у вас нет * нарезки *. Пожалуйста, прочитайте предоставленную ссылку. –

0

В C++ вы должны прочитать и записать какой-либо тип тега в файл, чтобы запомнить конкретный тип.

Виртуальный метод, такой как ShapeType get_type_tag(), сделал бы это, где возвращаемый тип является перечислением, соответствующим одному из конкретных классов.

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

0

Вы можете создать словарь заводских функций с помощью имени формы или идентификатора формы (shape_type).

// prefer std::shared_ptr or std::unique_ptr of course 
std::map<std::string, std::function<Shape *()>> Shape_Factory_Map; 

// some kind of type registration is now needed 
// to build the map of functions 
RegisterShape(std::string, std::function<Shape *()>); 
// or some kind of 
BuildShapeFactoryMap(); 

// then instead of your switch you would simply 
//call the appropriate function in the map 
Shape * myShape = Shape_Factory_Map[shape_type](); 

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

0

Все ответы до сих пор по-прежнему должны использовать коммутатор или карту где-нибудь, чтобы узнать, какой класс использовать для создания различных типов фигур. Если вам нужно добавить другой тип, вам придется изменить код и перекомпилировать.

Возможно использование шаблона Chain of Responsibility - лучший подход. Таким образом, вы можете динамически добавлять новые методы создания или добавлять их во время компиляции, не изменяя уже существующий код:

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

class Creator{ 
    Creator*next; // 1. "next" pointer in the base class 
public: 
    Creator() 
    { 
    next = 0; 
    } 
    void setNext(Creator*n) 
    { 
     next = n; 
    } 
    void add(Creator*n) 
    { 
     if (next) 
     next->add(n); 
     else 
      next = n; 
    } 
    // 2. The "chain" method in the Creator class always delegates to the next obj 
    virtual Shape handle(string type) 
    { 
     next->handle(i); 
    } 
); 

Каждый подкласс Творца будет проверять, если он может сделать тип и вернуть его, если он может, или делегировать следующий в цепочке.

0

Я создал фабрику в C++ некоторое время назад, когда класс автоматически регистрируется во время компиляции, когда он расширяет данный шаблон.

Доступно здесь: https://gist.github.com/sacko87/3359911.

Я не слишком уверен, как люди реагируют на ссылки за пределами SO, но это всего лишь несколько файлов. Однако, как только работа будет выполнена, используя пример внутри этой ссылки, все, что вам нужно сделать, чтобы иметь новый объект, включенный в завод, будет расширять класс BaseImpl и иметь статическое поле «Имя» (см. Main.cpp). Затем шаблон автоматически регистрирует строку и вводит ее в карту.Позвонив по телефону:

Base *base = BaseFactory::Create("Circle"); 

Вы можете, конечно, заменить Base for Shape.

Смежные вопросы