Thread: Patterns and anti-patterns

  1. #1
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964

    Patterns and anti-patterns

    I have a bunch of resources (images, scripts, audio etc) that is to be used by many different object across a program. Obviously i don't want each object to load an image, use it and then delete it, i need some resource management.

    Now a resource manager needs to be globally accesible and it needs to have a single mutable state, which immediately has me reaching for the singleton pattern.

    Would you say this is one of those places where a singleton is in order?

    It's generally frowned upon to use singletons and i'm not sure whether my initial assumptions about the scope and state of a resource manager are necessarly correct, i'd just hate to have to pass a resource manager around from object to object throughout the program.

    I've thought about some alternatives. I could make the resource manager a monostate object (making the list of currently loaded resources shared between instances of the manager), but in this case that would be functionally equivalent to a singleton, only instead of calling GetInstance() on some globally accessible object i'd have to instantiate the resource manager wherever i wanted to use a resource.

    Another possibility is to split up the resource management across the different types of resources, and then have each of those objects report back to some central list of resources that have already been loaded. I imagine it looking something like this:

    Code:
    ImageResource img_res("./resources/images/img.png");
    img_res.LoadResource();
    RenderImage(img_res.GetImage());
    ...ImageResource::LoadResource() then checks with some static class whether this resource has been used before and is already loaded, and if not, it loads it and adds it to the list. This means that i wont actually have to deal with any globally accessible class in the different objects of my program, the Resource classes will do that for me.

    Any thoughts?
    How I need a drink, alcoholic in nature, after the heavy lectures involving quantum mechanics.

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    You might want to buy POSA 3, more formally known as "Pattern Oriented Software Architecture, Volume 3" by Michael Kircher and Prashant Jain, published in 2004 by Wiley. ISBN 0-470-84525-2.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Now a resource manager needs to be globally accesible
    This assumption is the reason why singletons are used. But simple parameter passing and/or dependency injection make for better program structure IMO. So instead of being able to procure a resource manager anywhere, components that need one will need to have it passed in by whoever created them.

    Your unit tests will thank you.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #4
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964
    Quote Originally Posted by CornedBee View Post
    This assumption is the reason why singletons are used. But simple parameter passing and/or dependency injection make for better program structure IMO. So instead of being able to procure a resource manager anywhere, components that need one will need to have it passed in by whoever created them.

    Your unit tests will thank you.
    I agree, the idea of a singleton wasn't appealing. But since i might have quite a few objects and methods that need to access resources, simply passing around the resource manager isn't ideal either.

    Here is what i ended up doing:

    Code:
    #ifndef RESOURCE_HPP
    #define RESOURCE_HPP
    
    #include <string>
    #include <map>
    #include <memory>
    
    template <class ResourceType>
    class Resource
    {
        public:
            template <class ResourceLoader>
            Resource(const std::string &p_path, ResourceLoader p_resourceloader) : m_path(p_path)
            {
                if(!m_resource_cache.Contains(m_path))
                {
                    m_resource_cache.AddResource(m_path, p_resourceloader(m_path));
                }
            }
            
            ResourceType& Get() const
            {
                return m_resource_cache.GetResource(m_path);
            }
            
        private:
            std::string m_path;
            
            class ResourceCache
            {
                public:
                    void AddResource(const std::string &p_path, std::unique_ptr<ResourceType> p_resource)
                    {
                        m_resourcemap[p_path] = std::move(p_resource);
                    }    
            
                    ResourceType& GetResource(const std::string &p_path) const
                    {
                        return *(m_resourcemap.at(p_path));
                    }
            
                    bool Contains(const std::string &p_path) const
                    {
                        return (m_resourcemap.end() != m_resourcemap.find(p_path));
                    }
            
                private:
                    std::map<std::string, std::unique_ptr<ResourceType> > m_resourcemap;
            };
            
            static ResourceCache m_resource_cache;
    };
    
    template<class ResourceType>
    typename Resource<ResourceType>::ResourceCache Resource<ResourceType>::m_resource_cache;
    
    #endif
    It's a slight variant of the third pattern i mentioned in the OP. To use it you need simply supply a path to whatever resource you want to load, and a functor/function that does the actual loading:

    Code:
    std::unique_ptr<sf::Texture> LoadTexture(const std::string &p_path)
    {
        std::unique_ptr<sf::Texture> texture(new sf::Texture);
        if(!texture->loadFromFile(p_path))
            throw std::runtime_error("Unable to load texture: " + p_path);
                
        return texture;
    }
    
    .....
    Resource<sf::Texture> texture(path, LoadTexture);
    FunctionThatNeedsTexture(texture.get());
    It's working out quite well so far, any thoughts?
    How I need a drink, alcoholic in nature, after the heavy lectures involving quantum mechanics.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Design patterns in C
    By sashaKap in forum C Programming
    Replies: 2
    Last Post: 04-26-2009, 08:32 AM
  2. c programming on patterns
    By himankinishah in forum C Programming
    Replies: 2
    Last Post: 01-27-2009, 03:54 PM
  3. Printing Patterns
    By ferniture in forum C Programming
    Replies: 11
    Last Post: 11-17-2008, 10:00 PM
  4. Creating Patterns
    By incognito in forum Game Programming
    Replies: 5
    Last Post: 03-16-2003, 09:02 AM
  5. Patterns
    By Unregistered in forum A Brief History of Cprogramming.com
    Replies: 6
    Last Post: 04-29-2002, 04:02 PM