我想用以下类创建程序: 类播放器,它存储有关具有get / set功能的玩家的一些信息。 播放器可以作为 AttackPlayer ,它将拥有带有get / set功能的数据。 播放器也可以像 ProtectorPlayer 一样,还有一些其他自己的数据,其get / set函数与 AttackPlayer 不同。
播放器也可以是 TeamPlayer 或 FreePlayer ,每个类都有自己的数据等。
问题是如何正确实现层次结构?
起初我想过多重继承,无论如何都不好。 就像是: 播放机 AttackPlayer扩展了Player ProtectorPlayer扩展了Player
TeamPlayer扩展AttackPlayer或ProtectorPlayer FreePlayer扩展AttackPlayer或ProtectorPlayer
我也想过一些关于策略模式的东西,但它不适用于此,因为没有常见的算法。
是否有任何方法可以帮助组织此类互动?
另一种方法是在Player类中有一个字段,这有助于识别TeamPlayer / FreePlayer是Attack还是Protector类型,并根据它来访问适当的字段。 在这种情况下,继承将是这样的:
播放器 TeamPlayer扩展了Player FreePlayer扩展了播放器
攻击,保护没有继承的结构或类,但作为Player类中的字段。
但我不喜欢这种方法而且我正在寻找更好的设计。
答案 0 :(得分:6)
IMHO继承是错误的模型。相反,我会有一个球员类和不同的角色。这取决于玩家是否可以同时拥有多个角色。我会使用策略模式。
答案 1 :(得分:2)
composition和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;
}
执行此操作将产生:
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
编辑:更新以向界面添加__toString(),这是一个简单的方法组合示例。
答案 2 :(得分:0)
这可能是TDD非常方便的问题。这种方法起初似乎很慢,但是当设计不明显时它非常有用。 TDD倾向于使设计从代码本身中浮现出来。
如果您有兴趣尝试TDD,可以从我们公司教练编写的this blog开始。另外,如果可以,请看一下与TDD相关的两集here(第6a和6b集):它们来自Uncle Bob,所以真的建议讲座/观看。
答案 3 :(得分:0)
玩家可以是TeamPlayer或FreePlayer
TeamPlayer扩展AttackPlayer或ProtectorPlayer FreePlayer扩展AttackPlayer或ProtectorPlayer
我认为,这种方法还可以。想想存储数据。如果你想将它存储在数据库中,那将是一个很好的方法:你有一个包含所有玩家数据的Player表,并创建其他表(扩展Player表)来存储不同类型玩家的数据。 查看Class Table Inheritance
实际上,继承层次结构可以大于1,但不能超过4.如果继承很明显,那就没问题。主要规则是保持低复杂性。
另一方面,如果你不打算将这些数据存储在数据库中,或者将玩家对象中的外键存储到Free对象等,在你的情况下似乎更明显 - 使用另一种方法。
答案 4 :(得分:0)
它可以是工厂和策略的混合,如下所示:
玩家可以是团队成员或免费玩家,可以使用攻击者或保护者策略进行游戏。因此,Free Player或Team Player可以继承Player类,在其中我们可以关联可以是Attacker或Protector的Play策略。因此,在这种情况下,程序启动器可以调用Factory来决定玩家是自由玩家还是团队玩家,并且可以通过策略来使用。将策略作为Player中的关联将有助于保持代码更具可扩展性以添加新策略。玩家类型的工厂与策略是分开的,因此可以独立扩展。使用过多的继承可能会导致性能和代码维护方面的问题。
请不要像“特定于java”那样使用“extend”这样的词语。