Design patterns provide means to solve common problems. Every pattern has its purpose and allows to solve a particular problem (or more precisely, a class of problems with common aspects).
You should not ask yourself "Does my code follow a particular design pattern?". But rather "Does my code follow the right design pattern to solve a particular problem?"
Factory method pattern serves the purpose of decoupling interface implementation from its consumer.
Abstract factory pattern is actually very similar. You may even say that an abstract factory also follows factory method pattern. But the main difference is that abstract factory acts a bit like a pallete of similar objects to create. So in fact, it acts like multiple factory methods at the same time. These individual methods of abstract factory usualy return the same interfaces or subtypes, although I don't think it is strictly necesary.
Factory methods offer instantiation of a polymorphic object, where consumer is not interseted in aspects of the implementation.
Whereas abstract factory leaves the decision over the aspects of the created implementation to consumer which will decide based on the algorithm the consumer implements.
Now what's wrong with the implementation you showed us.
public EmailTemplate getEmail(EmailGeneratorFactory factory){
return factory.createEmail();
}
Do you see the usefulness of this method? I don't. The caller must know the EmailGeneratorFactory
instance, and it must know the instance of this singleton class. But nothing extra is done in addition to what can be done with the EmailGeneratorFactory
alone. Calling factory.createEmail()
can be done directly.
Since EMailGenerationSystem
class does not offer anything else, it is useless as a whole. I suspect that the author of the article you've linked did not understand it himself. And this code is not supposed to be a generic part of the pattern, but rather your application code, where somehow you have got an instance of the factory and do some work with it at some point of logic in your app.
Now, I already mentioned in comments that I see factory methods twice, and no abstract factory at all, in your code.
Let me write just one factory method.
Please excuse any ommited public modifiers, etc, as I am not used to Java.
interface EmailFactory
{
String generateEmail();
}
class BussinessEmailFactory implements EmailFactory
{
String generateEmail()
{
//use a String builder?
return "...";
}
}
class ReportEmailFactory extends EmailFactory {...}
Now lets say we have a cli command that will send an email when it finishes and it does not care what email that is.
class ReportCommand
{
private SomeServiceForWork worker;
private EmailSender emailSender;
private EmaiFactory emailFactory;
private String reportEmail;
ReportCommand(SomeServiceForWork, EmailSender, EmailFactory, String) ...
public void execute()
{
// doing some work
// ...
String emailBody = emailFactory.generateEmail();
emailSender.send(reportEmail, emailBody);
}
}
new ReportCommand(
new WeeklyReportWorker(),
sender,
new ReportEmailFactory(),
"..."
);
While abstract factory could look like this
interface EmailFactory
{
public String generateSuccessEmail();
public String generateErrorEmail(Exception $e);
}
class PlainEmailFactory implements EmailFactory
{
public String generateSuccessEmail()
{
return "...";
}
public String generateErrorEmail(Exception $e)
{
return "......";
}
}
class ReportCommand
{
private SomeServiceForWork worker;
private EmailSender emailSender;
private EmaiFactory emailFactory;
private String reportEmail;
ReportCommand(SomeServiceForWork, EmailSender, EmailFactory, String) ...
public void execute()
{
String emailBody;
try {
// doing some work
// ...
emailBody = emailFactory.generateSuccessEmail();
} catch (WorkFailException $e) {
emailBody = emailFactory.generateErrorEmail($e);
}
emailSender.send(reportEmail, emailBody);
}
}
new ReportCommand(
new WeeklyReportWorker(),
sender,
new PlainEmailFactory(),
"..."
);
Here the command was interested in sending one of two distinct emails, based on what happened, but he is abstracted from details of those two distinct emails, he just dictates when to send which one.
Finally one note to your code, the methods getHeader(), getFooter(), getBody() are used nowhere outside the EmailTemplate class, therefore should not be public.
Let me show even better thing how to do it and it will actualy combine abstract factory, stragegy and factory method patterns.
// this is the abstract factory interface
interface EmailComponentFactory
{
String getHeader();
String getBody();
String getFooter();
}
// this is the factory method interface
interface EmailFactory
{
String generateEmail();
}
// this is the factory method implementation
// it also acts as strategy owner
// and the abstract factory interface is the strategy
class CompositeEmailFactory implements EmailFactory
{
EmailComponentFactory factory;
String separator;
public CompositeEmailFactory(EmailComponentFactory f, String s = "\n") {factory = f; separator = s;}
String generateEmail()
{
String header = factory.getHeader();
String body = factory.getBody();
String footer factory.getFooter();
int separatorLegth = separator.length();
int headerLength = header.length();
int bodyLength = body.length();
int footerLength = footer.length();
int nonHeaderLength = bodyLength + footerLength;
int length = headerLength + nonHeaderLength;
if (headerLength > 0 && nonHeaderLength > 0) {
length += separatorLength;
}
if (bodyLength > 0 && footerLength > 0) {
length += separatorLength;
}
StringBuilder sb = new StringBuilder(length);
if (headerLength > 0 && nonHeaderLength > 0) {
sb.append(header);
sb.append(separator);
}
if (bodyLength > 0 && footerLength > 0) {
sb.append(body);
sb.append(separator);
}
sb.append(footer);
return sb.build();
}
}
In the above code you can also see how to use a string builder to allocate just the right amount of memory for entire string (not tested). It is a bit more complex, but it should be more effective.
Few final notes :)
Both factory methods and abstract factories can accept arguments (and different methods of abstract factory can accept different arguments).
interface IShape {...}
interface ICircle extends IShape {...}
interface ISquare extends IShape {...}
interface IRectangle extends IShape {...}
interface AbstractShapeFactory
{
public ICircle createCirle(double radius);
public ISquare createSquare(double width);
public IRectangle createRectangle(double width, double height);
}
Abstract factory is very similar to factory method accepting enum argument.
interface IColor {...}
interface AbstractColorFactory
{
IColor createRed();
IColor createGreen();
IColor createBlue();
}
enum ColorEnum {Red, Green, Blue};
interface ColorFactoryMethod
{
IColor createColor(ColorEnum color);
}
Factories can of course create other factories, but there must be a reason for it (in complex application there sure are). Anyway doing so does not make it abstract factory pattern. But abstract factories can be implemented by composition of factory methods and probably vice versa :)