面向对象设计在哪里放置非成员函数

2024-09-28 03:23:14 发布

您现在位置:Python中文网/ 问答频道 /正文

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

此外,还有一组通用的使用模型(或参数集),多个客户机将其用于多个工厂。在

例如,考虑一个例子。(注意,实际代码是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张课桌和一块黑板。我认为这最好由非成员函数/装饰器来实现,比如:

^{pr2}$

这似乎是“偏好非成员/非朋友对成员”范式的一个很好的例子。在

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

  1. 它们应该存在于工厂的文件/命名空间中吗?它们与工厂有着密切的联系,但它们对一般工厂是有局限性的,不需要用来使用工厂。

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

  3. 如果它们与客户机的公共基类共存(例如,可以想象一个“teacher”类/命名空间,它还将提供非成员函数makeTeacherRoom(),该函数将由MathTeacher和geographytacher继承。

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


Tags: 文件函数nameselfnone参数客户机factory
3条回答

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

C++支持全局函数(非成员函数),但是,对于应用程序使用单个对象,“做这个把戏”。在

另外,由于“教室”对象可以用许多可选参数实例化,所以您可能希望在调用构造函数(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(...)

干杯

就我个人而言,我会让他们成为班上的静态成员。在

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

它们对属于某个静态类的私有方法没有访问权限。这也意味着这些方法并没有与它们所作用的数据分开,如果你把它们放在某个地方的实用程序头中,就会出现这种情况。在

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

  1. 他们可以,因为使用的地方,但这不是必要的。在
  2. 他们可以,因为数据的位置,但同样。。。在
  3. 他们可以,尽管这个看起来会让事情变得更混乱。在生成实用程序类时,您可能最终不得不继承它们,或者将部件虚拟化以便稍后重写,这将很快变得难看。在
  4. 这是我个人的最爱,或者是它的变体。在

我通常会创建一个相关命名的util文件(或具有静态方法的类),并将其放在与其所使用的类相同的命名空间中(mutilate的更有用版本)。对于Education::Teacher类,可以有一个Education::TeacherUtils文件或类,其中包含对Teacher操作的函数。这保持了一个非常明显的命名绑定,但也将util函数放在它们自己的区域中,因此可以根据需要将它们包括在内(在Teacher.cpp或类似的函数中可以避免这一点)。在类的情况下,您可以将util和基类交为朋友,这偶尔会有所帮助(但很少使用,因为这可能是一种气味)。在

我见过一个命名变体Education::Utils::Teacher,但是这有点难以翻译成文件(除非你把东西放进utils目录中),而且还可能导致名称解析的奇怪(在某些上下文中,编译器可能会尝试使用Education::Utils::Teacher而不是{},而不是{})。因此,我更喜欢保留utils作为后缀。在

相关问题 更多 >

    热门问题