[Java] 객체 지향 프로그래밍의 5가지 기본 원칙 SOLID 를 예제와 함께 알아 보자. (1)

2023. 9. 27. 13:48·Programming Language/Java
반응형

SOLID란?

로버트 마틴이 2000년대 초반에 이름붙인 객체 지향 프로그래밍/설계의 5가지 기본 원칙의 앞글자를 딴 약어 SOLID

 

객체지향의 4대 원칙 - 캡슐화, 상속, 추상화, 다형성

다형성 - 똑같은 클라이언트 코드로 안에 들어있는 존재에 따라 다른 동작이 수행되는 것

 

 

 

 

 

 

단일 책임 원칙 (Single responsibility principle, SRP)

하나의 클래스는 하나의 책임만을 가져야 한다. 

하나의 클래스가 여러개의 책임을 가진다면, 그 클래스의 코드가 변경되어야할 이유가 여러개가 생긴 것이다.

 

단일 책임 원칙을 지키는 코드는 각각의 클래스가 응집력이 높기 때문에, 코드의 재사용성이 높아지고, 캡슐화를 통해 한 클래스의 변경이 다른 클래스에 영향을 미치지 않도록 만든다.

 

 

 

 

 

 

개방-폐쇄 원칙 (Open/closed principle, OCP)

소프트웨어는 확장에는 열려있고, 변경에는 닫혀있어야 한다.

 

  • 꼭 인터페이스가 필요한 것은 아니다.
  • 아래 개념을 활용해서도 개방-폐쇄 원칙을 지킬 수 있다. 
    • Enum
    • 디자인 패턴
    • 이벤트 기반 프로그래밍

 

개방 폐쇄 원칙을 지키는 코드는 클라이언트 코드가 추상화에 의존하고 있기 때문에, 확잘될 때와 변경될 때 모두 다른 코드에 영향을 주지 않게 만든다.

 

 

 

 

 

 

리스코프 치환 원칙 (Liskov substitution principle)

부모 클래스가 할 수 있는 행동은 자식 클래스도 할 수 있어야 한다.

 

 

 

리스코프 치환 원칙이 깨진 상황과 그 문제점

public class Parent {
    public void someMethod(int input) {
        // 어떤 input이든 상관 없음

        System.out.println("Parent 정상적으로 호출됨.");
    }
};
public class Child extends Parent {
    @Override
    public void someMethod(int input) {
        if(input <= 0)
            throw new RuntimeException("양수만 받을 수 있어요");

        System.out.println("Child 정상적으로 호출됨.");
    }
};

-> Parent 에서는 어떤 input 이든 상관 없었는데 Child 에서는 Input 이 0 이하일 경우 예외를 던지고 종료된다.

-> 부모 클래스가 할 수 있는걸 자식이 할 수 없는 상황이다.

-> 이게 무슨 문제냐고? 이제 Client 의 코드를 보며 문제점을 찾아보자.

 

public class Client {
    public void someClientMethod(Parent parentOrChild) {
        parentOrChild.someMethod(-1);
    }
}
public class LspExampleMain {
    public static void main(String[] args) {
        Client client = new Client();

//        Parent parent = new Parent();
        Child child = new Child();

        client.someClientMethod(child);
    }
}

-> Client 입장에선 parentOrChild 에 Parent 가 들어있는지 Child 가 들어있는지 모름

 

-> Child 의 someMethod 로 -1 을 넘기니까, 역시 예외가 터지는 것을 볼 수 있다.

 

해결책으로 세가지 정도가 떠오를 수 있겠다.

  1. Child 대신 Parent 인스턴스를 넣어준다.
  2. 파라미터로 양수만 넣어준다.
  3. instanceof 로 어떤 인스턴스인지 구분하여 호출한다.

 

 

그럼 이제 살펴보자.

  • Child 대신 Parent 인스턴스를 넣어준다?
    • Parent 로 타입이 정해져버리는 것은 다형성을 없애기 때문에 적절한 해결이 되지 못한다.
  • 파라미터로 양수만 넣어준다?
    • Parent 에서는 음수에서도 동작을 해야하는데, 상속으로 인해서 Parent 의 기능이 제한되는 것은 해결책이 되지 못한다.
  • instanceof 로 어떤 인스턴스인지 구분하여 호출한다?
    • 아래의 코드를 보자. 이것도 해결책이 되지 못한다. 타입에 따른 로직을 분리했기 때문에 다형성이 깨졌다.
    • Parent 와 Child 외에 상속이 더 진행된다면 Client 의 코드에 if 가 추가될 것이다.

 

public class Client {
    public void someClientMethod(Parent parentOrChild) {
//        parentOrChild.someMethod(-1);

        if(parentOrChild instanceof Parent && false == parentOrChild instanceof Child)
            parentOrChild.someMethod(-1);
        else if(parentOrChild instanceof Child)
            parentOrChild.someMethod(Math.abs(-1));
    }
}

 

A instanceof B 에서, A 가 B 와 동일한 타입이어야만 true 를 반환한다고 오해할 수 있는데, A 가 B 의 자식 타입이더라도 true 를 반환한다는 것을 유념하자.

 

 

그렇다면 해결첵은?

1. Parent 와 Child 는 애초에 상속 관계를 가지지 말았어야 한다.
2. 상속관계를 역전시켜서 Child 가 Parent 의 부모가 되었어야 한다.

2번의 방법을 설명하자면
Child 에서 input 으로 양수만 받을 것이고, 자식인 Parent 에서는 양수를 처리할 수 있기 때문이다.

 

따라서, 리스코프치환원칙을 지키지 않는 상속은 다형성을 깨트리기 때문에 
-> 상속을 하지 않는 것이 적절하다.

 

 

 

 

계약에 의한 설계

사전 조건은 자식 클래스에서 더 강해지면 안된다.

위의 예제에서 Child 에서 Input 의 범위를 제한하는 것을 말한다.

계약에 의한 설계의 내용은 이 것 말고도 많다. 지금 말고 시간이 될 때 찾아보자.

 

 

 

 

 

접근제어자에서도 발견할 수 있는 리스코프 치환 원칙

public class Parent {
	public void someMethod(int input) {}
};

public class Child extends Parent {
	private void someMethod(int input) {}
};

-> 다형성 X

public class Parent {
	private void someMethod(int input) {}
};

public class Child extends Parent {
	public void someMethod(int input) {}
};

-> 다형성 O

 

 

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'Programming Language > Java' 카테고리의 다른 글

[Java] final 을 사용하는 모든 경우 - 예제와 함께 이해하기  (0) 2023.09.30
[Java] 객체 지향 프로그래밍의 5가지 기본 원칙 SOLID 를 예제와 함께 알아 보자. (2)  (0) 2023.09.29
[Java] 의존 관계에 대해 예제와 함께 알아보자.  (0) 2023.09.26
[Java] stream API 와 Optional  (0) 2023.09.26
[Java] stream API를 예제와 함께 알아보자.  (0) 2023.09.26
'Programming Language/Java' 카테고리의 다른 글
  • [Java] final 을 사용하는 모든 경우 - 예제와 함께 이해하기
  • [Java] 객체 지향 프로그래밍의 5가지 기본 원칙 SOLID 를 예제와 함께 알아 보자. (2)
  • [Java] 의존 관계에 대해 예제와 함께 알아보자.
  • [Java] stream API 와 Optional
Giken
Giken
𝐒𝐲𝐬𝐭𝐞𝐦.𝐨𝐮𝐭.𝐩𝐫𝐢𝐧𝐭𝐥𝐧("𝐇𝐞𝐥𝐥𝐨 𝐖𝐨𝐫𝐥𝐝!");
  • Giken
    개발자 기켄
    Giken
  • 전체
    오늘
    어제
    • 분류 전체보기 (148)
      • Programming Language (26)
        • C (3)
        • C++ (2)
        • Java (19)
      • Web (4)
      • Database (1)
        • SQL (5)
      • Spring (10)
      • PHP (7)
      • Linux (1)
      • Server (1)
      • Infra (3)
      • Algorithm (74)
        • 백준 (71)
        • 프로그래머스 (0)
      • 프로젝트 (2)
      • Etc (8)
      • 낙서 (5)
  • 블로그 메뉴

    • GitHub
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    평년
    1330
    2588
    윤년
    백준
    SQL고득점키트
    C
    DB
    SQL
    프로그래머스
    2753
    9498
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Giken
[Java] 객체 지향 프로그래밍의 5가지 기본 원칙 SOLID 를 예제와 함께 알아 보자. (1)
상단으로

티스토리툴바