OOP의 기본 개념

OOP란 무엇인가?

OOP(Object Oriendted Programming), 영어 그대로 해석하면 객체 지향 프로그래밍이라는 뜻이다.

객체 지향 프로그래밍을 이해하기 위한 기본 용어(자바 기준)인 객체, 클래스, 인스턴스를 설명하자면 다음과 같다.

  • 객체란 현실 세계의 사물을 개념적으로 추상화한 것이라고 할 수 있다.
  • 클래스란 그 객체가 기능하기 위한 프로퍼티(상태), 메소드(행동)을 모아 만든 설계도이다.
  • 인스턴스란 클래스라는 설계도를 기반으로 메모리에 생성한 하나 하나의 객체라고 할 수 있다.


절차 지향 프로그래밍은 함수 단위의 순서대로 진행된다는 점에서 컴퓨터의 관점에 가까운 프로그래밍 방법이다.

반면, 객체 지향 프로그래밍은 사물을 객체로 추상화하고, 각 객체들이 상호작용하며 실행된다는 점에서 보다 사람의 관점에 가까운 프로그래밍 방법이라고 할 수 있다.


이 포스팅에서는 객체 지향 프로그래밍 특징으로 자주 언급되는 캡슐화, 상속, 다형성에 대해서만 다루고,

자세한 내용은 참고 글의 링크를 남기는 것으로 대체하겠다.

(자세한 내용: https://asfirstalways.tistory.com/177)


캡슐화

  • 객체가 기능하기 위해서 필요한 메소드와 변수들을 하나의 클래스 안에 묶는 것을 말한다.
  • 예를 들어, 자판기에서 “동전을 받아 커피를 준다”는 기능이 필요하다고 생각해보자
    • 동작하기 위한 간단한 메소드는 다음과 같이 생각할 수 있다.
      1. 받은 동전이 충분한지 확인한다.
      2. 컵을 내린다.
      3. 커피가루를 컵에 붓는다.
      4. 뜨거운 물을 컵에 붓는다.
    • 여기서 1,2,3,4 메소드는 모두 자판기 클래스안에 구현하는 것을 캡슐화라고 할 수 있다.


  • 은닉성

    흔히 은닉성과 캡슐화를 같은 말이라고 착각하는 경우가 많은데 이 둘은 동일한 개념이 아니다.

    정보의 은닉성은 캡슐화를 잘 지키면 따라오는 장점이라고 할 수 있다.


    위의 자판기의 경우, “동전을 받아 커피를 준다”는 기능에 필요한 메소드들이 내부에 구현되어 있고,

    외부에는 “동전을 받아 커피를 준다” 는 메소드만 노출하면 되기 때문에,

    실제 기능에 필요한 내부 메소드들은 외부에 공개하지 않음으로써 정보를 은닉할 수 있다.


    이러한 은닉성을 통해, 외부에서 내부 상태나 로직에 관여하여 오동작하는 것을 방지할 수 있다.


상속

실제 세계에서 상속이 부모의 재산을 물려받는다는 뜻과 같이 이해하면 된다.

상속해주는 클래스를 부모 클래스, 상속받는 클래스를 자식 클래스라고 할 수 있는데,

여기서 자식 클래스는 부모 클래스의 모든 것(변수, 메소드)를 물려받아서 사용할 수 있게 된다.

(엄밀히 말하면 접근제한자가 public, protected로 선언된 모든 것을 사용할 수 있게 된다.)


상속이 필요한 이유

상속이 필요한 이유는 코드의 중복을 없애기 위함이다.

코드의 중복이 많아지면 개발 단계에서도 피곤하지만, 유지 보수에서도 많은 비용이 들게 된다.

그래서 개발을 할때 코드의 중복은 받느시 피해야한다.


OOP에서는 상속을 통해 코드의 중복 문제를 일부 해결할 수 있다.

포유류 클래스에 여러 속성들을 정의해 놓고 포유류에 해당하는 종, 예를 들면 강아지 클래스가 필요한 경우 포유류 클래스와 상속 관계를 맺는다.

상속 관계를 맺으면 자식 객체를 생성할 때 부모 클래스의 속성들을 자동으로 물려 받기 때문에 자식 클래스에서 또 정의할 필요가 없다.

이것이 상속이 필요한 이유이다.

출처: https://velog.io/@hkoo9329/OOPObject-Oriented-Programming-객체-지향-프로그래밍-이란


다형성

다형성의 사전적 정의를 찾아보면 다음과 같다.

동일종(同一種)의 생물이면서도 형태나 성질이 다양성을 보이는 상태. 암수에 의한 크기·형태·색깔 등의 차이와 꿀벌에서의 여왕벌과 일벌 같은 것.

이를 OOP의 개념으로 해석하면 “같은 클래스임에도 형태나 성질이 다양성을 보이는 상태”라고 할 수 있을 것이다.

즉, 같은 클래스의 동일한 메소드임에도 다양한 행위를 할 수 있다는 뜻인데, 이러한 다형성은 상속과 오버라이딩을 통해 이룰 수 있다.


다형성을 이해하기 위해서는 우선 Up CastingOverriding에 대한 이해가 필요하다.

  • Up Casting

    • 자식클래스를 부모클래스로 Casting하는 것을 의미한다.

    • 예를 들어 동물이라는 클래스가 있고, 이를 상속받은 사자라는 클래스가 있다고 하자

      • “동물은 사자다”는 명제는 항상 참일 수 없지만, “사자는 동물이다”는 명제는 항상 참이다.
      • 즉, 자식 클래스는 항상 부모 클래스라고도 할 수 있는 것이다.
    • 간단한 Java 코드는 다음과 같다.

      public class Animal {
      		//변수, 메소드 정의
      		//...
      		public void run(){
      			System.out.println("동물이 달립니다.");
      		}
      }
      public class Rion extends Animal{
        	public void run(){
            System.out.println("사자가 달립니다.")
          }
      }
          
      //메인 함수
      public static void main(String[] args) {
        Animal animal1 = new Rion(); //Up Casting
      }
      
  • Overriding

    • 상속받은 자식 클래스가 부모 클래스의 메소드를 덮어 씌우는 것을 의미한다.

    • 위에서 Animal 클래스의 public void run()라는 메소드가 존재하는데,

      자식클래스에서 동일한 이름의 public void run()을 재정의함으로써 부모클래스의 run 메소드를 덮어씌울 수 있다.

    • 참고: 오버로딩과 오버라이딩 차이


이러한 Up Casting과 Overriding을 통해, 같은 클래스의 동일한 메소드라도 다양한 행동을 할 수 있는 것이다.


위의 Animal과 Rion 클래스를 예로 들자면,

아래와 같이 animal1과 animal2 모두 Animal 변수임에도 다른 결과가 나온다.

//메인 함수
Animal animal1 = new Animal();
Animal animal2 = new Rion();
animal1.run(); // "동물이 달립니다."" 출력
animal2.run(); // "사자가 달립니다."" 출력


이렇게 다형성을 이용하면 자식 클래스가 새로운 메소드를 만들 필요 없이 부모 메소드를 수정함으로써

불필요한 메소드명의 낭비를 줄일 수 있게 된다.