为什么要有接口和抽象类
开闭原则:
-
对扩展开放 新增功能、新需求时,可以新增代码、新增类,来扩展原有系统。
-
对修改关闭 已经写好、测试通过、正在运行的原有代码不要改,尽量不去动老逻辑,避免改出 Bug。
就是要封装确定的开发不确定的;
学习内容:
一、抽象类
1.1一句话定义
- 用
abstract修饰的类 - 不能直接 new 对象
- 可以包含:普通方法、抽象方法、字段、属性、构造方法
- 作用:做父类模板,强制子类必须实现某些方法
- 抽象方法就是没有大括号没有方法要做什么,就是正常声明加了
abstract修饰
1.2格式
cs
// 抽象类
public abstract class Animal
{
// 普通字段
public string Name { get; set; }
// 普通方法(有实现)
public void Eat()
{
Console.WriteLine($"{Name} 在吃东西");
}
// 抽象方法:没有方法体,必须加 abstract
public abstract void Shout();
}
子类必须要实现抽象方法,就是子类必须要override重写抽象方法
cs
public class Dog : Animal
{
// 必须实现抽象方法 Shout()
public override void Shout()
{
Console.WriteLine("汪汪汪");
}
}
public class Cat : Animal
{
public override void Shout()
{
Console.WriteLine("喵喵喵");
}
}
1.3推导过程
现在有一个猫和狗都会叫
cs
public class Cat
{
public void Shout()
{
Console.WriteLine("喵喵喵");
}
}
public class Dog
{
public void Shout()
{
Console.WriteLine("汪汪汪");
}
}
但是很麻烦可以直接建立一个动物类
cs
public class Animal
{
public string Name { get; set; }
public void Shout()
{
if(Name =="Cat")
{
Console.WriteLine("喵喵喵");
}
else if(Name == "Dog")
{
Console.WriteLine("汪汪汪");
}
}
}
但是这样如果后面要加别的动物也很麻烦,而且这样也违反了开闭原则,不是新功能也一直在修改原类;这时候就可以使用重写把这个Animal类作为基类,这样加一个动物就重写Shout方法就好了
cs
public class Animal //作为基类
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine("动物吃");
}
public virtual void Shout()
{
Console.WriteLine("动物叫");
}
}
public class Cat:Animal
{
public override void Shout() //重写
{
Console.WriteLine("喵喵喵");
}
}
public class Dog:Animal
{
public override void Shout() //重写
{
Console.WriteLine("汪汪汪");
}
}
现在Animal这个类一般也不会实例出来用Shout方法了那不如不写具体的方法把方法体删掉;这时候就是抽象方法了就把方法体删掉把virtual换成abstract 来修饰,类就变成了抽象类也要加abstract ,因为是基于重写的只是virtual换了所以派生类重写方法还是要有override;
而且现在派生类如果不重写Shout方法的话就会报错;
cs
public abstract class Animal //抽象类
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine("动物吃");
}
public abstract void Shout();//抽象方法
}
public class Cat:Animal
{
public override void Shout()
{
Console.WriteLine("喵喵喵");
}
}
public class Dog:Animal
{
public override void Shout()
{
Console.WriteLine("汪汪汪");
}
}
而且现在派生类如果不重写Shout方法的话就会报错;
cs
public abstract class Animal //抽象类
{
public string Name { get; set; }
public abstract void Shout();//抽象方法
}
public class Cat:Animal //这样就会报错
{
}
public class Dog:Animal
{
public override void Shout()
{
Console.WriteLine("汪汪汪");
}
}
二、接口
2.1 定义 接口是什么?
接口就是一份 "规矩"、一份 "合同"。 它只规定:必须做什么,不规定怎么做。
接口里只有 "要求",没有 "实现"
比如你跟快递约定:
- 必须能寄件
- 必须能查运费
这就是接口。
顺丰怎么做、京东怎么做,接口不管。你只要实现了这个接口,就必须按规矩写这两个方法。
注意接口的方法默认是public的因为接口里的所有方法都要被所有的派生类重写所以只能是默认public
2.2格式
用大写i开头 ,interface修饰
cs
public interface IExpress
{
void Send(); // 必须能寄
decimal GetFee(); // 必须能算运费
}
2.3推导
在抽象类里还有正常的方法和属性;那如果一个抽象类要全是抽象的方法呢
cs
public abstract class Animal
{
public string Name { get; set; }//常规属性
public void Eat()//常规方法
{
Console.WriteLine("动物吃");
}
public abstract void Shout();//抽象方法
}
public class Dog:Animal
{
public override void Shout()
{
Console.WriteLine("汪汪汪");
}
}
那就是接口了,因为接口可以理解为全是抽象方法的抽象类,所以可知:接口里的方法派生类必须要都要实现;而且这里不需要override了和继承差不多;
cs
interface iAnimal
{
void Shout();
}
public class Dog:iAnimal
{
public void Shout()
{
Console.WriteLine("汪汪汪");
}
}
2.4 作用
接口的功能用基类继承也可以做到为什么还要有接口呢?
2.4.1. 最核心原因:C# 类只能继承一个父类
现实世界里,一个东西往往有多种身份:
- 人:既是动物 ,又是劳动者 ,还是消费者
- 手机:既是通讯工具 ,又是相机 ,还是游戏机
但 C# 规定:一个类只能有一个直接父类,不能同时继承好几个类,但是可以继承无限的接口;
2.4.2. 接口代表 "能力",而不是 "种类"
- 类继承:是什么 (is a)
- Dog : Animal → 狗 是 动物
- 接口实现:能做什么 (can do)
- Dog : IRunnable → 狗 能跑
- Ca'r : IRunnable → 车 也能跑
它们不是一家人,但拥有同一种能力,就可以用同一个接口统一调用。有基类又有接口的时候基类要写在前面:
cs
class Player : GameObject, IMovable, IAttackable, IHealable//多接口
{
// 第一个GameObject是基类名字
}
没有基类的时候
class Player : IMovable, IAttackable, IHealable//多接口
{
}
三、抽象类继承、普通继承、接口使用
- 想强制子类必须实现方法 → 用 abstract 抽象类
- 想有默认实现,子类可选重写 → 用 普通类 + virtual
- 想多能力、多 "证书",(多个基类 )→ 用 接口
学习时间:
26.03.27