Мои два цента на эту тему: это правда, что вам нужно зависеть от абстракций (т. Е. интерфейсов), а не от реализаций.
Кстати, разве это не слишком далеко? Нет необходимости определять интерфейс для любого класса в вашей объектной модели. Обычно вы определяете интерфейсы, если вам нужно принять определенные объекты, выполняющие данный контракт.
Например, я бы не стал определять IAnimal
или ICat
интерфейсов. Вероятно, я бы определил абстрактный класс Animal
и только конкретный класс Cat
.
Если по какой-то причине мне нужно принять живых существ в какой-то API, которые могли бы съесть я бы определить интерфейс, как это:
public interface IFeedable
{
void Feed(Food food);
}
и если живое существо может говорить:
public interface ITalkative
{
void Talk(Food food);
}
Если нет признаков/свойств/поведения, которые могут быть исключительными для животных, я бы оставил эти интерфейсы , равно.
public abstract class Animal : ITalkative, IFeedable
{
public Animal(AudioPlayer audioPlayer)
{
AudioPlayer = audioPlayer;
}
private AudioPlayer AudioPlayer { get; }
public abstract void Feed(Food food);
public void Talk()
{
// Probably you would want to load an animal sound library
// here, and later pass the audio player with the sound library
// already loaded
OnTalk(AudioPlayer.LoadLibrary("animals"));
}
protected abstract void OnTalk(AudioLibrary audioLibrary);
}
public sealed class Cat : Animal
{
public Cat(AudioPlayer audioPlayer) : base(audioPlayer)
{
}
public override void Feed(Food food)
{
if(food is Vegetable)
{
throw new NotSupportedException("MeeEEEEooW (=O ò.ó)=O!!");
}
else if(food is Meat)
{
// Proceed to eat this meat!
}
}
protected override void OnTalk(AudioLibrary audioLibrary)
{
audioLibrary.Play("sweet-cat");
}
}
И если где-то вам нужно сделать объект говорить:
ITalkative talkative = some as ITalkative;
if(talkative != null)
{
talkative.Talk();
}
Или, если вам нужно кормить объект:
IFeedable feedable = some as IFeedable;
if(feedable != null)
{
feedable.Feed(new Vegetable());
}
Как вы можете видеть, вы не» t определять интерфейсы для всего, но только для тех вещей, которые вам нужно обрабатывать внутри некоторого API, и вам все равно, кто может делать какие-то действия и/или владеть некоторыми данными, но вы просто заботитесь о том, что объект может делать или подвергает определенные виды поведения и d ata соответственно.
Не могли бы вы добавить более общий метод для интерфейса, возможно, 'string speak()'.? Тогда какой бы класс не реализовал, он может иметь свое собственное сообщение. – tinstaafl
А как насчет классной рыбы, которая вообще не может говорить? – Eric
Тогда он может вернуть пустую строку. Или индикатор того, что звука нет. – tinstaafl