建模类时,首选的首选方法是什么:
构造者,或
工厂方法
使用其中任何一个会有什么考虑因素?
在某些情况下,我更喜欢有一个工厂方法,如果无法构造对象,则返回null.这使代码整洁.在执行替代操作之前,我可以简单地检查返回的值是否为null,与从构造函数中抛出异常相反.(我个人不喜欢例外)
比如说,我在类上有一个构造函数,它需要一个id值.构造函数使用此值从数据库填充类.如果不存在具有指定标识的记录,则构造函数将抛出RecordNotFoundException.在这种情况下,我将不得不在try..catch块中包含所有这些类的构造.
与此相反,我可以在这些类上有一个静态工厂方法,如果找不到记录,它将返回null.
在这种情况下,哪种方法更好,构造函数或工厂方法?
问问自己它们是什么,为什么我们拥有它们.它们都在那里创建一个对象的实例.
ElementarySchool school = new ElementarySchool(); ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside
到目前为止没有区别.现在想象我们有各种各样的学校类型,我们想从使用ElementarySchool转到HighSchool(来自ElementarySchool或实现与ElementarySchool相同的界面ISchool).代码更改将是:
HighSchool school = new HighSchool(); HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside
在接口的情况下,我们将:
ISchool school = new HighSchool(); ISchool school = SchoolFactory.Construct(); // new HighSchool() inside
现在,如果你在多个地方都有这个代码,你可以看到使用工厂方法可能相当便宜,因为一旦你改变了工厂方法就完成了(如果我们使用带接口的第二个例子).
这是主要的区别和优势.当您开始处理复杂的类层次结构并且希望从这样的层次结构动态创建类的实例时,您将获得以下代码.然后,工厂方法可以使用一个参数来告诉方法要实例化的具体实例.假设您有一个MyStudent类,您需要实例化相应的ISchool对象,以便您的学生成为该学校的成员.
ISchool school = SchoolFactory.ConstructForStudent(myStudent);
现在,您的应用程序中有一个位置包含业务逻辑,用于确定要为不同的IStudent对象实例化的ISchool对象.
所以 - 对于简单的类(值对象等),构造函数很好(你不想过度设计你的应用程序)但是对于复杂的类层次结构,工厂方法是一种首选方法.
这样你就可以按照第四组 "程序到界面,而不是实现" 的第一个设计原则.
您需要阅读(如果您有权访问)Effective Java 2 第1项:考虑静态工厂方法而不是构造函数.
静态工厂方法优点:
他们有名字.
每次调用它们时都不需要创建新对象.
它们可以返回其返回类型的任何子类型的对象.
它们减少了创建参数化类型实例的冗长程度.
静态工厂方法的缺点:
仅提供静态工厂方法时,不能将没有公共或受保护构造函数的类子类化.
它们不容易与其他静态方法区分开来
来自Gamma,Helm,Johnson和Vlissides的" 设计模式:可重复使用的面向对象软件的元素"的第108页.
使用Factory Method模式时
类无法预测它必须创建的对象类
一个类希望它的子类指定它创建的对象
类将责任委托给几个辅助子类之一,并且您希望本地化哪个辅助子类是委托的知识
默认情况下,构造函数应该是首选,因为它们更易于理解和编写.但是,如果您特别需要将对象的构造细节与客户端代码所理解的语义含义分离,那么您最好使用工厂.
构造函数和工厂之间的区别类似于变量和指向变量的指针.还有另一层次的间接,这是一个缺点; 但也有另一层灵活性,这是一个优势.因此,在做出选择时,建议您进行成本与效益分析.
仅在需要使用构造函数无法完成对象创建的额外控制时才使用工厂.
例如,工厂有可能进行缓存.
使用工厂的另一种方法是在您不知道要构造的类型的情况下.通常,您会在插件工厂方案中看到此类用法,其中每个插件必须从基类派生或实现某种接口.工厂创建派生自基类或实现接口的类的实例.
引自"Effective Java",第2版,第1项:考虑静态工厂方法而不是构造函数,p.5:
"请注意,静态工厂方法与设计模式中的工厂方法模式不同 [Gamma95,第107页].此项目中描述的静态工厂方法在设计模式中没有直接等效."
除了"有效的java"(在另一个答案中提到),另一本经典书也暗示:
首选静态工厂方法(使用描述参数的名称)来重载构造函数.
例如.不要写
Complex complex = new Complex(23.0);
而是写
Complex complex = Complex.fromRealNumber(23.0);
本书甚至建议将Complex(float)
构造函数设为私有,以强制用户调用静态工厂方法.
CAD/CAM应用程序的具体示例.
通过使用构造函数来制作切割路径.它是一系列线条和弧线,用于定义切割路径.虽然一系列的线和弧可以是不同的并且具有不同的坐标,但是通过将列表传递给构造函数可以容易地处理它们.
通过使用工厂将形成一个形状.因为虽然有一个形状类,但每个形状的设置都会有所不同,具体取决于它的形状类型.在用户进行选择之前,我们不知道我们将要初始化的形状.
说,我有一个类的构造函数,它需要一个id值。构造函数使用此值从数据库填充类。
这个过程绝对应该在构造函数之外。
构造函数不应访问数据库。
构造函数的任务和原因是初始化数据成员并使用传递给构造函数的值建立类不变。
对于其他所有方法,更好的方法是使用静态工厂方法,或者在更复杂的情况下使用单独的工厂或构建器类。
Microsoft的一些构造方法准则:
在构造函数中完成最少的工作。除了捕获构造函数参数外,构造函数不应做太多工作。任何其他处理的成本都应延迟到需要时再进行。
和
如果所需操作的语义不直接映射到新实例的构造,请考虑使用静态工厂方法而不是构造函数。