Java

OOP(Object Oriented Programming)

후후후하하하 2024. 4. 5. 13:12

 

 

  • 많은 객체들이 모여서 상호 협력하면서 데이터를 처리하는 방식.
  • 프로그램을 묶음 단위로 잘게 쪼개서, 추후에 가져다 쓰기 편하게 만들어놓은 프로그래밍 방식
  • 레고 블럭 조립하듯이 컴포넌트를 유연하고 변경이 용이하다.
  • 반대 개념인 절차적 프로그래밍(Procedure Programming)
    • 대표 언어 C
    • 함수를 이용해서 작은 부품을 만들고 이것을 결합해서 더 큰 프로그램을 만들어가는 테크닉
    • 하지만 어플리케이션이 복잡해질수록 실제 세계처럼 더 밀접한 모델링 방식이 필요했다.
    • 그래서 서로 연관된 함수와 변수를 모아서 박스를 만들고 거기에 이름을 붙여서 정리정돈을 한 수납상자를 만들었는데 이것이 Class이다.
    • 이런 클래스를 중심으로 프로그램의 구조를 만들어가는 프로그래밍 방법론이 객체 지향 프로그래밍이다.
  • 객체지향 기법은 '사용'기법이 아닌 '설계'기법이다.
  • 절차적 프로그래밍이 계산기와 같은 아웃풋을 생성하는 프로그램을 만드는 것이라면, 객체지향 프로그래밍은 라이브러리와 같이 다른 개발자가 이용할 수 있게 구조를 만드는 것이다. 거대한 규모의 애플리케이션은 객체지향과 절차적 프로그래밍이 잘 융합하여 만들어진다.
  • 설계 관점에서 왜 추상화, 상속, 다형성, 캡슐화를 사용하는지.
  1. 추상화
    • 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다.
    • 즉, 보통 알고는 있지만 정확하게 표현하기 힘든 것들을 중요한 부분이나 특징점을 잡아 설명하는 것을 '추상적으로 표현한다', '추상화한다'라고 한다.
    • 프로그래밍에서의 추상화는 클래스를 정의할 때 불필요한 부분들을 생략하고 객체의 속성 중 중요한 것에만 중점을 두어 개략화하는 것을 말한다.
    • 즉, 클래스들의 중요하고 공통된 성질들을 추출하여 부모(슈퍼) 클래스를 선정하는 개념과, 이벤트 발생의 정확한 절차나 방법을 정의하지 않고 대표할 수 있는 표현으로 대체하는 것을 말한다.
    • 두 가지로 나뉜다.
      • 객체의 관련 속성만 표시 - 데이터 추상화
      • 불필요한 세부 정보는 숨긴다 - 제어 추상화
    • 제어 추상화
      • 어떤 클래스의 메서드를 사용하는 사용자에게 해당 메서드의 작동방식과 같은 내부 로직을 숨기는 것을 말한다.
      • 결국 핵심은 보다 프로그래밍을 빠르게 설계하고 구현하기 위해 추상화를 사용한다.
      • 구체적이지 않고 추상적으로 메서드 동작을 가늠해 결과값만 받고 끝낸다.
      • 우리가 사용하는 for, while문도 사실 반복하는 개념을 제어 추상화 한 것이다. (반복 기능 추상화)
      • 객체 지향은 설계 이론이지 사용 이론이 아니다.
      • 결과적으로 생산성 증가, 가독성 증가, 에러 감소, 유지 보수시 시간 단축 등 프로그래밍 노력과 복잡성을 줄여준다.
    • 데이터 추상화
      • 대상을 간단한 개념으로 일반화하는 과정
      • 삼각형, 사각형, 원이라는 객체가 있을 때, 객체들의 공통 특징인 도형으로 묶어 이름을 붙이는 것.
      • 추상화를 할 수록 객체의 디테일함이 사라지고 공통된 특징만 남게 된다.
      • 공통된 기능들은 상위 요소에서 미리 구현하기 때문에 아이폰을 만들 때 아이폰마의 고유 기능 위주로 개발할 수 있게 된다.
  2. 상속(Inheritance)
    • 객체들 간의 관계를 구축하는 방법
    • 상위 클래스의 속성(변수)와 기능(메서드)를 재사용하여 하위 클래스가 전부 물려받는 것.
    • 코드의 중복 제거, 코드 재사용성 증대 효과
  3.  다형성(Polymorphism)
    • 같은 자료형에 여러 가지 타입의 데이터를 대입하여 다양한 결과를 얻어낼 수 있는 성질
    • 클래스가 상속 관계에 있을 때 나타나는 다채로운 성질
    • 장점 : 타입 묶음
  4. 캡슐화(Encapsulation)
    • 변수나 메서드들을 캡슐로 감싸서 안보이게 하는 정보 은닉 개념 중 하나이다.
    • 알약과 같다. 실제 약 내용은 가루약이지만 캡슐로 감싸면서 맛을 은닉하여 사람이 먹기 편하게 하였으며, 복잡한 재료들의 배합을 캡슐로 포장하여 다루기 편하게 하였다.
    • 객체의 속성(field)와 행위(method)를 하나로 묶고, 외부로부터 내부를 감싸 숨겨 은닉한다. 또한 외부의 잘못된 접근으로 값이 변하는 의도치 않는 동작을 방지하는 보호 효과도 누릴 수 있다.
    • 대표적으로 protected, default, private의 접근 제어자를 통해 구현이 가능하다.
    • getter(), setter() 를 통한 접근 기법의 좋은 점은 유효하지 않는 직접 변수 세팅을 메서드 내부에서 유효성 체크 로직을 통해 거를 수 있다는 점이다.
    • 대표 클래스 : String
      • String 데이터는 불변이라고 불리는데 그 이유가 캡슐화가 되어 있기 때문이다. 이는 String 클래스를 통해 내부적으로 각종 패스워드 및 인코딩/디코딩 작업이 이루어지기 때문에 보안적으로 굉장히 민감하기 때문이다.
      • String은 왜 불변으로 설계?
        • JVM에서는 따로 String Constant Pool이라는 영역 만들고 문자열들을 Constant화하여 다른 변수 혹은 객체들과 공유, 이 과정에서 데이터 캐싱이 일어나고 그만큼 성능적 이득
        • 데이터가 불변하다면 멀티 스레드 환경에서 동기화 문제 발생하지 않음
        • 보안측면. 데베 사용자 이름, 암호는 문자열로 전달됨. 변경가능하다면 해커 공격 가능
      • 리터럴을 이용한 방식 :  Heap 영역 내의 String constant pool에 저장. 메모리 절약 가능. 내부적으로 HashTable 구조를 가짐. 성능 보장
      • new 연산자를 이용한 방식 : Heap 영역에 각각 메모리 차지하며 존재
      • intern()
        • String을 리터럴로 선언할 경우 내부적으로 String의 intern() 메서드가 호출된다.
        • 해당 리터럴이 pool에 있는지 확인하고, 존재하면 해당 pool에 있는 리터럴을 리턴하고, 없다면 리터럴을 pool에 집어넣고 새로운 pool 주소값을 반환한다.
    • 정보 은닉(OOP의 핵심) : 보안 효과 + 알 필요가 없어 간편하게 사용 가능. 디자인 패턴 = 정보 은닉 기법 극대화
      1. 객체의 구체적인 타입 은닉(=업캐스팅)
        • 팩토리 패턴 + 다형성을 이용하여 정보 은닉
      2. 객체의 필드 및 메서드 은닉(=캡슐화)
        • 공개 메서드와 은닉 메서드를 구분하고 공개 메서드의 갯수를 최소화
      3. 구현 은닉(=인터페이스 & 추상 클래스)

 

객체 지향 설계의 5원칙 SOLID

좋은 설계란 시스템에 새로운 요구사항이나 변경사항이 있을 때, 영향을 받는 범위가 적은 구조.

SOLID 객체 지향 원칙을 적용하면 코드를 확장하고 유지 보수 관리하기가 더 쉬워지며, 불필요한 복잡성을 제거해 리팩토링에 소요되는 시간을 줄임으로써 개발 생산성 향상 가능.

  1. 단일 책임 원칙 - SRP(Single Responsibility Principle)
    • 클래스는 단 하나의 책임만 가져야 한다.
    • 책임이란 기능 담당을 의미함.
    • 즉, 하나의 클래스는 하나의 기능을 담당하여 하나의 책임을 수행하는데 집중되도록 클래스를 따로따로 여러개 설계하라는 원칙이다.
    • 한 책임의 변경으로부터 다른 책임의 변경으로의 연쇄작용 극복 가능
    • 프로그램의 유지보수성을 높이기 위한 설계 기법. 책임의 범위는 정해져 있는 것이 아니다.
    • 한 객체에 책임이 많아질수록 클래스 내부에서 서로 다른 역할을 하는 코드끼리 강하게 결합될 가능성이 높아진다.
    • Facade 패턴 : 건물의 정면을 의미한다. 건물 뒷부분은 보여주지 않고 건물의 정면만 보여주는 패턴.
    • 주의점
      • 클래스명은 책임의 소재를 알 수 있게 작명
      • 책임을 분리할때 항상 결합도, 응집도 따져가며
  2. 개방 폐쇄 원칙 - OCP(Open Closed Principle)
    • 확장에는 열려있어야 하며, 수정에는 닫혀있어야 한다.
    • 클래스를 확장을 통해 손쉽게 구현, 확장에 따른 클래스 수정은 최소화
    • 추상화 사용을 통한 관계 구축을 권장
    • 다형성과 확장을 가능케 하는 기본적인 설계 원칙
    • 먼저, 변경(확장)될 것과 변하지 않을 것을 엄격히 구분한다.
    • 이 두 모듈이 만나는 지점에 추상화(추상 클래스 or 인터페이스)를 정의한다.
    • 구현체에 의존이 아니라 추상화에 의존하도록 코드를 작성한다.
    • 대표적으로  데이터베이스 인터페이스인 JDBC
  3. 리스코프 치환 원칙 - LSP(Liskov Substitution Principle)
    • 서브 타입은 언제나 기반(부모) 타입으로 교체할 수 있어야 한다.
    • 다형성 원리를 이용하기 위한 원칙
    • 상위 클래스 타입으로 객체를 선언하여 하위 클래스의 인스턴스를 받으면, 업캐스팅된 상태에서 부모의 메서드를 사용해도 동작이 의도대로 흘러가야 하는 것.
    • 대표적으로 Collection 인터페이스
  4. 인터페이스 분리 원칙 - ISP(Interface Segregation Principle)
    • 인터페이스를 각각 사용에 맞게끔 잘게 분리해야한다는 설꼐 원칙.
    • SRP는 클래스의 단일 책임 강조, ISP는 인터페이스의 단일 책임 강조
    • 인터페이스를 사용하는 클라이언트를 기준으로 분리함으로써, 클라이언트의 목적과 용도에 적합한 인터페이스만을 제공하는 것이 목표.
    • 주의해야할 점은 한 번 인터페이스를 분리하여 구성해놓고 나중에 무언가 수정사항이 생겨서 또 인터페이스들을 분리하는 행위를 가하지 말아야한다. (한 번 구성하면 왠만하면 변경하며 안됨)
    • 다중 상속(구현)이 가능하기 때문에, 분리할 수 있으면 분리해서 implements하면 된다.
  5. 의존 역전 원칙 - DIP(Dependency Inversion Principle)
    • DIP 원칙은 어떤 class를 참조해서 사용해야 하는 상황이 생긴다면 그 클래스를 직접 참조하는 것이 아니라 그 대상의 상위 요소로 참조하라는 원칙
    • 즉 , 구현클래스에 의존하지말고 인터페이스에 의존하라는 뜻
    • 의존 관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것보다는 변화하기 어려운 것에 의존하라는 것.
    • 지향점은 각 클래스간의 결합도를 낮추는 것이다.