用于单独的访问器/增变器接口的C ++设计模式

时间:2012-04-04 09:53:18

标签: c++ design-patterns inheritance interface

我有一个数据结构“人”

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

我想围绕这个结构创建“视图”,以分离对不同成员变量的访问:

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

我用它来公开那些真正需要的方法/变量:

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

虽然上述代码符合我的目的,但有几个问题包括:

  1. 这里的公共继承并不完全是'是-a'的关系

  2. 我需要从Person派生IndianPerson,以及所有相应的接口。这导致了糟糕的钻石图案:

    struct IndianPerson: public Person {};
    class IndianPersonRead: public IndianPerson, public PersonRead {}; //Person Class common, Diamond pattern here!
    
  3. 这样的设计模式是否有名称?有哪些更好的方法来实现这种模式?我有一种感觉政策类可能有所帮助,但无法弄清楚如何实现这个

    任何例子都会有很大的帮助

4 个答案:

答案 0 :(得分:3)

对于你的场景,这似乎有点矫枉过正,但是,如果你想对哪些类可以调用你班级的不同方法进行细致的控制,那么c++ client-attorney idiom成语可能是合适的。

有关此成语的详细说明,请参阅http://drdobbs.com/184402053

这是一个粗略的例子(注意:虽然它是基于我目前使用的生产代码,但尚未编译):

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

可以使用如下:

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();
}

答案 1 :(得分:2)

我认为您的方法根本不正确:PersonReadPersonUpdate不是人。他们读取并修改了Person数据,但实际上并不是Person

同样,IndianPersonReadIndianPersonUpdate不是IndianPerson

我将以下关系分开:

  • PersonRead使用Person
  • PersonUpdate使用Person
  • IndianPerson继承自Person:是Person
  • IndianPersonRead继承自PersonRead并使用IndianPerson
  • IndianPersonUpdate继承自PersonUpdate并使用IndianPerson

我展示了我的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 :(得分:0)

首先,我会同意Tio的观点,PersonUpdate不是Person,因此继承使用错误。另外我相信你需要用你的目标来表示现实世界,所以像PersonUpdate这样的类是错误的,因为它们代表的是动作,而不是对象。

在您的情况下,一个解决方案可能是使用访问者设计模式,因此Person可以接受特别设计的IPersonStream接口,以便在将实现此接口的类中执行序列化。 人物流将接受其上的人物属性或为了好人的纪念品看看纪念品设计模式,并将其序列化为xml或任何你想要的。

答案 3 :(得分:0)

我没有设计模式名称,但为了解决您的问题,我会交换继承关系,并且让Person继承PersonReader和PersonWriter接口。这样,只能从Person读取的对象使用PersonReader接口,因此承诺不会更改它。

通过将Person的每个成员设为私有,您甚至可以确保不以其他方式访问Person,但是从Person继承的每个类都应该将这些成员设为私有。