SOLID Principle

The SOLID principles are an object-oriented approach applied to software structure design. It was introduced by Robert C. Martin.

The design principles encourage us to create more maintainable, understandable, and flexible software. Consequently, as our applications grow in size, we can reduce their complexity.

The word SOLID is an acronym for:

SOLID principles complement each other and work together to achieve the common purpose of well-designed software.

Example

To understand this principle, let's see an example with a class that violates this principle as follows:

class OrderService {

    private final PartnerWebClientImpl partnerWebClientImpl;

    public void sendOrderToPartner(Order order) {
        PartnerType partnerType = order.getPartnerType();
        if (partnerType == Partner1) {
            partnerWebClientImpl.sendToPartner1(order);
        } else if (partnerType == Partner2) {
            partnerWebClientImpl.sendToPartner2(order);
        } else {
            partnerWebClientImpl.sendToDefaultPartner(order);
        }
    }
}

interface PartnerWebClient {
    void sendToPartner1(Order order);
    void sendToPartner2(Order order);
    void sendToDefaultPartner(Order order);
}

class PartnerWebClientImpl {
    
    public void sendToPartner1(Order order) {
        // do something
    }

    public void sendToPartner2(Order order) {
        // do something
    }

    public void sendToDefaultPartner(Order order) {
        throw new UnsupportedPartnerException("Default partner not supported yet");
    }
}

enum PartnerType {
    Partner1,
    Partner2,
    DefaultPartner
}
  • Violate SRP: The PartnerWebClientImpl class does many things: send to partner 1, partner 2, and a default partner.

  • Violate OCP: If you have one more Partner, you have to change all classes.

  • Violate ICP: The implementation of sendToDefaultPartner() method alters the correctness of the partner. It should do something or remove completely.

  • Violate ISP: One general-purpose interface PartnerWebClient.

  • Violate DIP: The OrderService depends on the implementation PartnerWebClientImpl. It should depend on the interface PartnerWebClient.

Let's see how to apply SOLID:

class OrderService {

    private final PartnerFactory partnerFactory;
    
    public void sendOrderToPartner(Order order) {
        PartnerService partnerService = partnerFactory.getPartnerService(order.getPartnerType());
        partnerService.handle(order);
    }
}

class PartnerFactory {

    private final List<PartnerService> partnerServices;
    
    public void PartnerService getPartnerService(PartnerType partnerType) {
        return partnerServices.stream()
                .filter(service -> service.canHandle(partnerType))
                .findFirst()
                .orElseGet(() -> defaultPartnerService);
    }
}

interface PartnerService {

    boolean canHandle(PartnerType partnerType);

    void handle(Order order);
}

class Partner1Service implements PartnerService {

    private final PartnerWebClient partner1WebClient;

    @Override
    public boolean canHandle(PartnerType partnerType) {
        return partnerType == Partner1;
    }

    @Override
    public void handle(Order order) {
        partner1WebClient.send(order);
    }
}

class Partner2Service implements PartnerService {

    private final PartnerWebClient partner2WebClient;

    @Override
    public boolean canHandle(PartnerType partnerType) {
        return partnerType == Partner2;
    }

    @Override
    public void handle(Order order) {
        partner2WebClient.send(order);
    }
}

class DefaultPartnerService implements PartnerService {

    private final PartnerWebClient defaultPartnerWebClient;

    @Override
    public boolean canHandle(PartnerType partnerType) {
        return partnerType == null || partnerType == DefaultPartner;
    }

    @Override
    public void handle(Order order) {
        defaultPartnerWebClient.send(order);
    }
}

interface PartnerWebClient {
    void send(Order order);
}

class Partner1WebClient implements PartnerWebClient {
    public void send(Order order) {
        // do something
    }
}

class Partner2WebClient implements PartnerWebClient {
    public void send(Order order) {
        // do something
    }
}

class DefaultPartnerWebClient implements PartnerWebClient {
    public void send(Order order) {
        // do something
    }
}
  • Follow SRP: each class does one thing.

  • Follow OCP: easy to add new partners without modifying any existing class.

  • Follow ICP: default partner doesn't change the correctness of a partner.

  • Follow ISP: no general purpose or large interfaces.

  • Follow DIP: class depends on interfaces, not depends on implementation.

Last updated