سؤال

I want to make the program with the following classes: Class Player, which stores some information about players with get/set functions. Player can be as AttackPlayer, which will have own data with get/set functions. Also Player can be as ProtectorPlayer, also with some other own data with get/set functions different than in AttackPlayer.

Also Player can be TeamPlayer or FreePlayer, each of these classes have own data etc.

The question is how to implement the hierarchy correctly?

At first I thought about multiple inheritance, which is not good anyway. Something like: Player AttackPlayer extends Player ProtectorPlayer extends Player

TeamPlayer extend AttackPlayer or ProtectorPlayer FreePlayer extend AttackPlayer or ProtectorPlayer

Also I thought something about strategy pattern, but it is not applicable here, because there are no common algorithms.

Are there any approaches which helps to organize such interaction?

Another way to do it is to have a field in the Player class, which helps to identify wether the TeamPlayer/FreePlayer is Attack or Protector type, and access appropriate fields depending on that. In this case inheritance would be like this:

Player TeamPlayer extends Player FreePlayer extends Player

Attack, Protect structs or classes without inheritance, but as fields in Player class.

But I don't like such an approach and I am searching for a better design.

هل كانت مفيدة؟

المحلول

IMHO inheritance is the wrong model for this. Instead I would have a player class and different roles for it. It depends if a player can have multiple roles at the same time. I would use a strategy pattern.

نصائح أخرى

How about composition and interface/dependency injection ?

<?php

interface IPlayer {
    public function __toString();
}

class Player implements IPlayer {
    protected $_id;
    protected $_name;

    public function __construct( $id, $name ) { 
        $this->_id = $id;
        $this->_name = $name;
    }
    public function getId() { return $this->_id; }
    public function setId($id) { $this->_id = $id; }
    public function setName($n) { $this->_name = $n; }
    public function getName() { return $this->_name; }

    public function __toString() {
        return 'my name is ' . $this->_name . ' and my id is ' . $this->_id;
    }
}

class ComposedPlayer implements IPlayer {
    protected $_player;

    public function __construct( IPlayer $p ) {
        $this->_player = $p;
    }
    public function __set($k, $v) {
        $this->_player->$k = $v;
    }
    public function __get($k) {
        return $this->_player->$k;
    }
    public function __call($func, $args) {
        return call_user_func_array( array( $this->_player, $func ), $args );
    }
    public function __toString() {
        return $this->_player->__toString();
    }
}

class AttackPlayer extends ComposedPlayer {
    function getNameMod() {
        return 'attack player ' . $this->getName();
    }
    public function __toString() {
        return parent::__toString() . ' and im an attack player';
    }
}

class ProtectedPlayer extends ComposedPlayer {
    function getNameMod() {
        return 'protected player ' . $this->getName();
    }
    public function __toString() {
        return parent::__toString() . ' and im an protected player';
    }
}

class TeamPlayer extends ComposedPlayer {
    function getIdMod() {
        return $this->getId() - 10;
    }
    public function __toString() {
        return parent::__toString() . ' and im an team player';
    }
}

class FreePlayer extends ComposedPlayer {
    function getIdMod() {
        return $this->getId() + 10;
    }
    public function __toString() {
        return parent::__toString() . ' and im an free player';
    }
}

$free_attack_player = new FreePlayer( new AttackPlayer( new Player( 100, 'John' ) ) );
$free_protected_player = new FreePlayer( new ProtectedPlayer( new Player( 101, 'Bob' ) ) );
$team_attack_player = new TeamPlayer( new AttackPlayer( new Player( 102, 'Bill' ) ) );
$team_protected_player = new TeamPlayer( new ProtectedPlayer( new Player( 104, 'Jim' ) ) );

foreach ( array( $free_attack_player, $free_protected_player, $team_attack_player, $team_protected_player ) as $p ) {
    echo 'id: ', $p->getId(), ' name: ', $p->getName(), ' mod id: ', $p->getIdMod(), ' mod name: ', $p->getNameMod(), PHP_EOL;
}

foreach ( array( $free_attack_player, $free_protected_player, $team_attack_player, $team_protected_player ) as $p ) {
    echo $p, PHP_EOL;
}

Executing this will yield:

id: 100 name: John mod id: 110 mod name: attack player John
id: 101 name: Bob mod id: 111 mod name: protected player Bob
id: 102 name: Bill mod id: 92 mod name: attack player Bill
id: 104 name: Jim mod id: 94 mod name: protected player Jim
my name is John and my id is 100 and im an attack player and im an free player
my name is Bob and my id is 101 and im an protected player and im an free player
my name is Bill and my id is 102 and im an attack player and im an team player
my name is Jim and my id is 104 and im an protected player and im an team player

EDIT: updated to add a __toString() to the interface, an easy example of method composition.

This may be a problem where TDD can come very handy. The approach seems slow at first, but it is very useful when the design in not obvious. TDD tends to cause the design to emerge from the code itself.

If you are interested in giving TDD a try, you can maybe start from this blog, written by our company coach. Also, if you can, take a look at the two episodes here related to TDD (episode 6a and 6b): they are from Uncle Bob, so really suggested lectures/watch.

Player can be TeamPlayer or FreePlayer

TeamPlayer extend AttackPlayer or ProtectorPlayer FreePlayer extend AttackPlayer or ProtectorPlayer

I think, that this approach is ok. Think about the storing data. If you want to store it in the db, it would be nice approach: you have Player table with all of the player data, and create other tables(that extend Player table) to store data for different types of players. Look at the Class Table Inheritance

In fact, inheritance hierarchy can be greater than one, but not more than 4. If inheritance is obvious, that is ok. The main rule is keeping complexity low.

On the other hand, if you are not going to store this data in db or foreign keys in player object to Free object etc. seems more obvious in your case - use another approach.

It can be mix of factory and Strategy as below:

Player can be Team Player or Free Player and can play using Attacker or Protector strategy. So Free Player or Team Player can inherit Player class and in them we can associate Playing strategy which can be Attacker or Protector. So in this case program initiator can call Factory to decide whether player is Free Player or Team Player and can pass the Strategy to use to play. Having strategy as association in Player will help keep code more extensible to add new strategies. Factory for type of player is separate from strategy so can extend independently. Using too much inheritance can cause issues in performance and maintenance of code.

Please do not take words like "extend" as java specific.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top