Объектно-ориентированное программирование

Классы, переменные экземпляра и методы

Если вы пытаетесь научиться разрабатывать приложения для iOS, я думаю, что это хорошее место для начала. Я подчеркиваю Я, потому что это всего лишь мое мнение. Полное раскрытие: я всего лишь один человек, который знает кое-что о нескольких вещах. В любом случае, давайте поговорим о парадигмах программирования.

Вопреки распространенному мнению, программисты могут и занимаются дизайном… но не так, как вы думаете. Когда в программировании говорят о дизайне, обычно имеют в виду структуру программы, а не то, как она выглядит. Парадигмы программирования существуют для обеспечения соблюдения определенного стиля программирования, который помогает программистам читать и понимать исходный код. Они также помогают определить возможности различных языков программирования. Каждая парадигма существует, чтобы представлять принципиально разные подходы к построению решений задач программирования. Существует много существующих парадигм: императивная, функциональная, декларативная, символическая, объектно-ориентированная и логическая, и это лишь некоторые из них). Я не буду обсуждать большинство этих парадигм, но есть тонны ресурсов, которые будут. В основном я хочу сосредоточиться на двух, пожалуй, самых важных парадигмах: процедурной и объектно-ориентированной.

Вы, наверное, заметили, что я не включил процедурное программирование в свой отнюдь не исчерпывающий список парадигм программирования. Это потому, что процедурное программирование технически является подмножеством императивного программирования. Опять же, есть множество ресурсов, которые могут более глубоко разобраться в том, что это значит, но вам *на самом деле* не нужно это знать. Во всех смыслах и целях это просто означает, что процедурное программирование основано на системе, которая использует операторы для изменения состояния программы (подробнее об этом позже).

Чтобы понять объектно-ориентированное программирование, мы должны сначала узнать о его более старшем родственнике, процедурном программировании. Эта парадигма опирается на процедуры, которые делают программирование более понятным и сокращают время разработки. Процедуры подобны длинному списку инструкций со всеми данными, определенными в одном месте, и всей логикой в ​​другом — что-то вроде рецепта в поваренной книге — все связаны областью действия. Эти процедуры были инкапсулированы в модули, которые также содержали подпрограммы, подпрограммы, функции, методы и т. д. Чем более сложные процедуры выполняли эти функции и чем больше данных требовалось обработать, тем длиннее и зачастую более непослушным становился код. Отличным примером процедурного языка программирования является C:

int add(int a, int b);
int main(){
int firstNum = 6;
int secondNum = 15;
int sum;
sum = add(firstNum,secondNum);
printf(“sum= “,sum);
return 0;
}
int add(int a,int b){
int result;
result = a + b;
return result;
}

Здесь у нас есть простая программа сложения, написанная на C. Вам не нужно понимать C, чтобы понять, что происходит, поскольку это довольно просто. Если вы посмотрите на функцию int add(int a, int b),у нас есть две переменные, int a и int b, которые будут складываться и сохраняться в другой переменной result. Вызов процедуры add(firstNum,secondNum)принимает определенные нами данные, int firstNum = 6, int secondNum = 15,и отправляет их в нашу функцию, которая выполняет процедура. Результат сохраняется в переменной с именем sum. Как только переменная sum получает значение, оно распечатывается для пользователя. Когда эта программа выполняется, считывание должно выглядеть как sum= 21.Довольно просто, правда? Процедурное программирование должно быть интуитивно понятным — оно действует так, как вы ожидаете, исходя из синтаксиса.

Если вы узнаете больше о C (что вы можете сделать бесплатно), вы обнаружите, что это чрезвычайно мощный язык. Как только вы добавите циклы и условные операторы, вы сможете сделать довольно много. Мы могли бы превратить нашу простую функцию сложения в калькулятор, если бы захотели. Но что произойдет, если мы захотим взять наш калькулятор и использовать его с другой программой, которую мы пишем? Поскольку процедуры ограничены областью действия, вам нужно будет копировать и обновлять строку кода за строкой — довольно утомительно, но выполнимо. Но представьте, что наш калькулятор на самом деле состоит из тысяч строк специализированного кода, а программа, в которую вы его добавляете, имеет разные типы переменных, новые значения и т. д. Что нам теперь делать? Мы могли бы часами кропотливо обновлять код или найти способ получше.

Стало ясно, что если бы выполняемые функции можно было обобщить, процессы были бы более эффективными и менее подверженными ошибкам; это был объектно-ориентированный способ мышления. Идея заключалась в том, чтобы разбить данные и логику на более мелкие группы или объекты, которые выполняли аналогичные операции и могли воспроизводиться бесконечно.

Предполагается, что объектно-ориентированный подход имитирует то, как мы описываем объекты в *реальном мире*, поэтому, чтобы понять идею объектно-ориентированного программирования, вы должны понимать, что именно подразумевается под словом объект.

Попросив кого-нибудь описать, что представляет собой объект, вы получите довольно расплывчатый ответ. За неимением лучшего термина объект — это просто вещь. Подумайте об этом так: яблоко и апельсин — это одно и то же, но очевидно, что это не одно и то же. Они могут быть частью похожей группы вещей, например фруктов или вещей, растущих на деревьях (подробнее о наследовании позже), но они разные. Главное понять, что эти вещи различны и имеют свою индивидуальность. Они объекты.

Скажем, вместо яблока и апельсина у вас есть два апельсина. Да, это обе вещи, и они одного типа, но мы знаем, что все апельсины не одинаковы. Хотя они могут состоять из одних и тех же частей, у одного апельсина есть разные характеристики или атрибуты — возможно, зрелость, ямочки на кожуре, количество долек или сладость — которые делают каждый апельсин своим собственным апельсином. . Эти апельсины также действуют независимо друг от друга; то, что вы очистили один апельсин, не означает, что вы очистили все существующие апельсины. Можно сказать, что оба апельсина являются экземплярами одного и того же объекта.

Теперь избавьтесь от одного из этих апельсинов; у вас есть только один апельсин, точнее апельсин-клементин. Основываясь на том, что вы знаете обо всех апельсинах, у вас есть ожидания относительно того, что ваш апельсин-клементин может сделать, созреть, и что вы можете с ним сделать, очистить его, съесть, или сока из него. Кроме того, существует определенный набор атрибутов, описывающих апельсин — у него есть кожура, сегменты и сладкий вкус. Если «апельсин» колючий снаружи, липкий внутри и соленый на вкус, можно с уверенностью сказать, что ваш «апельсин» вовсе не апельсин.

Теперь давайте попробуем соединить точки (к вашему сведению, это описывается в терминах Objective-C). Хотя объекты в смысле программирования не всегда так просты, как яблоки и апельсины, принципы, связанные с тем, как мы различаем эти вещи в реальном мире, являются основой объектно-ориентированного программирования. В нашем примере ваш клементиновый апельсин является объектом, и то, как мы описали его, его атрибуты, кожуру, мякоть, вкус и его поведение, созревание, кожура, сок, поедание, были по большей части унаследованы от атрибутов и поведения, связанных с апельсином. (Атрибут и поведение взаимозаменяемы с переменными экземпляра и методами соответственно).

Эти определения не являются объектами; они описывают класс, мы назовем его Orange. Этот класс в основном представляет собой схему того, что делает клементин апельсином. Клементиновый апельсин — это экземпляр нашего класса Orange. У вас может быть несколько клементин, но каждый клементин существует независимо от другого… то, что вы очистили один апельсин, не означает, что вы очистили все существующие апельсины.

Чтобы создать класс, вам нужно сообщить компьютеру пару вещей. Сначала нам нужно создать заголовочный файл, содержащий все наши свойства и определения методов, с именем orange.h. Это будет выглядеть примерно так:

// orange.h
@interface Orange:NSObject
//here we declare our properties, which are just attributes we’d like our objects to have
@property (nonatomic) NSString *taste;
@property (nonatomic) int numberOfSegments;
@property (nonatomic) double thicknessOfRindInInches;
@property (nonatomic) int ageInDays;
// here are the instance methods
- (void) peel;
- (void) eat;
- (void) isItRipe;
//here are the class methods
+ (Orange *)newAlloc;
+ (int) totalOranges;
@end

Вам также потребуется создать файл реализации. Здесь мы подробно расскажем о реализации каждого метода.

#import “Orange.h”
static int numOfOranges = 0;
@implementation Orange
- (void) peel
{
NSLog(@”You peeled the orange!”);
}
- (void) eat
{
NSLog(@”You ate the orange!”);
}
- (void) isItRipe
{
if (ageInDays>=5)
NSLog(@”The orange is ripe”);
else
NSLog(@”The orange isn’t ripe yet!”);
}
+ (Orange *) newAlloc
{
numOfOranges++;
return [Orange alloc];
}
+ (int) totalOranges
{
return numOfOranges;
}
@end

Итак, чтобы использовать этот план, вам нужно создать экземпляр. Мы собираемся создать экземпляр clementine. Для этого нам потребуется выделить место для экземпляра Orange в памяти в функции main():

#include “Orange.h”

int main (int argc, const char * argv[])
{
@autoreleasepool {
//create an instance of Orange
Orange *clementine;
//allocate and initialize the instance of clementine
clementine = [[Orange alloc] init];

//set the instance variables by sending a message to our getter methods 
[clementine setTaste = @”sweet”];
[clementine setNumberOfSegments = @12];
[clementine setThicknessOfRindInInches = @.35];
[clementine setAgeInDays = @6];
//call an instance method
[clementine peel];
//call a class method
int count = [Orange totalOranges];
//output result
NSLog (@”The total number of oranges is= %i”, count);
}
return 0;}

Теперь, когда у нас есть класс и экземпляр объекта, мы сформировали иерархию. Клементина — это подтип класса Апельсин. Свойства апельсина присущи клементине, но значения этой клементины, ее переменные экземпляра, уникальны для нее.

Используя эти унаследованные методы и атрибуты, которые мы назначили в нашем файле main.m, вы можете выполнять различные задачи. Например, наш оранжевый класс вызвал метод peel. peel — это метод экземпляра. Это означает, что для того, чтобы использовать или вызвать этот метод, вам необходимо предоставить ему или передать экземпляр Orange, в данном случае clementine . Вы можете подумать об этом так: можете ли вы очистить апельсин, не имея физического апельсина? Очевидно нет. Это также относится к методам eat и isItRipe (подробнее о методах и сообщениях здесь).

Видите метод с + вместо -? Это метод класса. Как следует из названия, это метод, который вызывается внутри класса. Я буду использовать пример totalOranges.

Вы можете вызвать метод totalOranges, даже если у вас еще не определен экземпляр Orange, потому что у вас есть доступ к статической переменной с именем numOfOranges, определенной в самом классе как ноль. Класс может ссылаться на себя для получения результата. Даже если мы вызовем метод totalOranges до того, как создадим экземпляр апельсина, мы все равно получим результат. В данном случае ноль.

Если бы мы скомпилировали эту программу, результат был бы (надеюсь) таким:

Ты почистил апельсин!

Общее количество апельсинов = 1

Теперь, когда у вас есть определенный класс, вы можете создать столько экземпляров класса Orange, сколько захотите.

Надеюсь, это дало вам представление о том, как работает объектно-ориентированное программирование, что такое классы и в чем разница между методами класса и экземпляра. Предлагаю почитать методы и сообщения, открыть Xcode и попробовать построить иерархию классов в Objective C. Как только вы освоитесь, можете даже перейти к более сложным учебникам.