В моем проекте у меня есть объекты Resource, которые можно загружать и загружать из разных форматов. Алгоритмы загрузки реализованы в разных подклассах ResourceLoader.
class Resource
{
private:
// Not for client access
Type1 InternalData1;
Type2 InternalData2;
TypeN InternalDataN;
ResourceLoader Loader;
public:
// Any client interface
void UseResource();
};
class ResourceLoader
{
public:
void Load(Resource& R) = 0;
};
class ResourceLoaderFormat1: public ResourceLoader
{
public:
void Load(Resource& R) { ...loads Resource from Format1... }
};
class ResourceLoaderFormat2: public ResourceLoader
{
public:
void Load(Resource& R) { ...loads Resource from Format2... }
};
Загрузчик считывает ввод в заданном формате и инициализирует его целевой объект Resource R. Загрузчик хранится внутри ресурса, поэтому, если ресурс становится недействительным, он перезагружается, используя сохраненный загрузчик.
Вопрос в том, как загрузчик должен инициализировать ресурс?
Проблема заключается в том, что поля класса Resource должны быть доступны для записи из расширяемого набора классов и должны быть недоступны для записи из клиентского кода. Есть ли хорошее решение ООП? Благодарю.
Скажем, мы берем вариант
Сделайте Loader другом Resource.
С недостатком
Каждый новый загрузчик для определенного формата будет добавлен в заголовок ресурса, что убивает расширяемость и требует от любого программиста, пишущего расширения, касаться базового кода.
Однако, поскольку вы разбили свои загрузчики на базовый класс + производные классы, вы можете просто предоставить доступ к Loader
, и дать Loader
доступ к подклассам через protected
члены.
class Resource
{
Type1 InternalData1;
...
friend class ResourceLoader;
};
class ResourceLoader
{
...
protected:
static void setResourceInternalData1(Resource &r, const Type1 &val1);
...
};
Все ResourceLoader
подклассы могут теперь получить доступ к этим установщикам, потому что они protected
:
class ResourceLoaderFormat1: public ResourceLoader;
class ResourceLoaderFormat2: public ResourceLoader;
Это будет хорошо работать до тех пор, пока вы не измените данные членов Resource
слишком часто.
Другое решение (как описано в моем первом комментарии):
class ResourceClient
{
public:
virtual void UseResource() = 0;
}
class ResourceLoader
{
public:
virtual void SetResource() = 0;
}
class Resource:
public ResourceClient,
public ResourceLoader
{
private:
// Not for client access
Type1 InternalData1;
Type2 InternalData2;
TypeN InternalDataN;
ResourceLoader Loader;
public:
// Any client interface
virtual void UseResource();
virtual void SetResource();
};
class ResourceLoader
{
public:
void Load(ResourceLoader& R) = 0;
};
class ResourceLoaderFormat1: public ResourceLoader
{
public:
void Load(ResourceLoader& R) { ...loads Resource from Format1... }
};
class ResourceLoaderFormat2: public ResourceLoader
{
public:
void Load(ResourceLoader& R) { ...loads Resource from Format2... }
};
Набор функций, которым нужно написать Resource, использует указатель ResourceLoader, а восстановленная функция будет использовать указатель ResourceClient для доступа к ресурсу.
Преимущество этого решения в том, что вам не нужно объявлять другу какой-либо класс или функцию, используя доступ для записи. Juste использует правильный интерфейс в зависимости от того, что вы хотите сделать или хотите, чтобы функция была в состоянии сделать