【问题标题】:C++ Design pattern for separate accessor/mutator interfaces用于单独访问器/修改器接口的 C++ 设计模式
【发布时间】:2012-04-18 00:33:31
【问题描述】:

我有一个数据结构“Person”

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. 这里的公共继承并不完全是“is-a”关系

  2. 我需要从 Person 派生 IndianPerson 以及所有相应的接口。这会导致不好的菱形图案:

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

这种设计模式有名字吗?有什么更好的方法来实现这种模式?我感觉 Policy 类可能会有所帮助,但不知道如何实现这一点

任何例子都会有很大帮助

【问题讨论】:

    标签: c++ design-patterns inheritance interface


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      在您的情况下,一种解决方案可能是使用访问者设计模式,因此 Person 可以接受专门设计的 IPersonStream 接口,以便在实现该接口的类中执行序列化。 Person 流将接受 Person 属性,或者 Person 的 memento 看一下 memento 设计模式,并将其序列化为 xml 或任何你想要的。

      【讨论】:

        【解决方案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();
        }
        

        【讨论】:

          【解决方案4】:

          我认为您的方法根本不正确:PersonReadPersonUpdate 不是人。他们读取和修改个人数据,但不是真正的Person

          同样,IndianPersonReadIndianPersonUpdate 不是 IndianPerson

          我将这种关系分开如下:

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

          我展示了我的方法的一个例子:

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

          【讨论】:

          • +1:我同意这改进了继承的使用。但是,在尝试编写类似于问题中勾画的writePersonDataToFile 之类的东西时使用这种层次结构仍然很烦人:这样的函数不可能采用正确的类型(const Person&amp;并且还可以工作正确的例如IndianPerson 实例 - 除非每个人都知道如何序列化自己。假设您不想在每个 Person 中都有一个“序列化”方法,您可能需要一个 PersonSerializer 类树(而不是 PersonRead)。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-08-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-05
          • 1970-01-01
          相关资源
          最近更新 更多