C++ Which design pattern should be used when accessing class property globally

695 Views Asked by At

The question is pretty simple .Currently i am working on a project which have a class lets call it ResoureceHandler.cpp . This class has some properties , method which is needed by the remaining classes . some properties like username, userid which i can get , set simply by calling resourceHandler->getUserName() or vice versa . I can think of two ways

Method 1: Make the class singleton and access the method to get , set.

Method 2 make the class and properties static and access them without any instance .

But i am not sure either of them fall into proper design . What should be the ideal way to solve this kind problem ?

2

There are 2 best solutions below

1
user9335240 On

Well, if you want something high performance. Use either of the two methods.

But if you want good coding practice like other languages. You can try something like this:

class User {
private:
    int id;
    string name;
public:
    User(int id, const string &name): id(id), name(name) {}
    inline int getId() const { return id; }
    inline const string &getName() const { return name; }

    // Make a shared object of the User class
    static shared_ptr<User> currentUser;

    static inline void logIn(int id, const string &name) {
        User::currentUser = std::make_shared<User>(id, name);
    }
};

shared_ptr<User> User::currentUser = 0;

void doSomethingLengthyWithUser(shared_ptr<User> user) {
    static mutex mut; // The lock, for printing

    // Simulate long running process
    std::this_thread::sleep_for(std::chrono::milliseconds(3000));

    // This (lock block) is only to make the printed output consistent
    // Try to remove the block, and use the "cout" line only, and see.
    {
        lock_guard<mutex> l(mut);
        cout << "Done it with: " << user->getName() << endl;
    }
}

int main() {

    // Login first user
    User::logIn(1, "first");
    cout << "Logged in: " << User::currentUser->getName() << endl;

    // Now, we want to do something lengthy with the current user.
    // If you were in a different class, you could use the static variable
    std::thread doWithFirst(doSomethingLengthyWithUser, User::currentUser);

    // Login the second user
    User::logIn(2, "second");
    cout << "Logged in: " << User::currentUser->getName() << endl;

    // Do the lengthy operation with the second also
    std::thread doWithSecond(doSomethingLengthyWithUser, User::currentUser);


    // Wait for the second thread to end;
    doWithSecond.join();

    return 0;
}

Why all this?

If there was a lengthy user-related operation, and you suddenly mutate the currentUser. Being a shared_ptr this makes it still unreleased and unmutated INSIDE THE OPERATION, while the static variable will refer to the new one. Also an alternative approach is using copying, but in C++ it may cause some performance problem.

2
seccpur On

Rather suggesting a 3rd method. Avoid using global variables or singletons. A clean code should be the key. Use helper functions when necessary and namespaces like so. If your class is complex, use a proxy design pattern to reduce complexity of class objects and for a cleaner code.

//foo.h ( Avoid instantiating the foo class yourself)

namespace myproject { namespace part1
  {
    class foo  
    {
       string username_;
      public: 
       foo() { username_ = "foo";}
      //properties
         string get_username() const { return username_; }
      //methods
    };    

// helper functions 

  string get_username();

 }    
}

// foo.cpp

namespace myproject { namespace part1
{
  shared_ptr<foo>& get_foo()   
  {
     static shared_ptr<foo> pt;
       if( pt == nullptr)
           pt = make_shared<foo>();
       return pt; 
  }    

   string get_username()
   {
     return get_foo()->get_username();
   }
  }
}

//poo.h

namespace myproject { namespace part2
  {
    class poo  
    {
       string username_;
      public: 
         poo(){ username_ = "poo";}
      //properties
         string get_username() const {return username_;}
      //methods
    };


// helper functions 

  string get_username();

 }    
}

main.cpp

using namespace myproject;
int main()
{
  cout << part1::get_username() << endl;
  auto str2 = part2::get_username();
  return 0;
}