Как применить шаблон реестра, чтобы сделать «выбор класса зависимым от ввода» подчиняться открытому закрытому принципу?

например, у меня есть фрукты:

Fruit.h

#ifndef __Fruit__
#define __Fruit__
#include <string>
class Fruit{
public:
virtual void hi(std::string username)=0;
};
#endif

Apple.h

#include "Fruit.h"#include <stdio.h>
class Apple : public Fruit{
public:
virtual void hi(std::string message){
printf("Hi %s,I am apple\n",message.c_str());
}
};

Orange.h

#include "Fruit.h"#include <stdio.h>
class Orange : public Fruit{
public:
virtual void hi(std::string message){
printf("Hi %s,I am orange\n",message.c_str());
}
};

Мне нужно решить, какой использовать в соответствии со строкой ввода:

#include "Apple.h"#include "Orange.h"int main(){
std::string username="abc";
std::string input="Orange";
if(input=="Apple"){
Apple().hi(username);
}else if(input=="Orange"){
Orange().hi(username);
}
return 0;
}

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

0

Решение

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

Реестр — это глобальная ассоциация ключей и объектов, позволяющая
объекты должны быть достигнуты из любого места. Он включает в себя два метода: один, который
берет ключ и объект и добавляет объекты в реестр и тот, который
берет ключ и возвращает объект для ключа

Дело в том, что реестр не знает, как создать объект, просто как его получить. Это существенное отличие от творческих моделей.

В основном ваш фрукт должен измениться на

#include <string>
class Fruit{
public:
virtual std::string key() const = 0;
virtual void hi(std::string username) = 0;
};

А теперь вы вводите реестр

class FruitRegistry final {
public:
bool register(Fruit* fruit);
Fruit* locate(std::string key);

private:
std::map<string, Fruit*> registry;
};

Средства регистрации / получения фруктов должны быть одинаковыми независимо от фруктов. Здесь это может быть сделано с помощью строки карты для фруктов. Вы также можете спроектировать класс fruit, чтобы он использовал метод accept, который весьма полезен, когда ввод сложен (подумайте об инициализации фрукта в зависимости от его описания).

Когда это полезно? Когда он используется за интерфейсом для доступа к типу ресурсов, например img.load("cats.jpg"), Формат jpg, bmp, png весьма различен и может потребоваться отдельный движок для каждого, или один для jpg, другой для обоих bmp и png. Но пользователь не заботится об этих деталях. Обратите внимание, что все больше и больше типов изображений могут быть добавлены в будущем, не касаясь загрузки изображений.

Вопрос в том, чтобы обеспечить хороший img.load("cats.jpg") механизм регистрации под ним может быть сложным для разработки. Вы должны спросить себя:

  • Заботятся ли пользователи об яблоках и апельсинах (если здесь нет остановки, вам не нужен реестр)? Или он просто хочет съесть фрукт?
  • Насколько легко будет есть фрукты в реестре? (должно быть одной строкой)
  • Как часто или как долго можно добавлять новые виды фруктов? (должно быть навсегда)
  • Насколько разные фрукты? (должно быть полностью, только чтобы их можно было съесть)
0

Другие решения

Посмотри на Шаблон фабричного метода. Это то, что вам нужно.

1

Я думаю, я бы создал класс FruitRegistry, зарегистрировал генераторы и использовал его для генерации моих фруктов.

полный пример с использованием статической реализации реестра (c ++ 14):

#include <string>
#include <iostream>
#include <stdexcept>
#include <unordered_map>
#include <memory>

class Fruit{
public:
virtual void hi(std::string username)=0;
};

// static implementation, but it needn't be if you want different registers
class FruitRegister
{
using signature_type = std::unique_ptr<Fruit>();
using function_type = std::function<signature_type>;

struct Impl {
template<class Function>
void add(std::string name, Function&& f)
{
generators_.emplace(name, std::forward<Function>(f));
}

std::unique_ptr<Fruit> generate(std::string const& name) const
{
// NOTE: can throw if bad index
auto& generator = generators_.at(name);
return generator();
}

private:
std::unordered_map<std::string,
function_type> generators_;
};

static Impl& get_impl() {
static Impl impl_ {};
return impl_;
}

public:
template<class Function>
void add(std::string name, Function&& f)
{
get_impl().add(name, std::forward<Function>(f));
}

std::unique_ptr<Fruit> generate(std::string const& name) const
{
return get_impl().generate(name);
}
};class Apple : public Fruit{
public:
virtual void hi(std::string message){
printf("Hi %s,I am apple\n",message.c_str());
}
};

class Orange : public Fruit{
public:
virtual void hi(std::string message){
printf("Hi %s,I am orange\n",message.c_str());
}
};

//
// register all variants here
//
const bool added = [] {
auto r = FruitRegister();
r.add("Apple", []() { return std::make_unique<Apple>(); });
r.add("Orange", []() { return std::make_unique<Orange>(); });

return true;
}();

int main(){
std::string username="abc";
std::string input="Orange";

auto fr = FruitRegister();
auto thing = fr.generate(input);
thing->hi(username);
return 0;
}
0
По вопросам рекламы [email protected]