DevelopmentTools/Java

[Java] 객체 지향 설계 5원칙 - SOLID 원칙

수짱수짱 2024. 4. 19. 13:44

JAVA를 사용한다면 객체지향에 대해서 한 번 쯤은 들어봤을 것이다. (ex. OOP 4대 특징 : 추상화, 상속, 캡슐화, 다형성)

이러한 객체지향의 특징을 더 잘 사용하기 위해서 객체지향 설계(OOD) 원칙이 존재한다.

그 중 5대 원칙을 나열하여 SOLID 원칙이라고 불리게 되었고 아래에서 이 5가지 원칙을 자세하게 알아보도록 하자.

SOLID 원칙은 소프트웨어 설계에 있어 이해하기 쉽고 유연하며, 유지보수 및 확장이 편하다는 장점을 가지게 해준다.


1. S (SRP)

SRP (Single Reponsibility Principle) : 단일 책임 원칙

하나의 클래스 및 메소드는 하나의 책임만 가져야 한다.

 

수정되어야 하는 이유 및 시기가 다르다면 그 클래스는 단일 책임 원칙이 적용되지 않은 것이다.

수정 이유 및 시기가 다르니깐 클래스는 서로 분리가 되어야 하는 것이다.

 

SRP 원칙을 적용하면 다른 클래스들 끼리 서로 영향을 미치는 연쇄작용도 줄일 수 있다.

즉, 응집도는 높이고 결합도는 낮추는 것이다!

또한, 책임을 분배하여 코드의 '가독성'이 향상되고 '유지보수'가 용이해진다.

 

 

2. O (OCP)

OCP (Open Closed Principle) : 계방 폐쇄 원칙

확장에는 Open! 변경에는 Close!

 

요구사항의 변경이나 추가사항이 발생하더라도 기존 구성요소는 수정이 일어나지 않아야 하며 쉽게 확장이 가능하고 재사용할 수 있어야 한다는 의미이다.

OCP를 가능하게 하는 중요한 메커니즘은 바로 '추상화(Abstraction)'과 '다형성(Polymorphism)'이다.

 

OCP를 위해선 클래스를 설계할 때 '변할 부분'과 '변하지 않는 부분'을 명확히 구분해야 한다.

'변할 수 있는 부분'은 '추상화'를 통해 상속하는 클래스가 의존할 수 있도록 작성해야 한다.

 

예를 들어보자, 만약 어떤 게임에서 각 캐릭터마다 Q와 W 스킬 단축키가 있다고 생각해보자.

전직마다 캐릭터의 스킬이 변한다면 이때 interface를 활용한 '추상화'가 필요한 것이다.

interface Character {
    void QPressed();
    void WPressed();
}
class SomeCharacter implements Character {
    @Override
    public void QPressed() {
        rotateSlash();
    }

    @Override
    public void WPressed() {
        jump();
    }

    private void rotateSlash() {
        System.out.print("⚔⚔⚔");
    }

    private void jump() {
        System.out.print("🤸‍♂️");
    }
}

또한, 캐릭터의 구체화된 클래스의 회전베기나 점프 메소드는 언제든 변할 수 있으므로 private으로 선언하여 외부에서 접근할 수 없도록 한다.

 

 

3. L (LSP)

LSP (Liskov Substitution Principle) : 리스코프 치환 원칙

서브 타입은 언제나 자신의 기반 타입으로 변경할 수 있어야 한다.
= 상위 타입은 항상 하위 타입으로 대체될 수 있어야 한다.

 

즉, 부모 클래스가 들어갈 자리에 자식 클래스를 넣어도 역할 수행에 있어 문제가 없어야 한다는 것이다.

해당 리스코프 치환 원칙을 지킨다면 '다형성'과 '확장성'을 지킬 수 있으며 OCP도 구성하게 된다.

리스코프 치환 원칙 '위배'

위 사진을 보면 '할아버지' 자리에 '아들'이 들어간다면 역할 수행에 문제가 발생하기 때문에 LSP 원칙을 위배하는 것이다.

 

리스코프 치환 원칙 '준수'

위 사진은 '동물'자리에 '고래'가 들어가더라도 고래는 동물이 맞으므로 LSP 원칙을 지키고 있는 것이다.

= '고래'는 '동물'이 하는 메소드를 그대로 수행할 수 있다.

 

보통 LSP가 잘 지켜지지 않는 이유는 부모 클래스의 메소드를 override하는게 잦기 때문이다.

부모 클래스의 기존 메소드를 자식 클래스가 수정하면서 문제가 생기는 것이다.

단순하게 LSP를 지키기 위해서 상속은 하되 override를 하지 않는 것이라고도 할 수 있다

하지만 이것이 무조건적인 해결 방법이 될 수는 없다.

상속을 할 떄 override가 필요한 상황이라면  '기존 부모 클래스의 메소드가 하던 역할'을 유지하되 거기에 기능을 추가만 하는 것이다.

즉, 상속 과정중 메소드 재정의가 필요하다면 현재 자식 클래스가 부모 클래스 기존 메소드의 의미를 해치는지 확인하고 상속하면 된다.

 

 

4. I (ISP)

ISP (Interface Segregation Principle) : 인터페이스 분리 원칙

하나의 일반적인 인터페이스보다 여러 개의 구체적인 인터페이스가 낫다.

 

CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE.

클라이언트가 사용하지 않는 인터페이스에 의존하도록 강요해선 안 된다.

 

이 말은 즉 자신이 사용하지 않는 인터페이스는 구현하지 않도록 해야 한다는 원칙이다.

하나의 커다란 인터페이스를 상속받는 것이 아니라, 구체적이고 작은 단위로 분리된 인터페이스 중에 꼭 필요한 인터페이스만 상속해야 한다는 것이다.

SRP는 '클래스'의 단일 책임을 강조했다면 ISP는 '인터페이스'의 단일 책임을 강조하는 것이다.

 

 

5. D (DIP)

DIP (Dependency Inversion Principle) : 의존 역전 원칙

추상화 된 것은 변경될 가능성이 있는 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다.

 

1. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.

 

2. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS

1. 높은 수준의 모듈은 낮은 수준의 모듈에 의존해서는 안 됩니다. 둘 다 추상화에 의존해야 합니다.

2. 추상화는 세부 사항에 의존해서는 안 됩니다. 디테일은 추상화에 의존해야 합니다.

 

원문만 보면 이해가 쉽게 안 될 수 있다. 클래스 사이에 의존관계가 있을 수 밖에 없지만 구체적인 클래스에 의존하는 것이 아닌 최대한 '추상화 클래스'에 의존하라는 의미이다. 즉, interface를 활용하라는 의미이기도 하다.

 

예를 들어보자, 사용자가 있고 사용자는 아이폰을 사용한다. 따라서 사용자는 아이폰 클래스에 의존하고 있다.

아이폰 클래스는 '구체화된 클래스'이다. 따라서, 변화에 취약하다는 특징이 있다.

 

만약, 사용자가 아이폰이 아닌 갤럭시로 변경하고 싶다면? 코드에 많은 변경이 일어날 것이다.

이러한 구조는 변경에 취약한 구조를 만드는 것이다. 따라서, 이러한 구조를 개선하기 위해 의존성을 역전시키는 것이다.

 

현재 사용자가 의존하고 있는 아이폰 클래스를 '추상화된 클래스'로 만들어 해결할 수 있다.

스마트폰이라는 interface를 사용자가 의존하도록 변경하는 것이다. 이것이 "구체적인 것은 추상화에 의존해야 한다"의 의미이다.

스마트폰 interface아래엔 구체화 된 클래스로 아이폰, 갤럭시가 존재할 수 있는 것이다.

 

이 말이 '상위 모듈은 하위 모듈에 의존해선 안된다. 둘 다 추상화에 의존해야 한다'라는 의미이다.

이에 따라 외부 변동에 유연하게 대처할 수 있는 코드로 작성할 수 있게 되는 것이다.

또한 의존성 역전 원칙을 따라 '의존성 주입(DI)'라는 이점까지 취할 수 있게 된다.


여기까지 객체지향 설계 5원칙 SOLID를 알아보았다.

하지만 여기서 끝나는 것이 아니라이러한 SOLID 원칙을 평소에도 잘 새기며 개발에 적용하려고 하는 노력이 필요하다 !!!!

외우려고 하지말고 이해하여 잘 적용해보도록 하자.


Reference

 

객체지향 5원칙 : SOLID

디자인 패턴에 대해 공부하던 중 예전 소프트웨어 공학 때 배운 객체지향 5원칙(SOLID)에 대해 다시 정리해보기로 했습니다.

jaeyeong951.medium.com

 

[Java] 객체지향 설계 원칙 SOLID 알아보기

객체지향 프로그래밍 설계시 좋은 객체지향 설계를 위한 SOLID 라고 불리는 5가지 원칙이 있다. SOLID 원칙은 소프트웨어를 설계함에 있어 이해하기 쉽고, 유연하며, 유지보수 및 확장이 편하다는

dev-coco.tistory.com