본문 바로가기

DEVELOP/Java

[Java] 객체지향과 SOLID

반응형

객체 지향과 SOLID

소프트웨어 공학 내용이지만, 자바와도 연관성도 많으므로 자바 카테고리에 올리는 객체지향 정리.

객체 지향

객체지향은 컴퓨터 프로그램을 객체들의 모임으로 파악하고자 하는 관점. 각각의 객체는 데이터를 주고 받으며 협력한다. 프로그램을 유연하고 변경이 용이하게 만든다는 장점이 있다.

특징과 장점

  • 특징
    • 추상화: 현실세계의 개념을 반영하는 것. 객체들의 공통적인 특징, 데이터와 기능을 도출한다.
      • ex) 요청한 계산을 수행하는 프로그램을 만든다고 했을 때, Calculator라는 이름의 클래스를 만든다. 실제 계산기라고 생각하고 기능을 구현한다. 인간의 사고방식과 프로그램의 동기화.
    • 캡슐화: 객체의 역할을 수행하기 위한 하나의 목적을 위해 데이터와 기능들을 묶는 것
      • ex) Calculator 클래스에 add() 메소드를 구현한다. 클래스를 사용하는 클라이언트 입장에서는 add() 내부의 로직의 구현 방식은 중요하지 않다. 의도한 기능만 잘 수행하면 된다.
    • 상속성: 새로운 클래스가 기존의 클래스의 자료와 연산을 이용하게 해주는 것
      • ex) 공학용 계산기를 만들 때, 기존의 Calculator를 상속하는 ScientificCalculator를 만든다. 부모 클래스의 사칙연산 기능은 모두 그대로 사용하며, 추가 기능만 구현할 수 있다.
    • 다형성: 하나의 클래스나 메소드를 다양한 방식으로 이용할 수 있게 한다. (오버라이딩,오버로딩)
      • ex) 오버로딩: 두 정수를 계산하는 add(int x, int y) 메소드와 두 실수를 계산하는 add(float x, float y)를 각각 구현할 수 있다.
      • ex) 오버라이딩: Calculator의 add() 메소드에서는 계산 즉시 결과값을 출력하는 로직이 포함되어 있었다. ScientificCalculator에서는 모든 계산이 완료됐을 때 결과를 출력하고 싶다면 add() 메소드를 오버라이딩 한 후 새로 구현할 수 있다.
  • 장점
    • 코드의 재사용성
    • 개발 및 유지보수에 효율적
    • 캡슐화를 통해 데이터의 올바른 값 유지
    • 다형성을 통한 매소드의 활용

실세계의 사물을 추상화하여 멤버 변수와 메소드를 정의. 캡슐화를 통해 이와 같은 멤버변수와 메소드의 이용가능 범위를 적정하게 제한. 상속을 이용하여 부모 클래스의 기능을 자식클래스에서 물려받거나 재정의를 통해 다른 기능을 구현하는 다형성 실현

다형성

역할과 구현이란

  • 자동차(역할)
    • 현대 아반떼
    • 테슬라 모델3
    • 포르쉐 카이엔
  • 드라큘라(역할)
    • 김준수
    • 전동석
    • 신성록

역할과 구현을 분리하면

  • 클라이언트는 대상의 역할(인터페이스)만 알면 된다.
  • 클라이언트는 구현 대상의 내부 구조를 몰라도 된다.
  • 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않는다.
  • 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다.

클라이언트의 관점이 중요하다. 어떤 자동차 모델이 나와도, 동일한 기능이 구현되어 있어 운전에 무리가 없다.

자바에서는

  • 자바 언어의 다형성을 활용
    • 역할 == 인터페이스
    • 구현 == 클래스(인터페이스 구현체)
  • 객체를 설계할 때 역할과 구현을 명확히 분리
  • 객체 설계시 역할을 먼저 부여하고, 그 역할을 수행하는 구현체 만들기

다형성이란

  • 협력이라는 클라이언트와 서버의 객체 사이이 관계에 주목해야 한다.
  • 클라이언트를 변경하지 않고 서버의 구현 기능을 변경할 수 있다.

SOLID

  • SRP: 단일 책임 원칙(Single Responsibility)
    • 모든 클래스는 하나의 책임만을 가져야 한다.
    • ex) 계산기 클래스는 계산과 관련된 기능만 수행해야 한다.
  • OCP: 개방/폐쇄 원칙(Open/Closed)
    • 확장에는 열려있고, 수정에는 닫혀있어야 한다.
    • 기존의 기능은 수정하지 않고(Closed), 새로운 기능을 추가하는(Open) 방식으로 소프트웨어의 개선이 이루어져야 한다.
    • ex) 공학용 계산기를 추가하고 싶다면, 기존 계산기 클래스는 그대로 두고(수정 Closed) 계산기 인터페이스를 상속하는 공학용 계산기 클래스를 추가(확장 Open)한다.
  • LSP: 리스코프 치환 원칙(Liskov Substitution)
    • 자식 클래스는 부모 클래스 기능의 정확성을 깨뜨리지 않으면서 대체할 수 있어야 한다.
    • 즉 부모 클래스에서 실행 가능한 모든 행위는 자식 클래스에 구현되어 있어야 한다.
    • ex) 부모 계산기의 add() 메소드는, 자식 계산기에서도 더하기 기능을 수행해야 한다. 빼기 기능을 수행한다면 신뢰할 수 없다.
  • ISP 인터페이스 분리 원칙(Interface Segregation)
    • 특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다.
    • ex) 계산기 인터페이스 -> 입력, 계산, 출력 인터페이스로 분리
  • DIP: 의존관계 역전 원칙(Dependency Inversion)
    • 의존관계는 쉽게 변화하는 것보다 변하기 어려운 것과 맺어야 한다.
    • 즉 구체적인 클래스보다 인터페이스나 추상 클래스와 관계를 맺어야 한다.
    • 스프링에서는 컨테이너를 통해 실현된다.
      • 개발자는 역할(인터페이스)만 정해두고, 구체적인 배우는 스프링 컨테이너에서 지정

  • 객체지향의 사실과 오해
  • 토비의 스프링
  • 자바 ORM 표준 JPA 프로그래밍
반응형

'DEVELOP > Java' 카테고리의 다른 글

[Java] Call by Value와 Call by Reference  (0) 2023.03.15
[디자인 패턴] 전략 패턴  (0) 2022.12.10
[JPA] N+1 문제 해결  (0) 2022.10.23
[Java] JPA 영속성 컨텍스트  (0) 2021.10.16
[Java] MyBatis #과 $의 차이  (0) 2021.09.14
[Java] String의 equals()와 hashcode()  (0) 2021.05.13
[Java] JVM Garbage Collector  (0) 2021.05.01