좋은 객체 지향 프로그래밍이란?

정민기·2021년 4월 25일
0

스프링 핵심 원리

목록 보기
2/5
post-thumbnail

객체 지향 특징

  • 추상화
  • 캡슐화
  • 상속
  • 다형성

객체 지향 프로그래밍

  • 객체 지향 프로그래밍이란 프로그램을 여러 개의 독립된 단위인 '객체'들의 모임으로 보는 것이다. 각각의 객체는 메세지를 주고받고, 데이터를 처리하며 협력한다.

  • 객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다. 이때 프로그램을 유연하고 변경이 용이하게 만든다는 것은 컴포넌트를 마치 컴퓨터 부품을 갈아 끼우듯이 쉽고 유연하게 변경하면서 개발하는 것을 말한다.

컴포넌트 : 소프트웨어 시스템에서 독립적인 기능을 하는 모듈이다. 이때 컴포넌트는 객체, 클래스와는 다른 개념이다. 컴포넌트는 꼭 클래스나 객체로 만들어야만 하는 것도 아니고, 하나의 클래스로 구성되어야 하는 것도 아니다. 즉 하나의 컴포넌트는 하나 이상의 클래스들로 구성될 수 있다.

다형성

  • 다형성은 객체 지향의 핵심이다. 다형성을 실세계에 비유하면 세상을 역활과 구현으로 구분하는 것이다.
    위 그림을 보면 자동차의 역할을 하는 인터페이스를 사용하여 K3, 아반떼 등 자동차를 구현하면 클라이언트인 운전자는 자동차의 운전방법만 알면 모든 차를 운전할 수 있다. 즉 클라이언트는 구현 대상의 내부 구조를 몰라도 되고, 내부 구조가 변경되거나 구현 대상 자체가 변경되어도 영향을 받지 않는다. 이처럼 역할구현으로 구분하면 세상이 단순해지고, 유연해지며 변경도 편리해진다.

  • 자바 언어에 다형성을 적용하면 역할 = 인터페이스, 구현 = 인터페이스를 구현한 클래스, 구현 객체로 분류할 수 있다. 이때 오버라이딩을 통해 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있다.

  • 다형성의 본질은 클라이언트를 변경하지 않고, 서버의 구현 기능을 우연하게 변경할 수 있는것이다.

좋은 객체 지향 설계의 5가지 원칙 (SOLID)

1. SRP 단일 책임 원칙

  • 한 클래스는 하나의 책임만을 가진다는 원칙이다. 이때 '하나의 책임'이라는 개념이 모호한데 중요한 기준은 '변경'이다. 특정 클래스를 변경하였을 때 그로인한 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.
  • 책임의 범위를 잘 조절하는 것이 중요한데 책임의 범위가 너무 작을 경우 기능이 너무 쪼깨지게 되고, 범위가 커질 경우 단일 책임 원칙에 위배되기 때문이다. 이처럼 책임의 범위를 적절히 조절하는 것이 객체 지향의 묘미이다.

2. OCP 개방-폐쇄 원칙 (가장 중요한 원칙)

  • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다는 원칙이다. 확장을 할 때는 다형성을 활용하여 인터페이스를 구현한 새로운 클래스를 만드는 방법 등으로 가능하다.
  • 문제점 : 구현 객체를 변경하기 위해서는 코드를 변경해야한다. 때문에 다형성을 사용하였지만 OCP 원칙을 지킬 수 없게 된다. 이러한 문제를 해결하기 위해 객체를 생성하고, 연관 관게를 맺어주는 조립, 설정자가 필요하고 바로 스프링 컨테이너가 이 역할을 하게 된다.

3. LSP 리스코프 치환 원칙

  • 프로그램의 객체는 프로그램의 정확성을 깨트리지 않으면서 하위 타입의 인스턴스로 바꿔야 한다는 원칙이다. 이 때문에 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다. 예를 들면 자동차라는 인터페이스를 구현한 K3에서 엑셀을 밟으면 후진을 하는 기능을 넣는 경우 규약을 어기게 되므로 LSP 원칙이 깨지게 된다.

4. ISP 인터페이스 분리 원칙

  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다. 따라서 특정 인터페이스 자체가 변경되어도 클라이언트에 영향을 주지 않게 된다.
  • 이를 통해 인터페이스가 명확해지고, 대체 가능성이 높아진다.

5. DIP 의존관계 역전 원칙 (중요)

  • 구현 클래스에 의존하지 않고, 인터페이스에 의존하라는 원칙이다. 즉 다형성에서 구현보다는 역할에 의존해야 한다는 것이다.
  • 하지만 아래 코드와 같이 MemberService 클라이언트가 구현 클래스를 직접 선택하게 되고, MemberService는 인터페이스에 의존하지만, 구현 클래스도 동시에 의존하게 된다. 때문에 DIP를 위반하게 된다.
public class MemberService {
	private MemberRepository m = new MemoryMemberRepository();
}

정리

  • 객체 지향의 핵심은 다형성
  • 다형성만으로는 OCP, DIP를 지킬 수 없다.

객체 지향 설계와 스프링

  • 객체 지향 프로그래밍에서 다형성 + OCP, DIP를 가능하게 하기 위해 스프링이 개발되었다. 스프링은 DI(Dependency Injection: 의존관계, 의존성 주입) 컨테이너를 제공함으로써 OCP, DIP를 가능하게 지원한다. 이를 통해 클라이언트 코드의 변경 없이 기능을 확장하고, 쉽게 부품을 교체하듯이 개발을 할 수 있게 되었다.

최종 정리

  • 모든 설계에 역할과 구현을 분리하여야 한다.
  • 애플리케이션 설계는 다형성 뿐만 아니라 OCP, DIP를 지켜 언제든지 유연하게 변경할 수 있도록 만드는 것이 좋은 객체 지향 설계이다. 이를 위해 스프링 컨테이너를 이용한다.
  • 이상적으로는 모든 설계에 인터페이스를 부여하자. 예를 들면 어떤 DB를 쓸지 정해지지 않은 상황에서 개발해야 하는 경우 인터페이스를 먼저 만들어 놓으면 구현 단계의 선택을 뒤로 미루고 개발할 수 있다. 이처럼 확장과 변경이 용이하다. 하지만 인터페이스를 남발하면 추상화라는 비용이 발생한다. 코드가 추상화되면 구현 코드를 한번에 알아보지 못하고 인터페이스까지 들어가서 확인해야 코드를 이해할 수 있게 된다. 따라서 기능을 확장할 가능성이 없으면, 구체 class를 직접 사용하고, 나중에 필요한 경우 인터페이스를 도입하는 것이 좋다.



[Reference]

Inflearn 김영한 님의 스프링 핵심 원리 - 기본편 : https://www.inflearn.com/course/스프링-핵심-원리-기본편/dashboard

0개의 댓글

관련 채용 정보