工厂模式。 何时使用工厂方法?

什么时候在对象中使用工厂方法而不是Factory类是个好主意?

我喜欢从我的课程“人”的angular度思考devise模式,模式是人们相互交stream的方式。

所以,对我来说,工厂模式就像一个招聘机构。 你有一个人需要可变数量的工人。 这个人可能知道他们雇用的人需要一些信息,但就是这样。

所以,当他们需要一个新员工时,他们会打电话给招聘机构,告诉他们他们需要什么。 现在,要真正聘请某人,你需要知道很多东西 – 福利,资格validation等等。但是雇用人员并不需要知道这一点 – 招聘机构处理所有这些。

同样,使用Factory可以让消费者创build新的对象,而不必知道他们是如何创build的细节,或者他们的依赖关系是什么 – 他们只需要提供他们实际需要的信息。

public interface IThingFactory { Thing GetThing(string theString); } public class ThingFactory : IThingFactory { public Thing GetThing(string theString) { return new Thing(theString, firstDependency, secondDependency); } } 

所以,现在ThingFactory的消费者可以得到一个Thing,而不必知道Thing的依赖关系,除了来自消费者的string数据。

工厂方法应该被视为构造函数的一种替代方法 – 大多数时候构造函数的expression能力不足,

 class Foo{ public Foo(bool withBar); } 

不如以下expression:

 class Foo{ public static Foo withBar(); public static Foo withoutBar(); } 

当你需要一个复杂的过程来构造对象,当构造需要一个你不需要的实际类的依赖,当你需要构造不同的对象时,工厂类是很有用的。

我个人发现独立的工厂类是有意义的一种情况是,当您尝试创build的最终对象依赖于其他几个对象时。 例如,在PHP中:假设你有一个House对象,而House对象又有一个Kitchen和一个LivingRoom对象,而LivingRoom对象也有一个TV对象。

最简单的方法是让每个对象在其构造方法上创build子对象,但是如果这些属性相对嵌套,那么当你的House失败创build时,你可能会花一些时间来试图确定哪些是失败的。

另一种方法是执行以下操作(dependency injection,如果你喜欢这个术语):

 $TVObj = new TV($param1, $param2, $param3); $LivingroomObj = new LivingRoom($TVObj, $param1, $param2); $KitchenroomObj = new Kitchen($param1, $param2); $HouseObj = new House($LivingroomObj, $KitchenroomObj); 

在这里,如果创build一个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(); 

由于这里的工厂,创build一个House的过程被抽象出来(因为当你只是想创build一个House时,你不需要创build和设置每一个依赖关系),同时集中维护也容易维护。 还有其他一些原因,为什么使用单独的工厂可能是有益的(例如可testing性),但我发现这个特定的用例来说明工厂类如何有用。

明确区分使用工厂或工厂方法的想法是重要的。 两者都是为了解决相互排斥的不同types的对象创build问题。

让我们具体谈谈“工厂方法”:

首先,当你在开发图书馆或API,这些图书馆或API又将被用于进一步的应用程序开发,那么工厂方法是创造模式的最佳select之一。 背后的原因; 我们知道,何时创build所需function的对象,但对象的types将保持未定,或将决定ob是否传递dynamic参数

现在的重点是,使用工厂模式本身可以达到几乎相同,但是如果工厂模式用于上述突出问题,则会在系统中引入一个巨大的缺陷,那就是您的不同对象(子类对象)的逻辑会特定于某些业务条件,以便在将来需要扩展其他平台的库function时(更为技术上讲,您需要添加更多的基本接口或抽象类的子类,以便除了现有工厂之外,工厂将返回这些对象基于一些dynamic参数),那么每次你需要改变(扩展)工厂级的逻辑,从devise的angular度来看,这将是昂贵的操作和不好的。 另一方面,如果“工厂方法”模式将用于执行相同的事情,那么你只需要创build额外的function(子类),并通过注入dynamic注册它,这不需要更改您的基本代码。

 interface Deliverable { /*********/ } abstract class DefaultProducer { public void taskToBeDone() { Deliverable deliverable = factoryMethodPattern(); } protected abstract Deliverable factoryMethodPattern(); } class SpecificDeliverable implements Deliverable { /***SPECIFIC TASK CAN BE WRITTEN HERE***/ } class SpecificProducer extends DefaultProducer { protected Deliverable factoryMethodPattern() { return new SpecificDeliverable(); } } public class MasterApplicationProgram { public static void main(String arg[]) { DefaultProducer defaultProducer = new SpecificProducer(); defaultProducer.taskToBeDone(); } } 

当你需要几个具有相同参数types但具有不同行为的“构造函数”时,它们也很有用。

在以下情况下使用对象内的工厂方法是个好主意:

  1. 对象的类不知道要创build什么确切的子类
  2. 对象的类是这样devise的,它创build的对象是由子类指定的
  3. 对象的类将其职责委托给辅助子类,并不知道确切的类将采取这些职责

在以下情况下使用抽象工厂类是个好主意:

  1. 你的对象不应该依赖于它的内部对象是如何创build和devise的
  2. 一组链接的对象应该一起使用,你需要服务于这个约束
  3. 对象应该由几个可能的链接对象系列之一来configuration,这些链接对象将成为父对象的一部分
  4. 需要共享显示接口的子对象,而不是实现

让我们通过一个例子来理解FactoryFactoryMethod之间的区别

 interface Shape{ public double getArea(); } class Rectangle implements Shape{ double length; double width; public Rectangle(double length,double width){ this.length = length; this.width = width; } public double getArea(){ return length * width; } } class Square implements Shape{ double length; public Square(double length){ this.length = length; } public double getArea(){ return length * length; } } class ShapeFactory{ public static Shape getShape(String type){ Shape s = null; if ( type.equals("Rectangle")){ // get values of lenght and width without hard coding in real time application s = new Rectangle(10,20); }else if ( type.equals("Square")){ // get values of lenght without hard coding in real time application s = new Square(10); } return s; } } public class FactoryAndFactoryMethod{ public static void main(String args[]){ Shape s = ShapeFactory.getShape("Rectangle"); System.out.println("Area of shape for Rectangle instance:"+s.getArea()); s = ShapeFactory.getShape("Square"); System.out.println("Area of shape for Square instance:"+s.getArea()); } } 

输出:

 Area of shape for Rectangle instance:200.0 Area of shape for Square instance:100.0 

厂:

创build对象而不将实例化逻辑公开给客户端。

FactoryMethod

定义创build对象的接口,但让子类决定实例化哪个类。 Factory方法让类将实例化推迟到子类

在上面的例子中, getArea()Shape接口中的Factory methodRectangleSquareShape界面的子类。

重要提示:

  1. ShapeFactory只是根据传递给方法的参数创build相关的Shape to Client
  2. 现在Client调用Factory Method (在这个例子中: getArea())而不知道对象的实际types。
  3. Client与工厂方法在子类中的实际实现松散耦合。 它不关心具体的类。

用例:

何时使用: Client不知道在运行时需要创build哪些具体的类,但只想获得一个可以完成这个工作的类。

看看相关的SE问题,了解这些模式的结构: 工厂模式和抽象工厂模式有什么根本区别?

Factory Method和当前example类图唯一的偏差: getShape ()已被定义为ShapeFactory静态方法。 如果我们严格遵循模式,我们应该有一个抽象方法getShape ()和具体的ShapeFactory通过为getShape ()方法提供实现来返回相关的ShapeFactory

这是一个真正的品味问题。 工厂类可以根据需要抽象/接口,而工厂方法的重量更轻(也往往是可testing的,因为它们没有明确的types,但是它们需要一个众所周知的注册点,类似于服务定位器,但用于查找工厂方法)。

当返回的对象types具有私有构造函数,不同的工厂类在返回对象上设置不同的属性时,或者当特定的工厂types与其返回的具体types耦合时,工厂类非常有用。

WCF使用ServiceHostFactory类来检索不同情况下的ServiceHost对象。 IIS使用标准的ServiceHostFactory检索.svc文件的ServiceHost实例,但WebScriptServiceHostFactory用于将序列化返回给JavaScript客户端的服务。 ADO.NET数据服务有它自己特殊的DataServiceHostFactory,ASP.NET有它的ApplicationServicesHostFactory,因为它的服务有私有的构造函数。

如果你只有一个类正在消耗工厂,那么你可以在这个类中使用工厂方法。

考虑一个场景,当你必须devise一个订单和客户类。 为了简单和初始需求,您不需要在Order类中使用工厂,并用许多“新Order()”语句填充您的应用程序。 事情运作良好。

现在有一个新的要求,就是Order对象不能在没有Customer关联(新的依赖关系)的情况下实例化。 现在你有以下的考虑。

1-您创build构造函数重载,这将只适用于新的实现。 (不能接受的)。 2-您更改Order()签名并更改每个调用。 (不是一个好习惯和真正的痛苦)。

相反如果你为Order Class创build了一个工厂,你只需要修改一行代码就可以了。 我build议几乎每个聚合关联的工厂类。 希望有所帮助。

据资源制作网站,其意图是:

  • 定义一个创build对象的接口,但让子类决定实例化哪个类。 工厂方法让类将实例化推迟到子类。

  • 定义一个“虚拟”的构造函数。

  • 新运营商认为有害。

一个如何使用它的例子:

 abstract class AbstractFactoryMethod { abstract function makePHPBook($param); } class OReillyFactoryMethod extends AbstractFactoryMethod { function makePHPBook($param) { $book = NULL; switch ($param) { case "us": $book = new OReillyPHPBook(); break; // Other classes... case "other": $book = new SamsPHPBook(); break; default: $book = new OReillyPHPBook(); break; } return $book; } 

然后testing:

 function testFactoryMethod($factoryMethodInstance) { $phpUs = $factoryMethodInstance->makePHPBook("us"); echo 'us php Author: '.$phpUs->getAuthor(); echo 'us php Title: '.$phpUs->getTitle(); } echo 'Testing OReillyFactoryMethod'; $factoryMethodInstance = new OReillyFactoryMethod(); testFactoryMethod($factoryMethodInstance); 

工厂class级更重量级,但给你一定的优势。 在需要从多个原始数据源构build对象的情况下,它们只允许您将构build逻辑(也可能是数据聚合)封装在一个地方。 在那里可以抽象地testing,而不用关心对象接口。

我发现这是一个有用的模式,特别是在我无法replace和不充分的ORM,并希望高效实例化来自数据库表连接或存储过程的许多对象。

我引用了PHP大师的书。

工厂模式非常适合在基于驱动程序的设置中实例化众多变体之一,例如用于configuration,会话或caching的不同存储引擎。 工厂模式的最大价值在于,它可以将通常是很多对象设置的东西封装到一个简单的方法调用中。 例如,在设置logging器对象时,需要设置日志types(例如,基于文件的,MySQL或SQLite),logging位置以及潜在的项目(如凭证)。

工厂模式用于在实例化对象时扩充新的操作符,并且可以统一设置对象时可能发生的复杂性,或统一许多类似的对象。

我把工厂比作图书馆的概念。 例如,你可以有一个库来处理数字,另一个库可以处理形状。 您可以将这些库的function以逻辑命名的目录存储为NumbersShapes 。 这些是通用types,在形状的情况下,可以包括整数,浮点数,dobules,长或矩形,圆形,三angular形,五边形。

工厂petter使用多态性,dependency injection和控制反转。

Factory Patterns的目的是: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

假设您正在构build一个操作系统或框架,并且正在构build所有分立组件。

下面是PHP中Factory Pattern概念的一个简单例子。 我可能不是百分百的,但它只是一个简单的例子。 我不是专家。

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

AbstractFactory示例。

  TypeImpl<String> type = new TypeImpl<>(); type.addType("Condition"); type.addType("Hazardous"); AbstractTypeFactory<String, Tag> tags = new AbstractTypeFactory<String, Tag>(type) { @Override public Tag create(String string) { String tp = type.find(string); switch (tp) { case "Hazardous": return new HazardousTag(); case "Condition": return new ConditionTag(); default: return null; } } }; Tag tagHazardous = tags.create("Hazardous"); Tag tagCondition = tags.create("Condition"); } 

如果你想在使用方面创build一个不同的对象。 这是有用的。

 public class factoryMethodPattern { static String planName = "COMMERCIALPLAN"; static int units = 3; public static void main(String args[]) { GetPlanFactory planFactory = new GetPlanFactory(); Plan p = planFactory.getPlan(planName); System.out.print("Bill amount for " + planName + " of " + units + " units is: "); p.getRate(); p.calculateBill(units); } } abstract class Plan { protected double rate; abstract void getRate(); public void calculateBill(int units) { System.out.println(units * rate); } } class DomesticPlan extends Plan { // @override public void getRate() { rate = 3.50; } } class CommercialPlan extends Plan { // @override public void getRate() { rate = 7.50; } } class InstitutionalPlan extends Plan { // @override public void getRate() { rate = 5.50; } } class GetPlanFactory { // use getPlan method to get object of type Plan public Plan getPlan(String planType) { if (planType == null) { return null; } if (planType.equalsIgnoreCase("DOMESTICPLAN")) { return new DomesticPlan(); } else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) { return new CommercialPlan(); } else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) { return new InstitutionalPlan(); } return null; } }