>  기사  >  백엔드 개발  >  PHP 기본 튜토리얼 11: 캡슐화, 상속 및 다형성

PHP 기본 튜토리얼 11: 캡슐화, 상속 및 다형성

黄舟
黄舟원래의
2017-03-01 10:01:281641검색

이 섹션에서 설명하는 내용

  • 캡슐화

  • 상속

  • 다형성

  • 오버로딩

  • 재정의

머리말

PHP의 객체지향 접근방식은 JAVA의 객체지향 접근방식과 동일하며, 둘 다 캡슐화, 상속, 다형성의 세 가지 주요 기능으로 나뉩니다. 이 세 가지 기능은 여러 측면에서 객체 지향을 최적화합니다. 이 세 가지 특성은 객체지향을 개발할 때 고려해야 할 문제이기도 합니다.

캡슐화

객체 지향 용어로 캡슐화란 무엇인가요?

캡슐화: 추상화된 데이터와 데이터에 대한 작업을 함께 캡슐화합니다. 데이터는 내부적으로 보호됩니다. 프로그램의 다른 부분은 승인된 작업(멤버 메서드)을 통해서만 데이터에 대해 작업할 수 있습니다.

위에서 언급한 추상화는 사물의 클래스에 대한 공통 속성과 동작(메소드)을 추출하여 템플릿(클래스)을 구성하는 것입니다. 이러한 문제 연구 방법을 추상화라고 합니다.

저희 은행 계좌와 마찬가지로 누구의 계좌이든지 계좌번호와 비밀번호가 포함되어 있으며, 출금, 입금, 잔액 확인 등 일반적인 방법도 있습니다. 캡슐화 사용에 대한 우리의 아이디어는 다음과 같습니다.

<?php
    class Account{

        public $account_no;
        private $pwd;
        private $balance;

        public function __construct($account_no, $pwd = &#39;123456&#39;, $balance = 0.0){

            $this->account_no = $account_no;
            $this->pwd = $pwd;
            $this->balance = $balance;

        }

        //存款
        public function deposit($amount, $pwd){

            if($pwd == $this->pwd){
                echo &#39;<br> 存款成功&#39;;
                $this->balance += $amount;
            }else{
                echo &#39;<br> 密码不正确&#39;;
            }
        }

        //取款
        public function withdraw($amount, $pwd){
            if($pwd == $this->pwd){

                if($this->balance >= $amount){
                    echo &#39;<br> 取款成功&#39;;
                    $this->balance -= $amount;
                }else{
                    echo &#39;<br> 余额不足..&#39;;
                }
            }else{
                echo &#39;<br>密码错误&#39;;
            }
        }

        //查询
        public function query($pwd){

            if($pwd == $this->pwd){
                echo &#39;<br> 账号&#39; . $this->account_no . &#39; 余额=&#39; . $this->balance;
            }else{
                echo &#39;<br> 查询密码错误&#39;;
            }
        }

    }

    $account = new Account(123456789, &#39;hsp123&#39;, 2000000);

    $account->deposit(30000, &#39;hsp123&#39;);

    $account->query(&#39;hsp123&#39;);

    $account->withdraw(99999900, &#39;hsp123&#39;);

    $account->query(&#39;hsp123&#39;);

위 코드는 은행 업무를 캡슐화 코드를 통해 캡슐화한 것입니다. 우리가 운영할 때 내부 비즈니스가 어떻게 처리되는지 관리할 필요 없이 내부에서 제공되는 메소드만 호출하면 됩니다. 이것은 중요한 생각이다. 캡슐화를 구현하는 방법은 액세스 수정자를 사용하고 액세스 수정자의 액세스 특성을 사용하여 외부에서 사용이 허용되지 않는 속성이나 메서드를 캡슐화하는 것입니다. 이전 섹션에서 우리는 세 가지 접근 수정자에 대해 이야기했습니다.
그럼 보호된 회원 속성과 비공개 회원 속성에 어떻게 액세스하나요? 여기에는 세 가지 방법이 있습니다.

첫 번째

매직 메소드 __get() 및 __set()를 사용하여

<?php

    class Cat{
        private $name;
        private $age;
        public function __construct($name,$age){
            $this -> name = $name;
            $this -> age = $age;
        }

        public function __set($name,$value){
            //判断类中有没有这个属性
            if(property_exists($this,$name)){
                $this -> $name = $value;
            }else{
                echo &#39;没有这个属性&#39;;
            }
        }

        public function __get($name){
            return $this -> $name;
        }
    }

    $cat = new Cat(&#39;小白&#39;,2);
    $cat -> name = &#39;小花&#39;;
    echo $cat -> name;
    ......结果......
    小花

에 액세스합니다. 매직 메소드 set을 사용하고 속성을 가져옵니다. 보호된 속성의 값을 변경하는 방법이 있지만 이 방법은 권장되지 않습니다. 유연성이 충분하지 않기 때문에 들어오는 데이터를 확인할 수 없습니다. 다음 방법으로 들어오는 데이터를 확인할 수 있습니다.

두 번째 유형

은 각 개인 또는 보호 멤버 변수에 대해 getXxx() 및 setXxx() 메서드 쌍을 제공합니다.

<?php

    class Cat{
        private $name;
        private $age;
        public function __construct($name,$age){
            $this -> name = $name;
            $this -> age = $age;
        }

        public function setName($value){
            if(is_string($value)){
                $this -> name = $value;
            }
        }

        public function getName($password){
            if(&#39;12345&#39; == $password){
                return $this -> name;
            }else{
                echo &#39;密码不对&#39;;
            }
        }
    }

    $cat = new Cat(&#39;小白&#39;,2);
    $cat -> setName(&#39;小花&#39;);
    echo $cat -> getName(&#39;12345&#39;);
    ......结果......
    小花

이 방법을 사용하면 들어오는 데이터를 판단할 수 있습니다. 이 방법을 권장합니다.

세 번째 유형

은 통일된 방법을 사용하여 속성을 조작합니다.

<?php

    class Cat{
        private $name;
        private $age;
        public function __construct($name,$age){
            $this -> name = $name;
            $this -> age = $age;
        }

        //显示对象的信息
        public function showInfo(){
            echo $this -> name . &#39;的年龄是:&#39; . $this-> age;
        }
    }

    $cat = new Cat(&#39;小白&#39;,2);
    $cat -> showInfo();
    ......结果......
    小白的年龄是:2

이 세 가지 방법 중 우리는 개발 시 두 번째와 세 번째 방법을 더 자주 사용합니다. 단일 속성을 작업할 때는 두 번째 방법을 사용할 수 있습니다. 여러 속성을 작업할 때는 세 번째 방법을 사용할 수 있습니다. 옵션.

상속

상속의 출현으로 인해 코드의 중복성이 줄어드는 경우가 너무 많습니다. 코드가 더 명확해 보입니다. 그렇다면 상속이란 무엇입니까?

<?php
    //定义一个小学生,它有考试的方法,设置成绩的方法
    class Pupil {
        public $name;
        public $age;
        private $grade;

        public function __construct($name, $age){
            $this->name = $name;
            $this->age = $age;
        }

        public function showInfo(){
            echo &#39;<br> 学生的信息如下:&#39;;
            echo &#39;<br> 学生的名字是:&#39; . $this->name;
            echo &#39;<br> 学生的成绩是:&#39; . $this->grade;
        }
        //设置成绩
        public function setGrade($grade){
            $this->grade = $grade;
        }

        public function testing(){
            echo &#39;<br> 小学生在考试.........&#39;;
        }
    }

    //定义一个大学生,它有考试的方法,设置成绩的方法
    class Graduate {
        public $name;
        public $age;
        private $grade;

        public function __construct($name, $age){
            $this->name = $name;
            $this->age = $age;
        }

        public function showInfo(){

            echo &#39;<br> 学生的信息如下:&#39;;
            echo &#39;<br> 学生的名字是:&#39; . $this->name;
            echo &#39;<br> 学生的成绩是:&#39; . $this->grade;
        }
        //设置成绩
        public function setGrade($grade){
            $this->grade = $grade;
        }
        public function testing(){
            echo &#39;<br> 大学生在考试.....&#39;;
        }
    }

    //使用一下
    $pupil1 = new Pupil(&#39;小明&#39;, 40);
    $pupil1->testing();
    $pupil1->setGrade(100);
    $pupil1->showInfo();
    echo &#39;<br>&#39;;
    //使用
    $graduate1 = new Graduate(&#39;小华&#39;, 20);
    $graduate1->testing();
    $graduate1->setGrade(60);
    $graduate1->showInfo();

위 코드에서 두 클래스 모두 동일한 속성과 메서드를 가지고 있음을 알 수 있습니다. 코드에 이러한 코드가 많으면 코드 중복이 발생하여 유지 관리에 도움이 되지 않습니다. 이 문제를 해결하는 가장 좋은 방법은 상속을 사용하여 코드 재사용성을 높이는 것입니다.

상속된 구문:

class 方法名 extends 父类的方法名{

}

extends 키워드를 사용하여 상속됩니다. 이는 코드 재사용으로 이해될 수 있으며, 이는 프로그래밍을 인간의 사고에 더 가깝게 만듭니다. 여러 클래스가 동일한 속성(변수)과 메서드를 갖는 경우 이러한 클래스에서 부모 클래스를 추출하고 부모 클래스에서 동일한 속성을 정의할 수 있습니다. 및 메소드의 경우 모든 하위 클래스는 이러한 속성과 메소드를 재정의할 필요가 없으며 상위 클래스

<?php

    //使用继承来完成
    class Student{

        public $name;
        public $age;
        private $grade;

        public function __construct($name, $age){
            $this->name = $name;
            $this->age = $age;
        }

        public function showInfo(){

            echo &#39;<br> 学生的信息如下:&#39;;
            echo &#39;<br> 学生的名字是:&#39; . $this->name;
            echo &#39;<br> 学生的成绩是:&#39; . $this->grade;
        }
        //设置成绩
        public function setGrade($grade){
            $this->grade = $grade;
        }

    }


    //这里 extends 关键字就是表示 Pupil 类 继承了 Student类
    class Pupil extends Student{

        public function testing(){
            echo &#39;<br> 小学生在考试.........&#39;;
        }
    }

    class Graduate extends Student{

        public function testing(){
            echo &#39;<br> 大学生在考试.........&#39;;
        }
    }

    $pupil1 = new Pupil(&#39;小明&#39;, 40);
    $pupil1->testing();
    $pupil1->setGrade(100);
    $pupil1->showInfo();
    echo &#39;<br>&#39;;
    $graduate1 = new Graduate(&#39;小华&#39;, 20);
    $graduate1->testing();
    $graduate1->setGrade(60);
    $graduate1->showInfo();

만 상속하면 됩니다. 위 코드에서 동일한 속성과 메소드가 Student 클래스로 내보내지는 것을 볼 수 있습니다( 상위 클래스), 확장 키워드를 통해 상위 클래스를 상속하기 위해 두 개의 클래스를 더 작성합니다.
상속하는 클래스를 부모 클래스, 이 클래스를 상속받는 클래스를 자식 클래스라고 합니다.
상속하는 한 상위 클래스의 속성과 메서드를 사용할 수 있습니다. 그러나 상위 클래스의 속성이나 메서드가 private에 의해 수정되면 하위 클래스는 을 상속할 수 없습니다. 이것이 protected와 private의 차이점입니다.

하위 클래스는 상속을 통해 상위 클래스의 속성과 메서드를 얻으므로 상위 클래스의 코드 복사본이 하위 클래스에 할당된다는 의미인가요? 설마. 상속은 단순히 상위 클래스의 속성 및 메소드 정의를 하위 클래스에 복사하는 것이 아니라 검색 관계를 설정하는 것입니다. 다시 방문하면 이 검색 관계는 다음과 같습니다.

  1. 객체가 속성과 메소드를 작동할 때 먼저 이 클래스에 해당 속성과 메소드가 있는지 확인합니다. 그렇다면 존재하는지 판단합니다. 접근 권한이 있으면 접근하세요. 접근할 수 없으면 오류가 발생합니다.

  2. 객체가 속성과 메서드를 작동할 때 먼저 이 항목을 살펴보세요. 클래스 해당 속성과 메소드가 있습니까? 없으면 자체 상위 클래스를 찾고, 상위 클래스에 해당 클래스가 있으면 액세스할 수 있는지 확인합니다. 그렇지 않으면 오류가 보고됩니다.

  3. 이 검색 로직은 최상위 클래스로 이동합니다. . . . .

상속에는 아직 주의할 점이 많습니다.

  • 子类最多只能继承一个父类(指直接继承),这和c++是不一样的。

  • 子类可以继承其父类(或者基类)的 public ,protected修饰的变量(属性) 和 函数(方法)

  • 在创建某个子类对象时,默认情况下会自动调用其父类的构造函数(指在子类没有自定义构造函数情况时)

    <?php
        class A{
            public function __construct(){
                echo &#39;父类的构造函数&#39;;
            }
        }
    
        class B extends A{
    
        }
        //子类没有定义构造函数,就会执行父类的构造函数。
        $b = new B();
        .....结果......
        父类的构造函数
  • 如果在子类中需要访问其父类的方法(构造方法/成员方法  方法的访问修饰符是public/protected),可以使用父类::方法名(或者 parent::方法名 ) 来完成

    <?php
        class A{
            public function __construct(){
                echo &#39;父类的构造函数<br>&#39;;
            }
        }
    
        class B extends A{
            //子类定义了自己的构造函数,不会调用父类的构造函数。
            public function __construct(){
                parent::__construct();
                echo &#39;子类的构造函数<br>&#39;;
            }
        }
        //子类没有定义构造函数,就会执行父类的构造函数。
        $b = new B();
        .....结果......
        父类的构造函数
        子类的构造函数
  • 如果子类中的方法和父类方法相同,我们称为方法重写。

多态

多态通俗的讲就是多种形态,就是指在面向对象中,对象在不同情况下的多种状态(根据使用的上下文)PHP天生就是多态语言,同时PHP可以根据传入的对象类型不同,调用对应对象的方法

在PHP中变量的定义不像其他语言在变量名前面定义类型,而是直接用$符号来定义,可以接受任意类型的值。这就为多态创造了条件。根据传入的对象类型不同,调用对应对象的方法,我们也可以使用类型约束来对传入的值进行约束。

类型约束

类型约束的基本概念是 PHP 5 可以使用类型约束。函数的参数可以指定必须为对象(在函数原型里面指定类的名字),接口,数组(PHP 5.1 起)或者 callable(PHP 5.4 起)。如前PHP只支持这几种。

<?php

    class  MyClass
    {
         //传进的参数约束为cat或者cat的子类       
        public function  test (Cat $cat ){
            echo &#39;对象<br>&#39;;
        }
        //参数是数组
        public function  test_array (array  $arr ) {
             print_r($arr);
        }
    }
    class  Cat  {
    }

    $cat = new Cat();
    $myClass = new MyClass();
    $arr = array(1,2,4,5);
    $myClass -> test($cat);
    $myClass -> test_array($arr);
    .....结果......
    对象
    Array ( [0] => 1 [1] => 2 [2] => 4 [3] => 5 )

如果要进行类型约束,在参数前面写上类型就行了,可以试一下传入的不是约束的类型,会报一个致命的错误。

多态的示例:

<?php
    //多态的案例
    //定义动物的父类
    class Anmial{
        public $name;
        public function __construct($name){
            $this->name = $name;
        }
    }
    //猫类
    class Cat extends Anmial{
        public function showInfo(){

            echo &#39;<br> 猫猫名字是&#39; . $this->name;
        }
    }
    //狗类
    class Dog extends Anmial{
        public function showInfo(){

            echo &#39;<br> 狗名字是&#39; . $this->name;
        }
    }
    //猴子类
    class Monkey extends Anmial{
        public function showInfo(){

            echo &#39;<br> 猴子名字是&#39; . $this->name;
        }
    }

    //定义事物
    class Food{
        public $name;
        public function __construct($name){
            $this->name = $name;
        }
    }

    class Fish extends Food{
        public function showInfo(){
            echo &#39;<br> &#39; . $this->name;
        }
    }

    class Bone extends Food{
        public function showInfo(){
            echo &#39;<br> &#39; . $this->name;
        }
    }

    class Peach extends Food{
        public function showInfo(){
            echo &#39;<br>&#39; . $this->name;
        }
    }


    //主人类
    class Master{
        public $name;
        public function __construct($name){
            $this->name = $name;
        }
        //主人可以喂食
        //当我们的类型约束是父类时,可以接受 他的子类的对象实例
        public function feed(Anmial $aniaml, Food $food){

            echo &#39;<br> 主人 &#39; . $this->name;
            $aniaml->showInfo(); //使用多态,根据传入的值不同,调用不同的方法。
            echo &#39;<br> 喂得食物是&#39;;
            $food->showInfo();

        }
    }

    $dog = new Dog(&#39;黄狗&#39;);
    $cat = new Cat(&#39;花猫&#39;);
    $monkey = new Monkey(&#39;猴&#39;);

    $fish = new Fish(&#39;鲨鱼&#39;);
    $bone = new Bone(&#39;骨头&#39;);

    $peach = new Peach(&#39;桃子&#39;);

    $master = new Master(&#39;小明&#39;);

    $master->feed($dog, $bone);
    echo &#39;<br><br>&#39;;
    $master->feed($cat, $fish);
    echo &#39;<br><br>&#39;;
    $master->feed($monkey, $peach);

上面的代码在主人的喂食方法中,使用类型约束,根据传进去的对象不同,调用不同对象的方法。

重载

方法的重载

重载:简单说,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。

上面的关于方法的重载可以理解为在一个类中,有两个方法名一样的函数,但是函数的参数是不一样的,我们在调用的时候,系统会根据我们传入的参数的不同,而自动的调用不同的函数,这就是重载,但是在PHP中一个类中不能有两个方法名相同的函数,尽管你的参数不同,它会报一个Cannot redeclare的错误。那么在PHP中就不能重载了吗?其实可以的,利用魔术方法。

在上节中介绍魔术方法中有一个方法是当我们访问不可访问或不存在的方法时,系统自动调用的,也就是__call()方法。PHP中可以利用这个魔术方法进行重载

<?php
    class Calculate{

        //定义两个方法,计算加法,注意两个方法的方法名是不一样的
        private function add($a,$b,$c){ 
            return $a + $b + $c;
        }

        private function add1($a,$b){
            return $a + $b;
        }

        public function __call($name,$val_arr){
            if($name == &#39;add&#39;){
                //得到数组里面的参数,确定几个参数
                $num = count($val_arr);
                if($num == 2){
                    return $this -> add1($val_arr[0],$val_arr[1]);
                }else if($num == 3){
                    return $this -> add($val_arr[0],$val_arr[1],$val_arr[2]);
                }
            }
        }
    }

    $calculate = new Calculate();
    echo $calculate -> add(1,2);
    echo &#39;<br>&#39;;
    echo $calculate -> add(1,2,3);
    .....结果......
    3
    6

看到代码有没有被欺骗的感觉-_-,先把类中的两个方法设置成private,这样在类外就访问不到,当我们在类外访问add方法的时候,会调用魔术方法,然后通过传入的数组的个数,确定参数的个数,从而在魔术方法中去掉用合适的方法。这就是PHP的方法重载。

属性的重载

在PHP面向对象编程中,当你去给一个不存在的属性赋值时,PHP默认会’动态的’, 给你创建一个对应的属性,这个称为属性重载。

<?php

    class A{
        //在类中只定义一个变量
        public $name = &#39;小明&#39;;

    }
    $a = new A();
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($a);
    $a -> age = 12;
    var_dump($a);
    .....结果......
    object(A)#1 (1) {
      ["name"]=>
      string(6) "小明"
    }
    object(A)#1 (2) {
      ["name"]=>
      string(6) "小明"
      ["age"]=>
      int(12)
    }

从结果中可以看到,当我们给一个不存在属性age赋值后,在输出的类结构中出现了age这个属性,这就是属性的重载。

如果不想让类的属性自动增加,可以使用魔术方法__set()和__get()方法进行控制。

重写

方法的重写

在上面我们介绍了面向对象的继承机制。而重写是基于继承才引发出来的概念。当一个类继承了另外一个类后,在父类中有一个方法,子类继承,但是在子类中觉得父类的方法不能满足要求,需要子类在重写定义一个和父类的方法一样的方法进行重新的定义,称为重写。说白了就子类有一个方法,和父类(基类)的某个方法的名称、参数个数一样。

<?php

    class Animal{
        //动物都有吃这个行为,具体的要看是什么动物
        public function eat(){

            echo &#39;吃饭<br>&#39;;
        }
    }

    class Cat extends Animal{
        //对父类的方法进行重写
        public function eat(){
            echo &#39;猫在吃饭<br>&#39;;
        }       
    }
    $cat = new Cat();
    $cat -> eat();
    .....结果......
    猫在吃饭

如果父类的方法中使用了类型约束,那么子类的类型约束也必须一样。

在方法的重写中:

  1. 子类的方法的参数个数 ,方法名称,要和父类方法的参数个数,方法名称一样

  2. 子类方法不能缩小父类方法的访问权限(可以大于可以等于)

注意:如果父类的方法名是private,则子类并不会进行重写。

属性的重写

对于属性的重写也是,public 和 protected 可以被重写,private 的属性不能被重写

<?php

    class Animal{
        public $name = &#39;小花&#39;;
        protected $age = 12;
        private $sex = &#39;雄&#39;;
    }

    class Cat extends Animal{
        public $name = &#39;小白&#39;;
        protected $age = 4;
        private $sex = &#39;雄&#39;;
    }
    $cat = new Cat();
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($cat);
    ......结果......
    object(Cat)#1 (4) {
      ["name"]=>
      string(6) "小白"
      ["age":protected]=>
      int(4)
      ["sex":"Cat":private]=>
      string(3) "雄"
      ["sex":"Animal":private]=>
      string(3) "雄"
    }

可以看到name和age被重写了,private不能被重写。

总结

面向对象中,封装是一个很重要的思想,封装的实现,可以降低代码的耦合度,同时继承的时候减少代码的冗余度,php的独特语言结构让多态也散发着光芒,而重写的掌握,让我们对继承有了更深层次的了解。(ps:今天10.1号,祖国的生日,我要去给祖国母亲庆生去了)

 以上就是PHP基础教程十一之封装、继承、多态的内容,更多相关内容请关注PHP中文网(www.php.cn)!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.