JAVA

TIL(12) 객체지향 설계 SOLID 5원칙

나죽나강 2021. 6. 1. 00:12

/** 스프링 입문을 위한 자바 객체지향의 원리와 이해 -김종민 지음- */

 

# SOLID (SRP, OCP, LSP, ISP, DIP)

> 객체지향의 특성을 올바르게 사용하는 방법을 설명하는 것이다

> SOLID는 코드에 녹여내야 하는 개념이다

> 리팩터링과 유지보수가 수월하다

> 객체 지향 4대 특성 객체 지향 설계 5원칙, 디자인 패턴은 객체지향의 중요한 개념이다.

 

# SRP - 단일 책임 원칙

"어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다." - 로버트 C.마틴

 

> 단일 책임 원칙은 역할을 분리하는 것이다. 클래스의 역할과 책임에 따라 분리하여 기능에 변화가 있더라도 영향을 받지 않는다는 장점이 있다. 

> SRP는 A클래스와 B클래스에 공통점이 없다면 C(부모 클래스)를 제거하면 되고, 공토점이 많다면 C(부모클래스)를 상위 클래스로 해서 공통점은 C클래스에 구현하고 A, B클래스는 C클래스의 메소드를 상속받고 각각의 차이점만 구현하도록 한다.

 

package hello.hellospring.test;

public class 강아지 {
        final static Boolean 수컷 = true;
        final static Boolean 암컷 = true;
        Boolean 성별;
        
        Void 소변보다(){
            if(this.성별=수컷){
                //한쪽 다리를 들고 소변 보단 -수컷
            }
            else{
                //뒷다리를 굽혀서 소변 본다 -암컷
            }
        }
}

> 소변보다() 메서드가 암컷과 수컷의 행위를 모두 구현하려 하였기 때문에 단일 책임 원칙을 위배하고 있는 것이다.

> 메서드가 단일 책임 원칙을 제대로 지키지 않을 경우 나타나는 대표적인 형태가 if-else문이다

 

package hello.hellospring.test;

abstract class 강아지 {
    abstract 소변보다();
}

class 수컷강아지 extends 강아지{
    void 소변보다(){
        //한쪽 다리들고 소변보다
    }
}

class 암컷강아지 extends  강아지{
    void 소변보다() {
        //뒷다리 두 개를 굽히고 소변보다
    }
}

> 단일 책임원칙을 지킨 리팩터링 방식!

 

# SRP 정리

단일 책임 원칙은 모델링 과정을 담당하는 추상화와 가장 관련이 깊다. 애플리케이션의 경계를 정하고 추상화를 통해 클래스들을 선별하고 속성과 메서드를 설계할 때 반드시 단일 책임 원식을 고려해야 한다.

 

 

 

 

# OCP - 개방 폐쇄 원칙

"소프트웨어 엔티티(클래스, 모듈, 함수)는 확장에는 열려 있어야 하지만 변경에 대해서는 닫혀있어야 한다."

 

> 마티즈(수동 조작)이고 벤츠는(자동 조작)이다. 운전자가 마티즈에서 벤츠로 차를 바꾸게 된다면 운전자의 운전능력에 변화가 있어야 한다. 하지만 객체 지향 세계에서는 상위 클래스 또는 인터페이스를 중간에 둠으로써 운전자의 운전 습관에 영향을 받지 않도록 할 수 있다.

> 즉, 확장에는 개방돼 있어야 하고, 주변의 변화에는 폐쇄되어야 한다.

> 개방 폐쇄 원칙은  개체 지향 프로그래밍의 가장 큰 장점인 유연성, 재사용성, 유지보수성을 제공한다.

 

 

 

 

# LSP - 리스코프 치환 원칙

"서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다" - 로버트 C.마틴

 

하위 클래스 is a kind of 상위 클래스 - 하위 분류는 상위 분류의 한 종류다

구현 클래스 is able to 인터페이스 - 구현 분류는 인터페이스 할 수 있어야 한다

위 두 개의 문장대로 구현된 프로그램이라면 리스코프 치환 원칙을 잘 지킨 것이다.

 

> 이때, 상속은 조직도나 계층도가 아닌 분류도가 되어야 한다. 아버지 - 딸 구조(계층도/조직도)는 리스코프 원칙을 위배하고 있는 것이며, 동물 - 펭귄 구조(분류도)는 리스코프 치환 원칙을 만족하는 것이다.

 

# 정리

"하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 하는데 문제가 없어야 한다."

> 즉, 상속의 분류도가 올바르게 정의되어야 리스코프 치환 원칙을 만족할 수 있다.

> 위키피디아에서 리스코프 치환 원칙 참고! (링크) << 하면 삭제

> 상속 vs 인터페이스 비교!  (링크)  << 하면 삭제

 

 

 

 

# ISP - 인터페이스 분리 원칙

"클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다." -로버트 C.마틴

 

인터페이스 분리 원칙(ISP) vs 단일 책임 원칙(SRP)

> 단일 책임 원칙 - 하나의 역할만 하는 다수의 클래스로 분할하는 것.

> 인터페이스 분할 원칙 - 하나의 역할만 하는 다수의 인터페이스로 분할하는 것.

> 즉, 단일 책임 원칙(SRP)과 인터페이스 분할 원칙(ISP)은 같은 문제에 대한 두 가지 다른 해결책이라고 볼 수 있다.

하지만 특별한 경우가 아니라면 단일 책임 원칙을 적용하는 것이 더 좋은 해결책이라고 할 수 있다.

 

인터페이스 최소주의 원칙

> 인터페이스를 통해 메서드를 외부에 제공할 때는 최소한의 메서드만 제공하라는 것이다.

> 인터페이스는 그 역할에 충실한 최소한의 기능만 공개하는 거시다.

> 상위 클래스는 풍성할수록 좋고, 인터페이스는 작을수록 좋다. 

> 즉, 상위 클래스는 공통적인 부분으로 구성하고, 개인적인 부분은 인터페이스로 구현하는 것이 좋다.

 

 

 

 

# DIP - 의존 역전 원칙

"고차원 모듈은 저차원 모듈에 의존하면 안 된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야 한다."

"추상화된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다"

"자주 변경되는 구체 클래스에 의존하지 마라!" - 로버트 C.마틴

 

 

의존 역전 원칙은 자동차가 구체적인 타이어(스노우타이어, 일반타이어, 광폭타이어)가 아닌 추상화된 타이어 인터페이스에만 의존하게 함으로써 스노우타이어에서 일반 타이어로 변경돼도 자동차는 영향을 받지 않는 형태로 구성된다. 이것은 개방 폐쇄 원칙(OCP)와 비슷하다. 

 

기존에는 스노우타이어가 그 무엇에도 의존하지 않는 클래스였는데, 추상적인 것인 타이어 인터페이스에 의존하게 되었다. 이것이 의존의 방향이 역전된 것이다. 그리고 자동차는 자신보다 변하기 쉬운 스노우타이어에 의존하던 관계를 중간에 추상화된 타이어 인터페이스를 추가해 두고 의존 관계를 역전시키고 있다. 이처럼 자신보다 변하기 쉬운것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것의 변화에 영향받지 않게 하는 것이 의존 역전 원칙이다.

 

"자신보다 변하기 쉬운 것에 의존하지 마라!"

상위 클래스일수록, 인터페이스일수록, 추상클래스일수록 변하지 않을 가능성이 높기에 하위 클래스나 구체 클래스가 아닌 상위 클래스, 인터페이스, 추상 클래스를 통해 의존하라는 것이 의존 역전 원칙이다.