In a coding exercise I want to create a Java software for an hypothetical shop: I have a User superclass and three subclasses: administrator (who can manage the product catalog and the customer's list), customer (who chooses the products from the catalog and the system creates a list to be shown to an employee in order to proceed with the payment) and employee (who helps the customer with the payment and, if there are no problems, finally sends an email to the customer). Obviously these are functions that I just invented to justify the presence of three different User types.
User has the attributes: id, email, password. The administrator attributes are: first name, last name, salary, phone number. The employee attributes are: salary. The customer attributes are: first name, last name, date of birth, profession, residence.
Somewhere in the software code I obviously need to create new instances for each type of user. Why and how should I use a creational pattern (abstract factory / factory method) instead of the parameterized constructors of the three subclasses?
I think there's no need, but here is some code:
public class User{
private Integer id;
private String email;
private String password;
public User(Integer id, String email, String password) {
this.id = id;
this.email = email;
this.password = password;
}
//getter-setter
}
public class Customer extends User{
private String firstName;
private String lastName;
private String dateOfBirth;
private String profession;
private String residence;
public Customer(Integer id, String email, String password, String firstName, String lastName, String dateOfBirth, String profession, String residence) {
super(id, email, password);
this.firstName= firstName;
this.lastName= lastName;
this.dateOfBirth= dateOfBirth;
this.profession = profession;
this.residence = residence;
}
//getter-setter
}
//same for administrator and employee, just with different attributes
At a fundamental level, there is almost no difference between a constructor and a static method. They both require no receiver, and neither engages in type hierarchy in any way whatsoever. That last one is by far the best reason to use factories, but it's also the most in-depth one, so I'll save it for last.
In a normal (non-static, non-constructor) method call, you need a receiver: The object you call the method on. For example:
String x = "hello";
toLowerCase(); // not valid java
x.toLowerCase(); // this is fine!
You are allowed to omit the receiver only if there is some value of this
in the current context that would work as a receiver:
void foo() {
bar(); // no receiver?
}
void bar() {}
That's because the compiler knows you meant to write this.bar()
and if you inspect your class file e.g. with javap
you'll notice that the bytecode between writing bar()
and this.bar()
here would be completely identical in every way.
Constructors don't need a receiver1. static methods don't need a receiver.
But this difference, too, is mostly immaterial. After all, what is the difference between a receiver and a method argument? What meaningful difference is there in:
class StringUtils {
static String toLowerCase(String in) {
...
}
}
and
class String {
String toLowerCase() {
...
}
}
Basically none. At the JVM/class level, there isn't one - at least, not in this regard. At the JVM/class level, receivers are method parameters. java compiles x.someInstanceMethod()
the same way as it would compile someStaticMethod(x)
: By pushing x on the stack and then adding the bytecode to invoke the method. Except for one detail: The bytecode involved is different; for instance methods ('normal' ones - no static
) its INVOKEVIRTUAL
- for the static method, its INVOKESTATIC
(and for constructors its INVOKESPECIAL
, but there's effectively no difference between static and special for all intents and purposes). That is important and will be tackled last, as that gets to the type hierarchy stuff.
So, this difference (receivers) doesn't help us. There really is nothing we can do with factories that we couldn't do with constructors or vice versa, except the trivially obvious point, so let's get that out of the way first.
Constructors:
Apple
must necessarily return an object whose type is Apple
- it cannot return a Fruit
(well, all apples are Fruits, but if you invoke .getClass()
on the thing you make, it'll be Apple. You can't return some other non-apple fruit thing), and it can't return a GoldenDelicious
either.Finally! We found something where factories can offer a meaningful upgrade over constructors!
Factories are capable of returning different types that the thing they are in, and can return existing things instead of being forced to make a new thing. Thus:
USE A FACTORY WHEN: You need a method that returns different types depending on context. For example, if you want a single method that can return, depending on need, a Customer
or an Admin
or an Employee
, then a factory can do that, but a constructor can't. This isn't as useful as it sounds: The return type of this factory method would have to be User
which means you can't call any of the type-specific methods (such as getPhoneNumber()
, which only admins have).
USE A FACTORY WHEN: You want to return things that aren't necessarily freshly made. For example, if creation is quite expensive but you can easily cache values. Say, you have an object that represents all phone numbers in a given area code, which is produced by accessing some phone book API. It takes a long time and lots of network resources to do this; you might want to introduce a caching system (if the system just looked up all phone numbers for area code 345, and someone wants to make another AreaCodeLookupTable object for 345, maybe just return the one we already have instead of pinging that API yet again).
For testing purposes, and as a style maxim that is somewhat common in the java ecosystem, java programmers tend to dislike doing lots of stuff in constructors. However, it is objectively bad to have source code where it is possible to obtain a reference to an object that is in 'invalid' state: Because now all code needs to take into account what it should do if faced with such an object. Much, much simpler if having a reference implies it is therefore in well documented known state.
To solve that clash (constructors shouldn't do much, but, also, it should not be possible to observe objects in invalid state - these are mutually exclusive requirements!), you make the constructor simple but private
(or package private), and add a static method that creates an object and makes it valid, and only returns it if it can (throwing an exception otherwise). Now we have our cake and can eat it too: Our constructor is simple, but no code outside this file / this package can use it to produce an object in a state we don't want to leak or even document.
USE A FACTORY WHEN: Constructor is complicated, i.e. consists of a few phases of things you would prefer to test separately, and you feel the style of 'constructors should remain simple' is important. Expanding on whether this is a good idea style-wise is mostly beyond the purpose of Stack Overflow. Simply know that it's a thing lots of style guides mention.
Finally. We get to the real point of factories.
static methods just don't 'play' the hierarchy game. At all. Neither do constructors. There's no point talking about 'this static method overrides that one'. That's just not how it works - the type you call a static method on decides which one is called, at compile time. In contrast, if you call an instance method on an object, you get the most specific version:
List<String> x = new ArrayList<>();
x.get(5);
That calls the get method of ArrayList. The fact that x
is of type List
is immaterial. It's not about the type of the variable, it's about the type of the object the variable is pointing at (or, in java parlance, 'referencing'. Potayto, Potahto: "Points at" and "references" is the same idea).
static methods and constructors simply don't do this and cannot be made to do this.
Hence, if you want to apply the notion of type hierarchies to concerns that affect the entire class, factories are required. In this sense factories are not quite the right name: One extremely common 'class-wide concern' you might want to use in a type hierarchy is 'make new instances' - which is where the 'factory' name comes from. But it's not the only thing one might want to apply to a class as a whole.
In many programming languages, you have a single companion object that can implement interfaces, extend other classes, and so forth. Java isn't like that - so you invent that particular wheel yourself:
interface Fruit {}
interface FruitFactory<F extends Fruit> {
public F create();
public String getFruitType();
}
class AvocadoFactory implements FruitFactory<Avocado> {
public static final AvocadoFactory INSTANCE = new AvocadoFactory();
public Avocado create() { return new Avocado(); }
public String getFruitType() { return "berry"; }
}
class Avocado extends Fruit {}
class GroceryStore {
FruitFactory<?> specialOfTheDay = ...;
}
The key point here: The type of avocadoes is set in stone based on the entire 'type': ALL avocadoes are berries. By definition. Hence, it's 'weird' to put an instance methods on the Avocado
class for its type - all avocados have the same type, but instance methods imply that it's a property that could change depending on what avocado you have. Contrast to say, 'ripeness' or 'weight' - one avocado won't weigh the same as another, so that would be an instance property.
I could just do:
class Avocado {
public static String getFruitType() { "return berry"; }
}
(Key point: static
. EDIT: I forgot static
- the key to this snippet, whoops).
But the problem is, that doesn't play with hierarchy. I cannot write code that accepts any fruit as a concept (not an individual fruit ready to eat, no, a fruit category. Not 'an apple', no 'the concept of all apples') and does something interesting based on that concept's properties as a whole, such as its fruit type.
Factories make that possible.
USE A FACTORY WHEN: You need the entire category to, itself, be part of a type hierarchy; you need an interface to describe that category, for example.
Note that this factory does not have any static
in it, other than the INSTANCE
field. Because we don't want static
: We want type hierarchy, and neither constructors nor static methods engage with type hierarchy. You do have a separate class that follows its own 'factory' hierarchy, and generally a very simple way to obtain the generally one and only instance of that factory that exists.
You tell me. I provided all 4 reasons you should use one. If none of these reasons apply, you should almost certainly just write a constructor. It sounds like none of the 4 apply, but you'd know better.
[1] Constructors of non-static inner classes do need one. non-static inners are rather weird and would make this a significantly longer answer. There's more to it when such classes are involved, but the point of this answer wouldn't meaningfully change.