본문 바로가기
Programming/php

[PHP] 상속 관련 객체 지향 개념들- 추상 클래스(Abstract Class)

by 조창대 2024. 3. 21.
반응형

PHP 공부하면서 객체 지향 기법을 하나씩 접하고 있는데

내가 평소에 코딩할 때 객체 지향 기법들을 활용하지 못하고 있었구나... 를 절실히 깨닫는 중.

 

기본부터 충실히 잡고 가자!

 

이번 포스팅에선 객체 지향 기법 중 하나인 상속과, 상속과 관련된 기능들을 알아보려 한다.

 

 

상속 (Inheritance)


클래스의 상속이란 무엇인가..

상속은 클래스 간의 계층적 관계를 구성하여 코드 재사용성을 높이고, 객체 지향 프로그래밍의 중요한 특징 중 하나인 다형성의 문법적 토대를 마련한다.

쉽게 말해서 부모가 자식을 낳으면 부모 유전자가 자식에게 유전되는 것처럼,

B 클래스가 A 클래스를 상속한다고 하면 B 클래스가 A 클래스의 프로퍼티, 메서드를 상속받아 사용하거나 확장시킬 수 있다.

여기서 '확장'이 상속을 사용하는 주된 이유 중 하나이기도 하고, 오늘 다룰 추상 클래스를 이해하는 데 매우 중요한 개념이다.

부모가 자식을 세 명 낳았다고 가정했을 때 세 명은 각각 다른 인격, 외모, 성향을 가지잖아요?

B, C, D 클래스가 같은 A 클래스를 상속했어도 각각 쓰임새에 맞게 메서드 재정의를 하여 다양하게 쓰기 위해 확장을 한다.

하지만 부모가 자식에게 유전정보를 물려받지 못하는 것처럼, 자식 클래스가 변경된다고 해서 부모 클래스가 변경되진 않는다.

 

아래 클래스 다이어그램 참고~~!!

 

 

 

추상 클래스 (Abstract Class)


 

♥ 개념

추상화는 "공통성과 본질적인 부분만을 모아 추출"하는 것이다. 추상 클래스는 하위 클래스의 공통적인 기능을 모아 만든 클래스라고 할 수 있다. 

하위 클래스로 A클래스 / B클래스 / C클래스가 있다고 칠 때, 추상 클래스는 A클래스 / B클래스 / C클래스 간에 공통적인 메서드를 추출해 만든 클래스이다. 여기서 공통적인 메서드는 "추상 메서드"이며, 추상 클래스는 꼭! 추상 메서드를 가져야 한다.

 

 

내가 이전에 클래스와 객체 지향 프로그래밍을 다룬 글에서 클래스 = 요리 레시피, 객체 = 음식으로 비유해서 설명했었다.

 

>> 아래 참고

https://fhaktj8-18.tistory.com/entry/class-object-oriented-programming

 

클래스(class)와 객체 지향 프로그래밍(object oriented programming) 개념 정리

* 객체 지향 프로그래밍 ( Object Oriented Programming) 프로그래밍을 공부하는 사람이라면, ' 객체 지향'이라는 단어는 질리도록 들어봤을 것이다. 요즘 많이 쓰이는 C++, 자바, 파이썬의 기본 로직이기

fhaktj8-18.tistory.com

 

추상 클래스도 똑같이 비유를 들어보자면,, 떡볶이로 들어보겠다. 요즘 다이어트한다고 떡볶이 많이 못 먹으니까.

 

떡볶이는 한국인 소울 푸드인 만큼 여러 브랜드가 있다.

엽기 떡볶이, 신전 떡볶이, 배떡, 죠스 떡볶이 등등 ..

엽기 떡볶이는 마늘이 많이 들어가고 국물이 걸쭉한 느낌이며 어묵, 떡, 비엔나 소시지, 양배추가 기본 구성이다.

신전 떡볶이는 카레 가루가 들어가고 떡만이 기본 구성으로 나온다.

떡볶이에 들어가는 재료의 용량이나 다양성은 다 다르지만 공통적으로 떡과 고추장을 넣고 끓여야 한다. 왜냐면 떡이랑 고추장이 없으면 그건 떡볶이가 아니니깐!! 이 끓이는 행위가 브랜드 다른 음식들을 "떡볶이" 라는 연관 관계로 묶는 것이다.

 

그럼 "떡을 넣는다", "고추장을 넣는다", "끓인다"는 추상 메서드가 된다.

얼만큼, 어느 타이밍에, 몇 분동안 끓이는 건지에 대해선 브랜드마다 다르겠지만, 이 행위들은 '떡볶이'라는 공통적인 레시피 틀을 상속하며 자기 입맛에 맞게 레시피를 확장시키며 메뉴 개발을 한다.

그래서 떡볶이 레시피를 추상 클래스, 각 브랜드의 떡볶이 레시피를 추상 클래스를 상속받는 실체 클래스라고 할 수 있다.

 

 

언제 쓸까?

그럼 추상 클래스는 어느 용도로 쓰일까?

만약에 떡볶이라는 음식이 이 세상에서나 사람들 기억 속에서 뿅-하고 사라졌다고 하자.

사라진 후에도 여러 브랜드에서 개발에 개발을 거듭하여 떡볶이와 똑같은 음식을  우후죽순 만들어도

이들을 묶어줄 음식 카테고리가 없어서 같은 메뉴라 볼 수 없으며, 이름도 중구난방일 것이다.

엽기 떡탕, 신전 탄수화물 덩어리 .. 메뉴 이름이 이런 식이면 소비자 입장에서도 무슨 음식인지 전혀 감이 안 와서 매번 모험을 해야 할 것이다. 

그리고 브랜드 입장에서도 표준화된 레시피가 없어서 메뉴 개발할 때마다 매번 無에서부터 창조해야 해서 개발팀은 죽어나겠지..

 

그래서 '떡볶이 레시피'라는 추상 클래스에서 공통된 재료, 조리법을 명시해두면 이를 기반으로 필요에 맞게 확장하기 편해진다.

 

개발할 때에도 마찬가지이다.특히 혼자가 아닌 동료 여럿과 개발할 땐 추상 클래스가 다음과 같은 역할을 한다.

 

1. 필드와 메서드명 통일

- 여러 개발자가 같은 프로그램 개발 시에 필드, 메서드명 통일하여 한 눈에 알아보기 위함.

 

2. 실체 기능 구현 시, 유지보수 시 시간 절약

- 정해진 규격이 있으니, 기능 구현할 때 뼈대부터 잡을 필요가 없어 시간 절약됨.

 

3. 규격에 맞는 클래스 구현에 강제성 부여

- 추상 클래스 내 추상 메서드는 자식 클래스가 상속받을 시에 꼭 오버라이딩하여야 함.

 

 

예제 코드


 

그럼 개념은 어느 정도 정립되었으니 php 예제 코드로 살펴보자!

<?php
/** Class Abstraction */
abstract class tteokbokki
{
    protected $ingredient_1 = 'riceCake';
    protected $ingredient_2 = 'chili pepper paste';
    protected $ingredient_3 = 'sugar';
    public function prep()
    {
        echo '재료 준비';
    }
    abstract public function boil();
}

 

추상 클래스나 메서드 만드는 키워드는 'abstract' 이다.

클래스나 메서드 정의문 맨 앞에 abstract를 추가해주기만 하면 된다.

추상 클래스는 인스턴스를 가질 수 없다. 그래서 인스턴스 생성하려고 하면 이렇게 오류가 뜬다. 꼭 상속을 해서 사용해야 한다.

 

abstract class A {  }

abstract public function AA()

 

 

위 예제에선 떡볶이 추상 클래스, 이를 상속받는 brandA, brandB 실체 클래스를 구현했다.추상 클래스 내부를 보면 재료 데이터를 담은 ingredient_1 /  ingredient_2 /  ingredient_3 필드와,prep() 재료 준비 일반 메서드와, boil() 추상 메서드가 있다.

추상 클래스는 추상 메서드 뿐만 아니라 일반 변수, 일반 메서드를 추가할 수 있다. 실체 클래스는 추상 클래스의 일반 변수와 일반 메서드는 꼭 구현하지 않아도 된다.

그리고 추상 메서드는 동작부를 쓰지 않고 남겨둔다.

그래서 tteokbokki 추상 클래스를 상속받는 클래스들은 모두 boil() 을 오버라이딩해야 한다.

 

<?php
class brandA extends tteokbokki
{
    public function boil()
    {
        printf('%s는 200g, %s는 3스푼 넣고 끓입니다.', $this->ingredient_1, $this->ingredient_2);
    }
}

 

추상 클래스도 일반 클래스와 마찬가지로 'extends' 키워드로 상속받을 수 있다.

또 추상 클래스의 필드(멤버 변수)가 public, protected일 경우 사용 가능하다. protected일 땐 $this-> 로 접근하기!

 

tteokbokki 를 상속받은 brandA 클래스는 추상 메서드인 boil() 을 꼭! 오버라이딩해야 한다.

brandA  클래스 쓰임에 맞게 오버라이딩하면 된다. brandA만의 레시피로 boil() 을 오버라이딩해서 기존 떡볶이 레시피를 확장하는 것을 볼 수 있다.

 

 

<?php
class brandB extends tteokbokki
{
    public function boil()
    {
        printf('%s는 200g, %s는 2스푼 넣고 끓입니다.', $this->ingredient_1, $this->ingredient_3);
    }
}

 

brandB 클래스에서도 개성에 맞게 boil()을 오버라이딩하고 있다.

이렇게 상속 / 오버라이딩하며 기존의 클래스 틀은 유지한 채, 쓰임에 맞게 확장할 수 있는거다.

 

 

합치면 이렇게 된다.

<?php
/** Class Abstraction */
abstract class tteokbokki
{
    protected $ingredient_1 = 'riceCake';
    protected $ingredient_2 = 'chili pepper paste';
    protected $ingredient_3 = 'sugar';
    public function prep()
    {
        echo '재료 준비';
    }
    abstract public function boil();
}

class brandA extends tteokbokki
{
    public function boil()
    {
        printf('%s는 200g, %s는 2스푼 넣고 끓입니다.', $this->ingredient_1, $this->ingredient_2);
    }
}

class brandB extends tteokbokki
{
    public function boil()
    {
        printf('%s는 200g, %s는 2스푼 넣고 끓입니다.', $this->ingredient_1, $this->ingredient_3);
    }
}

$a = new brandA();
var_dump($a->boil());

$b = new brandB();
var_dump($b->boil());

-------------------------- 실행 결과 -------------------------- 

riceCake는 200g, chili pepper paste는 2스푼 넣고 끓입니다.NULL

riceCake는 200g, sugar는 2스푼 넣고 끓입니다.NULL

-------------------------- 실행 결과 -------------------------- 

 

실행 결과에서 NULL이 왜 출력되는진 모르겠지만...

아무튼 같은 추상 메서드를 호출했지만 각 클래스에서 쓰임에 맞게 오버라이딩한 결과, 실행 결과가 다르게 출력되었다.

 

 

 

평소 파이썬으로 코딩할 떈 추상 클래스를 사용하지 않았어서 생소한 개념이었다.

근데 쓰임을 알고 보니 내 코드에서 적용하면 좋겠다 생각한 게 몇 부분 있었다. 

php 강의에서 추상 클래스 개념을 처음 들었을 땐 뭔 소리야..? 했는데 어떨 때 쓰면 좋다라는 걸 알고 보니까 실무에서도 어느 지점에 쓰면 좋겠다라는 감을 잡을 수 있을 것 같다.

 

찰떡같은 예시 생각한다고 한 시간 날렸으니,, 이 글을 보시는 모든 분이 이해가 가셨길 바랍니다 ^^!

원래는 추상 클래스, 인터페이스, 트레이트 모두 쓰려고 했는데 분량조절 실패로,

다음 게시물에서 인터페이스 다뤄보겠습니다 Bye~~!

반응형