【问题标题】:Passing an entire Class as a parameter within another Class将整个类作为参数传递给另一个类
【发布时间】:2012-10-18 08:34:31
【问题描述】:

到目前为止,我觉得我已经理解了 OOP 编程的概念和优点,而且我在理解如何使用 PHP 中的类方面并没有遇到任何困难。

然而,这让我有点困惑。我想我可以理解它,但我仍然不确定。

我一直在关注一组视频教程(不确定链接到外部资源的规则,但我在 youtube 上找到了它们),而且它们很容易解释。除了,令人沮丧的是,当导师决定将一个类作为另一个类中的参数传递时。至少我认为这是正在发生的事情;

Class Game
{

    public function __construct()
    {
        echo 'Game Started.<br />';
    }
    public function createPlayer($name)
    {
        $this->player= New Player($this, $name);
    }
}


Class Player
{

    private $_name;

    public function __construct(Game $g, $name)
    {
        $this->_name = $name;
        echo "Player {$this->_name} was created.<br />";
    }
}

然后我正在实例化 Game 类的一个对象并调用它的方法;

$game = new Game();
$game-> createPlayer('new player');

令人沮丧的是,导师并没有真正解释他为什么这样做,而且据我所知,也没有显示代码中任何可以证明这一点的调用。

Player中的魔术方法构造函数是否作为引用传入Game类?这是否意味着整个类都可以在 Player 类中通过引用访问?当引用 $this 而不指向任何特定的方法或属性时,您是在引用整个类吗?

如果这是正在发生的事情,那我为什么要这样做?如果我在我的游戏类中创建了一个玩家,那么我当然可以在游戏类中访问我的玩家属性和方法,对吧?为什么我希望我的游戏类也包含在我的 Player 类中?例如,我可以在 Player 类中调用 createPlayer() 吗?

如果我的解释令人困惑,我深表歉意。

我想我的问题可以归结为:我作为参数传递的究竟是什么,为什么我要在每天的 OOP 编程中这样做?

【问题讨论】:

  • 在某些时候他必须使用Player 类中的变量$g。构造函数中的单词Game 是type hint,它强制只在该参数中传递Game 类的对象。其他任何事情都会引发异常。
  • 添加Class Player extends Game 不是更合乎逻辑吗?我不是PHP专家,但为什么会这样呢?
  • @AngeloA Player 没有扩展 Game,而是 Game 是 Player 的依赖项。在 Player 构造函数中,Game 的公共 API 将通过 $g 变量提供
  • “为什么我希望我的游戏类也包含在我的 Player 类中?” 这通常用于创建双向关联

标签: php oop methods


【解决方案1】:

该方法确实希望得到一个对象,它是Game 的实例,它会出错。

您在这里传递了一个Game 的实例:New Player($this, $name); 关键字$this 指的是您所在的对象实例。

最后....我(以及其他人)不知道作者为什么这样做,因为他在通过 Game 实例后没有使用它。

为什么要传递一个类的实例?
想象一个接受用户作为输入的类,并根据一些逻辑对他们做一些事情。 (代码中没有 cmets,因为函数名和类名应该是不言自明的)

class User{
  protected $name,$emp_type,$emp_id;
  protected $department;

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

  public function getEmpType(){
     return $this->emp_type;
  }

  public function setDep($dep){
    $this->department = $dep;
  }
}


class UserHub{

  public function putUserInRightDepartment(User $MyUser){
      switch($MyUser->getEmpType()){
           case('tech'):
                $MyUser->setDep('tech control');
                break;
           case('recept'):
                $MyUser->setDep('clercks');
                break;
           default:
                $MyUser->setDep('waiting HR');
                break;
      }
  }
}

$many_users = array(
    0=>new User('bobo','tech',2847),    
    1=>new User('baba','recept',4443), many more
}

$Hub = new UserHub;
foreach($many_users as $AUser){
   $Hub->putUserInRightDepartment($AUser);
}

【讨论】:

    【解决方案2】:
    /**
    * Game class.
    */
    class Game implements Countable {
        /**
        * Collect players here.
        * 
        * @var array
        */
        private $players = array();
    
        /**
        * Signal Game start.
        * 
        */
        public function __construct(){
            echo 'Game Started.<br />';
        }
    
        /**
        * Allow count($this) to work on the Game object.
        * 
        * @return integer
        */
        public function Count(){
            return count($this->players);
        }
    
        /**
        * Create a player named $name.
        * $name must be a non-empty trimmed string.
        * 
        * @param string $name
        * @return Player
        */
        public function CreatePlayer($name){
            // Validate here too, to prevent creation if $name is not valid
            if(!is_string($name) or !strlen($name = trim($name))){
                trigger_error('$name must be a non-empty trimmed string.', E_USER_WARNING);
                return false;
            }
            // Number $name is also not valid
            if(is_numeric($name)){
                trigger_error('$name must not be a number.', E_USER_WARNING);
                return false;
            }
            // Check if player already exists by $name (and return it, why create a new one?)
            if(isset($this->players[$name])){
                trigger_error("Player named '{$Name}' already exists.", E_USER_NOTICE);
                return $this->players[$name];
            }
            // Try to create... this should not except but it's educational
            try {
                return $this->players[$name] = new Player($this, $name);
            } catch(Exception $Exception){
                // Signal exception
                trigger_error($Exception->getMessage(), E_USER_WARNING);
            }
            // Return explicit null here to show things went awry
            return null;
        }
    
        /**
        * List Players in this game.
        * 
        * @return array
        */
        public function GetPlayers(){
            return $this->players;
        }
    
        /**
        * List Players in this game.
        * 
        * @return array
        */
        public function GetPlayerNames(){
            return array_keys($this->players);
        }
    } // class Game;
    
    
    /**
    * Player class.
    */
    class Player{
        /**
        * Stores the Player's name.
        * 
        * @var string
        */
        private $name = null;
    
        /**
        * Stores the related Game object.
        * This allows players to point to Games.
        * And Games can point to Players using the Game->players array().
        * 
        * @var Game
        */
        private $game = null;
    
        /**
        * Instantiate a Player assigned to a Game bearing a $name.
        * $game argument is type-hinted and PHP makes sure, at compile time, that you provide a proper object.
        * This is compile time argument validation, compared to run-time validations I do in the code.
        * 
        * @param Game $game
        * @param string $name
        * @return Player
        */
        public function __construct(Game $game, $name){
            // Prevent object creation in case $name is not a string or is empty
            if(!is_string($name) or !strlen($name = trim($name))){
                throw new InvalidArgumentException('$name must be a non-empty trimmed string.');
            }
            // Prevent object creation in case $name is a number
            if(is_numeric($name)){
                throw new InvalidArgumentException('$name must not be a number.');
            }
            // Attach internal variables that store the name and Game
            $this->name = $name;
            $this->game = $game;
            // Signal success
            echo "Player '{$this->name}' was created.<br />";
        }
    
        /**
        * Allow strval($this) to return the Player name.
        * 
        * @return string
        */
        public function __toString(){
            return $this->name;
        }
    
        /**
        * Reference back to Game.
        * 
        * @return Game
        */
        public function GetGame(){
            return $this->game;
        }
    
        /**
        * Allow easy access to private variable $name.
        * 
        * @return string
        */
        public function GetName(){
            return $this->name;
        }
    } // class Player;
    
    // Testing (follow main comment to understand this)
    $game = new Game();
    $player1 = $game->CreatePlayer('player 1');
    $player2 = $game->CreatePlayer('player 2');
    var_dump(count($game)); // number of players
    var_dump($game->GetPlayerNames()); // names of players
    

    以一种更好的方式重写您的代码,并添加了一些使该代码毫无意义的缺失变量:

    • 在玩家类中,您不存储游戏。
    • 在游戏类中,您只支持一名玩家。
    • 没有错误检查...任何地方。

    修复了所有这些加:

    • 添加了异常(以防止创建对象)
    • Try{} catch(...){} 任何 OOP 开发人员都应该知道的异常处理
    • 实现了 Countable 接口以允许 count($game) 玩家
    • 更多技巧,让您阅读愉快

    关注 cmets,希望您的代码在阅读后更有意义。

    【讨论】:

    • 感谢您重新编写代码。我肯定会在我的 IDE 中尝试一下,并尝试理解它所做的一切。 OOP 对我来说仍然有些令人生畏,已经做了这么长时间的程序,但在结构上我发现它更具可扩展性。再次感谢。
    【解决方案3】:

    它被称为type hinting,他没有将整个类作为参数传递,而是向类播放器提示第一个参数的类型

    PHP 5 引入了类型提示。函数现在能够强制参数为对象(通过在函数原型中指定类的名称)、接口、数组(自 PHP 5.1 起)或可调用(自 PHP 5.4 起)。但是,如果将 NULL 用作默认参数值,则允许将其作为任何后续调用的参数。

    (摘自php手册)

    这是否意味着整个类都可以在 Player 类中通过引用访问?

    不是整个类,但您可以访问作为参数传递的类的实例

    【讨论】:

    • 这很简洁地回答了我的问题。谢谢你。我仍然不是 100% 清楚我可以用类实例的引用做什么。我可以用 print_r 回显它,但我不知道如何访问任何对象的属性。
    猜你喜欢
    • 2023-01-18
    • 2020-03-14
    • 2013-10-13
    • 1970-01-01
    • 2021-01-19
    • 1970-01-01
    • 1970-01-01
    • 2011-03-31
    • 1970-01-01
    相关资源
    最近更新 更多