面向对象编程(OOP)是一种编程范式,它使用“对象”来设计应用程序和程序结构。在OOP中,有两种主要的设计方法:继承和组合。这两种方法都旨在通过代码重用来提高软件的可维护性和可扩展性。本文将探讨这两种方法的定义、用途,并提供相应的代码示例,以帮助读者理解它们在实际编程中的应用。
组合:组合是一种设计方法,它允许一个类包含其他类的实例,以便使用这些实例来执行与当前类相关的操作。这通常被称为“有一个”关系,例如,一个人有走路的能力。
继承:继承是一种设计方法,它允许通过使用已经定义的类来创建新的类。这通常被称为“是一个”关系,例如,汽车是一种交通工具。
为了更好地理解这两种方法,可以通过一个简单的软件示例来展示它们。假设有一个“战士”类,这个类可以攻击另一个“战士”。
在继承的示例中,有两个“战士”,它们可以使用不同的攻击方式来攻击对方。
public abstract class Fighter {
public int Health = 100;
public int Damage;
public string AttackName;
public string Name;
protected Fighter(int damage, string attackName, string name) {
Damage = damage;
AttackName = attackName;
Name = name;
}
public void Attack(Fighter fighter) {
fighter.Health -= Damage;
}
}
public class WeakFighter : Fighter {
public WeakFighter() : base(5, "Weak punch", "Weak fighter") { }
}
public class StrongFighter : Fighter {
public StrongFighter() : base(30, "Strong punch", "Strong fighter") { }
}
这个解决方案可能不是设计得非常好,但它展示了如何使用继承。然而,这种方法存在一些问题。例如,如果程序中有很多战士,每个战士都有多种攻击方式,那么可能需要创建大量的子类。这可能会导致代码管理变得非常复杂。
在组合的示例中,让“战士”类依赖于它的行为,这些行为被封装在“移动”类中。
public class Fighter {
public int Health = 100;
public string Name;
private Move _move;
public string MoveName {
get {
return _move.Name;
}
}
public int MoveDamage {
get {
return _move.Damage;
}
}
public Fighter(string name, Move defaultMove) {
Name = name;
_move = defaultMove;
}
public void Attack(Fighter defendant) {
if (_move != null)
_move.Attack(defendant);
}
public void SetMove(Move move) {
if (move != null)
_move = move;
}
}
public abstract class Move {
public int Damage { get; set; }
public string Name { get; set; }
protected Move(int damage, string name) {
Damage = damage;
Name = name;
}
public void Attack(Fighter defendant) {
defendant.Health -= Damage;
}
}
public class PunchMove : Move {
public PunchMove() : base(5, "Punch") { }
}
public class KickMove : Move {
public KickMove() : base(7, "Kick") { }
}
这个解决方案更好,因为它允许“战士”类依赖于“移动”类来攻击对手。这样,移动可以在运行时轻松更改,而不需要事先明确声明。