The Abstract Factory Pattern is a design pattern that provides an interface for creating families of related or dependent objets without specifying their concrete classes. This pattern is particularly useful when you need to create objects that are part of a group or have some common characteristics.

 

Key concepts of the Abstract Factory Pattern:

  1. Separate the process of creating objects from the actual implementation.
  2. Use interfaces to define factories and product families.
  3. Provide a common interface for creating a family of related objects.
  4. Enhance the maintainability and flexbility of the code by eliminating the need for hard-coded dependencies.

To further illustrate the Abstract Factory Pattern's advantages, let's consider an example involving a "Vehicle" class hierarchy. A vehicle can have different types such as car, motorcycle, or truck. Each vehicle type can have various attributes, such as color, engine type, number of wheels, etc. Using the Abstract Factory Pattern, you can create instances of different vehicle types while using the same construction process, making it easier to create different vehicle variations without having to write separate constructors for each combination.

 

Implementing the Abstract Factory Pattern:

  1. Create an interface for the abstract factory that declares methods for creating abstract products.
  2. Implement concrete factories that inherit from the abstract factory interface and create concrete products.
  3. Create an interface for each type of abstract product, which declares methods that the products must implement.
  4. Implement concrete products that inherit from the abstract product interfaces.
  5. Use the concrete factories to create instances of concrete products.

Here's a simple example of implementing the Abstract Factory Pattern using a "Vehicle" class hierarchy:

 

Abstract Vehicle class:

public abstract class Vehicle {
    public abstract String getColor();
    public abstract String getEngineType();
    public abstract int getNumberOfWheels();
}

 

Concrete Car class:

public class Car extends Vehicle {
    private String color;
    private String engineType;
    private int numberOfWheels;

    public Car(String color, String engineType, int numberOfWheels) {
        this.color = color;
        this.engineType = engineType;
        this.numberOfWheels = numberOfWheels;
    }

    @Override
    public String getColor() {
        return color;
    }

    @Override
    public String getEngineType() {
        return engineType;
    }

    @Override
    public int getNumberOfWheels() {
        return numberOfWheels;
    }
}

 

Abstract Factory interface:

public interface VehicleFactory {
    Vehicle createVehicle(String color, String engineType, int numberOfWheels);
}

 

Concrete CarFactory class:

public class CarFactory implements VehicleFactory {
    @Override
    public Vehicle createVehicle(String color, String engineType, int numberOfWheels) {
        return new Car(color, engineType, numberOfWheels);
    }
}

 

Client code:

public class Main {
    public static void main(String[] args) {
        VehicleFactory carFactory = new CarFactory();
        Vehicle car = carFactory.createVehicle("Red", "Gasoline", 4);
        System.out.println("Car color: " + car.getColor());
        System.out.println("Car engine type: " + car.getEngineType());
        System.out.println("Car number of wheels: " + car.getNumberOfWheels());
    }
}

Using the Abstract Factory Pattern improves the maintainability and flexibility of your code, making it easier to create complex objects with various attributes, and simplifying the process of extending the code with new types of objects.

'Java > Design Pattern' 카테고리의 다른 글

Understand of Builder Pattern  (0) 2023.04.06


Recently,  while studying Spring Security, I noticed a repetitive pattern in instance creation. I discovered that Java employed well-known design patterns following Object-Oriented Programming (OOP) principles. Today, I will explore the Builder Pattern sugin some exmaples.

 

The Builder Pattern is a design pattern that separates the construction of a complex object from its representation, allowing the same construction process to create different representations. This pattern is particularly useful when an object has many optional attribute, and it reduces the need for numerous constructors or complex parameter sets.

 

An example of using the Builder Pattern is when you have a class representing a sandwich. A sandwich can have various combinations of ingredients like bread, cheese, vegetable, and sause, Using the Builder Pattern, you can create different sandwich instances with different ingredients while following the same construction process

 

Key concepts of the Builder Pattern:

 

  1. Separate the construction of a complex object from its representation.
  2. Use the same construction process to create different representations.
  3. Reduce the need for multiple constructors or complex parameter sets.
  4. Simplify the creation of complex objects, making the code more maintainable and flexible.

 

To further illustrate the Builder Pattern's advantages, let's consider an example involving a "Car" class. A car can have different attributes such as color, engine type, number of doors, etc. Using the Builder Pattern, you can create car instances with different attributes while using the same construction process, making it easier to create different car variations without having to write separate constructors for each combination.

Implementing the Builder Pattern:

 

  1. Create the Builder class as a Static Nested Class. By convention, append "Builder" to the target class name. For example, the Builder class for a Car class would be named "CarBuilder".
  2. Make the Builder class's constructor public and accept essential values as parameters.
  3. Provide methods for optional values, ensuring that each method returns the Builder object itself.
  4. Define the build() method within the Builder class to provide the final product to the client program. Since object creation is provided only through build(), the target class's constructor should be private.
  5.  
public class Car {
    //required parameters
    private String engine;
    private int tires;

    //optional paramters
    private boolean ambientColor;
    private boolean extraTire;

    public String getEngine() {
        return engine;
    }

    public int getTires() {
        return tires;
    }

    public boolean isAmbientColor() {
        return ambientColor;
    }

    public boolean isExtraTire() {
        return extraTire;
    }

    private Car(CarBuilder builder) {
        this.engine = builder.engine;
        this.tires = builder.tires;
        this.ambientColor = builder.ambientColor;
        this.extraTire = builder.extraTire;
    }
    
    //Builder Pattern
    public static class CarBuilder{
        //required parameters
        private String engine;
        private int tires;

        //optional paramters
        private boolean ambientColor;
        private boolean extraTire;

        public CarBuilder(String engine, int tires) {
            this.engine = engine;
            this.tires = tires;
        }

        public void setAmbientColor(boolean ambientColor) {
            this.ambientColor = ambientColor;
        }

        public void setExtraTire(boolean extraTire) {
            this.extraTire = extraTire;
        }

        public Car build() {
            return new Car(this);
        }
    }
}

 

Using the Builder Pattern improves the maintainability and flexibility of your code, making it easier to create complex objects with various attributes.

 

This example is a code sinppet from the SecurityFilterChain class, used to configure an HttpSecurity instance. Although the concept is more complex than the previous example, it still applies the Builder Pattern to create an HttpSecurity instances.

 

    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .headers().frameOptions().sameOrigin() 
                .and()
                .csrf().disable()               
                .formLogin()                      
                .loginPage("/auths/login-form") 
                .loginProcessingUrl("/process_login")   
                .failureUrl("/auths/login-form?error")  
                .and()
                .logout()                       
                .logoutUrl("/logout")           
                .logoutSuccessUrl("/")
                .and()
                .exceptionHandling().accessDeniedPage("/auths/access-denied")
                .and()
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers("/orders/**").hasRole("ADMIN")       
                        .antMatchers("/members/my-page").hasRole("USER")  
                        .antMatchers("⁄**").permitAll()
                );              

        return http.build();
    }

 

In this code snippet, the Builder Pattern is used to configure various security settings for an HttpSecurity instance, such as headers, CSRF protection, authentication, and authorization. The builder methods are chained together to create a concise and readable configuration. The build() method at the end is called to create the final HttpSecurity instance with the specified configuration. This example demonstrates how the Builder Pattern can improve the readability and maintainability of code, even in more complex scenarios.

'Java > Design Pattern' 카테고리의 다른 글

About the Abstract Factory pattern  (0) 2023.04.09

+ Recent posts