Search code examples
javaoopdesign-patternsfactory-pattern

Factory Method pattern vs ordinary abstract class implementation


I'm trying to understand the Factory Method pattern. I've managed to distinguish it from the Simple Factory, but now I don't get why it's actually called a "pattern". Please, look at my example below:

Class Diagram link

enter image description here

Sample java code for Messenger:

public String exchangeMessages(String messageToSend) {
   Socket socket = getSocket();
   socket.open();
   socket.send(messageToSend);
   String receivedMessage = socket.receive();
   socket.close();
   return receivedMessage;
}

And for Client:

public static void main(String[] args) {
   Messenger messenger = new TCPMessenger("127.0.0.1", 4321);
   String result = messenger.exchangeMessages("Hello!");
   System.out.println(result);
}

If I understand correctly, everything makes sense because exchangeMessages method exists. We use the abstract factory method getSocket inside it and thanks to it we:

let subclasses decide which class to instantiate

But isn't it just an ordinary abstract class implementation? We take advantage of the code shared by both TCPMessenger and UDPMessenger - for me, this is not a pattern but a core feature of Object-Oriented Programming that everybody knows from the beggining of OOP language learning!

Moreover, I think that naming this creational is very confusing. We actually care about exchangeMessages method, so Client (part of the code that uses our "pattern") is not even aware of the Socket class which creation we're all about. It's more like a way to implement Messenger functionallity (which can be easily extended etc...) than a way to really create something.

Am I missing the point or is my example invalid? Please guys, let me know what do you thing about it.

Thanks in advance!


Solution

  • The Factory Method pattern is a simple pattern, but still a pattern nonetheless.

    Your Client and its relationship to the Factory Method

    The Gang of Four (GoF) definition of the Factory Method Pattern does not specifically mention a Client as a participant of the pattern itself. In reality the Client is most likely to use the Factory Method indirectly through some other operation.

    In your case as the Client does not care about the Socket being used, but just the Messenger itself. However, the Messenger is dependent upon a Socket as it has some behavior that uses a Socket Object.

    So whilst the Client itself does not care about the Socket, it does care about the Messenger which has a use dependency with Socket.

    As the Socket itself is abstract it gives us a problem, as we don't want to replicate the same logic in exchangeMessages(String) multiple times for the different implementations of Socket (TCPSocket and UDPSocket), especially when we may want to add more Socket classes at a later date.

    This is where the Factory Method pattern comes into play, as we can define all the generalized behavior for exchanging messages at an abstract level, but leave the creation of the actual Socket implementation (TCPSocket and UDPSocket) to subclasses of Messenger (TCPMessenger and UDPMessenger).


    Discussion of alternatives to the Factory Method Pattern

    You could just set the Socket as a field of Messenger, and inject it using a Strategy pattern approach or have a constructor that takes a Socket as a parameter. A drawback of this approach is that we'll have to maintain a reference to this Socket Object as a class field in anticipation of it's use.

    However, with the Factory Method Pattern, the Socket will only be created in the method exchangeMessages() when needed and then discarded when the method terminates, which may be of some benefit. This comes at the cost of having to create one of the Socket Objects each time exchangeMessages() is called, which may not be ideal.

    You could also pass the required Socket to the exchangeMessages() method to be used, but this may not as convenient for the user of the method in the long term.

    If your Messenger subclasses also have some specific behaviors (new methods / overridden implementations) not applicable to all types of Messenger, then Factory Method is certainly the way to go, as we need to subclass Messenger anyway and it allows us to strictly enforce the type of Socket created by the Factory Method getSocket().

    Perhaps others could contribute to the pros/cons of the Factory Method (specifically in Java as this the language tag for the question) and I can update this answer in due course.


    Validity of your example

    About whether your example is invalid or not. I think it helps to first fully understand the pattern and the important classes, operations and relationships in order to conform to the Factory Method pattern (as specified by the GoF).

    The Factory Method Pattern allows you to define an Operation (or possibly several operations) in an abstract Creator class (in your case Messenger) which will use some Object (a Product [Socket]) without knowing the specific Product in advance and deferring its creation (hence the term creational pattern) to the Factory Method.

    The Factory Method itself (getSocket()) is abstract in the Creator class and will be implemented by Concrete Creators (TCPMessenger, UDPMessenger) so polymorphism is used to achieve Object creation.

    The Factory Method will return a Concrete Product (TCPSocket, UDPSocket) and have a return type applicable to all Concrete Product classes (Socket) used by the Operation in the abstract Creator class.

    The Operation(s) (exchangeMessages(String)) that use the Factory Method will use the Concrete Product returned by the Factory Method by the interface Product which is applicable to all Concrete Product classes (Socket).

    The result is that this pattern allows new Concrete Product classes to be added by implementing / subclassing the Product interface / class (or its subclasses...) and introducing a new Concrete Creator which returns the newly added Concrete Product from its Factory Method, preventing the need to re-write your Operation(s) that use this Product.

    This means that we do not need to modify existing code when introducing new Concrete Products (Open/Closed Principle).


    Generalized UML for the Factory Method Pattern

    Factory Method Pattern

    Some notes on the UML:

    • The factoryMethod() can be given a default implementation in Creator (i.e. a default Product to return), in turn allowing Creator to be non-abstract
    • The factoryMethod() could be public if desired, but this is superficial to the intention of the pattern since the factoryMethod() is primarily intended to be used by operation()
    • Product does not have to be abstract class, it could be an interface or it could be a concrete class

    Validity of your example (continued)

    In reference to the UML (and your example), the important parts of the pattern in less wordy terms are as follows:

    There must exist

    • A Product interface (not neccesarily an interface in Java terms, could be a class or abstract class in reality) (in your case Socket)
    • ConcreteProduct classes that implement the Product interface (TCPSocket, UDPSocket)
    • An abstract Creator class (Messenger)
    • A Product factoryMethod() method that is defined in the Creator class (getSocket())

    Note: The Creator and factoryMethod() are usually abstract, but the Creator can define a default Product removing this requirement.

    • operation() methods in the Creator class that use the ConcreteProduct returned by the factoryMethod() (exchangeMessages(String))
    • At least one ConcreteCreator class (TCPMessenger and UDPMessenger))

    Such that

    • The ConcreteCreator classes implement the Product factoryMethod() to create and return a ConcreteProduct
    • The abstract Creator class uses the ConcreteProduct (returned by Product factoryMethod()) through the interface for all Product implementations

    The last two points reflect that Creator has a use dependency with the Product interface, and the ConcreteCreator classes have a create dependency with its corresponding ConcreteProduct class.

    Again, note that the Client class is not part of the pattern itself (as defined by the GoF).

    So in conclusion, your example would appear check out as the Factory Method in regards to the requirements listed above if the TCPMessenger and UDPMessenger class implement the getSocket() method correctly (which I assume they actually do).