OOP(Object Oriented Programming)

kirin.log·2021년 5월 29일
0

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

  • 객체의 관점에서 프로그래밍을 하는 것. (= 객체를 기준으로 코드를 나누어 구현)
  • 객체들의 유기적인 관계를 통해서 프로세스가 진행.
    (cf. C언어 = 절차 지향적 프로그래밍, 프로세스가 함수 단위로 순서대로 진행되는 것)

즉, 애플리케이션을 구성하는 요소들을 객체로 바라보고, 객체들을 유기적으로 연결하여 프로그래밍 하는 것을 말한다.

👉 Class & Instance

클래스는 설계도, 인스턴스는 직접 일을 하는 구현체.

클래스는 일종의 설계도이고 이것을 사용하여 우리가 사용할 수 있는 실제 물건으로 만들어내는 행위가 반드시 필요하다. 그리고 객체클래스를 사용하여 생성한 실제 물건이다.

소나타 ➡ 중형 세단 ➡ 세단 ➡ 자동차 ➡ 이동수단
강동원 ➡ 남자 ➡ 인간 ➡ 영장류 ➡ 포유류 ➡ 동물
오버워치 ➡ 블리자드가 만든 FPS 게임 ➡ FPS 게임 ➡ 게임

이렇게 상위 개념을 추적해 나가면서 설계하는 것이 OOP의 기초.
상위 개념을 만들어나가는 행위 자체를 추상화라고 한다.

여기서, "소나타"라는 클래스는 어떠한 실체가 있는 것이 아니다. 단지 제품의 이름일 뿐이다.
즉, 객체가 아니며 실체가 없다.
"소나타"에 핸들, 바퀴, 네비 등이 장착되어 있을 것이고 이러한 특성(속성)들을 기반으로 실제 "소나타"를 찍어내면, 그것이 실체가 된다.
이렇게 생산된 "소나타"들을 객체라고 할 수 있다.

결국 객체 지향이라는 말의 의미는, 이렇게 클래스를 사용하여 추상적인 개념들을 정의(=추상화)하고,
그 클래스를 사용하여 실제로 사용할 객체(=인스턴스)를 만들어냄으로써 현실 세계의 모든 것을 표현할 수 있다는 것에서 출발하는 것이다.


🪐 OOP의 특징

👉 캡슐화 (Encapsulation)

  • 캡슐화란, 하나의 객체에 대해 그 객체가 특정한 목적을 위한 필요한 변수나 메소드를 하나로 묶는 것을 의미한다.
  • 어떠한 클래스를 사용할 때 내부 동작이 어떻게 돌아가는지 모르더라도 사용법만 알면 쓸 수 있도록 클래스 내부를 감추는 기법

❗ 따라서 클래스를 우리가 만들 때, 훗날 이 클래스에서 만들어진 객체가 특정한 목적을 잘 수행할 수 있도록 사용해야할 변수와 그 변수를 가지고 특정한 액션(=메서드)를 관련성 있게 클래스에 구성 해야 한다.

또한 클래스를 캡슐화 함으로써 클래스를 사용하는 쪽에서는 머리 아프게 해당 클래스의 내부 로직을 파악할 필요가 없어진다. 클래스 내에서 사용되는 변수나 메소드를 원하는 대로 감출 수 있기 때문에 필요 이상의 변수나 메소드가 클래스 외부로 노출되는 것을 방어하여 보안도 챙길 수 있다.

이렇게 클래스 내부의 데이터를 감추는 것을 정보 은닉(Information Hiding)이라고 한다.

👉 정보은닉
캡슐화를 하는 중요한 목적은 바로 정보은닉이다. 유저 정보를 가지고 있는 User라는 객체에서 유저의 정보가 public으로 선언되어 있다면, 누구든 접근해서 유저 정보를 변경할 수 있다.
그렇기 때문에 private로 해서 데이터를 보호해서 접근을 제한해야한다.

이렇게 보호된 변수는 gettersetter 등의 메서드를 통해서만 간접적으로 접근이 가능하도록 하는 것이 캡슐화의 중요한 목적(=정보은닉)이다.(setter도 아무생각 없이 만들면 안된다.)

❗ 캡슐화와 정보은닉은 동일한 개념이 아니다.
캡슐화를 하면 불필요한 정보를 감출 수 있기 때문에, 정보은닉을 할 수 있다는 특징이 있다는 것이다.
예를 들어, 리모콘을 사용하는데 리모콘 내부 회로(private으로 정의된 속성)를 알 필요가 있느냐는 것이다. 사용자의 입장에서는 리모콘의 조작 기능, 즉 public으로 정의된 속성만 알면 된다는 것이다.


👉 추상화 (Abstraction)
추상화는 목적과 관련이 없는 부분을 제거하여 필요한 부분만을 표현하기 위한 개념이다.

객체들은 실제 모습(실제 데이터 메모리 할당)이지만, 클래스는 객체들이 어떤 특징들이 있어야 한다고 정의( ➡ 이 과정에서 객체들의 공통적인 요소나 특징을 추출)하는 추상화된 개념이다.

다시 말하자면, 추상화객체들의 공통된 특징을 파악해 정의해 놓은 설계 기법이라고 할 수 있다.

예를들어, 벤츠, 아우디, 티코 등등 여러 종류의 자동차가 있다. 이것을 다 클래스화하고 변수와 메서드 등을 개별적으로 만드는 것은 무모한 짓이다. (즉, 확장성 때문에 추상화할 필요가 있다.)

따라서 방금 나열한 자동차들의 공통적인 요소나 특징을 추출하는 과정인 추상화를 거쳐 요소를 끄집어 내면 바퀴, 핸들, 차문, 유리창 등 필수적인 부품이 있다.

바퀴는 굴러가고, 핸들은 돌아가고 차문은 열려야한다 공통적인 행동 즉 어떤 차든 필수적으로 필요한 메서드가 추출된다.

이러한 과정이 추상화하는 과정이다.


👉 상속 (Inheritance)

  • 상속이란 기존 상위클래스에 근거하여 새롭게 클래스와 행위를 정의할 수 있게 도와주는 개념이다. OOP에서 이를 부모 클래스, 자식클래스라고 표현한다.
  • 하위 클래스들은 상위 클래스가 가지고 있는 모든 속성을 그대로 물려받는데, 이 과정을 상속(Inheritance)이라고 한다.
  • 상위(기존) 클래스에 기능을 가져와 재사용할 수 있으면서도 동시에 새롭게 만든 클래스에 새로운 기능을 추가할 수 있게 만들어 준다.

예를들어, 고양이 클래스강아지 클래스가 있다고 했을때, 고양이와 강아지는 모두 포유류에 해당한다.
포유류는 여러 속성들이 정의되어 있는데 고양이와 강아지는 포유류의 이런 속성들을 갖고 있다.
하지만 아주 많은 속성이 다르기 때문에 좀 더 디테일하게 고양이, 강아지로 종을 나눈것이다.

즉. 포유류라는 클래스고양이와 강아지 클래스속성들을 물려준다.
이것을 상속이라고 하고, 포유류와 고양이, 포유류와 강아지 클래스는 상속 관계에 있다고 한다.

❗ 상속이 필요한 이유
상속이 필요한 이유는 코드의 중복을 없애기 위함이다. (✨ 코드의 재사용)
코드의 중복이 많아지면 개발 단계에서도 피곤하지만, 유지 보수에서도 많은 비용이 들게 된다.

OOP에서는 상속을 통해 코드의 중복 문제를 일부 해결할 수 있다.
포유류 클래스에 여러 속성들을 정의해 놓고 포유류에 해당하는 종, 예를 들면 강아지 클래스가 필요한 경우 포유류 클래스와 상속 관계를 맺는다.
상속 관계를 맺으면 자식 객체를 생성할 때 부모 클래스의 속성들을 자동으로 물려 받기 때문에 자식 클래스에서 또 정의할 필요가 없다.
이것이 상속이 필요한 이유이다.

OOP라는 프로그래밍 언어에서 상속은 extends라는 예약어로 표현되는데, 상위 개념 입장에서 보면 자신의 속성들이 하위 개념으로 넘어가면서 확장(extends)되는 것이므로 이렇게 표현한다.

class 자동차 {
    tire_number = 2;
    color = "red";
}
 
class 소나타 extends 자동차 {  // extends 예약어를 사용하여 '자동차'클래스 상속받음
    serial_number = 1234;
}
// '소나타' 클래스에는 tire_number 와 color 속성이 선언되지는 않았지만, 
// 부모 클래스인 '자동차'에서 속성을 그대로 물려받은 것이라고 볼 수 있다.
 
class 벤츠 extends 자동차 {  // extends 예약어를 사용하여 '자동차'클래스 상속받음
    serial_number = 1234;
}

즉, 추상화가 잘된 클래스를 하나만 만들어놓는다면 그와 비슷한 속성이 필요한 다른 클래스를 생성할 때 그대로 재사용할 수 있다

그리고 만약 자동차 시스템 전체에 걸친 변경사항이 생겼을 때도 '소나타', '벤츠'와 같은 클래스는 건드릴 필요없이 '자동차' 클래스 하나만 고치면 이 클래스를 상속받은 모든 하위 클래스에도 자동으로 적용되므로 개발 기간도 단축시킬 수 있고 휴먼 에러가 발생할 확률도 줄일 수 있다.

👉 다형성(Polymorphism)
다형성은 상속을 통해 기능을 확장하거나 변경하는 것을 가능하게 해준다. 즉, 다형성은 형태가 같은데 다른 기능을 하는 것을 의미한다(같은 동작이지만 다른 결과물이 나올때 다형이라고 생각하면 된다.).
이를 통해 코드의 재사용, 코드 길이 감소가 되어 유지보수가 용이하도록 도와준다.

예를 들면, 고양이 클래스에는 "울음"이라는 속성이 정의되어 있다고 하면,
사자는 고양이 과이기 때문에 사자 클래스는 고양이 클래스를 상속 받는다고 하면, 사자 클래스에도 "울음"이라는 속성이 자동으로 추가된다. 이것을 상속이라고 한다.

그런데 고양이와 사자의 울음소리는 다르다. 같은 "울음" 속성임에도 실제 울음소리는 다르죠.
이런것이 다형성이라고 말할 수 있다.

🎄 Overriding & Overloading
OOP에서 다형성의 개념을 녹여내는 방법은 두가지인데, 바로 오버라이딩(Overriding)과 오버로딩(Overloading)이다.

오버라이딩

  • 부모 클래스에서 상속받은 자식 클래스에서 부모클래스에서 만들어진 메서드를 자식 클래스에서 자신의 입맛대로 다시 재정의해서 사용하는 것을 말한다.

오버로딩

  • 같은 이름의 메서드를 사용하지만 메서드마다 다른 용도로 사용되며 그 결과물도 다르게 구현할 수 있게 만드는 개념
  • 오버로딩이 가능하려면 메서드끼리 이름은 같지만 매개변수의 갯수나 데이터 타입이 다르면 오버로딩이 적용
  • 메서드 이름이 같아도 문법 에러 ❌

🎄 오버로딩 오버라이딩 예시

//Overriding

public class Employee{
 
  public String name;
  public int age;
 
  public void print(){
    System.out.println("사원의 이름 = "+this.name+", 나이 = "+this.age);
  }
}

//Emplyee 상속
public class Manager extends Employee{
 
  String jobOfManage;
 
//overriding  Employee의 pirnt() 메소드 오버라이딩

public void print(){
    System.out.println("사원의 이름 = "+this.name+", 나이 = "+this.age);
    System.out.println("관리자 "+this.name+"은 "+this.jobOfManage+" 담당입니다.");
  }
}



//Overloading
public class OverloadingTest{
 
  public void test(){
    System.out.println("사용자 없음");
  }
 
  public void test(String name){
    System.out.println("사용자 이름 = " +name);
  }
 
  public void test(String name, int money){
    System.out.println("사용자 이름 = "+name+" , 사용료 = "+money);
  }
}

즉, 같은 행위를 하지만 용도와 목적에 부함하여 다양한 기능 수행과 처리, 결과를 낳을 수 있다.

다형성을 사용하면 좋은 점
같은 이름의 속성을 유지함으로서, 속성을 사용하기 위한 인터페이스를 유지하고, 메서드 이름을 낭비하지 않는다는 것이다.
예를 들어, 고양이와 사자의 울음소리를 호출하기 위해서 각 객체에서 roar() 메서드를 호출하면 된다.
roraCat(), roarLion()으로 각각을 정의할 필요가 없다는 것이다.
API가 많아질수록 복잡성은 증가하기 때문에 다형서은 유용하다.

profile
boma91@gmail.com

0개의 댓글