OO设计 - 放置非成员函数的位置

时间:2012-06-26 17:52:01

标签: c++ python oop

我有一个具有许多参数的复杂构造过程的类。多个客户端共享此类的对象,并且这些客户端参数的并集用于实例化该类。因此,我有一个工厂类来存储这些需求,检查各种客户端请求的一致性,并实例化该类。

此外,还有一组常用的使用模型(或多组参数),多个客户使用这些模型(或多组参数)。

例如,考虑一个例子。 (请注意,实际的代码是C ++,但我的经验是Python,所以我在Python中使用伪代码。是的,我知道这个例子实际上不会按原样运行。)

class Classroom:
    def __init__(self, room_size=None, n_desks=None, n_boards=None, 
                       n_books=None, has_globe=False, ... ):
        ...

class ClassroomFactory:
    def __init__(self):
        self._requirements = dict()

    def addRequirement(self, name, value):
        if name.startswith("n_"):
            self._requirements[name] = max(value, self._requirements.get(name, 0))
        ...

    def createClassroom(self):
        return Classroom(**self._requirements)


# instantiate the factory
factory = ClassroomFactory()

# "client 1" is a geography teaacher
factory.addRequirement("n_desks", 10)
factory.addRequirement("n_boards", 1)
factory.addRequirement("has_globe", True)

# "client 2" is a math teacher
factory.addRequirement("n_desks", 10)
factory.addRequirement("n_boards", 1)

# "client 3" is a after-school day-care
factory.addRequirement("room_size",  (20,20))
factory.addRequirement("has_carpet", True)

room = factory.createClassroom()

常用模式是教师,我们需要10个桌子和一个板子。我认为最好由非成员函数/装饰器提供,例如:

def makeTeacherRoom(factory):
    factory.addRequirement("n_desks", 10)
    factory.addRequirement("n_boards", 1)        
    return factory

这似乎是“喜欢非会员/非会员到会员”范例的一个很好的例子。

我正在努力的事情是,在更大的OO代码的框架内,这些类型的非成员函数/装饰器应该在哪里存在,无论是在命名空间还是在实际文件方面?

  1. 他们应该住在工厂的文件/命名空间吗?它们与工厂密切相关,但它们对一般工厂有限制,不需要用于工厂。

  2. 它们应该存在于客户端的文件/命名空间中吗?客户了解这些使用模型,但这会限制多个客户端之间的重复使用。

  3. 它们是否应该与客户端的公共基类一起使用(例如,可以想象一个“教师”类/命名空间,它还将提供非成员函数makeTeacherRoom(),它将由MathTeacher继承。和GeographyTeacher。

  4. 他们应该在“utils”文件中完全居住在其他地方吗?如果是,在哪个命名空间?

3 个答案:

答案 0 :(得分:1)

这主要是个人决定。您的大多数选项都没有技术负面影响。例如:

  1. 他们可以,因为使用地点,但没有必要。
  2. 由于数据的位置,他们可以再次......
  3. 他们可以,虽然这看起来确实会让事情变得更加混乱。制作实用程序类,您可能必须最终继承它们,或者使部件虚拟化以便稍后覆盖,这将很快变得丑陋。
  4. 这是我个人最喜欢的,或者是这个的变体。
  5. 我通常会创建一个相关命名的util文件(或带有静态方法的类),并将其放在与其使用的类相同的命名空间中(更有用的mutilate版本)。对于Education::Teacher类,您可以拥有一个Education::TeacherUtils文件或类,其中包含对Teacher运行的函数。这保持了一个非常明显的命名搭配,但也将util函数放在它们自己的区域中,因此可以根据它们的任何需要包含它们(在Teacher.cpp或类似的情况下会阻止它)。在类的情况下,你可以使util和基类成为朋友,这偶尔会有所帮助(但很少使用,因为它可能是一种气味)。

    我已经看到了一个命名变体Education::Utils::Teacher,但是转换为文件有点困难(除非你把东西放到utils目录中)并且还会导致名称解析奇怪(在某些情况下) ,当您不想要时,编译器可能会尝试使用Education::Utils::Teacher而不是Education::Teacher。因此,我更喜欢将utils作为后缀。

答案 1 :(得分:0)

您可能希望在应用程序的单例类中处理非成员函数。工厂可能从程序或其他对象执行。

C ++支持全局函数(非成员函数),但是,对于应用程序使用单个对象,“可以解决问题”。

此外,由于“Classroom”对象可以使用许多可选参数进行实例化,因此您可能需要在调用构造函数(在python中使用“ init ”)后分配它。

// filename: "classrooms.cpp"

class ClassroomClass
{
  protected:
    int _Room_Size;
    int _N_Desks;
    int _N_Boards;
    int _N_Books;
    bool _Has_Globe;

  public:
    // constructor without parameters,
    // but, can be declared with them
    ClassroomClass()
    {
      _Room_Size = 0;
      _N_Desks = 0;
      _N_Boards = 0;
      _N_Books = 0;
      _Has_Globe = false;
    } // ClassroomClass()

    public int get_Room_Size()
    {
      return _Room_Size;
    }

    public void set_Room_Size(int Value)
    {
      _Room_Size = Value;
    }

    // other "getters" & "setters" functions
    // ...

} // class ClassroomClass

class ClassroomFactoryClass
{
  public:
    void addRequirement(char[] AKey, char[] AValue);
} // class ClassroomFactoryClass    

class MyProgramClass
{
  public:
    ClassroomFactoryClass Factory;
  public:
    void makeTeacherRoom();

    void doSomething();
} // class MyProgramClass

void MyProgramClass::addRequirement(char[] AKey, char[] AValue)
{
  ...
}  // void MyProgramClass::addRequirement(...)

void MyProgramClass::makeTeacherRoom()
{
  Factory.addRequirement("n_desks", "10")
  Factory.addRequirement("n_boards", "1")   
}  // void MyProgramClass::makeTeacherRoom(...)

void MyProgramClass::doSomething()
{
  ...
}  // void MyProgramClass::doSomething(...)

int main(char[][] args)
{
   MyProgramClass MyProgram = new MyProgramClass();

   MyProgram->doSomething();

   delete MyProgram();

  return 0;
}  // main(...)

干杯

答案 2 :(得分:0)

我个人会让他们成为班上的静态成员。

class File
{
    public:

    static bool load( File & file, std::string const & fileName );

    private:

    std::vector< char > data;
};

int main( void )
{
    std::string fileName = "foo.txt";
    File myFile;

    File::load( myFile, fileName );
}

使用静态方法,他们可以访问类的私有数据,而不属于该类的特定实例。它还意味着方法不会与它们所处理的数据分开,就像你将它们放在某个实用程序头中一样。