C ++ Design pattern for individual accessor / mutator interfaces

I have a data structure "Human"

struct Person
{
  protected:
    string name;
    int age;
    string address;
    ...
}

      

I want to create "views" around this structure to separate access to different member variables:

class PersonRead: public Person
{
  public:
    string getName() {..}
    int getAge() {...}
    ...
}
class PersonUpdate: public Person
{
  public:
    void setAddress( string address_ ) {...}
    void setAge( int age_ ) {...}
    ...
}

      

I use this to expose only those methods / variables that are really needed:

int main()
{
...
    writePersonDataToFile ( (PersonRead) personObj );
    updatePersonData ( (PersonUpdate) personObj);
...
}

      

While the above code serves my purpose, there are several problems, including:

  • Heritage nature here is not exactly an 'is-a' relationship

  • I need to output IndianPerson from Person and all related interfaces. This results in a bad diamond pattern:

    struct IndianPerson: public Person {};
    class IndianPersonRead: public IndianPerson, public PersonRead {}; //Person Class common, Diamond pattern here!
    
          

Is there a name for such a design pattern? What are the best ways to implement this pattern? I feel like policy classes can help, but cannot figure out how to implement this.

Any examples could help.

+3


source to share


4 answers


This may seem like overkill for your scenario, but if you want fine-grained control over which classes can invoke various methods in your class, C ++ client-attorney idiom might be appropriate.

For a detailed description of this idiom see http://drdobbs.com/184402053

Here's an example (note: this wasn't compiled, although it's based on production code I'm currently using):



class Person
{
public:
   /// constructor destructor etc:

private:
    string getName() { return name; }

public:
    /// Writer Attourney that access to allows class PersonReader access 
    /// to getXXX functions
    class ReaderAttorney
    {
    private:
        /// Add additional reader member functions...
        static string readName( Person& p )
        { 
            return p.getName();
        }

        // Make any classes that shuold be allowde read access friends of the 
        // attorney here
        friend class PersonReader;
    };

    /// Writer Attourney that access to allows class PersonWriter access 
    /// to setXXX functions
    class WriterAttorney
    {
    private:
        /// Add additiona reader member functions...
        static string setName( Person& p, const string& newName )
        { 
            p.setName( newName );
        }
        friend class PersonWriter;
    };

private:
    string name;
    int age;
    string address;
};

      

It can be used like this:

void PersonWriter::setPersonDetails( const string& name, int age .... )
{
   // PersonWriter is a frend of WriterAttorney and is granted access
   Person::WriterAttorney::setName( name );
   Person::WriterAttorney::setName( age );

   // Note this will fail, since PersonWriter is not a friend of 
   // ReaderAttorney, ergo it is not granted read permission:
   Person::ReaderAttorney::readName();
}

      

+3


source


I think your approach is not entirely right: PersonRead

and PersonUpdate

are not human. They read and modify Person data, but they really aren't Person

.

In the same way IndianPersonRead

, IndianPersonUpdate

they are not IndianPerson

.

I divide this ratio as follows:



  • PersonRead

    use Person

  • PersonUpdate

    use Person

  • IndianPerson

    inherits from Person

    : yesPerson

  • IndianPersonRead

    inherits from PersonRead

    and usesIndianPerson

  • IndianPersonUpdate

    inherits from PersonUpdate

    and usesIndianPerson

I am showing an example of my apporach:

#include <string>
#include <iostream>
using namespace std;

struct Person
{
    string getname() const { return name; }
    string getaddress() const { return address; }
    void setaddress(const string & address_) { address = address_; }
    void setname(const string & name_) { name = name_; }

    protected:
        string name;
        int age;
        string address;
};

class PersonRead
{
    public:
        string getname(const Person & p) { return p.getname(); }
};

class PersonUpdate
{
    public:
        void setAddress(Person & p, const string & address_ ) {p.setaddress(address_); }
        void setname(Person & p, const string & name_ ) {p.setname(name_); }
};

struct IndianPerson : public Person
{
    string gettribe() const { return tribe; }
    void settribe(const string & tribe_) { tribe = tribe_; }
    protected:
    string tribe;
};

struct IndianPersonRead : public PersonRead
{
    public:
        string gettribe(const IndianPerson & p) const { return p.gettribe(); }
};

struct IndianPersonUpdate : public PersonUpdate
{
    public:
    void settribe(IndianPerson & p, const string & t)   { p.settribe(t); }
};

int main(int argc, char **argv)
{
    IndianPerson ip;
    IndianPersonUpdate ipU;
    IndianPersonRead ipR;

    ipU.settribe(ip, "Cheroki");
    ipU.setname(ip, "Charly");
    cout << ipR.getname(ip) << " : " << ipR.gettribe(ip) << endl;
}

      

+2


source


First of all, I agree with Tio's point of view. PersonUpdate is not Human, so there is a misuse of inheritance. Also, I believe that you need to target your classes to represent the real world, so classes like PersonUpdate are wrong because they represent an action, not an object.

In your case, one solution might be to use the Visitor Design Pattern, so Person can adopt a specially designed IPersonStream interface to perform serialization on classes that will implement that interface. The character thread will take attributes of faces on it or forever so that the Person Memo will take a look at the design template as a keepsake and serialize it to xml or whatever.

0


source


I don't have a project template name, but to solve your problems, I would replace the inheritance relation and let Person inherit from the PersonReader and PersonWriter interfaces . Thus, objects that only need to read from Person use the PersonReader interface and as such promises to not modify it.

By giving each individual Personal Access, you can even ensure that Identity doesn't have access differently, but then every class that inherits from Person must have these private members.

0


source







All Articles