2

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

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

例如,考虑一个例子。(请注意,实际代码是 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”文件中吗?如果是这样,在哪个命名空间中?

4

3 回答 3

1

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

  1. 由于使用地点的原因,它们可以,但这不是必需的。
  2. 由于数据的局部性,他们可以,但又一次......
  3. 他们可以,尽管这似乎确实会让事情变得更混乱。制作实用程序类,您最终可能不得不继承它们,或者将部分虚拟化以稍后覆盖,这将很快变得丑陋。
  4. 这是我个人最喜欢的,或者是这个的变体。

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

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

于 2012-06-26T18:14:20.463 回答
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 );
}

使用静态方法,它们可以访问类的私有数据,但不属于该类的特定实例。这也意味着这些方法不会与它们所作用的数据分开,如果您将它们放在某个实用程序标头中就会出现这种情况。

于 2012-06-27T01:27:54.857 回答
0

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

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(...)

干杯

于 2012-06-26T23:30:09.687 回答