何时在对象中使用工厂方法而不是Factory类是个好主意?
答案 0 :(得分:364)
我喜欢在我的课程“人”方面考虑设计模式,模式是人们互相交流的方式。
所以,对我而言,工厂模式就像一个招聘机构。你有一个人需要不同数量的工人。这个人可能知道他们雇用的人需要的一些信息,但就是这样。
因此,当他们需要新员工时,他们会致电招聘机构并告诉他们需要什么。现在,实际上雇用某人,你需要知道很多东西 - 福利,资格验证等。但招聘人员不需要知道任何这些 - 招聘机构处理所有这一点。
以同样的方式,使用Factory允许消费者创建新对象,而不必知道他们如何创建的细节,或者他们的依赖关系 - 他们只需要提供他们实际想要的信息。
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的依赖关系,除了来自消费者的字符串数据。
答案 1 :(得分:85)
工厂方法应该被视为构造函数的替代方法 - 主要是当构造函数不够表达时,即。
class Foo{
public Foo(bool withBar);
}
不像以下那样富有表现力:
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
当你需要一个复杂的构造对象的过程,当你需要构建不同的对象等构造需要你不想要的实际类的依赖时,工厂类很有用。
答案 2 :(得分:68)
我个人发现单独的Factory类有意义的一种情况是,您尝试创建的最终对象依赖于其他几个对象。例如,在PHP中:假设您有一个House
对象,而该对象又有一个Kitchen
和一个LivingRoom
对象,LivingRoom
对象有一个TV
里面的物体也是如此。
实现此目的的最简单方法是让每个对象在其构造方法上创建子级,但如果属性相对嵌套,则当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
时,你不需要创建和设置每一个依赖项)和同时集中,使其更容易维护。使用单独的工厂可能有益(例如可测试性)还有其他原因,但我发现这个特定的用例可以最好地说明工厂类如何有用。
答案 3 :(得分:16)
明确区分使用工厂或工厂方法背后的想法非常重要。 两者都旨在解决互斥的不同类型的对象创建问题。
让我们具体谈谈“工厂方法”:
首先,当您开发库或API时,它们将用于进一步的应用程序开发,那么工厂方法是创建模式的最佳选择之一。背后的原因; 我们知道什么时候创建一个具有所需功能的对象,但是对象的类型将保持不确定,或者将决定传递动态参数。
现在重点是,通过使用工厂模式本身可以实现大致相同但是如果工厂模式将用于上面突出显示的问题,则会在系统中引入一个巨大的缺点,那就是你的不同对象(子类)的逻辑对象)将特定于某些业务条件,因此将来当您需要为其他平台扩展库的功能时(在技术上,您需要添加更多子类的基本接口或抽象类,因此工厂将另外返回这些对象)现有的基于一些动态参数的那些)然后每次你需要改变(扩展)工厂类的逻辑,这将是昂贵的操作而不是从设计角度来看是不好的。 另一方面,如果“工厂方法”模式将用于执行相同的操作,那么您只需要创建其他功能(子类)并通过注入动态注册,这不需要更改基本代码。 / p>
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();
}
}
答案 4 :(得分:14)
当您需要具有相同参数类型但具有不同行为的多个“构造函数”时,它们也很有用。
答案 5 :(得分:13)
最好在以下对象中使用工厂方法:
在以下情况下使用抽象工厂类是个好主意:
答案 6 :(得分:8)
来自
的UML产品:它定义了Factory方法创建的对象的接口。
ConcreteProduct:实现产品界面
创建者:声明工厂方法
ConcreateCreator:实现Factory方法以返回ConcreteProduct的实例
问题陈述:使用定义游戏界面的工厂方法创建游戏工厂。
代码段:
import java.util.HashMap;
/* Product interface as per UML diagram */
interface Game{
/* createGame is a complex method, which executes a sequence of game steps */
public void createGame();
}
/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
public Chess(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Chess game");
System.out.println("Opponents:2");
System.out.println("Define 64 blocks");
System.out.println("Place 16 pieces for White opponent");
System.out.println("Place 16 pieces for Black opponent");
System.out.println("Start Chess game");
System.out.println("---------------------------------------");
}
}
class Checkers implements Game{
public Checkers(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Checkers game");
System.out.println("Opponents:2 or 3 or 4 or 6");
System.out.println("For each opponent, place 10 coins");
System.out.println("Start Checkers game");
System.out.println("---------------------------------------");
}
}
class Ludo implements Game{
public Ludo(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Ludo game");
System.out.println("Opponents:2 or 3 or 4");
System.out.println("For each opponent, place 4 coins");
System.out.println("Create two dices with numbers from 1-6");
System.out.println("Start Ludo game");
System.out.println("---------------------------------------");
}
}
/* Creator interface as per UML diagram */
interface IGameFactory {
public Game getGame(String gameName);
}
/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {
HashMap<String,Game> games = new HashMap<String,Game>();
/*
Since Game Creation is complex process, we don't want to create game using new operator every time.
Instead we create Game only once and store it in Factory. When client request a specific game,
Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
*/
public GameFactory(){
games.put(Chess.class.getName(),new Chess());
games.put(Checkers.class.getName(),new Checkers());
games.put(Ludo.class.getName(),new Ludo());
}
public Game getGame(String gameName){
return games.get(gameName);
}
}
public class NonStaticFactoryDemo{
public static void main(String args[]){
if ( args.length < 1){
System.out.println("Usage: java FactoryDemo gameName");
return;
}
GameFactory factory = new GameFactory();
Game game = factory.getGame(args[0]);
if ( game != null ){
game.createGame();
System.out.println("Game="+game.getClass().getName());
}else{
System.out.println(args[0]+ " Game does not exists in factory");
}
}
}
输出:
java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess
此示例通过实施Factory
来显示FactoryMethod
类。
Game
是所有类型游戏的界面。它定义了复杂的方法:createGame()
Chess, Ludo, Checkers
是游戏的不同变体,可为createGame()
提供实施
public Game getGame(String gameName)
FactoryMethod
中的IGameFactory
GameFactory
在构造函数中预先创建不同类型的游戏。它实现了IGameFactory
工厂方法。
游戏名称作为命令行参数传递给NotStaticFactoryDemo
getGame
GameFactory
接受游戏名称并返回相应的Game
对象。
<强>工厂:强>
创建对象而不将实例化逻辑暴露给客户端。
<强> FactoryMethod 强>
定义用于创建对象的接口,但让子类决定实例化哪个类。 Factory方法允许类将实例化延迟到子类
使用案例
何时使用:Client
不知道在运行时需要创建哪些具体类,但只想获得一个可以完成工作的类。
答案 7 :(得分:5)
这真的是品味问题。工厂类可以根据需要进行抽象/接口,而工厂方法的重量较轻(并且也往往是可测试的,因为它们没有定义的类型,但它们需要一个众所周知的注册点,类似于服务定位器,但用于定位工厂方法)。
答案 8 :(得分:4)
当它们返回的对象类型具有私有构造函数,不同的工厂类在返回的对象上设置不同的属性,或者特定的工厂类型与其返回的具体类型相结合时,工厂类非常有用。
WCF 使用ServiceHostFactory类在不同情况下检索ServiceHost对象。 IIS使用标准ServiceHostFactory检索 .svc 文件的ServiceHost实例,但WebScriptServiceHostFactory用于将序列化返回给JavaScript客户端的服务。 ADO.NET Data Services有自己的特殊DataServiceHostFactory,ASP.NET有ApplicationServicesHostFactory,因为它的服务有私有构造函数。
如果您只有一个消耗工厂的类,那么您可以在该类中使用工厂方法。
答案 9 :(得分:3)
考虑必须设计Order和Customer类的场景。为了简化和初始要求,您不需要为Order类生产工厂,并在许多新订单()&#39;中填写您的应用程序。声明。事情进展顺利。
现在有了一个新的要求,即如果没有Customer关联(新的依赖关系),Order对象就无法实例化。现在您有以下注意事项。
1-您创建构造函数重载,它仅适用于新实现。 (不能接受的)。 2-您更改Order()签名并更改每个invokation。 (这不是一个好习惯和真正的痛苦)。
相反如果你为Order Class创建了一个工厂,你只需更改一行代码就可以了。我建议几乎每个聚合关联的Factory类。希望有所帮助。
答案 10 :(得分:1)
任何将对象创建推迟到其需要使用的对象的子类的类都可以看作是工厂模式的一个例子。
的另一个答案中详细提到过答案 11 :(得分:0)
想象一下,您有不同偏好的不同客户。有人需要大众汽车,另一个需要奥迪,等等。有一件事很常见 - 这是一辆汽车。
为了让我们的客户满意,我们需要一家工厂。工厂只需要知道客户想要什么车,然后将这种车交付给客户。如果以后我们有另一辆车,我们可以轻松扩展我们的停车场和我们的工厂。
我们只用一个 create( ) 方法创建了三种不同的汽车。
结果:
如果你想让逻辑更清晰、程序更可扩展,工厂模式通常非常有用。
答案 12 :(得分:0)
我认为这取决于您要带入代码中的松散耦合度。
工厂方法很好地解耦了东西,但是工厂类号
换句话说,使用工厂方法比使用简单工厂(称为工厂类)更容易进行更改。
请看以下示例:https://connected2know.com/programming/java-factory-pattern/。现在,假设您想带一只新动物。在Factory类中,您需要更改Factory,但是在factory方法中,不需要,您只需要添加一个新的子类。
答案 13 :(得分:0)
如果要在使用方面创建不同的对象。这很有用。
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;
}
}
答案 14 :(得分:0)
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");
}
答案 15 :(得分:0)
我把工厂比作图书馆的概念。例如,您可以拥有一个用于处理数字的库,另一个用于处理形状。您可以将这些库的功能存储在逻辑命名的目录中Numbers
或Shapes
。这些是泛型类型,可以包括整数,浮点数,多臂形,长形或矩形,圆形,三角形,形状为五边形。
工厂petter使用多态,依赖注入和控制反转。
工厂模式的陈述目的是:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
因此,假设您正在构建操作系统或框架,并且您正在构建所有分立组件。
这是PHP中工厂模式概念的一个简单示例。我可能不是100%全部,但它的目的是作为一个简单的例子。我不是专家。
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 );
答案 16 :(得分:0)
工厂类更重量级,但给你一定的优势。在需要从多个原始数据源构建对象的情况下,它们允许您在一个位置仅封装构建逻辑(可能是数据的聚合)。在那里它可以在抽象的情况下进行测试,而不用考虑对象接口。
我发现这是一个有用的模式,特别是在我无法替换和不充分的ORM以及想要从DB表连接或存储过程中有效地实例化许多对象的情况下。