什么时候在对象中使用工厂方法而不是factory类是一个好主意?
当前回答
任何将对象创建延迟到其需要使用的对象的子类的类都可以视为Factory模式的示例。
我在https://stackoverflow.com/a/49110001/504133的另一个回答中详细提到过
其他回答
我把工厂比作图书馆的概念。例如,您可以有一个库用于处理数字,另一个库用于处理形状。您可以将这些库的函数存储在逻辑上命名为Numbers或Shapes的目录中。这些是泛型类型,可以包括整数,浮点数,双元,长或矩形,圆形,三角形,在形状的情况下,五边形。
该系统采用了多态、依赖注入和控制反转等技术。
Factory Patterns的目的是:定义一个用于创建对象的接口,但是让子类来决定实例化哪个类。工厂方法允许类延迟实例化到子类。
假设你正在构建一个操作系统或框架,你正在构建所有的离散组件。
下面是PHP中工厂模式概念的一个简单示例。我可能不会完全理解,但这只是一个简单的例子。我不是专家。
class NumbersFactory {
public static function makeNumber( $type, $number ) {
$numObject = null;
$number = null;
switch( $type ) {
case 'float':
$numObject = new Float( $number );
break;
case 'integer':
$numObject = new Integer( $number );
break;
case 'short':
$numObject = new Short( $number );
break;
case 'double':
$numObject = new Double( $number );
break;
case 'long':
$numObject = new Long( $number );
break;
default:
$numObject = new Integer( $number );
break;
}
return $numObject;
}
}
/* Numbers interface */
abstract class Number {
protected $number;
public function __construct( $number ) {
$this->number = $number;
}
abstract public function add();
abstract public function subtract();
abstract public function multiply();
abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Integer Implementation */
class Integer extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Short Implementation */
class Short extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Double Implementation */
class Double extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Long Implementation */
class Long extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
$number = NumbersFactory::makeNumber( 'float', 12.5 );
在对象中使用工厂方法是个好主意:
对象的类不知道它必须创建什么确切的子类 Object的类被设计成它创建的对象由子类指定 对象的类将其职责委托给辅助子类,并且不知道确切的类将承担这些职责
在以下情况下使用抽象工厂类是个好主意:
对象不应该依赖于其内部对象是如何创建和设计的 一组链接的对象应该一起使用,您需要满足这个约束 对象应该由几个可能的链接对象家族中的一个来配置,这些对象将成为父对象的一部分 它需要共享只显示接口而不显示实现的子对象
GOF定义:
定义一个用于创建对象的接口,但是让子类来决定实例化哪个类。工厂方法允许类延迟实例化到子类。
一般例子:
public abstract class Factory<T> {
public abstract T instantiate(Supplier<? extends T> supplier);
}
具体类
public class SupplierFactory<T> extends Factory<T> {
@Override
public T instantiate(Supplier<? extends T> supplier) {
return supplier.get();
}
}
实现
public class Alpha implements BaseInterface {
@Override
public void doAction() {
System.out.println("The Alpha executed");
}
}
public class Beta implements BaseInterface {
@Override
public void doAction() {
System.out.println("The Beta executed");
}
}
public interface BaseInterface {
void doAction();
}
public class Main {
public static void main(String[] args) {
Factory<BaseInterface> secondFactory = new SupplierFactory<>();
secondFactory.instantiate(Beta::new).doAction();
secondFactory.instantiate(Alpha::new).doAction();
}
}
短暂的优势
您正在分离可以变化的代码和不变的代码(即,使用简单工厂模式的优点仍然存在)。这种技术可以帮助您轻松地维护代码。 你的代码不是紧密耦合的;因此,你可以随时在系统中添加新的类,如Lion、Beer等,而无需修改现有的体系结构。因此,您遵循了“修改封闭,扩展开放”的原则。
当您需要几个具有相同参数类型但具有不同行为的“构造函数”时,它们也很有用。
我个人认为单独的Factory类有意义的一种情况是,当您试图创建的最终对象依赖于其他几个对象时。例如,在PHP中:假设你有一个House对象,它又有一个Kitchen和一个LivingRoom对象,LivingRoom对象内部也有一个TV对象。
实现这一点的最简单方法是让每个对象在它们的construct方法上创建它们的子对象,但是如果属性是相对嵌套的,那么当您的House创建失败时,您可能会花一些时间试图隔离到底是什么失败了。
另一种方法是做以下事情(依赖注入,如果你喜欢这个花哨的术语):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
在这里,如果创建House的过程失败了,那么只有一个地方可以查看,但每次想要一个新House时都必须使用这个块,这非常不方便。进入工厂:
class HouseFactory {
public function create() {
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
return $HouseObj;
}
}
$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();
由于这里的工厂,创建House的过程是抽象的(因为当您只想创建一个House时,您不需要创建和设置每个依赖项),同时是集中的,这使得它更容易维护。使用单独的工厂是有益的还有其他原因(例如可测试性),但我发现这个特定的用例最好地说明了工厂类是如何有用的。