开闭原则:如果不是为了修改bug或者优化/增添功能,不要乱改一个类的代码。
即,应该封装那些稳定的固定的确定的成员,把不确定的成员作为抽象成员,给子类实现。
一开始,为了减少重复代码和尽量减少对类的修改,引入了基类。
class Vehicle
{
private int _speed;
public virtual int Speed { get { return _speed; } set { _speed = value; } }
public void Fill()
{
Console.WriteLine("Add oil......");
}
public void Stop()
{
Console.WriteLine("Stopped......");
}
public virtual void Run()
{
Speed += 100;
Console.WriteLine("A vehicle is running!");
}
}
class Car : Vehicle
{
private int _rpm;
public override int Speed { get { return _rpm / 100; } set { _rpm = value * 1000; } }
public override void Run()
{
_rpm += 5000;
Console.WriteLine("A car is running!");
}
}
class Truck : Vehicle
{
public override void Run()
{
Console.WriteLine("A truck is running!");
}
}
}
后来,又因为基类的那个原来的方法也从不调用,为了减少维护代价,增加代码可读性,引入抽象类:
abstract class Vehicle
{
……
public abstract void Run();
}
抽象类事例如上。
抽象类是指函数成员没有被完全实现的类。类里面一旦有了抽象成员,类整体就必须用abstract修饰。抽象类不能实例化,故而,抽象类只能作为基类来派生子类,或者用于引用子类的实例。
抽象类的抽象成员最终一定由其子类实现,故函数成员不能是private。
其中,由子类实现,指的是在继承链上实现即可,没有要求其中某一个子类必须实现所有抽象的成员。
但是必须在缺少的子类前面加一个abstract。
也有全部成员都抽象的抽象类。
abstract class VehicleBase
{
public abstract void Fill();
public abstract void Stop();
public abstract void Run();
}
abstract class Vehicle : VehicleBase
{
public override void Fill()
{
Console.WriteLine("Add oil......");
}
public override void Stop()
{
Console.WriteLine("Stopped......");
}
}
这时候这个玩意就相当于接口,甚至可以直接改成接口。只需把:
abstract class VehicleBase
{
public abstract void Run();
public abstract void Fill();
public abstract void Stop();
}
改成
interface IVehicle
{
void Run();
void Fill();
void Stop();
}
且删除下面所有的override即可,并且在缺斤少两的类中加一句:
interface IVehicle
{
void Fill();
void Stop();
void Run();
}
abstract class Vehicle : IVehicle
{
public void Fill()
{
Console.WriteLine("Add oil......");
}
public void Stop()
{
Console.WriteLine("Stopped......");
}
abstract public void Run();
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("A car is running!");
}
}
}
接口里面的成员全都是抽象且公开的。
抽象类和接口都是软件工程的产物。
从具体类到抽象类再到接口,代码形式越来越抽象,内部实现的逻辑越来越少。
抽象类是未完全实现功能的类,可以有字段和非public成员,代表了“具体逻辑”。
抽象类为复用而生,也专门作为基类使用,同时具有解耦功能。
它体现了开闭原则:封装确定的,开放不确定的,推迟到合适的子类中去实现。
而接口是完全未实现逻辑的“类”(纯虚类)
接口为解耦而生。