Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Design Patterns and Project Standards Implementation Guide

Tech 2

Chain of Responsibility Pattern

This pattern decouples senders from receivers.

Benefits include: reducing coupling between sender and receiver objects, simplifying their structures, and enabling easy extension of new handlers.

Drawbacks involve debugging difficulties and potential performance issues, with risk of creating infinite loops if misused.

Common applications include interceptors and filters that process requests based on matching criteria. Interceptors use dynamic proxy implemantation while filters utilize callback functions.

Example filter implementation:

@Slf4j
@WebFilter(filterName = "requestModifier", urlPatterns = "/api/*")
public class RequestModificationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        EnhancedRequestWrapper wrapper = new EnhancedRequestWrapper(httpRequest);
        filterChain.doFilter(wrapper, servletResponse);
    }

    @Override
    public void destroy() {
        log.info("Cleaning up request modification filter!");
    }
}

Strategy Pattern

Encapsulates multiple algorithms that can replace each other, eliminating traditional if-else statements.

Implementation involves packaging each algorithm into separate classes implementing a comon interface.

Benefits include good extensibility and algorithm switching capabilities.

Drawbacks require knowledge of available algorithms and can become cumbersome when managing numerous algorithm classes.

Usage example: different discount calculations for various membership tiers like standard, platinum, and gold members.

The core concept utilizes polymorphism to replace conditional statements.

Template Method Pattern (Most Common + Manager Approach)

Defines an algorithmic structure while deferring specific steps to subclasses.

An abstract class publicly defines execution templates, allowing subclasses to override methods while maintaining consistent invocation patterns.

Benefits include code reusability by implementing common methods once instead of repeating in every subclass.

Applications involve abstract classes containing shared functionality for subclasses to utilize or override.

Implementation steps:

  1. Define interface:
public interface ProcessingInterface {
    List<ProcessingResult> execute(ProcessData data);
    String getProcessType();
}
  1. Abstract class extending interface:
public abstract class BaseProcessor implements ProcessingInterface {
    // Template method implementation
    // Shared processing logic
}
  1. Concrete implementations (different order types):
public class AirlineProcessor extends BaseProcessor {
    @Override
    public List<ProcessingResult> execute(ProcessData data) {
        // Airline-specific processing logic
        return null;
    }

    @Override
    public String getProcessType() {
        return "AIRLINE";
    }
}
public class RailwayProcessor extends BaseProcessor {
    @Override
    public List<ProcessingResult> execute(ProcessData data) {
        // Railway-specific processing logic
        return null;
    }

    @Override
    public String getProcessType() {
        return "RAILWAY";
    }
}
  1. Register implementations during startup:
@Component("processorRegistry")
public class ProcessorRegistry {
    @Autowired
    private ProcessingInterface[] processors;
    
    private Map<String, ProcessingInterface> processorMap = new HashMap<>();

    @PostConstruct
    public void initializeMap() {
        Arrays.stream(processors)
            .forEach(proc -> processorMap.put(proc.getProcessType(), proc));
    }

    public ProcessingInterface retrieveProcessor(String type) {
        return processorMap.getOrDefault(type, processorMap.get("DEFAULT"));
    }
}

This approach combines factory and template patterns.

Observer Pattern

When an object's state changes, all dependent objects receive notifications and automatic updates.

Purpose is loose coupling and weakened dependency relationships, primarily used within systems.

Publish-Subscribe Pattern

Producers generate objects and place them in queues or buffers for consumers to process.

Observer and observed maintain loose coupling, while publishers and subscribers have no direct coupling.

Observer patterns work within single applications, while publish-subscribe represents cross-application patterns like message middleware.

Adapter Pattern

Coordinates incompatible enterfaces, serving as bridges between incompatible components.

Focuses on conversion and compatibility.

Benefits include improved class reusability and flexibility.

Drawbacks create system complexity and are limited by Java's single inheritance model.

Example: InputStreamReader and OutputStreamWriter serve as adapters converting between byte and character streams.

Composite Pattern

Represents part-whole hierarchies, treating groups of similar objects as single entities.

Ensures consistent usage of individual and composite objects.

Applications include tree menus and file/directory management.

Decorator Pattern

Adds functionality without altering existing structure.

Example: BufferedInputStream adds caching capability to InputStream.

Design Principles

Single Responsibility Principle: Each class handles one specific responsibility.

Interface Segregation Principle: Decompose large interfaces into smaller granular ones to prevent change propagation.

Dependency Inversion Principle: High-level modules should not depend on low-level modules to reduce coupling.

Liskov Substitution Principle: Subtypes must be substitutable for their base types without affecting functionality.

Open-Closed Principle: Open for extension, closed for modification.

Law of Demeter: Minimize communication with unfamiliar objects, focus on own responsibilities.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.