我正在开发一款游戏,并且存在一个扩展更多基础类的Player
类。玩家可以导航,触发,获取英特尔,构建等。目前,所有这些功能都在一个类中定义,随着功能的增长,源代码和样式变得越来越麻烦。
我想创建一些派生的专用类,如class Navigator extends Player
,class Fighter extends Player
等,这将帮助我通过主题跨多个源模块传播所有功能,以方便他们的维护。
同时,我希望能够只创建一个Player
类的实例,因为游戏玩法也需要明确使用它的功能。所以我梦想着一些事情
$p = new Player();
$p->SomethingGeneric();
((Fighter) $p)->Fire();
((Navigator) $p)->MoveTo();
也就是说,我想调用派生类的方法而不显式创建它们的单独实例,因为专门的方法也会调用Player
方法并依赖它的属性。
所以我用谷歌搜索了一些语言魔法,比如php中的多态,接口和类型转换,但最简单,最优雅的解决方案尚未找到。优雅我的意思是每当我想添加派生类可以做的事情时,我只改变它的定义并在主项目中进行方法调用。要么我不太观察,要么我发现的所有多态性的例子只显示派生类的实例如何假装彼此,这不是这里的情况。
提前谢谢。
upd1:似乎我对游戏设计不够清楚,我的意思是什么。
这里,导航,战斗和建筑不是与玩家类相关的互斥功能。没有球员类。用户控制玩家可用的动作相同。根据情况和风景,用户可以选择导航,战斗,建造或其他任何东西。更重要的是,用户可以选择做一些不够专业的杂项,以便被一个动作组分开,例如将其他玩家ID解析为他们的名字等。
因此,每个用户只有一个Player
对象(我现在这样做)似乎很自然,但单类设计迫使我以某种方式实现播放器功能,例如
public function Navigation_MoveTo()
public function Navigation_GetETA()
public function Combat_FireWeapon()
public function Combat_RechargeHP()
public function Intel_GetLocalChat()
这是UGLY,因为它迫使我要么在一个文件中拥有数千个源代码行中的所有播放器代码,要么做一些包含变换。
因此问题是:有没有办法在其上面添加一层优雅抽象,以便我可以在单独的源文件中编写从Player
派生的导航,战斗等类,同时只使用一个实例每个用户Player
个班级,因为我需要在其上调用Player
AND Navigator
和Fighter
方法,具体取决于用户在任何特定时刻选择执行的操作。
我希望再澄清一点。
答案 0 :(得分:1)
<强>更新强>
我建议调查traits
。使用traits将允许您重用保存在更小,更易管理的组件中的代码,但仍将其功能注入多个类(并且使单个类具有使用多个特征的能力)。我可以举例说明你的例子:
trait FightingTrait {
protected $weapon;
public function fireWeapon()
{
$this->weapon->fire();
}
}
trait NavigatingTrait {
public function moveTo($location){ /* Implementation */ }
public function eta(){ /* Implementation */ }
}
class Player
{
use FightingTrait, NavigatingTrait;
}
如果您碰巧有其他可能需要触发或移动的东西,您也可以利用这些特性:
class Vehicle {
use FightingTrait, NavigatingTrait;
}
<强>上强>
我不认为你想这样做。乍一看,我认为这是对Liskov Substitution Principle的根本违反。非常一般:
如果S是T的子类型,则T的对象可以用S类对象替换。
不双向:您无法将S
替换为T
,因为S
可以添加{{1}上无法使用的功能}}。你有一个具体的T
对象(Player
)并且你试图强迫该对象属于你的一个子类型(T
),这不是继承应该如何工作的。 战斗机 是 播放器,但播放器不一定是战斗机。它也可以是 Navigator ,或者您将来可能需要的任何其他潜在子类型。
如果你想维护这个继承链(S
,Fighter > Player
),那么我建议改为设计它有点不同。将Navigator > Player
设为Player
类,因为您希望将其用于对所有不同基本类型中 shared 的功能进行分组。您的所有不同玩家类型是否都可以abstract
或MoveTo()
?然后它属于Fire()
类。如果Player
可以执行不同的Navigators
不能(反之亦然),那么该逻辑属于子类型而不属于共享父级。
请注意,在这种情况下,Fighters
是抽象的,您永远不会实际实例化Player
类型的对象。您只想拥有一个实例,这很好,但要使Player
实例成为具体基类之一(例如Player
或Fighter
),因为那些类型是< / em> a Navigator
。