Design pattern in SCI

Design pattern in SCI

Hello everyone, I'm Nani, a java backend from SCI team.Today, I would like to share with you about the Design pattern and how we applied it to solve problem in SCI’s project.
Design pattern in SCI

Design pattern

What's a design pattern?

A design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design. It's not a finished design that can be transformed directly into code, but rather a template for solving a problem that can be used in many different situations.

  • Design patterns can be categorized into three main types:

    • Creational Patterns: These patterns provide ways to instantiate single objects or groups of related objects. Examples include the Singleton, Factory Method, Abstract Factory, Builder, and Prototype patterns.
    • Structural Patterns: These patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient. Examples include the Adapter, Composite, Proxy, Flyweight, Facade, and Decorator patterns.
    • Behavioral Patterns: These patterns are concerned with algorithms and the assignment of responsibilities between objects. Examples include the Observer, Strategy, Command, Iterator, Mediator, Memento, and State patterns.

Why you should use design pattern?

  • Proven Solutions: Design patterns are proven solutions to common problems. They have been tested and refined over time, which means they are less likely to contain hidden issues. And they are the best practices in software design so they help you easier write code that is efficient and scalable.

  • Communication: Design patterns provide a shared language for developers. It helps developers communicate with each other more effectively, share knowledge and ideas, and better understand the system's design.

  • Reusability: By using design patterns, you can write reusable code that can be applied to different parts of your application or even in different projects.

  • Flexibility and extensibility: Design patterns often promote loose coupling and high cohesion, which make it easier to change and extend your code without affecting other parts of your system.

  • Testability: You can easily test each function without affecting other parts of the system

However, they should be used judiciously and adapted to fit the context of your project. Overusing or misapplying design patterns can lead to code that is unnecessarily complex or difficult to understand.

What does the pattern consist of?

  • The sections that are usually present in a pattern description:

    • Intent of the pattern briefly describes both the problem and the solution.

    • Motivation further explains the problem and the solution the pattern makes possible.

    • Structure of classes shows each part of the pattern and how they are related.

    • Code example in one of the popular programming languages(frameworks) makes it easier to grasp the idea behind the pattern.

Design patterns in SCI

In this section, I will describe some design patterns in SCI and how we applied it to solve the problem.

Chain of responsibility

  • Problem

    • Handling/Processing in Multiple Steps: In our system - the send cloud invoice system, to send an invoice, the system needs to validate sequentially information such as invoices’s information, receiver’s information, sending template, and 3rd parties request validations.

    • Dynamic Reconfiguration: After a period of development, the order or the number of validations needs to change dynamically and we need to provide a flexible structure that can be reconfigured at runtime. For example, we need to limit the size of invoices sent by email or need to take snapshots the receiver’s information to trace.
    • Extensibility: All the validations logic is contained in a monolithic object, and if we add or remove any validations then we need to modify the existing code base.

    • Reusability: Furthermore, the system supports many methods that need to reuse some common code parts. But It is very difficult because we only reuse part of these codes, not all of them.

    • And then we need to refactor our code before they become problems.

  • Solution

    • To solve this problem we used a chain of responsibility design pattern. The chain of responsibility relies on transforming particular behaviors into stand-alone objects called handlers.
    • In our case, validations are separated into classes handler with a method that performs these validations. These handler classes implement the same interface. Each concrete handler should only care about the following one having the execute method.
    • And these handlers are linked together into a chain by List. The List maintains a clear order of handlers, making the flow of request handling transparent and predictable. Adding or removing handlers from the chain is as straightforward as updating the List, allowing the chain to adapt to new requirements with minimal configuration.

    • To process a request, handlers pass the request along the chain. The request travels along the chain until all handlers have had a chance to process it. Each handler can process the request or forward it to the next handler or even stop the chain.

  • Pros and Cons 

    • Pros

      • We can execute handlers in a certain order.

      • Single Responsibility Principle: Separates the code into handlers and each handler handles its logic.

      • Open/Closed Principle: We can add, remove or change the order of handlers without breaking the existing code. It means that we only need to update the chain configuration.

      • Loose coupling: Instead of requesting to process objects containing references to all other objects, it only needs a reference to the next object.

    • Cons

      • Some requests may end up unhandled.

  • Structure

    • In our scenario, we have:
      • SendInvoiceChainService Interface: This defines an abstract method executed for handling requests. In our case, this handling request is added in chainData and passed along the chain.

      • Concrete Handlers: In this solution, we have three concrete classes InvoiceValidationHandler, PartnerValidationHandler, and SendInvoiceHandler

        • These are classes that implement the handler interface.

        • Each handler will have logic to determine whether it can handle the request or whether it should pass the request along to the next handler in the chain.

      • SendingService: The service configures the chain by linking, and ordering handlers together in List.

      • Client code sends requests to the first handler in the chain and starts to execute the chain.

 

Factory

  • Problem
    • SCI has developed an application for sending invoices that supports various sending methods such as mail post, email, and web. The first version of the app was designed to send invoices only by mail post, therefore all the logic was encapsulated within the MailingSending class...

    • After a while, the next version app needs to support new email sending method. This code is currently coupled to the MailingSending class and If we add EmailSending into the app, it would require making changes to entire codebase.

    • Moreover, add other sending methods like web, and store only to the app, we need to make these changes again.

    • Overtime the code becomes nasty and complexity increases with each sending method added.

  • Solution
    • To solve the problem we have applied the factory method pattern. The Factory Method pattern is a way to solve the problem of creating objects without specifying the exact class of object that will be created. This is done by replacing direct object construction calls (using the new operator) with calls to a special factory method.

    • In this project, We create a SendingService interface and concrete classes implementing the SendingService interface. A factory class SendingFactory is defined as the next step.

    • Our client class will use SendingFactory to get a Sending object. It will pass the sending method (EMAIL / MAILING / WEB_SENDING) to SendingFactory to get the type of object.

    • Create the SendingService interface

    • Create concrete classes implementing the SendingService interface. Each class implements the send() method differently: send an invoice by email, mailing or web.
    • Create a SendingFactory to generate a sending object of concrete class based on given information.

    • Use the SendingFactory to get the sending object of concrete class by passing the sending method.

  • Pros and cons
    • Pros

      • We avoid tight coupling between the creator and the concrete services.

        • Add new sending method to the app, we only need to create new subclass and override the factory method.

      • Single Responsibility Principle: We can move the creation code into a class, making the code easier to support.

      • Open/Closed Principle: We can add new sending service by new sending method without breaking existing code. And the only required change is an update to the sending factory, thus preserving the integrity of the existing codebase.

      • Hides the implementation from application

    • Cons:

      • The code may become more complicated since we need to introduce a lot of new subclasses to implement the pattern.

      • Produces a lot of generic code which results in less understandable code.

  • Structure

    • Super class - interface SendingService

    • Sub classes - EmailSendingServiceImpl, MailingSendingServiceImpl, WebSendingServiceImpl

    • Factory Class -SendingFactory

    • Client code use the SendingFactory to get sending object.

 

Conclusion

In software development, we always face common problems. Instead of having to spend a lot of time and effort to solve it ourselves, we should learn and use existing solutions to develop better. A design pattern provides us with such solutions.Hopefully, we will use it effectively to improve code quality in the project.

Reference

https://refactoring.guru/

https://sourcemaking.com/

More like this

Khi QA Nữ Lên sóng MFV
Jun 21, 2023

Khi QA Nữ Lên sóng MFV

MFV Tech Talk #3 - Service Operation | Kitto & Leon
Apr 12, 2024

MFV Tech Talk #3 - Service Operation | Kitto & Leon

Implementing Domain Driven Design
Nov 15, 2023

Implementing Domain Driven Design