추상화(Abstraction) - "우리 비슷한 점이 많네요~ 일로 오세요~"
정의
추상화가 무엇인지 딱딱하게 정의해보자면, 객체들의 공통 속성과 행위를 추출하는 것이다. 처음 프로그래밍을 접하는 사람은 무슨 소리인지 잘 모를수도 있을 것 같다.
다시 쉽게 말해보면, 공통점을 찾아 이름짓기이다.
왜?
귀가 길고, 초식 동물이면서, 귀엽고, 갈색 털이고, 콧구멍이 큰 동물과 귀가 길고, 초식 동물이면서, 귀엽고, 흰색 털이고, 콧구멍이 작은 동물 등이 있다. 이 친구들을 항상 이렇게 길게 불러야 할까? 털과 콧구멍 말고도 차이점은 많겠지만, 같은 점이 더 많으니 하나의 이름으로 묶어주면 더 편할 것이다.
자, 이제 "귀가 길고 어쩌고 저쩌고"라 부를 필요 없이 토끼라고만 부르면 된다! 이렇게 되면 코드 상에서도 하나의 클래스로 만들어서 한 번에 관리할 수 있다. (여기서 관리는 코드를 바꾼다는 의미이다) 만약, 모두 개별 객체라면 공통된 수정이 생겼을 때 객체 하나하나를 모두 수정해야 했을 것이다.
또 이제 토끼도 있고 원숭이도 있고 고양이도 있다. 이 친구들은 모두 진핵생물 중 종속영양을 하고, 운동성이 있고, 세포호흡을 하며, 유성생식이 가능하며, 배자 발생 시 포배가 생성되는 생물이다.(출처: 위키백과)
이렇게 또 묶어주면, 이 친구들 모두 수정할 점이 생기면 동물이라는 이름 아래에서만 수정하면 끝난다.
위에서 보았듯이, 추상화는 아래와 같은 목적으로 이용된다.
- 코드를 덜 바꾸기 위해
- 불필요한 구현을 제거하기 위해
- 다음 절차를 위해(캡슐화, 상속)
어떻게?
이제 코드로 살펴보자
public class AbstractionAnimals_Bad {
public static void main(String[] args) {
Person person = new Person();
if(person.rabbitPet != null) {
person.feedForRabbit();
}
person.feedForCat();
person.feedForRabbit();
person.feedForMonkey();
}
}
class Person {
Rabbit rabbitPet = null;
Monkey monkeyPet = null;
Cat catPet = null;
void feedForRabbit() {
if(rabbitPet != null) {
rabbitPet.eat();
}
}
void feedForMonkey() {
if(monkeyPet != null) {
monkeyPet.eat();
}
}
void feedForCat() {
if(catPet != null) {
catPet.eat();
}
}
}
class Rabbit {
String description = "긴 귀, 초식동물, 통통함";
void eat() {
System.out.println("냠냠");
}
}
class Monkey {
String description = "긴 팔, 잡식동물, 얼핏 보면 털많은 사람";
void eat() {
System.out.println("쩝쩝");
}
}
class Cat {
String description = "귀여움, 육식동물?, 귀여움";
void eat() {
System.out.println("뇸뇸");
}
}
위 코드는 나쁜 예시이다.
위 코드대로 보면,
- 사람이 반려동물(pet)을 갖는다는 것을 표현할 때 고양이, 개, 토끼 등 어떤 동물을 선택할지 모르기 때문에 일단은 모두 만들어 주어야 한다.
- 그리고, 이에 따라 먹이주는 것도 다 따로따로 구현해줘야 한다.. (null 처리도 다 따로따로)
- 또, 동물이 하나 추가되면 Person 클래스도 바꿔주어야 하는 상황이다(Dog가 추가되면 dogPet 속성을 추가해줘야 하기 때문에).
public class AbstractionAnimals_Good {
public static void main(String[] args) {
Person person = new Person();
person.setPet(new Rabbit());
person.feedForPet();
}
}
class Person {
Animal pet = null;
void setPet(Animal pet) {
this.pet = pet;
}
void feedForPet() {
if(pet != null) {
pet.eat();
}
}
}
interface Animal {
String description = "";
void eat();
}
class Rabbit implements Animal{
String description = "긴 귀, 초식동물, 통통함";
public void eat() {
System.out.println("냠냠");
}
}
class Monkey implements Animal{
String description = "긴 팔, 잡식동물, 얼핏 보면 털많은 사람";
public void eat() {
System.out.println("쩝쩝");
}
}
class Cat implements Animal{
String description = "귀여움, 육식동물?, 귀여움";
public void eat() {
System.out.println("뇸뇸");
}
}
위 코드는 추상화를 적용한 예시이다.
위 코드대로 보면,
- pet은 Animal형이므로 Animal을 구현한 클래스는 모두 들어갈 수 있다.
만약 여러 마리를 구현하고 싶다면, 배열 형태로 넣어주면 될 것이다. - 먹이주는 메서드는 feedForPet() 하나만 있어도 된다.
- 이제, 어떤 동물이 추가되어도 Person 클래스는 바뀔 이유가 없다.
오늘은 추상화의 정의와 목적, 예시를 알아보았다.
오늘 내용이 이해가 잘 가지 않았다면 "클래스, "객체", "속성과 메소드", "업 캐스팅"을 검색해보고 다시 보면 좋을 것 같다. 더 깊게 알아보고 싶다면 "상위 형식에 맞춰 프로그래밍", "단일 책임 원칙"을 검색해보면 될 것 같다.
'방법 > 객체지향프로그래밍' 카테고리의 다른 글
객체지향프로그래밍 (OOP) - 다형성 (1) | 2023.01.18 |
---|---|
객체지향프로그래밍 (OOP) - 구성 (0) | 2022.12.01 |
객체지향프로그래밍 (OOP) - 상속 (0) | 2022.10.22 |
객체지향프로그래밍 (OOP) - 캡슐화 (2) | 2022.09.22 |
객체지향프로그래밍 (OOP) - 서문 (0) | 2022.08.22 |