3
\$\begingroup\$

I'm trying to build a customer email generator in java using the abstract factory pattern. I understand how to use the factory method pattern; however, I'm a bit confused about the abstract factory pattern. I'm trying to generate an email based on customer type. Could you look at my code below and tell me if I'm using the abstract factory pattern correctly? My code works fine. I just want to make sure that I'm using the pattern correctly. Thank you

public abstract class EmailTemplate {
   public abstract String getHeader();
   public abstract String getBody();
   public abstract String getFooter();

   public String generateEmail(){
      return getHeader()+"\n"+getBody()+"\n"+getFooter();
   }
}
public interface EmailFactory {
    EmailTemplate createEmail();
}
public class BusinessEmail extends EmailTemplate {
  @Override
  public String getHeader() {
    return "Dear [business customer],";
  }

  @Override
  public String getBody() {
    return "Thank you for being our valued customer. We are so grateful for the pleasure of serving you and hope we met your expectations.";
  }

  @Override
  public String getFooter() {
    return "Best Regards," +
            "[name]";
  }
}
public interface EmailGeneratorFactory {
    EmailTemplate createEmail();
}
public class BusinessFactory implements EmailGeneratorFactory {
    @Override
    public EmailTemplate createEmail() {
        return new BusinessEmail();
    }
}
public class EMailGenerationSystem {
    private static EMailGenerationSystem EMailGenerationSystem = new EMailGenerationSystem();
    private EMailGenerationSystem(){};
    public static EMailGenerationSystem getInstance(){
        return EMailGenerationSystem;
    }
    public EmailTemplate getEmail(EmailGeneratorFactory factory){
        return factory.createEmail();
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ I dont see abstract factory design pattern at all. Why do you think this should be it? I only see factory method pattern, twice. \$\endgroup\$ – slepic Apr 5 at 6:38
  • \$\begingroup\$ Thank you for your feedback. I followed this example journaldev.com/1418/abstract-factory-design-pattern-in-java that's why I thought I was implementing the abstract factory pattern. If you don't mind, what needs to change to make this into an abstract factory? \$\endgroup\$ – rafiaTech Apr 5 at 18:25
  • \$\begingroup\$ I see, you have followed it well. Unfortunately the article does not describe abstract factory pattern, but a very bad example of factory method pattern surrounded with some useless code. Ill try to make an explanatory answer, but Its almost time to go sleep here, so hopefuly tommorow... \$\endgroup\$ – slepic Apr 5 at 18:36
  • \$\begingroup\$ Thank you @slepic \$\endgroup\$ – rafiaTech Apr 5 at 19:01
0
\$\begingroup\$

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 :)

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You are amazing. Thank you so much for demystifying this matter. \$\endgroup\$ – rafiaTech Apr 6 at 16:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.