store many of relation 1:1 between various type of objects : decoupling & high performance

前端 未结 6 1344
夕颜
夕颜 2021-02-01 08:26

I have 300+ classes. They are related in some ways.

For simplicity, all relation are 1:1.
Here is a sample diagram.

(In real case, there are aroun

6条回答
  •  甜味超标
    2021-02-01 09:07

    You could have each class contain a vector of strings that they accept upon creation of the class if the associations are known ahead of time. You could also add an update method that would update this container of names if more are discovered later. If the update function to update the list has been called and the class's container of names has been changed then the function also needs to have the class update itself with the proper class associations or relationships.

    Each class would need these elements at minimum and for storing different types into a single container will require the use of a common non functional abstract base class with some purely virtual methods.

    I'm using 2 classes that are derived from a common base interface for my example and the Primary class is named to represent the class that is having the relationship assigned to where the Associate class is the class is the delegated class that is being assigned to give the primary class that link of association.

    class Base {
    protected:
        std::vector vRelationshipNames_;
        std::vector vRelationships_;
    
    public:
        Base(){}
        virtual ~Base(){}
        virtual void updateListOfNames( std::vector newNames );
    };
    
    class Primary : Base {
    private:
        std::string objectName_;
    public:
        // Constructor if relationships are not known at time of instantiation.
        explicit Primary( const std::string& name );         
        // Constructor if some or all relationships are known. If more are discovered then the update function can be used.
        Primary( const std::string& name, std::vector relationshipNames );
    
        // Add by const reference
        void add( const Base& obj );
    
        // Remove by const reference or by string name.
        void remove( const Base& obj );
        void remove( const std::string& name );
    
        // If needed you can even override the update method.
        virtual void updateListOfNames( std::vector newNames ) override;
    };
    
    // Would basically have similar fields and methods as the class above, stripped them out for simplicity.
    class Associate : Base {
        std::string objectName_;
    };
    

    We can then use a function template that takes two class objects to search to see if the Associate Object is in the list of names of the Primary Object

    template 
    T& setRelationshipBetweenClasses( class T& primaryObject, class U& associateObject ) {
        // Search through primaryObject's list of names to see if associate class is listed
        // If it is not then return from function otherwise, we need to search to see
        // if this class was already added to its list of shared pointers. 
    
        // If it is not found then add it by calling the Primary's add function
    
        // Then we also need to call the Associates add function as well by 
        // passing it a const reference to the Primary class this way both
        // classes now have that relationship. 
    
        // we also return back the reference of the changed Primary object.      
    }
    

    EDIT

    The OP made a comment about using string and being slow; I used string here in the pseudo code just for clarity of understanding, you can replace the std::string with an unsigned int and just use a numeric ID. It will do the same and should be fairly efficient.

    EDIT

    For the OP -

    A common interface of classes without definitions and implementations but their declarations might look something like this:

    Example.h

    #ifndef EXAMPLE_H
    #define EXAMPLE_H
    
    struct CommonProperties {
        std::string name_;
        unsigned int id_;
    
        // Default
        explicit CommonProperties() : name_(std::string()), id_(counter_) {
            counter_++;
        }
        // Passed In Name
        explicit CommonProperties(const std::string& name) : name_(name), id_(counter_) {
            counter_++;
        }
    
    private:
        static unsigned int counter_;
    };
    
    
    class BaseObject {
    protected:
        CommonProperties properties_;
                                                                 // Sizes of Both Containers Should Always Match!
        std::vector> sharedObjects_; // Container of Shared Names
        std::vector sharedObjectIDs_;              // Container of Shared IDs
    
    public:
        explicit BaseObject(const std::string& strName) {
            properties_.name_ = strName;
        }
    
        // Virtual Interface for Abstract Base Class    
        virtual void add(const BaseObject& obj, const std::string& strName, const unsigned int id) = 0; // Purely Virtual Each Derived Class Must Implement
        virtual void update(const BaseObject& obj, const std::string& strName, const unsigned int id) = 0; // Also purely virtual
        virtual void remove(const std::string& strName) {} // Used string method to remove
        virtual void remove(const unsigned int id) {} // Use ID method to remove
    
        // Get Containers
        std::vector> getObjects() const { return sharedObjects_; }
        std::vector getIDs() const { return sharedObjectIDs_; }
    };
    
    
    class Primary : public BaseObject {
    // Member Variables
    public:
    protected:
    private:
    
    // Constructors, Destructor and Methods or Functions
    public:
        explicit Primary(const std::string& strName) : BaseObject(strName) {
        }
    
        // Must Have Purely Virtual
        void add(const BaseObject& obj, const std::string& strName, const unsigned int id) override {
            // Algorithm Here
        }
    
        void update(const BaseObject& obj, const std::string& strName, const unsigned int id) override {
            // Algorithm Here
        }
    
        // other public methods;
    protected:
    private:
    };
    
    class Associate : public BaseObject {
        // Member Variables:
    public:
    protected:
    private:
    
        // Constructors, Destructors and Methods or Functions
    public:
        explicit Associate(const std::string& strName) : BaseObject(strName) {
        }
    
        // Must Have Purely Virtual
        void add(const BaseObject& obj, const std::string& strName, const unsigned int id) override {
            // Algorithm Here
        }
    
        void update(const BaseObject& obj, const std::string& strName, const unsigned int id) override {
            // Algorithm Here
        }
    
    protected:
    private:
    
    };    
    
    #endif // EXAMPLE_H
    

    Example.cpp

    #include "stdafx.h"  // Used for common std containers and algorithms as well as OS and system file includes.
    #include "Example.h"
    
    unsigned int CommonProperties::counter_ = 0x00;
    

    With this example I have both a string and ID. I do this for several reasons; if you ever need to write to a readable file, or to print to the screen or some other output device the contents or properties of this object I have string for human readability. The ability to search, remove and add by a string is available but for the sake of efficiency it should be done by the mechanics of the hidden engine that will instead automatically generate the IDs for you and use the ID system instead for faster searches, comparisons and removals.

    For example let's say I generated 3 distinct objects that are of different classes: class1, class2, class3 their names and id's are set upon creation. I didn't show how to automatically generate a unique string with a base set of characters for a specific class and then append to that string a unique value each time that class is instantiated, but that is what I typically do if a name is not supplied. Supplying a name is optional and name generation is normally automatic. The table of the different classes and their properties name field would look like this:

    // CLASS NAME  |   ID
        "class1"   |   0x01
        "class2"   |   0x02
        "class3"   |   0x03
    

    Now what also makes this setup powerful is the fact that you can have multiple instances of the same class but each has their own unique name. Such as This

    class PickupTruck {};
    
    // Table of Names & IDS similar to above:
       "Chevy"     |  0x04
       "Dodge"     |  0x05
       "Ford"      |  0x06
       "GMC"       |  0x07
    

    Now if you want to distinguish between the name of the actually class and the name of the actual object description; just make sure that you add a std::string as a protected member to the Base or Super class that these classes derive from. This way that name would represent the string representation of that class type, where the property sheet name would be the actual descriptive name of that object. But when doing the actual searches and removals from your containers, using ids for simple loops, counters and indexing are quite efficient.

提交回复
热议问题