I'll start this piece with the place I will use the factory pattern, refactoring. It's a great way to decouple classes w/o having to rearchitect the inline-instantiated class.
It's still a smell in the system. It still needs to go away, but it's a very useful step to be able to improve the code.
OK, that out of the way; let's get onto the click-bait portion of this post.
The Factory Pattern is an Anti-Pattern
Anti-patterns are things that seem great and useful and should be used.d.. but really, just screw things over.
"But the Factory Pattern is everywhere, all the places!!"
So is Singleton, and it's an anti-pattern.
"But no one else thinks it is"
I think it is.
And since this is my blog - It's an antipattern. :)
Which Factory Pattern?
When I'm using "Factory Pattern" I mean the GOF pattern. Which is nicely represented in my top google search result from Tutorialspoint.com. I'm using this as the code actually shows what I dislike. Which we'll see when we get there.
There's one other point I'll highlight, that definitely shapes my thoughts on this pattern, is my core principle in OO code.
Driving principle
Have a representation for every concept that exist in the code.
I've got lots more details about it here.
Code's probably a poor choice of words here since the factory pattern is in the code. I don't know an exact term, a failing. I know the idea behind it. Which I'll blather about for a moment.
When developing a system, there's typically a business case for it. The concepts that are part of that business domain is what should be represented. It goes a little further to also include representations for how those core representations interact.
The Factory Pattern has nothing to do with the business domain. It has nothing to do with how the business domain objects interact. It has no corresponding concept in the system we're trying to build a representation of.t It is a construct that makes building the system in code easier.
Best analogy I can think of in the 10 seconds I gave myself is a building.
Let's say the front door of a building are really cool. When we talk about the buildings door, the truck used to get it there has no bearing on it. Was it a semi? Pickup? Put together on site? Who knows - WHO CARES?! When we're talking about "the door", how we got said door is not part of the conversation. It's not part of the system.
A factory pattern is similar. It's how the object we actually care about got to us. It has no reason to exist in a finished system. Which is part of why I'm fine with it in a system being refactored. How we get a new front door is very relevant to replacing the front door.
This is why I say the factory pattern this defies this principle; because it's not a concept OF the code. It's a pattern IN the code... Small difference, but I think it's huge to how we approach object organization and construction.
Going back to my reference site; it has a few blocks of code. I want to highlight "Step 4", which shows what I don't like.
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//get an object of Circle and call its draw method.
Shape shape1 = shapeFactory.getShape("CIRCLE");
//call draw method of Circle
shape1.draw();
//get an object of Rectangle and call its draw method.
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//call draw method of Rectangle
shape2.draw();
//get an object of Square and call its draw method.
Shape shape3 = shapeFactory.getShape("SQUARE");
//call draw method of square
shape3.draw();
}
}
Specifically (adjusted for brevity)
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.draw();
Why does shapeFactory
exist? What does it do? It gives us an object that does something... No. The code does not WANT the Shapefactory
. It wants a Shape
. This is what has the behavior. It's what DOES something for the system.
What do we do instead? Have an object we can interact with.
The Factory itself is a typical strategy pattern
public class ShapeFactory {
//use getShape method to get object of type shape
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
I'm going to ignore the null
for a few.
If we can know at the time the consumer is constructed what type we'll use, then there's some easy ways to make that happen. I had a few written up... but it deflected from the case that's being shown (and that I dislike) of how to get the strategy pattern w/o a factory pattern.
We don't know yet
I want to answer how we can avoid a factory if we don't know what we want to create yet.
The Factory Demo Code has something like
public void drawThis(String shapeType){
Shape shape = _shapeFactory.getShape(shapeType);
shape.draw();
}
How do we do this w/o the factory? Have the factory BE the thing we want.
public interface ShapeFactory{
void draw(String shapeType)
}
public class ShapeHiddenFactory : ShapeFactory {
public void draw(String shapeType){
getShape(shapeType).draw();//Igoring null safety
}
//Direct Copy
prviate Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
Here we have to use a slightly different shape. I'm not trying to be good about naming. ShapeFactory
is a terrible name. I want to get away from shapes. Maybe DrawShape
? and the class as a DrawShapeFactory
- What? A factory?! Yeah, that's what it is at the moment. It smells. OK, for now I'll call it the AllDrawShape
.
The value here is the use changes from
public void drawThis(String shapeType){
Shape shape = _shapeFactory.getShape(shapeType);
shape.draw();
}
to
public void drawThis(String shapeType){
_drawShape.draw(shapeType);
}
Hmmmm... This looks like a complete passthrough... Whatever contains the object that has drawThis
might be able to, INSTEAD, have a reference to _drawShape
. I've seen this type of encapsulation remove LAYERS of abstraction.
If object selection/creation is an expensive operation, the AllDrawShape
can cache the result as the sole instance, or a map looking up by the string. We still have this flexibility, but w/o the knowledge of there being a Factory.
So Busy
The AllDrawShape
is way too busy. It knows WAY too much. Let's restrict it's knowledge a bit.
Right now it's tightly coupled to the instantiation of multiple classes. It has to know when/how to create them all. This class will be modified everytime those conditions change AND a new one is added AND one is removed.
That's many things. Doing many things is overloading the object and function.
This is how I prefer "factories" to look. Responsibility for creation of any object is in one place.
public class AllDrawShape : DrawShape {
public AllDrawShape(){
this(new NullStringMakeShape(
new CircleMakeShape(
new RectangleMakeShape(
new SquareMakeShape(
new DefaultMakeShape()))))
}
private AllDrawShape(MakeShape makeShape){
_makeShape = makeShape;
}
public void draw(String shapeType){
_makeShape.make(shapeType).draw();
}
}
interface MakeShape{
Shape make(String shapeType)
}
//Code condensed
class NullStringMakeShape : MakeShape{
public NullStringMakeShape(MakeShape nextAction) => _nextAction = nextAction;
public Shape make(String shapeType){
if(shapeType == null) return NullShape(); //NullObject Pattern
return _nextAction.make(shapeType)
}
}
class DefaultMakeShape:MakeShape{
public Shape makeShape(String shapeType) => NullShape();
}
class CircleMakeShape : MakeShape{
public CircleMakeShape(MakeShape nextAction) => _nextAction = nextAction;
public Shape make(String shapeType){
if("CIRCLE".equalsIgnoreCase(shapeType)) return Circle();
return _nextAction.make(shapeType)
}
}
// Repeat for Square and Rectangle
This is gonna look strange to most people. Very familiar for anyone that's coded with me extensively in the past few years.
When to create any of the objects exists in only one place. There is now an authoratative class on how to create a Circle
.
What if we want it to always behave the same?
We use the next copy.
How it'll look in the code... I don't know. Depends on what we're trying to achieve.
One thing about the factory, it's easy to architect it into the code. But it's easy because it doesn't flex. It becomes a choke-point for the code. The rest of the code MUST abide by it. You don't get a say on how a Circle
gets instantiated, the factory does it how the factory does it. Hell or high-water; that's how you create a circle.
Oh, sure - You can create two ShapeFactories... That'll be a help to the system. ... No. You don't create more factories. You're locked into this poor system design.
What if we know
When we know the type before we create the object, or we're just returning the object (kinda like a factory); it's very similar to when we don't know.
public class AllShape : Shape {
public AllShape(String shapeType){
this(shapeType, new NullStringMakeShape(
new CircleMakeShape(
new RectangleMakeShape(
new SquareMakeShape(
new DefaultMakeShape()))))
}
private AllShape(String shapeType, MakeShape nextAction){
_shapeType = shapeType;
_nextAction = nextAction;
}
public void draw(){
cached().draw();
}
private Shape cached(){
if(_cached == null){
_cached = _nextAction.make(_shapeAction)
}
return _cached;
}
}
Now, you can create an instance of a shape dynamically; BUT - Your code HAS THE SHAPE. It has EXACTLY what it wants to interact with. There's no need for the factory.
If you hate the crap that is in the constructor... Sure
public class AllShape : Shape {
public AllShape(String shapeType){
_shapeType = shapeType;
}
public void draw(){
cached().draw();
}
private Shape cached(){
if(_cached == null){
_cached = make(_shapeAction)
}
return _cached;
}
prviate Shape make(String shapeType){
if("CIRCLE".equalsIgnoreCase(shapeType)) return new Circle();
if("SQUARE".equalsIgnoreCase(shapeType)) return new Square();
if("RECTANGLE".equalsIgnoreCase(shapeType)) return new Rectangle();
return NullShape();
}
}
You can smush it all into that class. I think it ends up a cluttered mess.
I'll skip going through the refactoring for make
that I'd normally do, which will get me to the point it's a little cleaner fto have classes. Suffice to say; We can now create the object we want to interact with. This is what the system wants, not a factory.
Another principle I have towards coding is when the consumer MUST do multiple things, do it for them.
When we have the factory, the consumer MUST ALWAYS do multiple steps.
public void drawThis(String shapeType){
Shape shape = _shapeFactory.getShape(shapeType);
shape.draw();
}
When you use either of the methods I use
public void drawThis(String shapeType){
_drawShape.draw(shapeType);
}
and
public void drawThis(){
_shape.draw();
}
A single thing to interact with to get the behavior desired.
I find it core to good Object Oriented programming that objects have behavior FOR the system being developer. A factory has no behavior the system cares about. They exist solely to get something the system does care about. Let's have the system see only that.
What'd I say?
I rambled a lot here. I don't know everything I said. I don't proof read. I dump my thoughts then post it.
The key of why I don't like factories is because the code doesn't want a factory. It wants what the factory provides. Find a way to design the instantiation of concrete implementations so the code never has to know where/how it's happening. A factory FORCES the code to know what's happening.
The factory is a choke-point that's going to limit relationship discovery.
A factory hides that the object is being instantiated there and then. It seriously hinders attempting to refactor creation relationships into different objects.
I think factories are an anti-pattern and a hinderance to further evolution of the system.