Многие из вас, читая учебник по программированию, сталкивались с таким примером:
/*
** Inheritance is useful when different classes share functionality. */
using System; class Animal
{
protected string speakSound = "";
public void Speak() {
Console.WriteLine(speakSound);
}
}
class Cat : Animal
{
public Cat() {
this.speakSound = "Meow";
}
}
class Dog : Animal
{
public Dog() {
this.speakSound = "Woof";
}
}
class ZooClass
{
static void Main(string[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.Speak(); // Prints Meow
dog.Speak(); // Prints Woof
}
}
А многие, прочитав этот пример, подумают:
«Да, я знаю, как использовать это в реальном приложении»
Этот пример объясняет, как работает наследование, но не дает никаких подсказок о том, какие проблемы вы можете решить с его помощью и почему это лучше, чем альтернативный подход к проблеме.
Это все равно, что объяснять молоток как «полезный инструмент, у которого есть деревянная часть и металлическая часть, и при ударе чего-то металлической частью он начинает биться».
Шаблоны и методы программирования - это инструменты, которые используются, чтобы упростить реализацию сложного поведения или логики, сохраняя при этом поддерживаемость кода и легкость его построения.
Большинство примеров из реальной жизни могут быть слишком сложными для начинающих, но я думаю, что существует золотая середина между примерами из реальной жизни из 1000 строк и «Корова идет мычание».
Обучение на реальном примере из реального проекта
Начни с реальной проблемы. Если возможно, в реальном проекте (без каких-либо деталей, не имеющих отношения к рассматриваемой проблеме)
Движок стратегии в реальном времени с открытым исходным кодом для ранних игр Westwood, таких как Command & Conquer: Red Alert, написанный на C »
В этой игре вы можете построить отряд под названием «Шпион», который может проникать в здания врага и, в зависимости от того, в какое здание проникают, может вызывать различные эффекты.
Некоторые примеры включают (из CNC Wiki):
Аэродром Отображает производимый в настоящее время.
Юнит при выборе здания.Отображает количество боеприпасов на приземлившихся самолетах.
Силовая установка Отображает вертикальный индикатор состояния силы врага в виде пяти квадратов.
Завод по переработке руды Отображает показатель мощности и количество руды, находящейся на данный момент.
Теперь вот проблема: как мне реализовать различные эффекты проникновения?
// Not like this
if (building.name == "Airfield") { airfieldInfiltration(); }
if (building.name == "Power Plant") { powerPlantInfiltration(); }
if (building.name == "Ore Refinery" { oreRefineryInfiltration(); }
В большинство зданий невозможно проникнуть, поэтому создание базового класса не кажется правильным, но все здания имеют совершенно разные действия, поэтому объединение всего в один класс тоже не кажется правильным.
После того, как читатель поймет проблему, вы представите решение:
Интерфейсы
OpenRA использует интерфейс под названием INotifyInfiltrated (объявленный здесь), который используется для реализации различных функций, когда шпион входит во вражеское здание.
Интерфейс реализован для любых зданий, в которые можно проникнуть, и поведение эффекта обнаруживается внутри каждого из различных классов зданий, а не в базовом классе или некотором огромном объекте операторов if-else.
Пример кражи денег с использованием интерфейса можно найти здесь. Обратите внимание, что поведение заключено в свои собственные классы, потому что здания не запрограммированы жестко, поэтому он не строго следует приведенному примеру, но нет причин, по которым это невозможно.
public interface INotifyInfiltrated {
void Infiltrated(Player playerInfiltrating);
}
class Airfield : INotifyInfiltrated
{
public void Infiltrated(Player playerInfiltrating) {
// Displays currently produced unit when
// the building is selected
Unit producingUnit = this.GetCurrentlyProducingUnit()
producingUnit.revealToPlayer(playerInfiltrating);
// Displays amount of ammunition on
// currently landed aircraft
Unit landedUnit = this.GetCurrentlyLandedUnit();
landedUnit.revealStatsToPlayer(playerInfiltrating);
}
}
class PowerPlant : INotifyInfiltrated
{
public void Infiltrated(Player playerInfiltrating) {
// Displays a five-box vertical indicator
// of enemy power status
this.revealPowerStatusToPlayer(playerInfiltrating);
}
}
class OreRefinery : INotifyInfiltrated
{
public void Infiltrated(Player playerInfiltrating) {
// Displays capacity indicator
//and amount of presently held ore
this.revealOreStatusToPlayer(playerInfiltrating);
}
}
Теперь читатель увидел реальный пример того, как интерфейс используется для решения реальной проблемы программирования. Они могут применять это всякий раз, когда сталкиваются с подобной проблемой, когда необходимое поведение «одинаково для некоторых, но не для всех».
Так что, прочитав это, возможно, в следующий раз, когда кто-то спросит об интерфейсах, вам не придется заставлять бедных виртуальных кошек и собак лаять и мяукать, чтобы выразить свою точку зрения.
Спасибо за просмотр! С тобой было интересно, оставь комментарий и подпишись. Еще больше интересного будет.