например, у меня есть фрукты:
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 для сопоставления с правильной функцией. Я слышал, что шаблон реестра может заставить этот случай подчиняться принципу открытого закрытого типа, это правда? если да, то как здесь реализовать шаблон реестра?
Это может стать довольно многословным, так что вместо этого я использую слова.
определение шаблона реестра :
Реестр — это глобальная ассоциация ключей и объектов, позволяющая
объекты должны быть достигнуты из любого места. Он включает в себя два метода: один, который
берет ключ и объект и добавляет объекты в реестр и тот, который
берет ключ и возвращает объект для ключа
Дело в том, что реестр не знает, как создать объект, просто как его получить. Это существенное отличие от творческих моделей.
В основном ваш фрукт должен измениться на
#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")
механизм регистрации под ним может быть сложным для разработки. Вы должны спросить себя:
Посмотри на Шаблон фабричного метода. Это то, что вам нужно.
Я думаю, я бы создал класс 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;
}