• 우선 객체지향의 5원칙을 이해하기에 앞서 절차 지향과 객체지향의  차이에 대해 알아보겠습니다.

  • 절차 지향이란?
    • 물이 위에서 아래로 흐르듯이 순차적인 프로그램 흐름을 중시하는 프로그래밍 방법입니다.
    • 대표적인 예시로는 C언어가 있습니다.
    • 이는 컴퓨터의 작업 처리와 비슷하기 때문에 속도가 빠릅니다.
    • 컴파일러의 발달과 소프트웨어 언어의 발달로 객체지향 프로그래밍의 탄생 계기를 만들었습니다.
    • 객체지향은 기능별로 모듈화 하여 중복되는 연산을 하지 않게 모듈을 재활용합니다.
    • 덕분에 개발자 관점에서 작성되는 코드량이 적어 유지보수에 용이합니다.

절차지향 예시 코드

  • (이미지는 구글 이미지 중 괜찮은 이미지를 갖고 왔습니다. 출처는 밑에 남기겠습니다.)

  • 객체지향이란?
    • 실제 세계를  모델링하여 소프트웨어를 개발하는 방법입니다.
    • 즉 데이터(Property)와 절차(Method)를 하나의 덩어리로 묶어서 생각하게 됩니다.
    • 이는 마치 컴퓨터 부품을 하나씩 사다가 컴퓨터를 조립하는 것과 같은 방법입니다.

객체지향 예시 코드

  • 객체지향의 4대 특성
    • 캡슐화
    • 상속
    • 다형성
    • 추상화
  • 캡슐화
    • 데이터와 알고리즘이 하나의 묶음으로 정리된 것입니다. 데이터를 감추고 외부 세계와의 상호작용은 메서드를 통합니다. 라이브러리로 만들어 확장에 유리합니다. (결합도가 낮아지고 응집도가 높아져 OCP(계방 폐쇄 원칙)에 전제조건입니다.)
  • 상속
    • 상속은 이미 작성된 클래스를 이어받아서 새로운 클래스를 생성하는 기법입니다. 기존 코드를 재활용해 사용합니다. (재사용을 통해 코드 양이 적어집니다. -> 유지보수 용이)
  • 다형성
    • 하나의 이름으로 많은 상황에 대처하는 기법입니다. 개념적으로 동일한 작업을 하는 함수들에게 똑같은 이름을
      부여할 수 있으므로 코드가 더 간단해지는 효과가 있습니다. (상위 클래스로 확장에는 열려있으며 결합도는 낮춘다. EX 인터페이스로 추상화할 시 다른 메서드는 추가를 못함. 하위 클래스로는 확장할 수 있음)
  • 추상화
    • 추상화는 다른 객체들과 구분되는 핵심적인 부분(업무 로직을 포함한 기능)에 집중하여, 복잡도를 관리할 수 있게 해 준다.

 

  • 위 4가지 특성들로 인해 객체지향의 장점은 다음과 같습니다.
    • 1. 신뢰성 있는 소프트웨어를 쉽게 작성할 수 있습니다.
    • 2. 코드를 재사용하기 쉽습니다.
    • 3. 업그레이드가 쉽습니다. (확장에 용이합니다.)
    • 4. 디버깅이 쉽습니다. (절차 지향의 경우 특정 기능 분리가 안되어있어 전체를 봐야 합니다.)

  •  이러한 이유들로 엔터프라이즈 수준의 프로젝트에  객체지향 기법이 합리적인 이유로 채택되었습니다.

  • 객체지향 3대 특성과 더불어 객체지향에서 꼭 지켜야 할 5가지 원칙 SOLID에 대해서 알아보겠습니다.

  • 1) SRP(Single Responsibility Principle) : 단일 책임 원칙
    • 객체는 오직 하나의 책임을 가져야 한다. (즉 객체가 변경될 때 이유는 하나여야 합니다.)
    • SRP원리를 적용하면 무엇보다도 책임 영역이 확실해지기 때문에 한 책임의 변경에서 다른 책임의 변경으로의
      연쇄작용에서 자유로울 수 있습니다.
    • (다시 말해 결합력은 낮추고 확장성은 높일 수 있습니다.) 뿐만 아니라 책임을 적절히 분배함으로써 코드 가독성 향상, 유지보수 용이라는 이점도 있습니다. 
    • OCP원리뿐 아니라 다른 객체지향 원칙들의 기초가 됩니다.
    • '책임'이라는 단어를 상기하며 엔터프라이즈급 프로젝트에서 복잡한 프로세스에 적용하는 연습을 해야 합니다.

  • 관건은 책임만 분리(CLASS 분리)하는 것이 아니라 분리된 두 클래스 간의 관계의 복잡도를 줄이도록 설계하는 것입니다.
  • 만약 Extract Class 된 각각의 클래스들이 유사하고 비슷한 책임을 중복해서 갖고 있다면 Extract Superclass를 사용할 수 있습니다.
  • 다시 말해 자주 변경될 프로퍼티, 메서드 와 변경이 일어나지 않을 프로퍼티, 메서드를 분리하는 것입니다. 

  • 2) OCP(Open-Closed Principle) : 개방-폐쇄 원칙
    • 소프트웨어 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에 열려 있고, 변경에는 닫혀 있어야 한다는 원리입니다.
    • 변경을 위한 비용은 줄이고 확장을 위한 비용은 극대화한다는 의미입니다. 다시 말해 요구사항의 변경이나 추가사항이 발생하더라도, 기존 핵심 기능(ex 기타를 친다 Method) 은 수정이 일어나지 말아야 하며,
    • 기존 구성요소(ex 기타의 제조사 필드)를 인터페이스로 추상화하여  쉽게 확장해서 재사용할 수 있게 해야 한다는 뜻입니다.
    • OCP는 재사용 가능한 코드를 만드는 기반이며 OCP의 전제 조건은 추상화와 다형성입니다.
    • 즉 한마디로 객체지향의 극대화에 아주 중요한 원리입니다. 

  • OCP를 적용하려면 고려해야 할 3가지 사항이 있습니다.
    • 1. 확장되는 것과 변경되지 않는 모듈을 분리하는 과정에서 크기 조절에 실패하면 오히려 관계가 더 복잡해질 수 있습니다. 설계자의 좋은 자질 중 하나는 이런 크기 조절과 같은 갈등 상황을 잘 포착하여 (아깝지만) 비장한 결단을 내릴 줄 아는 능력에 있습니다.
    • 2. 인터페이스는 가능하면 변경되어서는 안 됩니다. 따라서 인터페이스를 정의할 때 여러 경우의 수에 대한 고려와 예측이 필요합니다. 설계자는 적절한 수준의 예측 능력이 필요한데, 설계자에게 필요한 또 하나의 자질은 예지력입니다.
    • 3. 인터페이스 설계에서 적당한 추상화 레벨을 선택해야 합니다. 그래디 부치(Grady Booch)에 의하면 ‘추상화란 다른 모든 종류의 객체로부터 식별될 수 있는 객체의 본질적인 특징’이라고 정의하고 있습니다. 즉, 이 '행위'에 대한 본질적인 정의를 통해 인터페이스를 식별해야 합니다.
  • 3) LSP(Liskov Substitution Principle) : 리스 코프 치환 원칙
    • “서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다.”라고 할 수 있습니다.
    • LSP원리도 역시 서브 클래스가 확장에 대한 인터페이스를 준수해야 함을 의미합니다.
    • 다형성과 확장성을 극대화하려면 하위 클래스를 사용하는 것보다는 상위의 클래스(인터페이스)를 사용하는 것이 더 좋습니다.
    • 상속은 구현 상속(extends 관계)이든 인터페이스 상속(implements 관계)이든 궁극적으로는 다형성을 통한 확장성 획득을 목표로 합니다.
    • 상속을 통한 재사용은 기반 클래스와 서브 클래스 사이에 IS-A관계가 있을 경우로만 제한되어야 합니다. 그 외의 경우에는 합성(composition, 클래스 안에 클래스 선언)을 이용한 재사용을 해야 합니다.

    • 사용해야 할 때를 구분하자면 보통 IS A 관계와 HAS A관계로 구분한다.
    • 이것은 예로 들자면 한 원에 대해 '원은 반지름을 가진 점이다.'와 '원은 반지름과 점을 가지고 있다.'로 구분할 수 있다. 이것의 자세한 설명은 아래 참조의 블로그들에 잘 되어있다. 
  • 참조

  • 결론
    • 상속은 다형성과 따로 생각할 수 없습니다.
    • 그리고 다형성으로 인한 확장 효과를 얻기 위해서는 서브 클래스가 기반 클래스와 클라이언트 간의 규약(인터페이스)을 어겨서는 안 됩니다.
    • 결국 이 구조는 다형성을 통한 확장의 원리인 OCP를 제공하게 됩니다. 따라서 LSP는 OCP를 구성하는 구조가 됩니다.
  • 4) ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
    • ISP원리는 한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다는 원리입니다
    • 즉 어떤 클래스가 다른 클래스에 종속될 때에는 가능한 최소한의 인터페이스만을 사용해야 합니다.
    • ISP를 ‘하나의 일반적인 인터페이스보다는, 여러 개의 구체적인 인터페이스가 낫다’라고 정의할 수 도 있습니다.
    • SRP가 클래스의 단일 책임을 강조한다면 ISP는 인터페이스의 단일 책임을 강조합니다.
    • 하지만 ISP는 어떤 클래스 혹은 인터페이스가 여러 책임 혹은 역할을 갖는 것을 인정합니다.
    • 이러한 경우 ISP가 사용되는데 SRP가 클래스 분리를 통해 변화에의 적응성을 획득하는 반면, ISP에서는 인터페이스 분리를 통해 같은 목표에 도달합니다.

ISP 적용 예시 코드

  • 5) DIP(Dependency Inversion Principle) : 의존성 역전 원칙
    • 의존 관계의 역전 Dependency Inversion 이란 구조적 디자인에서 발생하던 하위 레벨 모듈의 변경이 상위 레벨 모듈의 변경을 요구하는 위계관계를 끊는 의미의 역전입니다.
    • 실제 사용 관계는 바뀌지 않으며, 추상화된 객체를 매개로 메시지를 주고받음으로써 관계를 최대한 느슨하게 만드는 원칙입니다.

    • DIP의 키워드는 ‘IOC’,
      • ‘훅 메서드(슈퍼클래스에서(슈퍼클래스에서 디폴트 기능을 정의해두거나 비워뒀다가 서브클래스에서 선택적으로 오버라이드 할 수 있도록 만들어둔 메서드를 훅(hook) 메서드라고 합니다.
      • 서브클래스에서는 추상 메서드를 구현하거나, 훅 메소드를 오버라이드 하는 방법을 이용해 기능의 일부를 확장합니다.)’’,‘확장성’입니다.
      • 이 세 가지 요소가 조합되어 복잡한 컴포넌트들의 관계를 단순화하고 컴포넌트 간의 커뮤니케이션을 효율적이게 합니다.
      • 예시로는 아래와 같은 것들이 있습니다.
        • 1) 통신 프로그래밍 모델

        • 2) 이벤트 드드리븐, 콜백 그리고 JMS 모델

        • 3) Layering (OSI 7 계층 또는 TCP/IP 4 계층) 
    • DIP는 복잡하고 지난한 컴포넌트 간의 커뮤니케이션 관계를 단순화하기 위한 원칙입니다. 

  • 결론
    • SOLID 원칙들은 소프트웨어 작업에서 프로그래머가 소스 코드가 읽기 쉽고 확장하기 쉽게 될 때까지 소프트웨어 소스 코드를 리팩토링리팩터링 하여 코드 냄새를 제거하기 위해 적용할 수 있는 지침입니다.
    • 이 원칙들은 애자일 소프트웨어 개발 적응적 소프트웨어 개발의 전반적 전략의 일부입니다.
    • 객체지향 원칙과 사고방식이 중요하다는 건 분명한 사실이나, 이것보다 더 우선해야 할 것이 고객의 요구사항대로 동작해야 한다는 것을 전재로 한 후에 적용해야 합니다.

'JAVA' 카테고리의 다른 글

GC(Garbage Collector)이란?  (0) 2020.08.07
JVM 이란?  (0) 2020.08.07
Jit 컴파일러란? 최적화 튜닝에 대해서  (0) 2020.08.07
Java 어노테이션 (annotation) 이란?  (0) 2020.08.07
리플렉션(reflection)이란?  (0) 2020.07.09

+ Recent posts