본문 바로가기

프로그래밍 공부/PHP

PHP - 인스턴스, 메서드 그리고 상속과 트레이트

인스턴스

객체(Object)를 추상화 하는 것을 클래스(Class)라고 하고, 클래스를 실체화 하면 인스턴스(Instance)라고 한다. 이것은 JavaScript에서 클래스를 사용할 때도 마찬가지다.

좀 더 쉽게 설명하면 클래스는 객체를 '추상화'한것이며, 인스턴스는 '실체화'를 통해 객체의 예를 만든 것이다. 하나의 클래스에서 여러개의 인스턴스를 가질 수 있다.

객체(Object) -> 클래스(Class) -> 인스턴스(Instance)의 관계
클래스(Class) 실체화
->
인스턴스(Instance)
이름 김철수
직업 회사원
나이 30세
1
2
3
4
5
6
7
8
9
<?php
    class User{
        public $name = "철수";
        public function print_hello(){
            print $this->name;
            print " 님 안녕하세요!<br>";
        }
    }
?>
cs

메서드 안에서 멤버 변수에 접근하려면 $this라는 특별한 변수를 사용한다. 또한 $this->변수와 같은 방법으로 변수의 내용을 참조할 수 있다. 이때 뒤에 참조될 변수는 $를 붙이지 않는다.

1
2
3
4
5
6
7
8
9
10
<?php
    $user = new User();
    class User{
        public $name = "철수";
        public function print_hello(){
            print $this->name;
            print "님 안녕하세요!<br>";
        }
    }
?>
cs

new 키워드는 객체 변수 = new 클래스();와 같은 방법으로 사용하는데, 이것으로 객체의 인스턴스를 생성한다. 생성된 객체는 객체 변수로 할당한다. 클래스에 정의되어 있는 메서드에 접근하려면 이 객체 변수를 사용한다. 

또한 인스턴스는 클래스의 앞이나 뒤에 작성해도 상관없이 작성한다. 위의 코드에서 $user에 클래스 User의 인스턴스를 할당하기 위해 new 키워드를 사용한 것을 알 수있다. User를 작성할 때는 괄호를 붙이지 않아도 상관 없지만 인스턴스를 작성할 때 인수를 넘기고 싶은 경우에 괄호를 사용하고 안에 인수를 넣어야 한다.

클래스를 읽어 들이는 방법은 include_once(); 함수를 사용한다. 하나의 클래스는 하나의 파일에 정의하고 파일명을 클래명과 똑같이 지정하면 클래스를 정의한 파일을 한눈에 알 수 있다. 정의한 파일은 일반적인 PHP 파일과 마찬가지로 파일명.php 형식으로 저장한다.

include_once();함수는 반드시 코드의 맨 앞에 작성해야 한다.

메서드

클래스에서 생성한 인스턴스는 객체 변수에 할당이 된다. 이 변수를 사용하려면 클래스의 각 메서드에 접근할 수 있는데, 객체지향 언어들은 내부 데이터(멤버 변수)를 변경하려면 반드시 메서드를 사용해야 한다. 이처럼 정해진 절차 이외에는 내부 데이터에 접근할 수 없는 구조를 캡슐화 또는 정보 은닉이라 한다. 비록 클래스를 별로 사용하지 않은 JavaScript도 객체지향 언어 이기 때문에 같은 개념을 가진다.

메서드를 실행할 때는 객체 변수에 람다식(->)을 사용해 객체 변수->메서드();와 같이 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<html>
<head>
    <title>클래스 테스트</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body bgcolor="#FFFFFF" text="#000000">
    <font size="4">클래스 테스트</font>
    <br><br>
    <?php
        $newuser = new User();
        $newuser->print_hello();
        class User{
            public $name = "철수";
            public function print_hello(){
                print $this->name;
                print " 님 안녕하세요!<br>";
            }
        }
    ?>
</body>
</html>
cs

위의 코드에서 클래스의 인스턴스를 $newuser에 할당하고 클래스 User();에 정의된 print_hello();를 $newuser->print_hello();와 같이 작성해 실행한다.

위의 코드에서 public은 접근 제한 키워드이며 종류로는 public, private 그리고 protected의 세 종류가 있다.

접근 제한 키워드 설명
public 외부에서 참조할 수 있음
private 변수 선언한 클래스 안에서만 참조할 수 있음
protected 변수 선언한 클래스와 상속 받은 클래스에서 참조할 수 있음
1
2
3
4
5
6
7
8
9
10
11
12
<?php
    class Test{
        public $str1 = '공개';
        private $str2 = '비공개';
    }
    $test = new Test();
    print $test->str1;
    print "<br>";
    // 오류가 발생한다.
    print $test->str2;
    print "<br>";
?>
cs

$str1을 public으로 선언하고, $str2를 private으로 선언을 하면 $str1은 정상적으로 "공개"가 출력이 되지만 $str2는 오류가 발생한다. 멤버 변수는 위와 같이 선언해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
    class Test{
        public function TestPublic(){
            print "공개<br>";
        }
        function TestNothing(){
            print "선언없음<br>";
        }
        private function TestPrivate(){
            print "비공개<br>";
        }
    }
    $test = new Test();
    $test->TestPublic();
    $test->TestNothing();
    // TestPrivate(); 만 오류가 발생한다.
    $test->TestPrivate();
?>
cs

메서드를 private와 public으로 제한을 할 수 있다. 위의 코드도 private로 선언한 TestPrivate();를 실행할 때만 오류가 발생한다.

상속과 트레이트

상속은 클래스를 정의할 때 다른 클래스의 내부 데이터(멤버)나 절차(메서드)를 이어받아 새로운 클래스를 작성하는 기능이다. JavaScript에서는 프로토타입으로 상속을 하는데 클래스를 사용해서 상속도 가능하다. 객체지향에서 상속하는 클래스를 슈퍼클래스, 상속 받은 클래스를 서브클래스라고 부른다.

서브클래스는 슈퍼클래스가 가진 기능을 상속받기 때문에 슈퍼클래스에 있는 멤버 변수나 메서드를 따로 정의하지 않아도 그대로 사용할 수 있고, 서브클래스 안에서는 슈퍼클래스에 있는 같은 메서드명으로 정의하는 오버라이드(Override)도 할 수 있다.

1
2
3
4
5
<?
    class SubClass extends SuperClass{
        // 처리를 기술한다.
    } 
?>
cs

protected로 선언된 멤버 변수와 메서드는 슈퍼클래스와 상속받은 서브클래스에서만 접근할 수 있다. 여러 개의 클래스를 슈퍼클래스로서 상속하는 것을 다중 상속이라고 하는데 PHP 5에서는 다중 상속을 할 수 없다. 상속할 수 있는 클래스는 단 하나뿐이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<html>
<head>
    <title>클래스 테스트</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body bgcolor="#FFFFFF" text="#000000">
    <font size="4">클래스 테스트</font>
    <br><br>
    <?php
        class User{
            private $name = null;
            public function print_hello(){
                print $this->name;
                print " 님 안녕하세요!<br>";
            }
        }
        class Guest extends User{
            private $name = "게스트";
            public function print_hello(){
                print $this->name;
                print " 님 안녕하세요!<br>";
            }
        }
        $newuser = new Guest();
        $newuser->print_hello();
    ?>
</body>
</html>
cs

메서드를 재정의(오버라이드)하려면 슈퍼 클래스의 메서드와 같은 이름의 메서드를 서브클래스 안에서 정의해야 한다. 변수도 같은 방법으로 재정의할 수 있다. 

final 키워드는 오버라이드나 상속을 금지시킬 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
<?php
    class Test{
        final public function test_method(){
            // 처리가 기술된다.
        }
    }
    final class Test2{
        public function test2_method(){
            // 처리가 기술된다.
        }
    }
?>
cs

final 키워드를 지정한 클래스와 메서드에 extends 키워드를 사용해 상속 또는 오버라이드를 시도하면 오류가 발생하고 동작하지 않는다.

트레이드(Trait)는 코드를 재이용하기 위한 방법이다. 클래스의 상속과는 다른 방법으로 기능(메서드)이나 멤버 변수(프로퍼티)를 클래스에 추가할 수 있다. 다만 클래스 처럼 인스턴스를 작성할 수는 없지만, 몇 가지 지능을 합쳐서 클래스를 다중 상속을 받은 것처럼 여러 가지 기능을 사용할 수 있기 때문에 편리하다.

트레이트를 사용해 메서드를 클래스에 포함할 때 이름이 같은 메서드가 존재한다면 우선순위가 높은 메서드를 오버라이드한다.

클래스의 메서드가 최우선이며, 다음으로 트레이트의 메서드 그리고 상속한 메서드 순이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
    trait TraitName{
        public function TraitMethod1(){
            // 처리 1
        }
        private function TraitMethod2(){
            // 처리 2
        }
    }
    class ClassName1{
        use TraitName;
    }
    class ClassName2{
        use TraitName;
    }
?>
cs

trait 키워드로 선언한 메서드를 이용하려면 클래스 내부에서 use 키워드를 사용한다. 트레이트가 여러 개로 선언되어 있다면 use 키워드를 ,로 구분하여 여러 개로 지정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<html>
<head>
    <title>Trait 테스트</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body bgcolor="#FFFFFF" text="#000000">
    <font size="4">Trait 테스트</font>
    <br><br>
    <?php
        trait SayMorning{
            public function print_morning(){
                print $this->name;
                print "님, 안녕하세요!<br>";
            }
        }
        class User{
            private function print_hello(){
                print $this->name;
                print "님, 반갑습니다!<br>";
            }
        }
        class Guest extends User{
            use SayMorning;
            private $name = "게스트";
            public function print_hello(){
                print $this->name;
                print "님, 처음 뵙겠습니다!<br>";
            }
        }
        $newuser = new Guest();
        $newuser->print_hello();
        $newuser->print_morning();
    ?>
</body>
</html>
cs

PHP에서 클래스 상속은 하나의 메서드만 가능하지만 트레이트를 사용하면 여러 개를 추가할 수 있다. 클래스 내부에서 트레이트를 지정하는 것처럼 트레이트 내부에서 use로 지정해 다른 트레이트를 포함 시키는 것도 가능하다.

명칭(또는 함수 이름) 형태 설명
new 객체 변수 = new 클래스(); 객체의 인스턴스를 생성
$this $this->변수 메서드 내부의 멤버 변수에 접근
include_once include_once("파일명.php"); 클래스 읽어 들이기
method(메서드) 객체 변수->메서드(); 메서드 실행
접근 제한 키워드 public class 클래스명{}; 또는 public function 함수명 (){}; 외부에서 참조가능
  private class 클래스명{}; 또는 private function 함수명 (){}; 변수를 선언한 클래스에서만 참조가능
  protected class 클래스명{}; 또는 protected function 함수명 (){}; 변수를 선언한 클래스와 상속 받은 클래스에서만 참조 가능
extends class 새로운 클래스명 extends 상속할 클래스명{}; 클래스의 상속
final final 클래스명{}; 또는 final function 함수명 (){}; 오버라이드나 상속 금지
trait trait 트레이트명{}; 트레이트 기능
use class 클래스명(){ use 트레이트명1, 트레이트명2 }; 클래스에 트레이트 추가