객체지향 설계와 스프링

2 minute read

스프링 컨셉

  • 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임 워크
  • 좋은 객체 지향 어플리케이션을 개발할 수 있게 도와주는 프레임워크

좋은 객체 지향 프로그래밍

객체 지향 특징

프로그램을 보다 유연하고 변경 용이하게 만들 수 있다. 코드의 변경을 최소화하고 유지보수하는데 유리하다

- 추상화 : 객체의 공통적인 속성과 기능을 추출하여 정의하는 것 (추상클래스, 인터페이스), 역할과 구현의 분리
인터페이스에서 객체의 역할만을 정의하여 객체들 간의 관계를 보다 유연하게 연결, 추상 메서드나 상수를 통해 어떤 객체가 수행해야 하는 핵심적인 역할만을 규정해두고, 실제적인 구현은 해당 인터페이스를 구현하는 각각 객체들에서 하도록 프로그램 설계

- 상속: 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 것
상속의 경우, 상위 클래스의 속성과 기능들을 하위클래스에서 그대로 받아 사용하거나 오버라이딩을 통해 선택적으로 재정의 할 수 있음. 반면 인터페이스를 통한 구현은 반드시 인터페이스에 정의된 추상 메서드의 내용이 하위클래스에서 정의 되어야 함.

상속관계의 경우 인터페이스를 사용하는 구현에 비해 추상화의 정도가 낮음. 인터페이스가 역할에 해당하는 껍데기만 정의해두고, 하위 클래스에서 구체적인 구현을 강제하는 것에 비해, 상속 관계의 경우 상황에 따라 모든 구체적인 내용들을 정의해두고 하위 클래스에서는 단순이 가져다가 재사용할 수 있음.

- 다형성(Polymorphism): 어떤 객체의 속성이나 기능이 상황에 따라 여러가지 형태를 가질 수 있는 성질
객체 지향 프로그래밍에서 다형성이란 한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든 것을 의미함. 상위 클래스 타입의 참조변수로 하위 클래스의 객체를 참조할 수 있도록 하는 것.

객체 지향 프로그래밍은 추상화, 상속, 다형성의 특성을 활용하여 프로그래밍을 설계할 떄 역할과 구현을 구분하여 객체들 간의 결합도를 낮추고 느슨한 관계 설정을 통해 유연하고 변경이 용이한 프로그램 설계를 가능하게 만듦

- 캡슐화
클래스 안에 서로 연관있는 속성과 기능들을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것

인터페이스(역할)을 안정적으로 잘 설계하는 것이 중요함!!

객체지향에서는 다형성이 가장 중요함!

출처 : https://www.codestates.com/blog/content/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8A%B9%EC%A7%95

참고 : https://www.codestates.com/blog/content/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8

SOLID

SRP : 단일 책임 원칙(single responsibility principle)

  • 한 클래스는 하나의 책임만 가져야 한다.
  • 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것

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

  • 확장에는 열려있으나 변경에는 닫혀 있어야 한다.
  • 다형성을 활용하라.
  • 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현
  • 어플리케이션을 사용영역과 구성영역으로 나눔.

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

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함.
  • 하위클래스 is a kind of 상위클래스(하위 분류는 상위 분류의 한 종류이다.)
  • 구현클래스 is able to 인터페이스(구현 분류는 인터페이스 할 수 있다.)
  • 즉 하위클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스 인스턴스 역할을 하는데 문제가 없어야 한다.

ISP : 인터페이스 분리 원칙(Interface segregation principle)

  • “클라이언트는 자신이 사용하지 않는 메서드에 의존관계를 맺으면 안된다.”    -로버트 C.마틴-
  • 하나의 범용 인터페이스보다는 여러개의 구체적인 인터페이스가 낫다.

DIP : 의존관계 역전 원칙(Dependency inversion principle)

  • 추상화에 의존해야지, 구체화에 의존하면 안된다.
  • 역할(Role)에 의존해야지 구현에 의존하면 안된다.
  • 의존성 주입은 이 원칙을 따르는 방법 중 하나다.

다형성 만으로는 구현 객체를 변경할때 클라이언트 코드도 함께 변경된다. 다형성 만으로는 OCP, DIP를 지킬 수 없다. 뭔가가 더 필요하다.


정리

  1. 모든 설계에 역할과 구현을 분리하자
  2. 이상적으로는 모든 설계에 인터페이스를 부여하자, 하지만 추상화라는 비용이 발생한다
  3. 기능을 확장할 가능성이 없다면, 구체 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩터링해서 인터페이스를 도입하는 것도 방법이다

IoC, DI, Container

제어의 역전 IoC(Inversion of Control)

  • 프로그램에 대한 제어 흐름에 대한 권한은 모두 AppConfig가 갖고 있음.
  • 제어의 흐름을 직접 제어하는 것이 아닌, 외부에서 관리하는 것을 IoC라 한다.

프레임워크 VS 라이브러리

  • 프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 프레임워크 (제어의 역전, ex. Junit)
  • 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 라이브러리(ex. XML, JSON으로 변환하는 메서드 호출)

의존관계 주입 DI(Dependency Injection)

  • 인터페이스(추상)에만 의존한다. 실제 어떤 구현 객체가 사용될지는 모른다.
  • 정적인 클래스 의존관계와, 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계 둘을 분리해서 생각해야 한다.
  • 의존관계 주입을 사용하면, 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.

AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너/DI컨테이너라고 한다.

참고서적 : 조영호 객체지향의 사실과 오해

Categories:

Updated: