Передача всего класса в качестве параметра в другом классе

До сих пор я чувствовал, что понял концепцию и преимущества ООП-программирования, и у меня не было никаких трудностей с пониманием того, как работать с классами в 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 без указания на какой-либо конкретный метод или свойство, вы ссылаетесь на весь класс?

Если это то, что происходит, то зачем мне это делать? Если я создал игрока внутри своего игрового класса, то, конечно же, я могу просто получить доступ к своим свойствам и методам игрока в игровом классе, верно? Почему я хочу, чтобы мой игровой класс был внутри моего класса игрока? Могу ли я тогда, например, вызвать createPlayer() в классе Player?

Я извиняюсь, если мое объяснение было вообще запутанным.

Я думаю, мой вопрос сводится к; что именно я передаю в качестве параметра, и зачем мне это делать в повседневном ООП-программировании?


person Martyn Shutt    schedule 29.10.2012    source источник
comment
В какой-то момент он должен использовать переменную $g внутри класса Player. Слово Game в конструкторе является подсказкой типа, которая объекты класса Game передаются в этом параметре. Все остальное вызовет исключение.   -  person Michael Berkowski    schedule 29.10.2012
comment
Не логичнее ли было бы добавить Class Player extends Game. Я не эксперт по PHP, но почему это сделано именно так?   -  person Angelo A    schedule 29.10.2012
comment
@AngeloA Player не расширяет Game, скорее Game является зависимостью Player. Внутри конструктора Player публичный API Game будет доступен через переменную $g.   -  person Phil    schedule 29.10.2012
comment
Зачем мне нужен класс Game внутри класса Player? Это часто используется для создания двунаправленной ассоциации.   -  person Phil    schedule 29.10.2012


Ответы (3)


Это называется подсказкой типа, и он не передает весь класс в качестве параметра, а ratter намекает Class Player о типе первого параметра

PHP 5 вводит подсказку типа. Теперь функции могут принудительно задавать параметры как объекты (указав имя класса в прототипе функции), интерфейсы, массивы (начиная с PHP 5.1) или вызываемые (начиная с PHP 5.4). Однако, если NULL используется в качестве значения параметра по умолчанию, это будет разрешено в качестве аргумента для любого последующего вызова.

(Извлечено из руководства по php)

Означает ли это, что весь класс доступен внутри класса Player по ссылке?

Не весь класс, но вы можете получить доступ к экземпляру класса, который вы передаете в качестве параметра

person Bruno Vieira    schedule 29.10.2012
comment
Это довольно лаконично отвечает на мой вопрос. Спасибо. Я все еще не на 100% понимаю, что я мог бы сделать с этой ссылкой на экземпляр класса. Я могу повторить это с помощью print_r, но я не знаю, как получить доступ к каким-либо свойствам объекта. - person Martyn Shutt; 29.10.2012

Метод ДЕЙСТВИТЕЛЬНО ожидает получить объект, который является экземпляром Game чего-либо еще, и он выдаст ошибку.

Здесь вы передаете экземпляр Game: New Player($this, $name); Ключевое слово $this относится к экземпляру объекта, в котором вы находитесь.

И последнее... Я (и никто другой, если на то пошло) понятия не имею, почему автор это сделал, поскольку он не использует экземпляр Game после его передачи.

Зачем вам передавать экземпляр класса?
Представьте себе класс, который принимает пользователей в качестве входных данных и согласно некоторой логике что-то с ними делает. (Комментариев в коде нет, так как имя функции и имя класса говорят сами за себя)

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);
}
person Itay Moav -Malimovka    schedule 29.10.2012

/**
* 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(...){} Обработка исключений, которую должен знать любой ООП-разработчик
  • Реализован интерфейс Countable, позволяющий подсчитывать ($game) игроков.
  • Еще несколько приемов, которые дадут вам хорошее чтение

Следите за комментариями, и я надеюсь, что после прочтения ваш код станет более понятным.

person CodeAngry    schedule 29.10.2012
comment
Спасибо, что приложили усилия по переписыванию кода. Я обязательно попробую его в своей IDE и постараюсь понять все, что он делает. ООП все еще несколько пугает меня, так как я так долго занимался процедурами, но структурно я нахожу его гораздо более масштабируемым. Еще раз спасибо. - person Martyn Shutt; 29.10.2012