我有一个三维几何的库。该库有Point、Vector、Axis、Plane等类。该库有一个接口和实现层次结构,但只是所有类都继承自GeometryObject类。
class Point : public GeometryObject;
class Vector : public GeometryObject;
class Axis : public GeometryObject;
class LinePiece : public GeometryObject;
class Plane : public GeometryObject;
每个类都有一个编号的构造函数,例如:
Point::Point(){}
Point::Point(std::array<double, 3> theCoords){}
Vector::Vector(std::array<double, 3> theComponents){}
Axis::Axis(const Point& thePoint1, const Point& thePoint2){}
Axis::Axis(const Point& thePassingPoint, const Vector& theDirectionVector){}
Plane::Plane(const Point& thePoint1, const Point& thePoint2, const Point& thePoint3){}
Plane::Plane(const Point& thePassingPoint, const Vector& theNormalVector){}
如图所示,构造函数具有不同的签名。只有Point类具有默认的ctor。
目前,该库没有API。库的用户必须包括他们要使用的每个几何图形对象的标题。但是我想通过一个单独的接口访问这个库。
#include<cs.hxx>
#include<Point2D.hxx>
#include<Point3D.hxx>
#include<Vector2D.hxx>
#include<Vector3D.hxx>
#include<Axis.hxx>
#include<LinePiece.hxx>
#include<Circle.hxx>
#include<Plane.hxx>
int main()
{
Point3D point1 { Point3D(std::array<double, 3>{ 11., 12., 13. }) };
// Other objects
}
如果我有一个创建者,用户代码是:
#include<Creator.hxx>
int main()
{
Point3D point1 { Creator::createPoint3D(std::array<double, 3>{ 11., 12., 13. }) };
// OR with a builder for example
Builder builder = Builder("Point3D", std::array<double, 3>{ 11., 12., 13. }) // Just an example
GeometryObject point2 { Creator::create(builder) };
// Other objects
}
因此,我想创建一个创建者类来充当库的API。
从这里开始,为了简单起见,我将使用静态创建者函数。根据设计模式(抽象工厂、工厂方法或构建器等),设计会有所不同。
创建者类的定义很简单:
class Creator {
static GeometryObject create(...);
}
或
class Builder {
Builder(...);
}
class Creator2 {
static GeometryObject create(const Builder&);
}
我离开了"对于参数,因为每个对象的构造函数都不同。一个直接的解决方案是为每个构造函数创建一个静态成员函数:
class Creator {
static Axis createAxis(const Point& thePoint1, const Point& thePoint2);
static Axis createAxis(const Point& thePassingPoint, const Vector& theDirectionVector);
}
但是,创建者类与库实现是完全耦合的。库中的任何更改也必须反映给创建者。例如,当一个新的构造函数被添加到库中的类时,必须更新Creator。我学习了抽象工厂、工厂方法和建筑商设计模式。据我所知,创建者类必须独立于要创建的类的定义。但我无法处理可变构造函数的问题。
我有一个创建者设计模式作为我的API是对的吗?如何创建与几何图形库的实现分离(解耦)的Creator类?我应该使用哪种设计模式?
注意:正如我提到的,只有Point有一个默认的ctor。其他物体在物理上是不可能的。例如,矢量不能使所有分量为零。我可以指定默认值。例如,对于"平面",默认值为全局坐标系的x-y平面。但是,平面对象有两个成员:一个点和一个向量。因此,当我创建默认平面时,我必须创建一个点和一个向量。然而,库的用户不会意识到这些点和矢量对象,我认为这不是一个好的设计。另一种解决方案是使用deault构造函数来创建不完整的对象。但这打破了RAII,并不可取。因此,我删除了Point的默认ctors accept。
当前,库没有API。
是的,您的库确实有API。用户可以直接使用这些类。你似乎对什么是";API";应该看起来像还是不像。
库的用户必须包括他们想要使用的每个几何体对象的标题。
这完全没问题。我们已经习惯了。这是大多数图书馆所做的:你需要包括你使用的,你不想包括你不使用的。
但我想通过单个接口访问库。
这是您想要的,但在您的库拥有API之前,您不需要做任何额外的事情。API是公共接口。简而言之:用户可以使用的东西。不要想太多。如果用户想使用Point
,他们会包含相应的标头并使用它。如果他们想使用不同的类,他们会包括不同的标头并用它。
因此,我想创建一个创建者类来充当库的API。
问题就从这里开始了。您想要编写一些没有实际用途的额外代码(其主旨是"我想要一个API",但已经有了)。此外,你希望这个额外的代码不依赖于它所依赖的东西。这是不可能的。无论您选择什么模式,以及某些Builder
、Creator
或Factory
的接口看起来如何,其实现都需要创建对象,因此其实现取决于对象的创建方式。有些东西不能对一项任务负责,而是完全独立于该任务。你可以将关注点分开,但将关注点与自身分开是没有意义的。
如果你的类有不同的构造函数,那么用户需要阅读手册/标题,看看类有什么构造函数,并相应地调用它。当他们不需要调用构造函数,而是需要用相同的参数调用不同的函数,然后将这些参数转发给构造函数时,这种情况不会消失。
我有一个创建者设计模式作为我的API是正确的吗?
否。
我应该使用哪种设计模式?
无。设计模式是为了解决问题。你没有问题需要解决。用户可以通过直接调用构造函数来完成同样的操作,也可以通过调用其他东西来完成,然后将参数转发给构造函数。
使用字符串来选择要创建的类型可能看起来像工厂,这会有所帮助。尽管我看不到这一点的应用。在您的示例中,Builder builder = Builder("Point3D", std::array<double, 3>{ 11., 12., 13. })
最终将为用户创建一个Point3D
。我在这里没有看到多态性的应用,所以用户可以简单地创建一个Point3D
:Point3D p{{11.,12.13.}};
。