Creational patterns
Creational design patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.
Popular ones-
- Singleton - lets you ensure that a class has only one instance, while providing a global access point to this instance.
- Factory Method - provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
- Abstract Factory - lets you produce families of related objects without specifying their concrete classes.
- Builder - Lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.
- Prototype - Lets you copy existing objects without making your code dependent on their classes.
Factory Method
Section titled “Factory Method”The Factory Method Design Pattern is a creational pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumltitle General Factory Method Patternhide empty members
skinparam backgroundColor #ffffffskinparam Shadowing falseskinparam DefaultFontName Arialskinparam DefaultFontSize 17skinparam ArrowColor #475569skinparam rectangleBorderColor #64748bskinparam rectangleBackgroundColor #f8fafcskinparam classBackgroundColor #f8fafcskinparam classBorderColor #64748bskinparam interfaceBackgroundColor #ecfdf5skinparam interfaceBorderColor #10b981skinparam noteBackgroundColor #fef3c7skinparam noteBorderColor #d97706
interface Creator { + factoryCreator(): Product}
abstract AbstractCreator { + {abstract} factoryCreator(): Product (abstract) + someOperation()}
interface Product {
}
note right of Creator::factoryCreator returns ConcreteProduct;end note
note bottom of Creator Creator interface delegates the instance creation to it's implementations through factoryCreator()
// Driver.java Product product = creatorImplA.factoryCreator(); // use the product product.doStuff();end note
class ConcreteCreatorA { + factoryCreator(): Product}
class ConcreteCreatorB { + factoryCreator(): Product}
ConcreteCreatorA ..> Product : createsConcreteCreatorB ..> Product : creates
AbstractCreator <--> Creator : or
ConcreteCreatorA --|> Creator : implementsConcreteCreatorB --|> Creator : implements@endumlError generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumltitle General Simple Factory Pattern
hide empty membersskinparam backgroundColor #ffffffskinparam Shadowing falseskinparam DefaultFontName Arialskinparam DefaultFontSize 13skinparam ArrowColor #475569skinparam rectangleBorderColor #64748bskinparam rectangleBackgroundColor #f8fafcskinparam classBackgroundColor #f8fafcskinparam classBorderColor #64748bskinparam interfaceBackgroundColor #ecfdf5skinparam interfaceBorderColor #10b981skinparam noteBackgroundColor #fef3c7skinparam noteBorderColor #d97706
class Factory { + {static} createProduct(String type): Product}
class Client { + doSomething(String productType)}
interface Product { + doStuff()}
class ConcreteProductA implements Product { + doStuff()}
class ConcreteProductB implements Product { + doStuff()}
Client ..> Factory : usesFactory ..> ConcreteProductA : createsFactory ..> ConcreteProductB : creates
note right of Factory::createProduct switch (type) { case "A": return new ConcreteProductA(); case "B": return new ConcreteProductB(); default: throw new Error("Unknown type"); }end note
note bottom of Factory The Factory class centralizes object creation logic in one placeend note@endumlExample
Section titled “Example”Let’s take an example of simple notification sending application, which was designed without using factory method pattern. The application was designed initially only for sending email notification, so application works well, however it’s difficult to extend now coz the notification service is having email notifications hardcoded.
There’s two ways to solve this problem:
class NotificationService { public void sendNotification(String message) { EmailNotification email = new EmailNotification(); email.send(message); }}following is sample implementation of email notification
5 collapsed lines
class EmailNotification implements Notification { public void send(String message) { System.out.println("Sending an Email notification..."); }}Solution #1(Method style)
Section titled “Solution #1(Method style)”now if we want to add SMS notification and push notifications due to per new requirements. it would be difficult, here the difficulty is not much apparent but if it were a huge code base then we would need to rewrite the entire app or update the hardcoded instances.
Solution is to use Factory Method pattern, which will allow us to create new notification types without modifying the existing code.
Idea being instead of hardcoding the EmailNotification object creation we will let subclasses decide which notification type to create.
Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumltitle Factory Method Pattern
skinparam backgroundColor #ffffffskinparam Shadowing falseskinparam DefaultFontName Arialskinparam DefaultFontSize 13skinparam ArrowColor #475569skinparam rectangleBorderColor #64748bskinparam rectangleBackgroundColor #f8fafcskinparam classBackgroundColor #f8fafcskinparam classBorderColor #64748bskinparam interfaceBackgroundColor #ecfdf5skinparam interfaceBorderColor #10b981
interface Notification { + send(String message)}
abstract class NotificationService { + {abstract} createNotification(): Notification + sendNotification(String message)}
class EmailNotificationService extends NotificationService { + createNotification(): Notification}
class SMSNotificationService extends NotificationService { + createNotification(): Notification}
class PushNotificationService extends NotificationService { + createNotification(): Notification}
class EmailNotification implements Notification { + send(String message)}
class SMSNotification implements Notification { + send(String message)}
class PushNotification implements Notification { + send(String message)}
EmailNotificationService ..> EmailNotification : createsSMSNotificationService ..> SMSNotification : createsPushNotificationService ..> PushNotification : creates
note right of NotificationService::sendNotification Notification notification = createNotification(); notification.send(message);end note
note "Factory Method Pattern delegates\nproduct creation to subclasses" as N1@endumlFirst we create the creator abstract class with createNotification abstract method, which will be implemented by subclasses to create specific notification objects.
abstract class NotificationService { public abstract Notification createNotification();
public void sendNotification(String message) { Notification notification = createNotification(); notification.send(message); }}The Notification interface will be implemented by all concrete notification classes.
3 collapsed lines
interface Notification { public void send(String message);}Next we create the individual products
class EmailNotification implements Notification { public void send(String message) { System.out.println("Sending an Email notification..."); }}similarly we have following files-
5 collapsed lines
class SMSNotification implements Notification { public void send(String message) { System.out.println("Sending an SMS notification..."); }}5 collapsed lines
class PushNotification implements Notification { public void send(String message) { System.out.println("Sending a Push notification..."); }}Solution #2(Class style) or simple factory
Section titled “Solution #2(Class style) or simple factory”Here, you use a separate class with a static method that creates objects. This centralizes object creation logic in one place, but it’s not as flexible as the Factory Method, since you may need to modify the factory whenever new types are added.
Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumltitle Simple Factory Pattern
skinparam backgroundColor #ffffffskinparam Shadowing falseskinparam DefaultFontName Arialskinparam DefaultFontSize 13skinparam ArrowColor #475569skinparam rectangleBorderColor #64748bskinparam rectangleBackgroundColor #f8fafcskinparam classBackgroundColor #f8fafcskinparam classBorderColor #64748bskinparam interfaceBackgroundColor #ecfdf5skinparam interfaceBorderColor #10b981
interface Notification { + send(String message)}
class NotificationFactory { + {static} createNotification(String type): Notification}
class NotificationService { + sendNotification(String message, String type)}
class EmailNotification implements Notification { + send(String message)}
class SMSNotification implements Notification { + send(String message)}
class PushNotification implements Notification { + send(String message)}
NotificationService ..> NotificationFactory : usesNotificationFactory ..> EmailNotification : createsNotificationFactory ..> SMSNotification : createsNotificationFactory ..> PushNotification : creates
note right of NotificationFactory::createNotification switch (type) { case "EMAIL": return new EmailNotification(); case "SMS": return new SMSNotification(); case "PUSH": return new PushNotification(); default: throw new IllegalArgumentException(); }end note
note "Simple Factory centralizes object creation\nin a single class" as N1@endumlclass NotificationFactory { public static Notification createNotification(String type) { switch (type) { case "EMAIL": return new EmailNotification(); case "SMS": return new SMSNotification(); case "PUSH": return new PushNotification(); default: throw new IllegalArgumentException("Unknown notification type"); } }}Then the NotificationService class will use this factory to create notification objects.
class NotificationService { public void sendNotification(String message, String type) { Notification notification = NotificationFactory.createNotification(type); notification.send(message); }}-
Encapsulation - Centralizes and hides object creation logic.
-
Loose Coupling - Clients depend on interfaces/abstractions instead of concrete classes.
-
Scalability - Easy to introduce new product types without changing client code.