Если есть интерфейс с подписью метода, как показано ниже,
class Interface2
{
public:
virtual ~Interface2() {}
virtual uint getId() = 0;
virtual std::string getName() = 0;
};
class Interface
{
public:
virtual ~Interface(){}
virtual std::list<Interface2*> getData(uint id) = 0;
};
Итак, в основном цель getData(uint id) - открыть внутреннее хранилище интерфейсов Interface2.
При реализации конкретного класса Interface он будет хранить хранилище объектов, состоящих из конкретных классов Interface2.
Например, следующее может быть способом реализации этих интерфейсов.
class Interface2Imp : public Interface2
{
public:
uint getId() { return id;}
std::string getName() { return name;}
private:
uint id;
std::string name;
};
class InterfaceImp : public Interface
{
public:
std::list<Interface2*> getData(uint id)
{
std::list<Interface2*> returnList;
std::list<Interface2Imp> & a = storage[id];
for(auto & it : a)
{
returnList.push_back(&it);
}
return returnList;
}
private:
std::map<int, std::list<Interface2Imp> > storage;
}
Но глядя на то, как реализован getData(uint id), мы видим, что он выполняет итерацию по своему внутреннему хранилищу, а затем снова заполняет его до списка интерфейсов, что выглядит дорогостоящим (и уродливым).
Есть ли лучший способ раскрыть внутренние данные интерфейсов, которые также оказываются открытыми как интерфейсы?
Но тогда InterfaceImp должен будет выполнять операции с содержимым своего хранилища как Interface2, а не как Interface2Imp. Или вы предлагаете перенести его на Interface2Imp, когда когда-нибудь понадобится для внутреннего использования?





в его нынешнем виде необходимо выполнить итерацию и копирование. Класс хранит список экземпляров X, вам нужен список указателей на экземпляры X. Это две разные вещи. Возможно, вы могли бы сделать кое-что из std :: algorithm и т. д., Но это не меняет того факта, что итерация и клонирование должны быть выполнены.
Как насчет изменения интерфейсов и реализации. Сделайте так, чтобы класс InterfaceImp сохранял список shared_ptr в Interface, а метод getData должен возвращать список shared_ptr. Теперь контейнер может напрямую возвращать свои данные. (Также вы не перемещаете необработанные указатели)
Сказав это, на данный момент у вас есть лучшая инкапсуляция. Вы не раскрываете свои внутренние данные вызывающему абоненту, вы их клонируете.
У вас в основном multimap<int, Interface2Imp>, но с одной изюминкой: внешние пользователи должны видеть Interface2*, а не Interface2Imp&.
Это типовой вариант использования адаптеры итератора. Вы можете украсть один из Способствовать росту или написать свой собственный. В последнем случае вам понадобится класс, который содержит реальный член multimap<int, Interface2Imp *>::const_iterator, перенаправляет ему большинство методов итератора-y и лишь немного по-разному адаптирует operator *() и operator ->() (возвращает то, что возвращает внутренний член, но с преобразованием в указатель / ссылку базового класса) .
Если вы не хотите упоминать Interface2Imp даже в приватной части адаптера итератора, спрячьте его с помощью идиомы Pimpl.
Альтернативным решением было бы хранить интеллектуальные указатели для экземпляров реализации интерфейса на 1-м месте:
class InterfaceImp : public Interface
{
public:
const std::list<std::shared_ptr<Interface2>>& getData(uint id)
{
return storage[id];
}
private:
std::map<uint, std::list<std::shared_ptr<Interface2>> > storage;
}
Если вам нужен доступ к экземплярам Interface2Imp для доступа к функциям или данным, которые не видны через Interface2, вы всегда можете использовать static_cast<Interface2Imp*> с этими экземплярами, потому что ваш класс InterfaceImp знает, что использовалось для создания экземпляров реализаций std::shared_ptr<Interface2>.
Вы даже можете выполнять полные запросы времени выполнения с помощью dynamic_cast<Interface2*>, если это необходимо и не влияет на производительность вашего приложения.
Почему бы не сделать
storagestd::map<int, std::list<std::unique_ptr<Interface2>>>, а затем просто вернуть ссылку наstorage[id]?