SOLID - 5가지 원칙

겔로그·2022년 10월 8일
0

Java

목록 보기
9/10
post-thumbnail

개요

오늘은 Clean Code에서 알게 된 개념 SOLID에 대해 알아보는 시간을 가져보겠습니다. 이 내용은 solid-principles-with-almost-real-life-examples-in-java를 참고하여 작성했습니다.

SOLID란?

SOLID란, 객체지향 개발자가 알아야될 5개의 원칙으로 5개의 원칙을 고려하면서 개발할 경우 좀 더 유지보수에 용이한 소프트웨어를 개발할 수 있다는 장점이 존재합니다.
그럼 5개의 원칙이 무엇인지 알아보겠습니다.

SOLID

  • SRP: 단일 책임 원칙
  • OCP: 개방 폐쇄 원칙
  • LSP: 리스코프 치환 원칙
  • ISP: 인터페이스 분리 원칙
  • DIP: 의존성 역전 원칙

그럼 각각의 원칙이 어떤 개념인지에 대해 좀 더 알아볼까요?

SRP(단일 책임 원칙)이란?

단일 책임 원칙이란, 하나의 클래스는 하나의 역할만을 수행하는 것을 의미합니다.

OCP(개방 폐쇄 원칙)이란?

개방 폐쇄 원칙은 명칭 정의와 동일하게 확장에는 개방되어있고, 수정에는 폐쇄적이어야 한다는 의미입니다.

기존 코드베이스를 건드리지 않고 기능 확장에 유연하게 대응할 수 있는 방식입니다. 대표적인 예시로는 추상 클래스 및 인터페이스 사용이 존재합니다.

예시

기존 코드

public String hashPassword(String password, HashingType hashingType)
{
    if(HashingType.BASE64.equals(hashingType))
    {
        hashedPassword="hashed with Base64";
    }
    else if(HashingType.MD5.equals(hashingType))
    {
        hashedPassword="hashed with MD5";
    }

    return hashedPassword;
}

OCP 적용

추상 클래스 적용

public abstract class Hashed
{
    PasswordHasher passwordHasher;
    String hash;
    
    public void generateHash(String password)
    {
        hash = passwordHasher.hashPassword(password);
    }
}

public class Base64 extends Hashed
{
    public Base64()
    {
        this.passwordHasher = new Base64Hasher();
    }
}

인터페이스 적용


public interface PasswordHasher
{
    String hashPassword(String password);
}

public class Base64Hasher implements PasswordHasher
{
    @Override
    public String hashPassword(String password)
    {
        return "hashed with 64";
    }
}

public class MD5Hasher implements PasswordHasher
{
    @Override
    public String hashPassword(String password)
    {
        return "hashed with SHA256";
    }
}

LSP(리스코프 치환 원칙)이란?

  • 하위 클래스는 상위 클래스의 각 기능을 수행할 수 있어야 하며, 상위 클래스로 취급될 수 있어야 합니다.
  • 즉, 하위 클래스가 상위 클래스로 선언이 되어도 에러 없이 동작하는 코드를 작성해야 합니다.

앞서 본 코드를 한 번 보실까요?

public abstract class Hashed
{
    PasswordHasher passwordHasher;
    String hash;
    
    public void generateHash(String password)
    {
        hash = passwordHasher.hashPassword(password);
    }
}

public class Base64 extends Hashed
{
    public Base64()
    {
        this.passwordHasher = new Base64Hasher();
    }
}

다음 코드는 Base64 클래스가 Hashed라는 추상 클래스를 상속했습니다. 만약 해쉬값이 필요없는 클래스 NoHash가 Hashed 클래스를 상속받으면 어떤 결과가 발생할까요?

=> 정답은 정상동작은 하나 NoHash가 Hashed의 역할은 수행할 수 없습니다.

이런 경우 리스코프 치환 원칙을 위반했다고 판단하고 코드 로직을 수정합니다.

ISP(인터페이스 분리 원칙)이란?

만약 하나의 인터페이스가 모든 기능을 제공한다면 어떨까요? 모든 개발자는 그 인터페이스에 어떤 기능이 있는지 알아야 할 것이고, 특정 기능을 사용하기 위해 해당 인터페이스에 다음 기능이 존재하는지를 확인할 것입니다.

인터페이스 분리 원칙은 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙입니다. 즉, 용도에 알맞게 기능을 분리해 인터페이스로 선언하라는 것이죠.

예시

ops 인터페이스에서 user1 user2 user3는 각각 op1 op2 op3 메소드만 구현한다고 할 때, 클래스로 구현하면 다음과 같은 구조도로 구현을 할 수 있습니다.

변경 전

하지만 각각의 user클래스는 사용하는 메소드는 1개씩이므로 인터페이스를 분리하여 다른 클래스에 미치는 영향을 최소화시킵니다.

변경 후

기능에 알맞게 인터페이스를 분리하여 이용하시는 것을 권장드립니다.

DIP(의존성 역전 원칙)이란?

가끔 하는 실수중에 하나는 기능에서 마음에 안드는 부분이 있을 경우, 상위 클래스 로직을 수정하고 강제로 하위 클래스에 끼워 맞추는 느낌으로 개발하는 것입니다.

의존성 역전 원칙이란 추상화에 의존해야지, 구체화에 의존하면 안된다라는 의미에서 나온 원칙입니다. DIP를 만족하려면 어떤 클래스가 도움을 받을 때 구체적인 클래스보다는 인터페이스나 추상클래스와 의존관계를 맺도록 설계해야 합니다.

예시

public class PasswordService
{
    private Base64Hasher hasher = new Base64Hasher();
    void hashPassword(String password)
    {
        hasher.hashPassword(password);
    }
}

다음 코드를 봤을 경우 PasswordService와 Base64Hasher 클래스가 밀접한 관계에 있다는 것을 알 수 있습니다.(Base64Hasher가 없을 경우 PasswordService가 동작 불가능)

다음 경우, DIP을 적용하면 다음과 같이 구현될 수 있습니다.


public class PasswordService
{
    private PasswordHasher passwordHasher;
    
    public PasswordService(PasswordHasher passwordHasher)
    {
        this.passwordHasher = passwordHasher;
    }
    
    void hashPassword(String password)
    {
        this.passwordHasher.hashPassword(password);
    }
}

PasswordHasher 인터페이스를 추가해 Base64Hasher와의 관련성을 없앨 수 있습니다. PasswordService는 Base64가 아닌 다른 Hash를 사용하고 싶을 경우 PasswordHasher 내부 로직을 통해 해싱 클래스를 좀 더 유연하게 변경할 수 있습니다.

결론

오늘은 SOLID에 대한 개념에 대해 알아보았습니다. 생각보다 단순하면서도 실제로 코딩하면 매우 어려운 원칙들인 것 같습니다.

앞으로 개발할 때 5개의 원칙을 의식하면서 개발하며 더 나은 개발자가 되기 위해 노력해볼 계획입니다.

감사합니다.

Reference

https://betterprogramming.pub/solid-principles-with-almost-real-life-examples-in-java-b292a4e2c18b

https://medium.com/@avinash.dhumal/solid-design-principles-the-interface-segregation-2ca61f860907

https://medium.com/@avinash.dhumal/solid-design-principles-the-single-responsibility-a2af2753a2f9

https://medium.com/@avinash.dhumal/solid-design-principles-the-open-closed-a636d7c76109

https://medium.com/@avinash.dhumal/solid-design-principles-the-liskov-substitution-52deeee2ec4e

profile
Gelog 나쁜 것만 드려요~

0개의 댓글