FACTORY PATTERN

Himanshu verma
7 min readJun 6, 2023

--

What is Factory Pattern?

We create object without exposing the creation logic to the client and refer to newly created object using a common interface.

Why Factory Pattern?

Let’s say we have a class, in which we have to return object on the basis of a condition. If we have 50 classes of same type then we are just duplicating the same code.

So here we can use the factory pattern

Let me explain it with an example :-


interface Shape {
fun draw()
}

class Rectangle : Shape {
override fun draw() {
TODO("Not yet implemented")
}

}

class Circle : Shape {
override fun draw() {
TODO("Not yet implemented")
}

}

class Triangle : Shape {
override fun draw() {
TODO("Not yet implemented")
}

}

class ShapeFactory(cost: Int) {
var obj: Shape
/**
*Constructor Injection
*/
init {
obj = if (cost < 100)
Rectangle()
else if (cost in 101..199)
Circle()
else
Triangle()
}

fun getShape(): Shape {
return obj
}

}

class Main {
val currentShape = ShapeFactory(200).getShape().draw()
}

We have different shapes, and each has it’s own draw function . We had a business logic according to which we had to display different shape.
So what we did is made use of Factory Pattern .
Injected the type of Shape on the basis of Cost (different objects on the basis of a condition) and made an function to the type of shape and then made call to the draw() function of that corresponding shape.

So we didn’t repeat any class object again and again.

Hope you got it :)

When to use Factory Pattern ?

  • When you want to encapsulate object creation: If you have a complex object creation process or if the process may vary based on conditions, encapsulating this logic in a factory method can simplify client code and promote reusability.
  • When you want to decouple client code from concrete classes: Using the Factory Method Pattern allows you to create objects through an interface or abstract class, abstracting away the specific implementation details of the concrete classes from the client code. This promotes loose coupling and makes it easier to modify or extend the system without impacting existing client code.
  • When you need to support multiple product variations: If your application needs to create different variations of a product or if new types of products may be introduced in the future, the Factory Method Pattern provides a flexible way to accommodate these variations by defining factory methods for each product type. (Like while designing coffee machine we can use it, as on the basis of user selection we have to use different ingredients.)
  • When you want to support customization or configuration: Factories can be used to encapsulate configuration logic, allowing clients to customize the creation process by providing parameters or configuration options to the factory method.

Advantages of Factory Pattern?

  • Decoupling: It separates object creation logic from the client code that uses those objects. This makes the code more flexible and maintainable because changes to the creation process don’t require modifications to client code.
  • Extensibility: It’s easy to introduce new product types without changing the client code. You simply need to create a new Concrete Creator subclass and implement the factory method to produce the new product.
  • Testability: It simplifies unit testing by allowing you to mock or stub out product creation during tests. You can test different product implementations in isolation without relying on actual object creation.
  • Code Reusability: The factory method can be reused in different parts of the application where object creation is needed. This promotes centralizing and reusing object creation logic.
  • Encapsulation: It hides the concrete product classes from the client code, making the code less dependent on specific implementations. This improves maintainability and reduces coupling.

Disadvantages of Factory Method Design Pattern

The disavantages of Factory Method Design Pattern are:

  • Increased Complexity: It introduces additional classes and interfaces, adding a layer of abstraction that can make the code more complex to understand and maintain, especially for those unfamiliar with the pattern.
  • Overhead: The use of polymorphism and dynamic binding can slightly impact performance, although this is often negligible in most applications.
  • Tight Coupling Within Product Hierarchies: Concrete Creators are still tightly coupled to their corresponding Concrete Products. Changes to one often necessitate changes to the other.
  • Dependency on Concrete Subclasses: The client code still depends on the abstract Creator class, requiring knowledge of its concrete subclasses to make correct factory method calls.
  • Potential for Overuse: It’s important to use the Factory Method pattern judiciously to avoid over-engineering the application. Simple object creation can often be handled directly without the need for a factory.
  • Testing Challenges: Testing the factory logic itself can be more complex.

Let’s learn father of it Abstract Factory

What is Abstract Factory?

Abstract Factory patterns work around a super-factory which creates other factories. This factory is also called as factory of factories. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object.In Abstract Factory pattern an interface is responsible for creating a factory of related objects without explicitly specifying their classes.

Let me show an example:-

interface Bike {
fun speed()
}

class BikeOne : Bike {
// Sporty
override fun speed() {
TODO("Not yet implemented")
}

}

class BikeTwo : Bike {
// Cruiser
override fun speed() {
TODO("Not yet implemented")
}

}

class BikeThree : Bike {
// Cruiser
override fun speed() {
TODO("Not yet implemented")
}

}

interface BikeFactory {
fun getBikeType(): Bike
}

class SportyBike(cost: Int) : BikeFactory {
override fun getBikeType(): Bike {
return obj
}

val obj: Bike

init {
obj = BikeOne()
}

}

class CruiserBike(cost: Int) : BikeFactory {
override fun getBikeType(): Bike {
return obj
}

val obj: Bike

init {
obj = if (cost < 100) {
BikeTwo()
} else {
BikeThree()
}
}

}

class VehicleType(type: String) {
val obj: BikeFactory

fun getBike():BikeFactory{
return obj
}
init {
obj = if (type == "Sports") {
SportyBike(100)
} else {
CruiserBike(200)
}
}

}

class main{
val speedOfTheBike = VehicleType("Sports").getBike().getBikeType().speed()
}

As shown in the example we have two factories :- CruiserBike and SportyBike, and it is controlled by Another Factory Vehicle Type.

No duplicity of code

Clear and concise code.

When to use Abstract Factory Method?

  • A system should be independent of how its products are created, composed, and represented.
  • A system should be configured with one of multiple families of products.
  • A family of related product objects is designed to be used together, and you need to enforce this constraint.
  • You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations.

Advantages of Abstract Factory Pattern?

  • Isolation of concrete classes:

The Abstract Factory pattern helps you control the classes of objects that an application creates.

Because a factory encapsulates the responsibility and the process of creating product objects, it isolates clients from implementation classes.

Clients manipulate instances through their abstract interfaces. Product class names are isolated in the implementation of the concrete factory; they do not appear in client code.

  • Exchanging Product Families easily:

The class of a concrete factory appears only once in an application, that is where it’s instantiated.

This makes it easy to change the concrete factory an application uses.

It can use various product configurations simply by changing the concrete factory.

Because an abstract factory creates a complete family of products, the whole product family changes at once.

  • Promoting consistency among products:

When product objects in a family are designed to work together, it’s important that an application use objects from only one family at a time. AbstractFactory makes this easy to enforce.

Disadvantages of Abstract Factory Method Design Pattern

  • Complexity:

Abstract Factory can introduce additional complexity to the codebase.

Having multiple factories and abstract product interfaces may be overkill for simpler projects.

  • Rigidity with New Product Types:

Adding new product types (classes) to the system can be challenging.

You might need to modify not just the concrete factories but also the abstract factory interface, potentially impacting existing code.

  • Increased Number of Classes:

As you introduce more abstract factories and product families, the number of classes in your system can grow rapidly.

This can make the code harder to manage and understand, particularly for smaller projects.

  • Dependency Inversion Principle Violation:

In some cases, the Abstract Factory pattern may lead to a violation of the Dependency Inversion Principle, especially if client code directly depends on concrete factory implementations rather than the abstract interfaces.

  • Limited Extensibility:

Extending the abstract factory hierarchy or introducing new product families might require modifications to multiple parts of the code, potentially leading to cascading changes and making the system less extensible.

  • Not Ideal for Simple Systems:

The Abstract Factory pattern may be overkill for smaller, less complex systems where the overhead of defining abstract factories and products outweighs the benefits of the pattern.

Hope you understood Abstract Factory too :)

Links to entire series of Design Pattern :)

Starting with Solid principles

Strategy Design Pattern

Observer Design Pattern

Decorator Design Pattern

References :- https://www.tutorialspoint.com/design_pattern/abstract_factory_pattern.htm

In case of doubt,

Contact :-https://www.linkedin.com/in/himanshu-verma-318903171/

--

--