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

本文实例讲述了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数组(Array)操作技巧大全》、《PHP基本语法入门教程》、《PHP运算与运算符用法总结》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》

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

(0)

相关推荐

  • PHP设计模式之建造者模式(Builder)原理与用法案例详解

    本文实例讲述了PHP设计模式之建造者模式(Builder)原理与用法.分享给大家供大家参考,具体如下: 这个建造者模式,我们也可以称为生成器模式,核心思想是将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式,简单点来说就是为了消除其它对象复杂的创建过程. 例如:汽车,他的发动机引擎有好多品牌,轮胎也有各种材质,内饰更是千奇百怪:鸟,他的头.翅膀以及脚有各种颜色和形状,在创建这种复杂对象的时候,我们建议使用建造者模式. 先来看一个案例来感受下什么

  • PHP设计模式之建造者模式定义与用法简单示例

    本文实例讲述了PHP设计模式之建造者模式.分享给大家供大家参考,具体如下: 建造者模式: 将复杂对象的创建过程和表示进行分离(好吧,我也看不懂什么意思1). 来点我人类能听懂的话: 1.在客户端看来需要的仅仅是实例化后的类对象(大多数情况下是需要类的属性). 2.传统方式下当类有了之后,一般直接通过new class()的方式直接进行实例化,然后通过$obj->set1()构建属性1,$obj->set2()构建属性2,$obj->set3()构建属性3... 3.传统方式有一个很大的弊

  • php设计模式 Proxy (代理模式)

    代理,指的就是一个角色代表另一个角色采取行动,就象生活中,一个红酒厂商,是不会直接把红酒零售客户的,都是通过代理来完成他的销售业务.而客户,也不用为了喝红酒而到处找工厂,他只要找到厂商在当地的代理就行了,具体红酒工厂在那里,客户不用关心,代理会帮他处理. 代理模式,就是给某一对象提供代理对象,并由代理对象控制具体对象的引用. 代理模式涉及的角色: 抽象主题角色,声明了代理主题和真实主题的公共接口,使任何需要真实主题的地方都能用代理主题代替. 代理主题角色,含有真实主题的引用,从而可以在任何时候操

  • 学习php设计模式 php实现建造者模式

    建造者模式可以让一个产品的内部表象和和产品的生产过程分离开,从而可以生成具有不同内部表象的产品. 一.Builder模式结构图 二.Builder模式中主要角色 抽象建造者(Builder)角色:定义一个抽象接口,规范产品各个组成成分的建造(即规范具体建造者的方法实现).其中所规范的方法中必须包括建造方法和结果返回方法 具体建造者(ConcreteBuilder)角色:实现抽象建造者角色所定义的方法.具体建造者与业务逻辑关联性较大,应用程序最终会通过调用此角色中所实现的建造方法按照业务逻辑创建产

  • php设计模式之建造器模式分析【星际争霸游戏案例】

    本文实例讲述了php设计模式之建造器模式.分享给大家供大家参考,具体如下: 星际里面有不少的任务关,也可以自己编辑地图,画面上有各种地形,建筑和部队. 这存在一个问题,初始化画面的流程很乱. 待解决的问题:完成初始化画面的工作,同时尽量减少各种绘制细节的耦合. 思路:既然星际的画面由几个部分组成:地图(就是地形和矿产),建筑,部队.那么我们把他们看成是零件,组装起来就是最后的产品(整个画面). 建造器(Builder)模式示例: <?php //规范制造各个零件的接口 interface Bui

  • php设计模式 DAO(数据访问对象模式)

    复制代码 代码如下: <?php /** * 数据访问对象(Data Access Object) 示例 * * @create_date: 2010-01-04 */ class BaseDAO { var $_db = null; var $_table = null; function BaseDAO($config) { $this->_db = new MysqlDB(); // 这里的不能进行操作 } /** * 获取处理 * * @param array $filter // 过

  • php设计模式 Template (模板模式)

    继承关系由于自身的缺陷,被专家们扣上了"罪恶"的帽子."使用委派关系代替继承关系","尽量使用接口实现而不是抽象类继承"等等专家警告,让我们这些菜鸟对继承"另眼相看".其实,继承还是有很多自身的优点所在.只是被大家滥用的似乎缺点更加明显了.合理的利用继承关系,还是能对你的系统设计起到很好的作用的.而模板方法模式就是其中的一个使用范例. GOF给模板方法(Template Method)模式定义一个操作中的算法的骨架,而将一些步

  • PHP设计模式之观察者模式(Observer)详细介绍和代码实例

    [意图] 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新[GOF95] 又称为发布-订阅(Publish-Subscribe)模式.模型-视图(Model-View)模式.源-监听(Source-Listener)模式.或从属者(Dependents)模式 [观察者模式结构图] [观察者模式中主要角色] 1.抽象主题(Subject)角色:主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者. 抽象主题提供了增加和

  • php设计模式之抽象工厂模式分析【星际争霸游戏案例】

    本文实例讲述了php设计模式之抽象工厂模式.分享给大家供大家参考,具体如下: 星际争霸是战略游戏,所以同样的兵种,敌我显示是不同的. 典型的就是鼠标的颜色,点中自己的物体的时候,鼠标颜色变成绿色,点中敌人的物体的时候,鼠标颜色变成红色. 还有就是每个物体的状态,点中自己的物体的时候,状态区显示完整的状态,点中敌人的物体的时候,状态区显示一部分信息. 我们假设只考虑鼠标和人族的运输船,玩家自己的运输船点中后状态区会显示里面装载的部队,而点中敌人的则不会显示里面是否装载部队. 这样我们就有四种对象:

  • php设计模式 Builder(建造者模式)

    复制代码 代码如下: <?php /** * 建造者模式 * * 将一个复杂对象的构建与它的表示分离,使用同样的构建过程可以创建不同的表示 */ class Product { public $_type = null; public $_size = null; public $_color = null; public function setType($type) { echo "set product type<br/>"; $this->_type =

  • php设计模式之工厂方法模式分析【星际争霸游戏案例】

    本文实例讲述了php设计模式之工厂方法模式.分享给大家供大家参考,具体如下: PHP手册上提到的工厂模式,其实是简单工厂模式.这里来讨论简单工厂模式的扩展:工厂方法模式. 待解决的问题:虽然简单工厂解决了动态返回不同类型对象的问题,但是实际情况当中,往往在新建一个对象的时候,需要做一些额外处理,比如制造机枪兵的时候需要判断水晶矿是否大于50,而制造火焰兵的时候需要同时判断水晶矿是否大于50和气矿大于25,还有是否建造了研究院.如果把这些代码全部放到工厂制造类里面,会使得制造类很臃肿,而且随着工厂

随机推荐