객체 지향은 4가지의 특성을 가지고 있다.
- 추상화 (Abstraction)
- 캡슐화 (Encapsulation)
- 상속 (Inheritance)
- 다형성 (Polymorphism)
이번엔 추상화에 대해서 알아보자. (뭔가 순서가 뒤죽박죽이지만 상관은 없으므로 그냥 넘어가자)
추상화 (Abstraction) 란?
조금 추상적인 얘기를 하자면, 당신이 Animal 클래스를 만든다고 하자. 이 세상에는 여러가지 동물이 존재한다. 개, 고양이, 쥐, 소 등등... 보통 살아 움직이는 존재를 동물이라 칭하게 된다. 만약 당신이 "강아지가 뭐야?" 라는 질문을 받는다고 생각해보라.
이 질문을 받은 당신은 강아지에 대해 여러가지 설명을 시작할 것이다. 네 발로 걷고, 멍멍 하며 짖고, 뭐 대충 그런 동물을 강아지라 부른다고 설명을 할 것이다. 좀 애매하긴 하지만 나름대로 설명은 된다.
고양이도 마찬가지로 설명할 수 있다. 역시 네 발로 걷고, 야옹하고 울며, 뛰기를 잘하는 동물이다.
위에서 강아지와 고양이는 동물이라고 했으므로 Dog 클래스와 Cat 클래스는 위의 Animal 클래스를 상속받는다. 따라서 animal 클래스에 '발의 개수' 나 '울음소리' 메서드를 만들어두면, 각 동물에서 해당 메서드를 상속받아서 사용할 수 있게 된다. 아래와 같이 말이다.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Animal : MonoBehaviour { public void bark() { } public void legCount() { } } public class Cat : Animal { private void Start() { bark(); legCount(); } } public class Dog : Animal { private void Start() { bark(); legCount(); } }
그런데 위에서 봤지만, 이런 특성은 동물들마다 전부 달라진다. 개는 멍멍, 고양이는 야옹, 소는 음메... 이런 정보를 Animal 클래스에서 선언해준다고 해보자. 아래와 같이 구현할 수 있을 것이다.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Animal : MonoBehaviour { public void bark(string animalType) { if(animalType == "cat") { Debug.Log("야용"); } else if(animalType == "dog") { Debug.Log("멍멍"); } } public void legCount(string animalType) { if (animalType == "cat") { Debug.Log("4개"); } else if (animalType == "dog") { Debug.Log("4개"); } } } public class Cat : Animal { string animalType = "cat"; private void Start() { bark(animalType); legCount(animalType); } } public class Dog : Animal { string animalType = "dog"; private void Start() { bark(animalType); legCount(animalType); } }
틀리진 않는다. 그저 상속 받은 클래스의 타입을 체크해서 해당 동물의 특성을 가져오게 만들 뿐이다. 그런데 이런 방법이 과연 객체 지향이 추구하는 방향인가라고 한다면, 당연히 아니다. 이번엔 다음 코드를 보자.
using System.Collections; using System.Collections.Generic; using UnityEngine; public abstract class Animal : MonoBehaviour { public abstract void bark(); public abstract void legCount(); } public class Cat : Animal { public override void bark() { Debug.Log("야옹"); //throw new System.NotImplementedException(); } public override void legCount() { Debug.Log("4개"); //throw new System.NotImplementedException(); } private void Start() { bark(); legCount(); } } public class Dog : Animal { public override void bark() { Debug.Log("멍멍"); //throw new System.NotImplementedException(); } public override void legCount() { Debug.Log("4개"); //throw new System.NotImplementedException(); } private void Start() { bark(); legCount(); } }
코드를 분석하면서 추상화에 대해 좀 더 알아보자.
- abstract은 추상화 클래스 & 메서드를 정의하는 것이다. 이 추상화 메서드는 내용을 정의하지 않고, 상속 받은 자식 클래스에서 정의하게 된다. 추상화 클래스는 추상화 메서드를 가지고 있을 수도 있고, 없을 수도 있다. 추상화 클래스라고 반드시 추상화 메서드를 가지는 것은 아니다.
- 추상화 클래스는 객체 생성을 할 수 없다. 이유가 와닿지 않을 수도 있는데, 상식적인 부분에서 보통 개 1마리, 개 2마리 이렇게 세지, 동물 1마리, 동물 2마리와 같이 세지는 않는 걸 생각한다면 이해하는데 조금 도움이 되지 않을까... 좀 더 구체적으로, 추상 클래스에 있는 추상 메서드들은 내용이 정의되지 않는다. 대신 상속 받은 클래스에서 정의하게 되는데, 즉 추상화 클래스는 어떤 '존재'에 대한 특성을 설명하고 정리하기 위한 이름 그대로 '추상적인 클래스'이다.
- 상속 받은 클래스에서 부모 추상화 클래스의 내용을 전부 정의하지 않으면 해당 클래스 또한 추상화 클래스가 된다. 즉, 해당 클래스의 하위 클래스를 만들어 조상 클래스의 내용을 정의해줘야 한다. 2번과 같이 생각해서, 추상화 클래스는 객체를 생성하지 못하지만, 하위 클래스는 (추상화 클래스가 아니라면) 객체를 생성할 수 있다. 개 1마리, 개 2마리 처럼 말이다.
+ 참고로, unity & visual studio 기준 하위 클래스에서 부모 추상화 클래스의 메서드를 override를 할 때
throw new System.NotImplementedException(); 이 추가 되는데, 단순히 구현되지 않은 메서드에 대한 예외 처리이다. 지워주고 메서드를 구현해주면 되겠다.
+ 자바를 사용해봤다면 알겠지만, 자바에서는 메서드 위에 @override로 오버라이딩 해주는데, C#에서는 public override ... 로 메서드를 오버라이딩 한다. C#이 자바를 겨냥한 언어다 보니 비슷한 점이 많긴 한데, 알아두고 넘어가자. (어차피 까먹으면 구글링해서 찾으면 되니까..)
첫번째에서 Animal 클래스에 모든 경우를 선언하는 것 보다, 추상화를 이용해서 해당 클래스의 특성을 각각 선언한 것이 보기 훨씬 좋고 깔끔하다. 따라서 추상화를 잘 사용하면 좀 더 직관적이고 깔끔한 코딩을 할 수 있을 것이다.
첫번째에서 Animal 클래스에 모든 경우를 선언하는 것 보다, 추상화를 이용해서 해당 클래스의 특성을 각각 선언한 것이 보기 훨씬 좋고 깔끔하다. 따라서 추상화를 잘 사용하면 좀 더 직관적이고 깔끔한 코딩을 할 수 있을 것이다.
댓글
댓글 쓰기