php面向对象基础详解【星际争霸游戏案例】


Posted in PHP onJanuary 23, 2020

本文实例讲述了php面向对象基础。分享给大家供大家参考,具体如下:

前言

面向对象博大精深,对于从未接触过得的人,会觉得一头雾水。
学习的资料很多,但大多比较抽象,所以我用经典的游戏-星际争霸来讨论PHP面向对象。
现在假设我们来用PHP开发星际争霸,从而接触PHP面向对象。
注意,为了便于学习,除了特殊说明,否则各部分代码之间没有关联。而且同一件事情往往用的是不同的代码。
另外我也不去考证各个兵种的属性数字,仅仅用来说明。

一、类和对象

如果玩家制造了一个机枪兵,那么我们怎么表示他呢,因为每个机枪兵有几个基本的数据要记录:剩余的血,杀敌数量,攻击力等等。
我们可以用一个数组来记录一个机枪兵剩余的血和杀敌数量,因为这对于每个机枪兵是独立的。
但攻击力比较麻烦,因为经过升级,攻击力会增加,这就必须要找出所有表示机枪兵的数组,然后进行修改,非常麻烦。
从这里我们可以看出一件事情,首先每个机枪兵有独立的数据需要记录和修改,比如剩余的血。同时他们有相同的数据需要共用,比如攻击力。
这时候面向对象就能帮上我们的忙了。

1.1、类的定义

我们先来处理一部分问题,也就是每个机枪兵独有的数据。

<?php 
  class marine
  {
    public $blood = 50; //剩余的血
    public $kills = 0; //杀敌数量
    //这个函数(通常叫做方法)表示攻击敌人时候的运行代码
    function attack($enemy)
    {
      //攻击敌人的代码
    }
  }
?>

这叫做类,我们建立了一个表示所有机枪兵的类marine,这里面保留了需要每个兵独有的数据,比如上面代码里的剩余的血。

1.2、对象的创建和使用

接下来我们来使用对象,也就是每个机枪兵:

<?php
  $m1 = new marine();
?>

通过new后面加一个类的名字和括号,我们新建了一个机枪兵$m1,$m1被叫做类marine的对象,我们可以把它想象成一个特殊变量,只不过里面保存了多个数据。
如果需要使用或者操作某个机枪兵的血(对象的属性),只要用$m1->blood来表示就可以了:echo $m1->blood;//输出机枪兵$m1剩余的血

我们再建立一个机枪兵

<?php 
  $m2 = new marine();
?>

如果此时$m1被敌人攻击过了,还剩下10个血。而$m2没受过攻击:

<?php
  echo $m1->blood;//结果是10
  echo $m2->blood;//结果是50
?>

使用对象可以很简单的保存每个机枪兵的血,不会互相影响。
如果机枪兵$m1攻击敌人的时候,可以这样使用对象的方法:

<?php
  $m1->attack($z1);//假设攻击的是某个小狗的对象$z1
?>

不同的类内可以用同名的函数,比如小狗的类Zergling里面也可以有一个函数attack
要注意的是,从PHP5开始,无论在哪里改变一个对象的属性,都能改变它。比如上面一个小狗对象被作为参数传入机枪兵的attack函数,执行函数之后这个小狗对象的血减少了,这和一般的函数不同。但这是很直观的,如果一个小狗被攻击了,它的血就应该减少。

二、构造函数和析构函数

每次我们新建一个机枪兵的时候,总人口应该加1,如果一个机枪兵被杀,人口应该减少1。
可以通过构造函数和析构函数来自动处理:

<?php
  class marine
  {
    //构造函数
    function __construct()
    {
      //增加总人口的代码
    }
    //析构函数
    function __destruct()
    {
      //减少总人口的代码
    }
  }
?>

在一个类中,名字为__construct的函数叫做构造函数,每次new新建一个类的对象的时候就会执行:

<?php
  $m1 = new marine();//每次制造一个机枪兵时系统会调用类marine的构造函数,自动增加总人口
?>

在一个类中,名字为__destruct的函数叫做析构函数,每次销毁一个类的对象的时候就会执行:

<?php
  unset($m1);//unset可以用于对象,表示销毁一个对象。每次一个机枪兵被杀时系统会调用类marine的析构函数,自动减少总人口
?>

三、静态

机枪兵的攻击力是属于所有机枪兵对象,每个机枪兵的攻击力都是一样的,如果升级,应该一起变化。
这就用到static,表示静态:

<?php
  class marine
  {
    static $attackNumber = 10; //攻击力的数字
    //这个函数表示攻击敌人时候的运行代码
    function attack($enemy)
    {
      //攻击敌人的代码,$enemy->blood表示敌人对象的血属性
      $enemy->blood -= self::$attackNumber;
    }
  }
?>

静态属性表示类所有的对象都共享的属性,一旦改变,所有的对象都跟着变化。
静态属性用static开头,比如上面的static $attackNumber。
静态属性可以用类直接访问:

<?php
  echo marine::$attackNumber;//显示10
?>

如果类以内的函数访问,用self::$attackNumber表示本类的$attackNumber属性
所以如果我们升级了机枪兵的攻击力,所有的机枪兵都受影响,这就是面向对象的好处之一,也解决了我们前面讨论的共同数据的问题。

函数也可以是静态的,这样就可以用类直接访问,不需要新建对象来调用:

<?php 
  class marine
  {
    static $attackNumber = 10; //攻击力的数字
    //这个函数表示机枪兵升级的运行代码
    staticfunction upgrade()
    {
      self::$attacknum++;
    }
  }
?>

如果科技建筑升级完毕,直接就调用这个函数:

<?php
  marine::upgrade();
?>

四、继承

兵营用来造机枪兵,坦克房用来制造坦克,他们都是建筑,但是却有很多不同,如果用一个类“建筑”来表示,很困难。
但我们要保留他们的共性,比如都能飞行,不希望飞行的代码在各个类重复写,又要让他们能各自独立的生产不同的东西。
所以我们可以用继承来处理,继承表示父子关系,被继承的叫父类,继承的叫子类。用extends表示继承

<?php 
  //建筑类
  class building
  {
    function fly()
    {
      //建筑飞行的代码
    }
  }
  //兵营类
  class marineBuilding extends building
  {
    function createMarine()
    {
      //制造机枪兵的代码
    }
  }
  //坦克房类
  class tankBuilding extends building
  {
    function createTank()
    {
      //制造坦克的代码
    }
  }
?>

接下来,我们看看继承产生的效果:

<?php 
  //如果造了一个兵营:
  $mb1 = new marineBuilding();
  //一旦他需要飞行,就可以直接使用建筑类的函数fly(),尽管兵营类的定义里没有这个函数
  $mb1->fly();
  //而他要制造机枪兵的时候:
  $mb1->createMarine();
?>

同样是继承建筑类的坦克房类,就无法制造机枪兵,因为这是兵营类的个性。
如果在子类中的函数调用父类的函数,要使用parent,比如parent::fly()
注意,一个类只能有一个父类,PHP不允许多重继承,也就是说一个孩子只能有一个爹,一个爹可以有N个孩子!

五、访问控制

如果用$attackNumber = 10表示属性的话,系统默认是public $attackNumber = 10,所以建议这样写:

<?php 
  class marine
  {
    public static $attackNumber = 10; //攻击力的数字
  }
?>

public表示这个属性是公共的,也就是在任何地方都可以访问和操作的。

但这就存在一些问题,如果有玩家知道了类marine的一些代码结构,那他做个简单的补丁程序,运行的时候加载上去:

<?php
  //补丁
  marine::$attackNumber = 10000;
?>

这样的话,他的机枪兵有10000的攻击力,呵呵,这样的话,谁打得过他!

为此我们要用private,表示这个属性只有类里面的函数才能访问:

<?php 
  class marine
  {
    private static $attackNumber = 10; //攻击力的数字
    //这个函数表示机枪兵升级的运行代码
    function upgrade()
    {
      //这样防止无限升级
      if(self::$attacknum<13)
      {
        self::$attacknum++;
      }
    }
  }
?>

这样一来,只有升级才能改变机枪兵的攻击力。
但是现在往往是团队开发,而且很多用到类的继承,如果private的话,子类就无法访问了,但又不希望随便都可以修改某些属性。
那么可以用protected,protected的属性可以被子类的函数访问。

六、重载

6.1、属性重载

如果我们把地面部队作为一个类,让机枪兵类来继承他,这时候如果地面部队类和机枪兵类里面都定义了攻击力$attackNumber,那么每个兵的攻击力就决定于机枪兵类,而不是地面部队。这就叫做重载。

<?php 
  //地面部队
  class groundArmy
  {
    public $attackNumber = 5;
  }
  //机枪兵
  class marine extends groundArmy
  {
    public $attackNumber = 10; //攻击力的数字
  }
  $m1 = new marine();//新建一个机枪兵
  echo $m1->attackNumber;//显示攻击力为10
?>

6.2、函数重载

重载也可以用于函数,子类的函数如果和父类函数同名,除非另行说明,否则子类的对象默认调用子类内的函数。
比如人族的鬼兵类ghost和神族类的黑暗圣堂类(隐刀),都是隐形兵种,但是鬼兵隐形的时候会减少能量,黑暗圣堂根本没有能量属性。
如果我们把隐形能力作为父类,鬼兵类ghost和神族类的黑暗圣堂类DarkTemplar来继承它,同时实现不同的隐形代码:

<?php
  //隐形能力类
  class concealAbility
  {
    //这个函数表示隐形的运行代码
    function conceal()
    {
      //隐形的运行代码
    }
  }
  //鬼兵类
  class ghost extends concealAbility
  {
    $energy = 150;
    //这个函数表示隐形的运行代码
    function conceal()
    {
      //隐形的运行代码
      //减少鬼兵的能量,$this表示当前对象,也就是当前这个鬼兵
      $this->energy -= 25;
    }
  }
  //黑暗圣堂类
  class DarkTemplar extends concealAbility
  {
    //这个函数表示隐形的运行代码
    function conceal()
    {
      //隐形的运行代码,不影响能量
    }
  }
  //新建一个鬼兵
  $g1 = new ghost();
  //显示能量为150
  echo $g1->energy;
  //鬼兵隐形
  $g1->conceal();
  //显示能量为125
  echo $g1->energy;
  //新建一个黑暗圣堂
  $d1 = new DarkTemplar();
  //黑暗圣堂隐形,他没有能量属性
  $g1->conceal();
?>

七、接口

PHP不允许多重继承,那么有些问题就难办了。
假如为了规范处理,我们把隐形的能力建立一个类,然后把飞行能力放一个类,那么人族的侦察机怎么处理?不能继承两个类!
那我们不用继承也行,但是开发组的其他人一旦涉及到侦察机,要把长长的代码读一遍吗?有没有可能知道类的所有方法的简要描述?
可以用到接口interface,一个类可以执行(继承)多个接口,接口中定义的函数不能有函数体,执行接口的类必须将这些函数完整定义。
这样我们知道侦察机实现了飞行能力接口,必然有接口里面描述的飞行方法://隐形能力的接口

<?php 
  interface concealAbility
  {
    public function conceal();
  }
  //飞行能力的接口
  interface flyAbility
  {
    public function fly();
  }
  //侦察机类
  class Wraith implements flyAbility, concealAbility
  {
    //这个函数表示侦察机飞行的运行代码
    function fly()
    {
      //飞行的运行代码
    }
    //这个函数表示侦察机隐形的运行代码
    function conceal()
    {
      //隐形的运行代码
    }
  }
?>

八、总结

我们讨论了PHP面向对象的基本知识,通过星际争霸这一经典的游戏来说明,大家可以看到面向对象的初步作用。
我们看到通过面向对象可以使代码更加清晰,类将代码组织起来,比较方便的重复使用。
同时对象也减少了变量的冲突,方便相关性数据的保存和使用。
如果要解决的问题涉及很多方面,面向对象可以演化出更加灵活和有技巧的方式,比如通常提到的设计模式,和很多框架。
当然,面向对象也有缺点,从上面的代码可以看到,首先代码就多了,简单的任务如果定义许多类,反而麻烦。
对于简单任务,面向对象也可能使代码运行的效率降低。
深入的探讨,超出了本文的范围。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP注释实例技巧
Oct 03 PHP
PHP session有效期问题
Apr 26 PHP
php 运行效率总结(提示程序速度)
Nov 26 PHP
PHP函数篇之掌握ord()与chr()函数应用
Dec 05 PHP
PHP实现的英文名字全拼随机排号脚本
Jul 04 PHP
PHP+MySQL修改记录的方法
Jan 21 PHP
php使用explode()函数将字符串拆分成数组的方法
Feb 17 PHP
PHP配置把错误日志以邮件方式发送方法(Windows系统)
Jun 23 PHP
PHP类的声明与实例化及构造方法与析构方法详解
Jan 26 PHP
使用phpexcel类实现excel导入mysql数据库功能(实例代码)
May 12 PHP
php版微信返回用户text输入的方法
Nov 14 PHP
php实现数组中出现次数超过一半的数字的统计方法
Oct 14 PHP
PHP数据源架构模式之表入口模式实例分析
Jan 23 #PHP
TP3.2.3框架文件上传操作实例详解
Jan 23 #PHP
php的RSA加密解密算法原理与用法分析
Jan 23 #PHP
PHP检查文件是否存在,不存在自动创建及读取文件内容操作示例
Jan 23 #PHP
PHP实现一个按钮点击上传多个图片操作示例
Jan 23 #PHP
利用PHP内置SERVER开启web服务(本地开发使用)
Jan 22 #PHP
PHP读取文件,解决中文乱码UTF-8的方法分析
Jan 22 #PHP
You might like
php MYSQL 数据备份类
2009/06/19 PHP
深入mysql_fetch_row()与mysql_fetch_array()的区别详解
2013/06/05 PHP
php使用get_class_methods()函数获取分类的方法
2016/07/20 PHP
JS location几个方法小姐
2008/07/09 Javascript
Ext javascript建立超链接,进行事件处理的实现方法
2009/03/22 Javascript
javascript 通用简单的table选项卡实现
2010/05/07 Javascript
Javascript 绘制 sin 曲线过程附图
2014/08/21 Javascript
用Jquery.load载入页面后样式没了页面混乱的解决方法
2014/10/20 Javascript
vue中的模态对话框组件实现过程
2018/05/01 Javascript
vue+echarts实现动态绘制图表及异步加载数据的方法
2018/10/17 Javascript
JS eval代码快速解密实例解析
2020/04/23 Javascript
通过实例解析chrome如何在mac环境中安装vue-devtools插件
2020/07/10 Javascript
vue3.0 的 Composition API 的使用示例
2020/10/26 Javascript
JS addEventListener()和attachEvent()方法实现注册事件
2021/01/11 Javascript
[01:41]DOTA2 2015国际邀请赛中国区预选赛第三日战报
2015/05/28 DOTA
Python3里的super()和__class__使用介绍
2015/04/23 Python
python中self原理实例分析
2015/04/30 Python
Python2.x版本中cmp()方法的使用教程
2015/05/14 Python
Python3使用requests发闪存的方法
2016/05/11 Python
对Tensorflow中权值和feature map的可视化详解
2018/06/14 Python
python和mysql交互操作实例详解【基于pymysql库】
2019/06/04 Python
详解Python 切片语法
2019/06/10 Python
django的model操作汇整详解
2019/07/26 Python
python中pygame安装过程(超级详细)
2019/08/04 Python
python实现简单图书管理系统
2019/11/22 Python
Flask项目中实现短信验证码和邮箱验证码功能
2019/12/05 Python
小 200 行 Python 代码制作一个换脸程序
2020/05/12 Python
Tensorflow加载Vgg预训练模型操作
2020/05/26 Python
调用HTML5的Canvas API绘制图形的快速入门指南
2016/06/17 HTML / CSS
Luxplus荷兰:以会员价购买美容产品等,独家优惠
2019/08/30 全球购物
Java里面如何把一个Array数组转换成Collection, List
2013/07/26 面试题
《掌声》教学反思
2014/02/23 职场文书
债务授权委托书范本
2014/10/17 职场文书
入党培养人考察意见
2015/06/08 职场文书
十大公认最好看的动漫:《咒术回战》在榜,《钢之炼金术师》第一
2022/03/18 日漫
Apache SkyWalking 监控 MySQL Server 实战解析
2022/09/23 Servers