Вообще говоря:
Интерфейсы описывают методы, на которые будет реагировать объект. Это контракт, который объект обязуется выполнить.
Абстрактные классы описывают основные функциональные возможности и предоставляют специализированные функциональные возможности подклассу.
Таким образом, в основном вы используете интерфейс, когда хотите, чтобы объекты, отличающиеся по своей природе, реагировали на один и тот же конкретный метод.
И вы используете абстрактный класс, когда вам нужны специализированные версии какого-либо класса.
Допустим, вы хотите создать систему, в которой любой тип объекта может быть идентифицирован по уникальному идентификатору, и вам все равно, к какому классу они принадлежат.
Возможно, у вас есть:
Животные
Транспорт
Компьютерные гаджеты.
Whatever.
Поскольку это не связанные темы, вы можете выбрать реализацию и интерфейс, скажем:
public interface IIdentifiable
{
public long GetUniqueId();
}
И все классы, которые хотят выполнить этот контракт, будут реализовывать этот интерфейс.
public class IPod: IIdentifiable
{
public long GetUniqueId()
{
return this.serialNum + this.otherId;
}
}
public class Cat: IIdentifiable
{
public long GetUniqueId()
{
return this.....
}
}
Оба, и IPod, и Cat, имеют очень разную природу, но они оба могут реагировать на метод "GetUniqueId ()", который будет использоваться в системе каталогов.
Тогда это можно использовать так:
...
IIdentifiable ipod = new IPod();
IIdentifiable gardfield = new Cat();
store( ipod );
store( gardfield );
....
public void store( IIdentifiable object )
{
long uniqueId = object.GetUniqueId();
// save it to db or whatever.
}
С другой стороны, у вас может быть абстрактный класс, определяющий все общее поведение, которое может иметь объект, и позволяющий подклассу определять специализированные версии.
public abstract class Car
{
// Common attributes between different cars
private Tires[] tires; // 4 tires for most of them
private Wheel wheel; // 1 wheel for most of them.
// this may be different depending on the car implementation.
public abstract void move();
}
class ElectricCar: Car
{
public void move()
{
startElectricEngine();
connectBattery();
deploySolarShields();
trasnformEnertyToMovemetInWheels();
}
}
class SteamCar: Car
{
public void move()
{
fillWithWather();
boilWater();
waitForCorrectTemperature();
keepWaiting();
releasePreasure....
}
}
Здесь, два типа автомобилей, по-разному реализуют метод "переместить", но они имеют общие черты в базовом классе.
Чтобы сделать вещи более интересными, эти две машины могут также реализовать интерфейс de IIdentifiable, но при этом они просто берут на себя обязательство реагировать на метод GetUniqueId, а не по характеру машины. Вот почему Car сам не может реализовать этот интерфейс.
Конечно, если идентификация может быть основана на общих атрибутах, которые могут иметь автомобили, GetIdentifiableId может быть реализован базовым классом, и подклассы наследуют этот метод.
// case 1 ... каждый подкласс реализует интерфейс
public class ElectricCar: Car, IIdentifiable
{
public void move()
{
.....
}
public long GetUniqueId()
{
....
}
}
public class SteamCar: Car, IIdentifiable
{
public void move()
{
.....
}
public long GetUniqueId()
{
....
}
}
Случай 2, базовый класс реализует интерфейс, и подкласс получает от него выгоду.
public abstract class Car: IIdentifiable
{
// common attributes here
...
...
...
public abstract void move();
public long GetUniqueId()
{
// compute the tires, wheel, and any other attribute
// and generate an unique id here.
}
}
public class ElectricCar: Car
{
public void move()
{
.....
}
}
public class SteamCar: Car
{
public void move()
{
.....
}
}
Надеюсь, это поможет.