使用对象时从一种实现类型向另一种实现类型转换是否正确? ISP

问题描述 投票:2回答:1

我有一个简单的代码库,几乎没有'武器'具体类,该类实现了不同的合同以便被其客户使用。

我的合同:

public interface IWeaponPrimaryAttack
{
    void DoAttack();
}
public interface IWeaponSecondaryAttack
{
    void DoSecondaryAttack();
}
public interface IReloadable
{
    void Reload();
}

具体实施或实际武器:

public class Katana : IWeaponPrimaryAttack, IWeaponSecondaryAttack
{
    public void DoAttack(){Console.WriteLine ("Swing");}
    public void DoSecondaryAttack() {Console.WriteLine ("Stab");}
}
public class ShotGun : IWeaponPrimaryAttack, IReloadable
{
    public void DoAttack(){Console.WriteLine ("Swing");}
    public void Reload() {//reload it}
}

使用这些具体类的客户端:

public class PrimaryAttack
{
    private IWeaponPrimaryAttack _attack;
    public PrimaryAttack(IWeaponPrimaryAttack attack)
    {
        _attack = attack;
    }
    public void DoAttack()
    {
        _attack.DoAttack();
    }
}
public class SecondaryAttack
{
    private IWeaponSecondaryAttack _attack;
    public SecondaryAttack(IWeaponSecondaryAttack attack)
    {
        _attack = attack;
    }
    public void DoSecondaryAttack()
    {
        _attack.DoSecondaryAttack();
    }
}
public class WeaponReload
{
    private IReloadable _reloader;
    public WeaponReload(IReloadable reloader)
    {
        _reloader = reloader;
    }
    public void Reload()
    {
        _reloader.Reload();
    }
}

当然,只有在用户从多种武器中选择一种(ShotGun,Katana等中的一种)时,才能知道具体类别的实例化。

假设使用者选择ShotGun作为武器,根据选择,它可能像这样:

IWeaponPrimaryAttack weapon = new ShotGun(); // get it from a factory
PrimaryAttack primaryAttack = new PrimaryAttack(weapon);
primaryAttack.DoAttack();

现在WeaponReload必须在此处进行类型转换才能使用。

WeaponReload reloader = new WeaponReload ((IReloadable)weapon);
reloader.Reload();

我有疑问,

  • 我真的真的必须最终结束类型转换吗?
  • 或者有更好的方法来处理此对象创建部分。
  • 或者我可以提出一个更好的设计,不一定会以投射结束。
  • 或者像这样强制转换完全没问题。
c# oop design-patterns solid-principles
1个回答
1
投票

不确定为什么需要所有这些额外的委派包装器?无论如何,这里有一些事情在起作用。

最大化构图

您在这里为每种武器类型使用了具体的类型,但是您也可以更加强调构图,并拥有一个包罗万象的Weapon类,将其所有内部工作委托给strategies

例如

而不是Weapon shotgun = new Shotgun();,您可以将Weapon shotgun = Weapons.shotgun()的工厂方法显示为:

return new Weapon.Builder()
    .withPrimaryAttack(...)
    .withoutSecondaryAttack()
    .withSlowReload().build();

最大化构图可以使设计变得非常灵活,并且可以根据需要动态引入新的武器类型,甚至可以在运行时更改武器的某些方面(例如,shot弹枪现在可以使用b / c来发射拾起的强化武器)

跟随ISP

在上述基于合成的方法中,您可能会注意到Weapon的界面会因武器可以做的所有事情而变得肿。依赖于Weapon类的客户端将间接依赖于它们可能永远不会调用的所有各种隐式接口方法。

为了减少客户端耦合,您可以很好地确保Weapon功能被隔离到许多接口中,例如IReloadable等。Weapon类将全部实现它们,但是客户端代码仅对子集感兴趣的武器功能仍然可能取决于这些接口,而不是Weapon

例如

reload(weapon);

void reload(IReloadable reloadable) {
    if (stamina < ...) throw ...;

    reloadable.reload();
}

ISP和功能检测

考虑您的原始设计,我认为使用instanceof作为特征检测机制从根本上没有错。

使用instanceof匹配具体类型当然是错误的,但是匹配接口很可能就可以了。

weapon instanceof Shotgun //bad
weapon instanceof IReloadable //ok

请注意,在投射前应始终与instanceof核对。还请注意,您需要考虑一种使武器实施者知道他们可以实施的潜在武器功能接口集合的方法。

© www.soinside.com 2019 - 2024. All rights reserved.