Search code examples
c++initializationloaderfriendresource-management

Initialize object from another one in C++


In my project I have Resource objects which can be loaded and reloaded from different formats. Loading algorithms are implemented in different ResourceLoader subclasses.

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... }
};

Loader reads input in a given format and initializes its target Resource object R. Loader is stored inside a resource, so if resource becomes invalid, it reloads itself using the loader stored.

The question is how a Loader should initialize a Resource?

  1. Make all InternalData fields public. It grants client access to them, which is not good.
  2. Make Loader a friend of Resource. Each new loader for a particular format will be added to a resource header, killing extensibility, and requiring any programmer who writes an extension to touch base code.
  3. Provide setter for each InternalData. Not far from making all them public, as any client may change data it must not change.
  4. Provide Resource::Setup(InternalData1, InternalData2, InternalDataN), or wrap them all in some structure and pass that structure. Same as 3. except that all fields are set at a time.

The problem is that a Resource class fields must be accessible for write from extensible set of classes and must be inaccessible for write from a client code. Is there any good OOP solution? Thanks.


Solution

  • Say we take the option

    Make Loader a friend of Resource.

    With the drawback

    Each new loader for a particular format will be added to a resource header, killing extensibility, and requiring any programmer who writes an extension to touch base code.


    However, since you split your loaders into a base class + derived classes, you can just grant access to Loader, and give Loader subclasses access through protected members.

    class Resource
    {
        Type1 InternalData1;
        ...
        friend class ResourceLoader;
    };
    
    class ResourceLoader
    {
        ...
    protected:
        static void setResourceInternalData1(Resource &r, const Type1 &val1);
        ...
    };
    

    All ResourceLoader subclasses can now access these setters because they are protected:

    class ResourceLoaderFormat1: public ResourceLoader;
    class ResourceLoaderFormat2: public ResourceLoader;
    

    This will work well as long as you don't change the data members of Resource too often.