객체지향 설계 5원칙

SOUTH DARIA·2022년 1월 29일
1

개념 박아넣기

목록 보기
1/3

한 4개월 전이었나..
그 쯤 수호(가명)께 이런 질문을 한 적이 있다.

"어떻게 해야 MSA 아키텍처를 잘 지키고 있는지 모르겠어요. 기사 관련 api는 다 Rider Controller에 넣어야할까요??"

한 분이 답변을 주셨다.

한 클래스에서 하는 일은 하나만 있는 것이 좋아요.SPR 아세요? 단일 책임 원칙

분명 아는 건데 도저히 기억이 나지를 않는거다..

~~''공부에 너무 소홀했다..'' 라는 생각이 스쳐가고 책을 펼쳤다...~~

![https://velog.velcdn.com/images%2Fsouth_daria%2Fpost%2Ffdbcad8f-421b-43db-a22b-4a8673970e50%2Fimage.png%5D(https%3A%2F%2Fimages.velog.io%2Fimages%2Fsouth_daria%2Fpost%2Ffdbcad8f-421b-43db-a22b-4a8673970e50%2Fimage.png)

객체지향이란?

객체지향이란 컴퓨터 프로그래밍의 패러다임이며 프로그램을 명령어의 묶음으로 보는것이아니라 객체(Object) 단위로 나눈다는 개념

SOLID란?

고체?

실선??

아니아니.... 아재개그 ㅈㅅ...

프로그래밍에서 SOLID란 객체지향 설계 5원칙을 말한다.

  • SPR(Single Responsibility Principle) : 단일 책임 원칙
  • OCP(Open Closed Principle) : 개방 폐쇄 원칙
  • LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
  • ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
  • DIP(Dependency Inversion Principle) : 의존 역전 원칙

5원칙의 앞글자를 따서 S O L I D 라고 부른다.

소프트웨어의 유지 보수와 확장을 쉽게 하기 위해서 개발자들이 한 약속이랄까...

단일 책임 원칙 (a.k.a SRP)

'모든 클래스는 하나의 책임만 가진다.'

모든 클래스(모듈, 함수 ...) 는 하나의 책임만 가지며, 클래스가 변경되는 이유는 단 한 개여야 하고 그 책임은 캡슐화 되어야한다.

만약 클래스가 두개 이상의 책임을 가지게 되면, 후에 클래스를 변경할 때 확인해야할 사항이 많아지고 사이드이펙트의 위험도도 높아진다. 즉 유지보수가 힘들어질 확률이 높다.

public class Sex{
    final static Boolean isFemale = true;
    final static Boolean isMale = false;

    void printSex(){
    	if(this.isFemale){
        	System.out.println("여자");
        } else {
        	System.out.println("남자");
        }
    }
}

예를 들어 이 printSex라는 메소드는 SRP를 위반했다고 말할 수 있다.여자, 남자를 모두 구현하려고 하고있기 때문이다.메소드가 분기처리를 위해 if문을 사용하고 있다면 단일 책임 원칙을 지키지 않을 가능성이 높다. 해당 메소드를 이렇게 다르게 구현할 수 있다.

public abstract class Sex{
	abstract void printSex();
}

public class Female extends Sex{
	void printSex(){
    	System.out.println("여자");
    }
}

public class Male extends Sex{
	void printSex(){
    	System.out.println("남자");
    }
}

객체지향 4대 특성 중 모델링 과정을 담당하는 추상화와 가장 관계가 깊다.

개방 폐쇄 원칙 (A.K.A OCP)

'확장에는 열려 있어야 하고, 변경에는 닫혀있어야 한다.'

프로그램의 기존 기능은 확장이 가능하지만, 한 번 작성한 코드를 바꾸지 않아도 된다는 것이다.이 말은 즉, 코드를 수정하지 않아도 기능을 확장할 수 있어야한다는 의미이다.

예제와 함께 정리해보는 것이 쉬울 것 같다.

public boolean purchase(Object card, String name, int price)
{
    boolean result;
    
    switch (card.toUpperCase())
    {
        case "A" -> result = ((CardA) card).send(price);
        case "B" -> result = ((CardB) card).send(price);
        case "C" -> result = ((CardC) card).send(price);
        
        default -> {
            System.out.println("유효하지 않은 카드사");
            result = false;
        }
    }
    
    return result;
}
public boolean purchase(Purchasable purchasable, int price)
{
    return purchasable.send(price);
}

instanceof와 같은 타입 확인 연산자가 사용된다면 해당 코드는 개방 폐쇄 원칙을 지키지 않을 가능성이 높다.

어떻게 상속 구조를 짤지, 어떤 걸 추상 멤버(메소드, 속성)로 할지는 개발자의 몫이다.미래의 변화에 대해 예측을 잘해서 추상화를 해야 하며.너무 과하게 예상해서 추상시켜도 안되고, 아무것도 확장할 수 없게 다 폐쇄적으로 숨기면 안 된다. 적당히 알아서 잘해야 한다.(해당 코드 참조 : https://blog.itcode.dev/posts/2021/08/14/open-closed-principle.html)

리스코프 치환 원칙 (a.k.a LSP)

'서브 타입은 언제나 기반 타입으로 교체할 수 있어야한다.'

즉, 하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 수행하는 데 문제가 없어야 한다. 이것은 OOP 4대 특성의 상속, 인터페이스 원칙이 잘 지켜진 다면 LSP는 잘 적용 된 것이다.

핸드폰의 볼륨 up 버튼은 소리가 커지는 기능, 만약 핸드폰이 꺼지게 한다면 LSP를 위반했다고 볼 수 있다.

인터페이스 분리 원칙 (a.k.a ISP)

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

ISP는 한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다는 원리이다. 즉, 어떤 클래스가 다른 클래스에 종속될 때에는 가능한 최소한의 인터페이스만을 사용해야 한다.

SRP가 클래스의 단일책임을 강조한다면 ISP는 인터페이스의 단일책임을 강조한다. 하지만 ISP는 어떤 클래스 혹은 인터페이스가 여러 책임 혹은 역할을 갖는 것을 인정한다. SRP가 클래스 분리를 통해 변화에 적응성을 획득하는 반면, ISP에서는 인터페이스 분리를 통해 같은 목표에 도달 한다.

예를 들어 아이폰을 만든다고 가정해보자.

아이폰 MAX, 아이폰 MAX PRO, 아이폰 MINI 를 모두 만들어야하는데, 모든 기능을 아이폰이라는 인터페이스에 몰아넣는 것이 맞을까?

모든 IPhone 시리즈에 적용되는 인터페이스이다.

interface IPhone {
    String telephone();

    String internet();
    
    String photo();
    
}

IPhone 13 시리즈에만 적용되는 인터페이스이다.

interface IPhone13 extends IPhone {
    String HDR4(); //13부터 HDR4로 바꼈으니 해당 기능을 넣어보겠다.
    
}

IPhone 13 Pro에만 적용되는 인터페이스이다.

interface IPhone13Pro extends IPhone, IPhone13  {
	String Promotion();// 뭔지 모르겠지만 13 기능 중에 pro만 추가되었다고 해서..    
}

Override.. 글쓴이 체력 저하로 생략

class IPhone13Pro implements IPhone13Pro  {
	
    @Override
    ~~~~~~~~생략
}

의존 역전 원칙 (a.k.a DIP)

'추상화에 의존해야하며, 구체화에 의존하면 안된다.'

고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 된다.
저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야 한다.
하위의 모듈이 상위 모듈에 정의한 추상 타입(interface)에 의존 해야한다.
즉, 자신보다 변하기 쉬운 것에 의존하면 안된다는 의미이다.

새로 산 바지가 크다고 살을 찌워서 다른 옷이 안 맞아질 수 있다..

참조

https://ko.wikipedia.org/wiki/%EB%A6%AC%EC%8A%A4%EC%BD%94%ED%94%84_%EC%B9%98%ED%99%98_%EC%9B%90%EC%B9%99
https://dahye-jeong.gitbook.io/java/java/2020-03-21-solid
https://www.nextree.co.kr/p6960/
https://wooaoe.tistory.com/57
https://sjh836.tistory.com/159

profile
고양이와 함께 - 끄적끄적 개발하고 이씁니다 ~!

0개의 댓글