Problem
Imagine that you’re creating a logistics management application. The first
version of your app can only handle transportation by trucks, so the bulk
of your code lives inside the Truck
class.
After a while, your app becomes pretty popular. Each day you receive dozens of requests from sea transportation companies to incorporate sea logistics into the app
Great news, right? But how about the code? At present, most of your code
is coupled to the Truck
class. Adding Ships
into the app would require making changes to the entire
codebase.
Moreover, if later you decide to add another type of transportation to the app, you will probably need to make all of these changes again.
As a result, you will end up with pretty nasty code, riddled with conditionals that switch the app’s behavior depending on the class of transportation objects.
Solution
new
operator) with calls to a special factory method.new
operator, but it’s being called from within the factory method.
Objects returned by a factory method are often referred to as products.
However, consider this: now you can override the factory method in a subclass and change the class of products being created by the method.
There’s a slight limitation though: subclasses may return different types of products only if these products have a common base class or interface.
Also, the factory method in the base class should have its return type declared as this interface.
For example, both Truck
and Ship
classes should implement the Transport
interface, which declares a method called deliver
.
Each class implements this method differently: trucks deliver cargo by
land, ships deliver cargo by sea. The factory method in the RoadLogistics
class returns truck objects, whereas the factory method in
the SeaLogistics
class returns ships.
Transport
. The client knows that all transport objects are supposed to have
the deliver
method, but exactly how it works isn’t important to the client.
public abstract class Computer { public abstract String getRAM(); public abstract String getHDD(); public abstract String getCPU(); @Override public String toString(){ return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+", CPU="+this.getCPU(); } }
Factory Design Pattern Sub Classespublic class PC extends Computer { private String ram; private String hdd; private String cpu; public PC(String ram, String hdd, String cpu){ this.ram=ram; this.hdd=hdd; this.cpu=cpu; } @Override public String getRAM() { return this.ram; } @Override public String getHDD() { return this.hdd; } @Override public String getCPU() { return this.cpu; } }
Notice that both the classes are extendingComputer
super classpublic class Server extends Computer { private String ram; private String hdd; private String cpu; public Server(String ram, String hdd, String cpu){ this.ram=ram; this.hdd=hdd; this.cpu=cpu; } @Override public String getRAM() { return this.ram; } @Override public String getHDD() { return this.hdd; } @Override public String getCPU() { return this.cpu; } }
Factory ClassNow that we have super classes and sub-classes ready, we can write our factory class. Here is the basic implementation.
public class ComputerFactory { public static Computer getComputer(String type, String ram, String hdd, String cpu){ if("PC".equalsIgnoreCase(type)) return new PC(ram, hdd, cpu); else if("Server".equalsIgnoreCase(type)) return new Server(ram, hdd, cpu); return null; }
Here is a simple test client program that uses above factory design pattern implementation.public class TestFactory { public static void main(String[] args) { Computer pc = ComputerFactory.getComputer("pc","2 GB","500 GB","2.4 GHz"); Computer server = ComputerFactory.getComputer("server","16 GB","1 TB","2.9 GHz"); System.out.println("Factory PC Config::"+pc); System.out.println("Factory Server Config::"+server); } }
Output of above program is:Factory PC Config::RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz Factory Server Config::RAM= 16 GB, HDD=1 TB, CPU=2.9 GHz