OOP (Object-Oriented Programming, 객체 지향 프로그래밍)

박영준·2023년 3월 30일
0

Programming

목록 보기
2/6

객체 지향 프로그램이란 무엇이고 어떻게 활용할 수 있는지에 대한 것은 개발자 면접에서 자주 나오는 질문 중의 하나!

1. 정의

  • 모든 데이터를 현실에 빗대어 객체로 다루는 프로그래밍 기법

  • 프로그래밍에서 필요한 데이터를 추상화시켜, 상태와 행위를 가진 객체로 만들고
    객체들 간의 상호작용을 통해, 로직을 구성하는 프로그래밍 방법

  • 객체지향 프로그래밍을 지원하는 언어 : C++, C#, Java, Python, JavaScript, Ruby, Swift ...

    참고: 클래스, 객체, 인스턴스 - 2. 객체

  • 객체 지향 vs 비객체 지향적 코드

    // 비객체 지향적 코드 1
    int hour1, hour2, hour3
    int minute1, minute2, minute3
    float second1, second2, second3
    
    // 객체 지향적 코드 1
    Time t1 = new Time();
    Time t2 = new Time();
    Time t3 = new Time();
    
    // 비객체 지향적 코드 2
    int[] hour = new int[3];
    int[] minute = new int[3];
    int[] second = new int[3];
    
    // 객체 지향적 코드 2
    int[] hour = new int[3];
    t[0] = new Time();
    t[1] = new Time();
    t[2] = new Time();

2. 등장 배경

1) 순차적 (비구조적) 프로그래밍

  • 정의한 기능의 흐름에 따라, 순서대로 동작을 추가하며 프로그램을 완성하는 방식

  • 간단한 프로그램일 일 경우 : 코드 흐름이 직관적인 장점
    규모 큰 프로그램 일 경우 : 더 이상 직관적인 코드 X
    → A → B → C 라는 동작을 구현 도중, C 에서 A 로 돌아가야할 상황에서 goto 를 활용하고 이를 무분별하게 사용함으로 인해 코드가 지저분해진다.

그래서 등장한 것이 절차적(구조적) 프로그래밍이다.

2) 절차적(구조적) 프로그래밍

데이터와 함수가 논리적으로 묶여있을 수 없는 구조이므로, 동작이 추상적이다.

따라서, 이를 묶기 위한 패러다임으로 '객체 지향 프로그래밍' 이 등장

참고: POP (Procedural-Oriented Programming, 절차 지향 프로그래밍)

3. 객체 지향 언어의 특징

1) 캡슐화 (Encapsulation)

(1) 정의

  • 데이터(변수, 데이터 구조)와 함수(데이터를 다루는 방법들)를 하나의 클래스로 묶어서, 외부에서 쉽게 접근하지 못하도록 은닉하는 것
    • 객체의 연산을 통해서만 접근이 가능하게 하는 것

(2) 필요성

  • private으로 선언한 데이터는 자기 자신을 통해서만 접근 가능

    • 정보 은닉 : 접근제어자(setter, getter)를 통해, 데이터의 세부 내용이 객체 은닉된다.
      • 중요한 데이터를 쉽게 바꾸지 못하게 한다.
        → 데이터 변경으로 인한 에러 ↓
      • 객체들 간에 데이터를 자세히 알 필요가 없으므로, 코드가 단순해짐
      • 외부 객체의 직접 접근을 막아서, 프로젝트 확장 시 오류를 최소화
      • 각 객체간의 수정이 다른 객체에 미치는 영향을 최소화
      • 정보 은닉화를 통해 높은 응집도, 낮은 결합도를 유지할 수 있도록 설계 가능
      • 데이터가 변경되어도, 다른 객체에 영향을 주지 않아 독립성이 좋음
      • 처리된 결과를 사용하므로, 이식성이 좋음
  • 객체와 함수의 재사용이 쉽다.

    • 객체를 모듈화 할 수 있어, 새로운 시스템의 구성에 하나의 모듈처럼 사용이 가능
    • 변수와 메소드(속성과 기능을 정의)를 클래스(캡슐)에 넣어서 분류하기 때문

    참고: 응집도(Cohesion), 결합도(Coupling)

2) 추상화 (Abstraction)

(1) 정의

  • 아우디, 니싼, 볼보는 모두 '자동차'에 해당된다.
    자동차라는 추상화 집합을 만들어두고, 자동차들이 가진 공통적인 특징들을 만들어서 활용한다.

  • 여러 종류의 자동차들이 공통적으로 가지고 있는 가속, 브레이크, 속도와 같은 것들을 모아
    자동차라는 객체를 모델링한다.

  • 객체에서 공통된 속성과 행위를 추출 하는 것

  • 공통의 속성과 행위를 찾아서, 타입을 정의하는 과정

  • 불필요한/복잡한 정보는 숨기고 중요한 정보만을 표현함으로써, 프로그램을 간단하게 만드는 것

(2) 필요성

  • '현대'와 같은 다른 자동차 브랜드가 추가될 수도 있다.
    이때 추상화로 '자동차'를 구현 해놓으면, 다른 곳의 코드를 수정할 필요 없이 추가로 만들 부분만 새로 생성해주면 된다.
  • 시스템 구조를 시각적으로 표현 가능
  • 완전한 시스템이 구축되지 않더라도, 개략적으로 모델을 만들어 테스트 & 추가 조작 가능

3) 상속성 (Inheritance)

(1) 정의

  • 부모 클래스에 정의된 모든 것(속성(필드)과 행위(메서드))을 자식 클래스가 물려받는다.

    • 부모 객체가 가진 필드와 메서드를 자식 객체가 사용 가능해진다
    • 즉, 재정의 할 필요성 X
  • 부모 객체에 있는 것만 수정하면, 자식 객체 전부 반영이 된다. (예시 : MachineSystem 에서 bluePrint: BluePrint)

(2) 필요성

  • 자식클래스에서 새로운 함수 추가 or 부모 클래스의 함수 재정의(Overriding)해 사용할 수 있다.
    → 새로운 클래스(자식)가 기존의 클래스(부모)의 데이터와 연산을 이용할 수 있다.

  • 코드 작성이 간결

  • 최소한의 상속 규칙만 지키면 되므로, 프로젝트의 확장 시 오류 최소화

  • 클래스의 재사용 쉬움

(3) 단점

  • 상위 클래스의 변경이 어려워짐

  • 불필요한 클래스 증가 할 수 있다.

  • 상속 자체가 잘못 된 경우도 발생할 수 있다.

참고: 상속

4) 다형성 (Polymorphism)

(1) 정의

  • Car 클래스를 토대로 A자동차 객체와 B자동차 객체를 만들었을 때,
    이 두 객체의 경적 소리가 다르다면 ‘경적을 울리다’라는 행위(horn(); 메서드의 구현)를 다르게 재정의 하여 사용 가능

  • 여러 가지 형태를 가질 수 있는 능력

  • 서로 다른 클래스의 객체가 같은 동작 수행 명령을 받았을 때, 각자의 특성에 맞는 방식으로 동작하는 것

  • 하나의 변수명, 함수명이 상황에 따라 다른 의미로 해석 될 수 있는 것

    • 하나의 클래스 내부에 같은 이름의 행위를 여러개 정의 (오버로딩)
    • 상위 클래스의 행위를 하위 클래스에서 재정의 (오버라이딩)
      참고: 오버로딩, 오버라이딩
  • 어떠한 요소에 여러 개념을 넣어놓는 것

    예시
    '비싼 자동차'라는 개념을 일반화(상속)하여 람보르기니, 페라리 등의 객체를 만들었다.
    '비싼 자동차'라는 클래스의 '배기음 재생' 이라는 메소드를 실행했을 때,
    자식 클래스들이 각기 다른 배기음을 내뿜는다.(다형성)

(2) 필요성

  • 불필요하게 너무 많은 함수명을 만들어서 가독성이 떨어지는 문제를 해결할 수 있다.

(3) 다형성 & 상속

  • 다형성 + 상속은 시너지가 엄청나다.
    다형성 구현을 통해 코드를 간결하게 해주고, 유연함을 갖추게 해준다.
    구체적으로 현재 어떤 클래스 객체가 참조되는지는 무관하게 헐렁하게 프로그래밍하는 것이 가능하다

  • 부모 클래스 타입의 참조변수로, 자식 클래스의 인스턴스를 참조할 수 있도록 한다.

    • 서로 상속 관계인 경우에

예시 1

// 기존의 방식
Tv t = new Tv();				// Tv 인스턴스를 다루기 위해서, Tv타입의 참조변수를 사용
SmartTv s = new SmartTv();		// SmartTv 인스턴스를 다루기 위해서, SmartTv타입의 참조변수를 사용

// 상속에서는 이렇게도 가능하다
Tv t = new SmartTv();

예시 2 : 기존의 참조 VS 상속에서의 참조

// 기존의 참조
SmartTv s = new SmartTv();

// 상속에서의 참조
Tv t = new SmartTv();
SmartTv s = new Tv();		// 에러
  • 상속에서의 참조
    • 참조변수 t는 SmartTv 인스턴스의 모든 멤버를 사용할 수는 X
    • 이런 그림을 생각하면 된다

(4) 다형성 & 매개변수

기존 코드

Product 부모 클래스

class Product {
	int price;
    int point;
}

TV 자식 클래스

class Tv extends Product {
}

Computer 자식 클래스

class Computer extends Product {
}

Buyer 클래스

class Buyer {
	int money = 1000;
    int point = 0;
    
    void buy (Tv t) {		// 구입할 제품을 매개변수로 받는다 -> Tv를 구매하기 위해 매개변수의 타입을 Tv로
    	money = money - t.price;
        ...
    }
    
    void buy (Computer c) {			// Computer를 구매하기 위해
    	money = money - c.price;
        ...
    }
}
  • 이 경우, 제품의 종류가 늘어날 때마다 새로운 buy()메서드를 추가해야만 한다

다형성 & 매개변수

Buyer 클래스

class Buyer {
	int money = 1000;
    int point = 0;
    
    void buy (Product p) {		// 구입할 제품을 매개변수로 받는다 -> Tv를 구매하기 위해 매개변수의 타입을 Tv로
    	money = money - p.price;
        ...
    }
}
  • 메서드의 매개변수로 Product 부모 클래스를 상속받기만 하면, 제품마다 메서드를 추가하지 않아도 된다

(5) 다형성 & 인터페이스

  • 인터페이스 타입의 참조변수로, 이를 구현한 클래스의 인스턴스를 참조할 수 있음

  • 인터페이스 타입으로 형변환 가능

  • 메서드의 매개변수의 타입으로도 인터페이스가 사용될 수 있음

    // Fightable 인터페이스를 구현한 Fighter 클래스
    class Fighter extends Unit implements Fightable {
        public void mov(int x, int y) {
        }
    
        public void attack(Fightable f) {		// attack의 매개변수로 Fighter 인스턴스를 넘겨줄 것이다. attck(new Fighter()) 과 같은 의미
        }
    }
  • 리턴 타입이 인터페이스

    • 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 의미
      Fighter method() {
          Fighter f = new Fighter();
          return f;				//  method()의 리턴 타입은 Fighter 인터페이스이므로, Fighter 인터페이스를 구현한 Fighter클래스의 인스턴스 주소를 반환 
          // return new Fighter(); 로 묶을 수 있다
      }

(6) 다형성 vs 제네릭

참고: 제네릭 (Generic, 제네릭스)

  • 제네릭 : <타입> or List<T>(모든 타입 다 받음) --> 우선순위! (실제로 더 多 사용)

  • 다형성 : 상속과 연결지어서

(7) 다형성 & 제네릭

List<TV> tvList = new ArrayList<TV>();		// OK : ArrayList가 List구현
List<TV> tvList = new LinkedList<TV>();		// OK : LinkedList가 List구현

제네릭 클래스간의 다형성은 성립됨

4. 장단점

장점

  1. 클래스 단위로 모듈화시켜서 개발하므로, 업무 분담이 편리하고 대규모 소프트웨어 개발에 적합

  2. 클래스 단위로 수정 가능하므로, 유지 보수가 편리

  3. 클래스 재사용 or 상속을 통한 확장으로, 코드 재사용이 용이

단점

  1. (상대적으로) 처리속도 느리다.

  2. 객체의 수 多 면, 용량 大

  3. 설계 시, 시간 多 노력 多 필요

5. 객체 지향 5대 원칙 (SOLID)

참고: 객체 지향 5대 원칙 (SOLID)


참고: OOP (객체지향), AOP(관점지향)
참고: 객체지향 프로그래밍이란?
참고: 객체지향 프로그래밍 제대로 이해하기
참고: Generics 02 제네릭스 용어, 제네릭 타입과 다형성

profile
개발자로 거듭나기!

0개의 댓글