In my estimation the answer given by @TomDalling is indeed correct (for what it's worth), however there still seems to be a lot of confusion in the comments.
What I've done here is create some slightly atypical examples of the two patterns and tried to make them appear at first glance quite similar. This will help to pinpoint the crtical differences that separate them.
If you're completely new to the patterns these examples are propably not the best place to start.
Factory Method
Client.javaish
Client(Creator creator) {
ProductA a = creator.createProductA();
}
Creator.javaish
Creator() {}
void creatorStuff() {
ProductA a = createProductA();
a.doSomething();
ProductB b = createProductB();
b.doStuff();
}
abstract ProductA createProductA();
ProductB createProductB() {
return new ProductB1();
}
Why is there a Creator
and a Client
?
Why not? The FactoryMethod
can be used with both, but it will be the type of Creator
that determines the specific product created.
Why isn't createProductB
abstract in Creator
?
A default implementation can be provided, subclasses can still override the method to provide their own implementation.
I thought factory methods only create one product?
Each method does return just one product, but the creator can use multiple factory methods, they just aren't necessarily related in any particular way.
Abstract Factory
Client.javaish
AbstractFactory factory;
Client() {
if (MONDAY) {
factory = new Factory2();
} else {
factory = new AbstractFactory();
}
}
void clientStuff() {
ProductA a = factory.createProductA();
a.doSomething();
ProductB b = factory.createProductB();
b.doStuff();
}
Wait! Your AbstractFactory
isn't, well... er Abstract
That's okay, we're still providing an interface. The return types on the create methods are super-types of the products we want to make.
Holy Smoke Batman! Factory2
doesn't override createProductA()
, what happened to "families of products"?
There's nothing in the pattern that says an object can't belong to more than one family (although your use case might prohibit it). Each concrete factory is responsible for deciding which products are allowed to be created together.
That can't be right, the Client
isn't using dependency injection
You've got to decide what your concrete classes will be somewhere, the Client
is still written to the AbstractFactory
interface.
The confusion here is that people conflate composition with dependency injection. The Client
HAS-A AbstractFactory
regardless of how it got it. Contrast with the IS-A relationship, Client
and AbstractFactory
have no inheritance between them.
Key Differences
- Abstract Factory is always about families of objects
- Factory Method is just a method that allows subclasses to specify the type of concrete object
- Abstract Factory provides an interface to a Client and is separate from where the products are used, the Factory Method could be used by the Creator itself or exposed to a Client.
Summary
The purpose of a factory is to provide a objects, either to a client or itself.
A creator has its own responsibilities and may need to use objects or pass them to a client
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. - GoF
An abstract factory only:
Provide[s] an interface for creating families of related or dependent objects without specifying their concrete classes. - GoF
PlantUML code if you want to play with the diagrams:
@startuml FactoryMethod
abstract class Creator {
creatorStuff()
{abstract} createProductA(): ProductA
createProductB(): ProductB
}
class Creator1 {
createProductA(): ProductA
}
class Creator2 {
createProductA(): ProductA
createProductB(): ProductB
}
together {
interface ProductA {
doSomething()
}
class ProductA1
' class Product1B
}
together {
interface ProductB {
doStuff()
}
class ProductB1
class ProductB2
}
Client --> Creator
Creator <|-- Creator1
Creator <|-- Creator2
Creator --> ProductB1
ProductA1 <-- Creator1
ProductA1 <-- Creator2
ProductB2 <-- Creator2
ProductA <|.. ProductA1
ProductB <|.. ProductB1
ProductB <|.. ProductB2
ProductA <- Creator
@enduml
@startuml AbstractFactory
together {
interface ProductA {
doSomething()
}
class ProductA1
}
together {
interface ProductB {
doStuff()
}
class ProductB1
class ProductB2
}
class AbstractFactory {
createProductA(): ProductA
createProductB(): ProductB
--
-
}
class Factory2 {
createProductB(): ProductB
}
Client --> AbstractFactory
AbstractFactory <|-- Factory2
ProductA <|.. ProductA1
ProductB <|.. ProductB1
ProductB <|.. ProductB2
AbstractFactory --> ProductA1
AbstractFactory --> ProductB1
ProductB2 <-- Factory2
@enduml