WHe*_*ann 6 c# polymorphism inheritance design-patterns
我正在努力坚持良好的OO设计原则和设计模式等.因此,在开发我的C#应用程序时,我经常可以找到多种解决方案来解决设计和架构问题,我总是希望找到并实现更"规范"的解决方案,以期构建高度可维护和灵活的软件,并成为更好的OO程序员.
所以假设我有这两个抽象类:Character和Weapon.
abstract class Weapon
{
public string Name { get; set; }
}
abstract class Character
{
public Weapon weapon { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
派生类可能是Sword和Staff从Weapon,并Warrior与Mage从Character等(这是所有假设,而不是与我的实际的软件!).
每个Character都有一个Weapon,但是对于每一个实现,Character我都知道Weapon它的具体实现.例如,我知道(我想强制执行!)在运行时,每个实例Warrior都有一个Weapon类型Sword.我当然可以这样做:
class Sword : Weapon
{
public void Draw() { }
}
class Warrior : Character
{
public Warrior()
{
weapon = new Sword();
}
}
Run Code Online (Sandbox Code Playgroud)
但是这样,每当我想在我的Weapon内部正确使用我的对象时Warrior,我必须执行一个演员,我相信这不是一个很好的练习.此外,我没有办法防止自己弄乱,也就是说,没有类型安全!
一个理想的解决方案是能够使用Weapon weapon属性覆盖Warrior类中的Sword weapon属性,这样我有类型安全性,如果用户使用my Warrior作为a Character,他仍然可以使用my Sword作为Weapon.可悲的是,C#似乎不支持这种结构.
所以这是我的问题:这是一种经典的OO问题,有一个名称和一个记录良好的解决方案吗?在那种情况下,我非常想知道问题的名称和解决方案.一些良好阅读材料的链接将非常有用!如果没有,您会提出什么样的课堂设计,以便以优雅和惯用的方式保持功能并强制实施类型安全?
谢谢阅读!
Eri*_*ert 12
更新:这个问题是我博客上一系列文章的灵感来源.感谢有趣的问题!
这是一种经典的OO问题,有一个名字和一个记录良好的解决方案吗?
你遇到的根本问题是在OO系统中很难为子类添加限制.说"一个角色可以使用武器",然后"战士是一种只能使用剑的角色"很难在OO系统中建模.
如果你对这背后的理论感兴趣,你会想要寻找"利斯科夫替代原则".(以及为什么"Square"不是"Rectangle"的子类型的任何讨论,反之亦然.)
然而,有办法做到这一点.这是一个.
interface ICharacter
{
public IWeapon Weapon { get; }
}
interface IWeapon { }
class Sword : IWeapon { }
class Warrior : ICharacter
{
IWeapon ICharacter.Weapon { get { return this.Weapon; } }
public Sword Weapon { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)
现在Warrior 的公共表面区域有一个永远是剑的武器,但任何使用该ICharacter界面的人都会看到一个Weapon类型的属性IWeapon.
这里的关键是摆脱"是一种特殊的"关系,走向"能履行契约"的关系.而不是说"战士是一种特殊的角色",而是说"战士是一个可以在需要角色时使用的东西".
所有这些说,你正在开始一个充满迷人问题的世界,在那里你会发现OO语言实际上代表这些东西是多么糟糕.一旦你开始提出更复杂的问题,例如"当一个战士使用锋利的剑来攻击穿着皮甲的兽人时会发生什么?",你很快就会发现自己陷入双重调度问题并学习如何用访客模式来解决它. " 并发现你有四个类层次结构 - 角色,武器,怪物,盔甲 - 所有这些都有影响结果的子类.虚方法只允许您在一个运行时类型上进行调度,但您会发现需要两个,三个,四个或更多级别的运行时类型调度.
它变得一团糟.考虑不要尝试在类型系统中捕获太多.
只需使用泛型类型约束!这是一个例子
public abstract class Weapon
{
public string Name { get; set; }
}
public interface ICharacter
{
Weapon GetWeapon();
}
public interface ICharacter<out TWeapon> : ICharacter
where TWeapon : Weapon
{
TWeapon Weapon { get; } // no set allowed here since TWeapon must be covariant
}
public abstract class Character<TWeapon> : ICharacter<TWeapon>
where TWeapon : Weapon
{
public TWeapon Weapon { get; set; }
public abstract void Fight();
public Weapon GetWeapon() { return this.Weapon; }
}
public class Sword : Weapon
{
public void DoSomethingWithSword()
{
}
}
public class Warrior : Character<Sword>
{
public override void Fight()
{
this.Weapon.DoSomethingWithSword();
}
}
Run Code Online (Sandbox Code Playgroud)