面试题答案
一键面试接口与抽象类在设计原则方面的主要区别
- 定义与实现
- 接口:接口仅定义成员的签名,不包含成员的实现。接口成员默认是公共的(public),且不允许使用访问修饰符。例如:
public interface IMyInterface
{
void MyMethod();
}
- **抽象类**:抽象类可以包含抽象成员和非抽象成员。抽象成员只有声明,没有实现,需要在派生类中实现;非抽象成员则有具体的实现。抽象类中的抽象成员必须使用 `abstract` 关键字声明,且访问修饰符可以是 `public`、`protected` 等。例如:
public abstract class MyAbstractClass
{
public abstract void AbstractMethod();
public void ConcreteMethod()
{
Console.WriteLine("This is a concrete method.");
}
}
- 继承与实现
- 接口:一个类可以实现多个接口,实现接口的类必须实现接口中定义的所有成员。这使得接口在实现多态性方面具有更大的灵活性,尤其适用于需要多个不同类型行为的场景。例如:
public class MyClass : IMyInterface
{
public void MyMethod()
{
Console.WriteLine("Implementation of MyMethod in MyClass.");
}
}
- **抽象类**:一个类只能继承一个抽象类。继承抽象类的非抽象类必须实现抽象类中的所有抽象成员。抽象类更强调类型之间的继承关系,适用于具有共同属性和行为的类型层次结构。例如:
public class MyDerivedClass : MyAbstractClass
{
public override void AbstractMethod()
{
Console.WriteLine("Implementation of AbstractMethod in MyDerivedClass.");
}
}
- 成员修饰符
- 接口:接口成员不能使用任何访问修饰符,它们隐式地是公共的。这是因为接口的目的是定义外部可访问的契约。
- 抽象类:抽象类中的成员可以使用不同的访问修饰符,如
public
、protected
、internal
等。protected
成员可以在派生类中访问,这为派生类提供了一定的灵活性,同时保护了基类的实现细节。
- 可实例化性
- 接口:接口不能被实例化,它只是一个契约定义。
- 抽象类:抽象类同样不能被实例化,但它可以包含构造函数,用于初始化派生类共有的成员。
应用场景选择
- 优先选择接口的场景
- 多态行为组合:当一个类需要表现出多种不同类型的行为,并且这些行为之间没有直接的继承关系时,应优先选择接口。例如,一个
Bird
类可能既需要实现IFlyable
接口(表示飞行行为),又需要实现ISwimmable
接口(表示游泳行为)。
- 多态行为组合:当一个类需要表现出多种不同类型的行为,并且这些行为之间没有直接的继承关系时,应优先选择接口。例如,一个
public interface IFlyable
{
void Fly();
}
public interface ISwimmable
{
void Swim();
}
public class Duck : IFlyable, ISwimmable
{
public void Fly()
{
Console.WriteLine("Duck is flying.");
}
public void Swim()
{
Console.WriteLine("Duck is swimming.");
}
}
- **插件式架构**:在构建插件式系统时,接口非常有用。不同的插件可以实现相同的接口,使得系统能够动态地加载和使用这些插件。例如,一个图形绘制系统可能定义一个 `IDrawable` 接口,不同的图形类(如 `Circle`、`Rectangle`)实现该接口,系统可以根据需要动态加载和绘制不同的图形。
2. 优先选择抽象类的场景
- 共享实现与状态:当一组相关的类具有一些共同的属性、方法实现或状态时,应优先选择抽象类。例如,在一个游戏开发中,所有的游戏角色可能都继承自一个抽象的 Character
类,该抽象类包含一些共同的属性(如生命值、攻击力)和方法(如移动方法)的实现。
public abstract class Character
{
protected int health;
protected int attackPower;
public Character(int health, int attackPower)
{
this.health = health;
this.attackPower = attackPower;
}
public void Move()
{
Console.WriteLine("Character is moving.");
}
public abstract void Attack();
}
public class Warrior : Character
{
public Warrior(int health, int attackPower) : base(health, attackPower) { }
public override void Attack()
{
Console.WriteLine("Warrior attacks with power {0}", attackPower);
}
}
- **定义类型层次结构**:当需要定义一个清晰的类型层次结构,并且希望在基类中提供一些通用的行为和属性时,抽象类是更好的选择。例如,在一个动物分类系统中,`Animal` 抽象类可以作为所有动物类的基类,定义一些共同的属性和行为,如 `Eat` 方法,而具体的动物类(如 `Dog`、`Cat`)继承自 `Animal` 类并实现其抽象成员。