본문 바로가기

방법/객체지향프로그래밍

객체지향프로그래밍 (OOP) - 추상화

추상화(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 클래스는 바뀔 이유가 없다.

 

  오늘은 추상화의 정의와 목적, 예시를 알아보았다.

오늘 내용이 이해가 잘 가지 않았다면 "클래스, "객체", "속성과 메소드", "업 캐스팅"을 검색해보고 다시 보면 좋을 것 같다. 더 깊게 알아보고 싶다면 "상위 형식에 맞춰 프로그래밍", "단일 책임 원칙"을 검색해보면 될 것 같다.