본문 바로가기

프로그래밍 공부/PHP

PHP - 클래스의 설계와 Iterator

클래스의 설계

실제로 PHP를 통한 업무에 클래스를 설계할 때 유즈케이스 다이어그램을 그린다거나 시나리오를 분석하여 클래스를 추출하는 작접을 한다. 시나리오를 작성해보고, 시나리오를 기반으로 사이트에 필요한 기능을 작성한다. 그리고 기능에 필요한 정보와 그에 맞는 처리를 적고 종합적으로 분석해 틀을 만들어보는 과정이 필요하다.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!-- 시나리오 
김철수는 사이트를 방문한 회원으로, 회원전용 콘텐츠를
이용하고자 회원등록을 하려고 한다.
회원이 되려면 이름과 메일 주소, 패스워드가 필요하다.
등록이 완료되면 등록 완료 메일이 도착한다.
로그인 화면에서 설정한 메일 주소와 패스워드로 로그인
하면 회원 전용 화면이 표시된다.-->
 
<!-- 회원제 사이트 기능
1. 사용자는 필요한 정보를 입력하고 회원으로 등록한다.
2. 등록 완료 메일이 도착한다.
3. 사용자는 로그인 화면에서 로그인 한다.
4. 사용자는 회원만 볼 수 있는 콘텐츠를 볼수 있다. -->
 
<!-- 필요한 정보의 예
회원의 이름, 메일 주소, 패스워드, 주소 등 -->
 
<!-- 필요한 처리의 예
회원 정보 등록, 등록 완료 메일 발송, 회원 정보 편집,
패스워드 재발급, 탈퇴 처리 등 -->
 
<!-- 한글로 작성한 클래스
회원 클래스{
    회원번호:
    성(Last name):
    이름(First name):
    주소:
    메일 주소:
    패스워드:
    
    회원 정보 등록(){
    }
    등록 완료 메일 송신(){
    }
    회원 정보 편집(){
    }
    패스워드 재발급(){
    }
    회원 탈퇴(){
    }
}
주소 클래스{
    회원번호:
    우편번호:
    시/도:
    시/군/구:
    읍/면:
    도로명 건물번호
    동/층/호
    (법정도/공동주택명)
    전화번호
} -->
cs

필요한 데이터와 메서드를 틀로 만들어 보았다면 PHP 문법으로 고쳐써본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
    class Member{
        private $id;
        private $lastname;
        private $firstname;
        private $email;
        private $password;
        // 회원 정보 입력
        public function regist(){
        }
        // 입력 완료 메일 발송
        public function registMail(){
        }
        // 회원 정보 편집
        public function edit(){
        }
        // 패스워드 재발급
        public function resendPassword(){
        }
        // 탈퇴 처리
        public function quit(){
        }
    }
?>
cs

데이터는 클래스의 내부에서만 처리하기 위해 접근제한 키워드로 private를 사용한다. 메서드들은 모두 클래스 외부에서 실행하기 위해 키워드로 public을 사용한다.

클래스 안에서도 상수인 const를 정의 할 수 있다. JavaScript와 마찬가지로 같은 상수의 기능을 수행한다. 상수는 일반적인 PHP의 변수와 다르게 $를 사용하지 않는다. 클래스 안에서 참조하는 경우에는 self 키워드와 연산자 ::를 이용해 self::상수명과 같이 사용한다. 인스턴스를 나타내는 $this는 사용하지 않으며 클래스 자신을 나타내는 self를 사용하는 것이다.

1
2
3
4
5
6
7
8
<?php
    class Member{
        const AdultAge = 20;
        function printAdultAge(){
            print self::AdultAge;
        }
    }
?>
cs

접근자(Accessor)는 클래스 안에 private 변수에 접근할 때 사용한다. JavaScript의 게터와 세터와 비슷한 구조를 가지고 있다. 데이터에 데이터를 설정하는 메서드는 set+변수명으로 데이터를 취득하는 메서드는 get+변수명의 형식으로 메서드를 작성한다.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?php
    class Member{
        private $id;
        private $lastname;
        private $firstname;
        private $email;
        private $password;
 
        // 접근자(Accessor)
        public function getId($id){
            return $this->id;
        }
        public function setId($id){
            $this->id = $id;
        }
        public function getLastname(){
            return $this->lastname;
        }
        public function setLastname(){
            $this->lastname = $lastname;
        }
        public function getFirstname(){
            return $this->firstname;
        }
        public function setFirstname(){
            $this->firstname = $firstname;
        }
        public function getEmail(){
            return $this->email;
        }
        public function setEmail(){
            $this->email->$email;
        }
        public function getPassword(){
            return $this->password;
        }
        public function setPassword(){
            $this->password->$password;
        }
 
        // 회원 정보 입력
        public function regist(){
        }
        // 입력 완료 메일 발송
        public function registMail(){
        }
        // 회원 정보 편집
        public function edit(){
        }
        // 패스워드 재발급
        public function resendPassword(){
        }
        // 탈퇴 처리
        public function quit(){
        }
    }
?>
cs

접근자가 지나치게 많아지면 보기가 어렵기 때문에 일반적으로는 접근자의 양을 줄이는 것이 가독성에 도움을 준다.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
    class Member{
        private $id;
        private $lastname;
        private $firstname;
        private $email;
        private $password;
 
        // 접근자(Accessor)
        public function getId($id){
            return $this->id;
        }
        public function setId($id){
            $this->id = $id;
        }
        public function getLastname(){
            return $this->lastname;
        }
        public function setLastname(){
            $this->lastname = $lastname;
        }
        public function getFirstname(){
            return $this->firstname;
        }
        public function setFirstname(){
            $this->firstname = $firstname;
        }
        public function getEmail(){
            return $this->email;
        }
        public function setEmail(){
            $this->email->$email;
        }
        public function getPassword(){
            return $this->password;
        }
        public function setPassword(){
            $this->password->$password;
        }
 
        // 회원 정보 입력
        public function regist(){
        }
        // 입력 완료 메일 발송
        public function registMail(){
        }
        // 회원 정보 편집
        public function edit(){
        }
        // 패스워드 재발급
        public function resendPassword(){
        }
        // 탈퇴 처리
        public function quit(){
        }
    }
    $member = new Member();
    $member->setId("1");
    $member->setLastname('당신의 성');
    $member->setFirstname('당신의 이름');
    $member->setEmail('당신의 이메일 주소');
    $member->setPassword('패스워드');
 
    print $member->getId()."<br>";
    print $member->getLastname()."<br>";
    print $member->getFirstname()."<br>";
    print $member->getEmail()."<br>";
    print $member->getPassword()."<br>";
?>
cs

Member 클래스를 사용해 실제로 데이터를 설정하게되면, new 키워드로 Member 클래스의 인스턴스를 생성하고 클래스라는 설계도를 이용해 메모리에서 동작하는 $member라는 객체를 작성한다. setId(); 메서드에서 1을 인수로 넘겨 회원번호를 설정하고 차례대로 성과 이름, 메일 주소, 패스워드도 각 메서드에 설정한다. 그리고 getId와 이하 정보들도 같은 방법으로 데이터를 취득하고 표시한다.

Iterator

디자인 패턴은 소프트웨어 개발에서 재이용할 수 있는 23가지 설계 패턴을 말한다. Iterator은 그 23가지의 디자인 패턴중 하나이다. Iterator는 데이터를 저장하는 클래스에 변경을 가하지 않고 반복 처리에 필요한 데이터를 취득할 수 있는 것이다. foreach의 반복 안에 데이터를 취득하는 처리를 기술하지 않기에 한눈에 볼 수 있는 프로그램으로 디자인 할 수 있다.

이전에 작성한 Member 클래스의 인스턴스 하나는 한 명의 회원이 된다. 회원 목록을 표시하려면 여러 개의 인스턴스를 모두 갖고 있어야 한다. 여기에는 Members 클래스를 작성하여 이 기능을 구현한다.

1
2
3
4
5
6
7
8
9
10
11
<?php
    class Members implements IteratorAggregate{
        private $members = [];
        public function add(Member $member){
            $this->members[] = $member;
        }
        public function getIterator(){
            return new ArrayIterator($this->members);
        }
    }
?>
cs

implements 키워드 뒤에 구현할 인터페이스명인 IteratorAggregate를 붙여 선언한다.

$members는 []를 이용해 배열을 초기화 한다. 이곳에 객체 형식의 회원 정보를 저장한다.

add라는 이름의 메서드를 사용해 Member 클래스의 객체 $member를 인수로 받는다. ()안의 Member는 타입 힌팅(Type Hinting)이라고 부른다. 여기서는 인수의 형을 제한한다. add 메서드 안의 $this->members는 배열 $members를 메서드 안에서 참조하고 있다.

$this 변수를 사용해 $this->members로 배열을 나타낸다. 여기에 Member 클래스의 객체 변수 $member를 추가한다.

getIterator 메서드는 IteratorAggregate 인터페이스 안에 정의되어 있는 추상 메서드를 오버라이드 한 것이다. 추상 메서드는 오버라이드하여 같은 이름의 메서드를 구현하지 않으면 오류가 발생하기 때문에 실제로 동작하지 않는 getIterator 메서드를 여기에 정의하고 있다. new 키워드로 SPL안에 포함되어있는 이터레이터 관련 클래스인 ArrayIterator 클래스의 인스턴스를 생성해 객체 변수를 돌려준다. 배열로서 데이터를 탐색할 수 있도록 값을 반환해야 하기 때문에 배열 $this->members를 ArrayIterator로 감싸서 값을 돌려주고 있는 것이다.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php
    class Member{
        private $id;
        private $lastname;
        private $firstname;
        private $email;
        private $password;
        
        public function __construct($id$lastname$firstname$email$password){
            $this->setID($id);
            $this->setLastname($lastname);
            $this->setFirstname($firstname);
            $this->setEmail($email);
            $this->setPassword($password);
        }
 
        // 접근자(Accessor)
        public function getId($id){
            return $this->id;
        }
        public function setId($id){
            $this->id = $id;
        }
        public function getLastname(){
            return $this->lastname;
        }
        public function setLastname(){
            $this->lastname = $lastname;
        }
        public function getFirstname(){
            return $this->firstname;
        }
        public function setFirstname(){
            $this->firstname = $firstname;
        }
        public function getEmail(){
            return $this->email;
        }
        public function setEmail(){
            $this->email->$email;
        }
        public function getPassword(){
            return $this->password;
        }
        public function setPassword(){
            $this->password->$password;
        }
 
        // 회원 정보 입력
        public function regist(){
        }
        // 입력 완료 메일 발송
        public function registMail(){
        }
        // 회원 정보 편집
        public function edit(){
        }
        // 패스워드 재발급
        public function resendPassword(){
        }
        // 탈퇴 처리
        public function quit(){
        }
    }
?>
cs

앞서 작성했던 Member 클래스로 인스턴스를 생성함과 동시에 값을 설정할 수 있도록 컨스트럭터(Constructor)를 추가한다. 컨스트럭터는 인스턴스를 생성할 때 최초로 실행되는 처리를 기술한 특별한 메서드이다. __constructor로 사용한다. 이 메서드의 ()안에 지정된 인수는 컨스트럭터로 값이 넘어간다. 인수와 인수는 ,로 구분한다. JavaScript의 생성자 함수(Constructor)와 유사하다.

컨스트럭터와 반대되는 것은 디스트럭터(Destructor)가 있다. 컨스트럭터는 인스턴스 생성 시 내부 처리를 실행하지만 디스트럭터는 인스턴스 소멸 시 처리를 실행한다. __destruct로 사용한다. 사용의 예로 객체를 참조하는 객체 변수가 하나도 없을 때나 PHP 코드 종료 시 실행이 된다.

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
<?php
    // 관련 파일을 읽어 들입니다.
    require_once 'memberclass.php';
    require_once 'membersclass.php';
 
    // 회원 데이터 Dummy 작성
    $member1 = new Member(1"성1""이름1""email1@gmail.com""password1");
    $member2 = new Member(2"성2""이름2""email2@gmail.com""password2");
    $member3 = new Member(3"성3""이름3""email3@gmail.com""password3");
    $member4 = new Member(4"성4""이름4""email4@gmail.com""password4");
    $member5 = new Member(5"성5""이름5""email5@gmail.com""password5");
 
    // Members 클래스에 회원 데이터를 추가
    $members = new Members();
    $members->add($member1);
    $members->add($member2);
    $members->add($member3);
    $members->add($member4);
    $members->add($member5);
 
    // getIterator에서 Iterator 취득
    $iterator = $members->getIterator();
 
    // 반복 처리
    foreach($iterator as $member){
        print $member->getId()." ";
        print $member->getLastname()." ";
        print $member->getFirstname()." ";
        print $member->getEmail()." ";
        print $member->getPassword()."<br>";
    }
?>
cs

require_once 문으로 PHP 파일을 읽어 들여 실행할 수 있다. 여기에서는 Member의 memberclass.php와 Members 클래스의 membersclass.php 파일을 읽어들인다. new Member로 Member 클래스의 인스턴스를 생성하고 있다. 이때 인수에 값을 지정하고 Member 클래스의 컨스트럭터에 값을 넘기고 있다. 수치는 그대로 두고 문자열은 "로 묶고 인수와 인수 사이는 ,로 구분한다. 

인수의 수는 반드시 컨스트럭터에 설정된 인수의 수와 같아야 한다.

Members 클래스에 회원 데이터를 넣으려면 Members 클래스에 구현된 add 메서드를 이용한다.

getIterator 메서드에 따라 이터레이터를 취득하고 배열이나 객체를 반복 처리하는 foreach문을 이용해 print문으로 출력한다.

명칭(또는 함수 이름) 형태 설명
const const 상수명 = 값; 상수 선언 및 정의
set set변수명(){}; 데이터를 설정하는 메서드
get get변수명(){};   데이터를 취득하는 메서드
Iterator   23가지 디자인 패턴중 하나
Iterator 인터페이스 class 클래스명 implements 인터페이스명{}; Iterator 인터페이스 구현
컨스트럭터 __construct(){}; 인스턴스 생성 시 내부 처리 실행
디스트럭터 __destruct(){}; 객체 변수가 없거나 PHP 종료 시에 내부 처리 실행
require_once require_once("파일명.php 또는 경로"); PHP 파일을 읽어 들임