"Магия" программирования

Большинство технологий, которые включают программирование, на первый взгляд кажутся невероятной магией, а на самом деле созданы на стыке математики и электроники. Обе эти составляющие по отдельности – совершенно бесполезны.

Без ПО самый мощный компьютер - просто груда мигающего и гудящего хлама. Точно также и программное обеспечение — вы можете распечатать его и даже записать шариковой ручкой. Может быть даже ваши записи будут неплохо выглядеть, но без компьютера они останутся просто бессмысленными каракулями.

Но пока у нас есть наши родные ПК, работающие в двоичном коде, программ будет только больше и они будут становиться только сложнее. Так что давайте их изучать — поверьте, пригодится.

Волшебство ООП

Объектно-ориентированное программирование — мощнейший инструмент разработчика. Писать на современных языках гораздо удобнее, чем, допустим, на языке C. Например, для того чтобы вывести пользователю целое число, а не дробь, которая «крутилась» в уравнении, мне пришлось немного подумать. И написал я вот что:

Допустим, у нас есть некая переменная y, куда мы передали дробное значение:
float y = 2.0  
Проверить, целое это число или нет мы должны простыми средствами. Для этого создадим пустую переменную x, инициализируем её нулем и напишем такой код:
float a = y;
int x = 0;
x = (int)y;
y--=x;

if (x == 0) {
printf (“Наша переменная равна  %d”, x);
}
else {
printf (“Наша переменная равна %f”, a);
}

Мы сделали очень простую вещь — привели данные к целому виду, а затем отняли от нашей дроби. Потом написали условие, по которому в случае отсутствия реального остатка пользователь увидит  вместо дроби целое число. Этот маленький «хак» работает, но не в каждом случае под рукой имеется простое и эффективное решение.

 

Объектно-ориентированное программирование избавлено от этих хлопот. Там чаще всего большинство технологий представлено в готовом виде. Так, например, в языке Java легко проверить корректность ввода методом parseInt из класса Integer пакета Java.lang. При вводе нецелого числа или вообще чего угодно, программа выдаст ошибку (исключение), которую программисту необходимо обработать должным образом — поместить туда фразу «вы допустили ошибку», перебросить пользователя обратно к окну ввода или даже дать ему ещё несколько пробных попыток. Но, собственно, мы сегодня не об этом, а о «волшебстве».

Магические классы

Самым настоящим чудом в ООП является тот факт, что программу можно писать не одним бесконечным листингом, как в C, а разбить её на файлы и блоки — классы и методы. Это просто здорово. Ведь в одном классе мы можем хранить запускающую функцию main, в других — методы нашей программы, в третьих — данные или интерфейс соединения с БД. Но как же нам реализовать наше волшебство? Не волнуйтесь — сейчас придумаем вместе.
Создадим, например, класс OneClass:
public class OneClass {
}
И поместим туда наши данные — несколько переменных.
public class OneClass {
int x = 9;
int y = 10;
int z = 11;
}

По умолчанию эти переменные имеют пакетный доступ — т. е – все классы (файлы) нашего пакета могут ими воспользоваться — скопировать, изменить, переопределить. Но как же это сделать? А очень просто — создать экземпляр нашего класса OneClass в другом классе или после метода main.
OneClass cl = new OneClass();

Теперь мы получили возможность пользоваться всеми методами и полями класса (так называются наши переменные, которые написаны сразу после имени класса и поэтому видны во всем OneClass). Скопируем их себе – создадим свои переменные и перебросим туда нужные значения:
public class TwoClass {
OneClass cl = new OneClass();
int x = cl.x;
int y = cl.y;
int z = cl.z;
}

Не стоит бояться одинаковых имен — в другом классе они не перепутаются с изначальными. И теперь у нас есть данные из класса OneClass. 

Но с ними нужно быть очень осторожными. Ведь мы не только можем взять их из первого класса, но и переписать их значения. И хорошо, если мы сделаем это обдуманно. А если просто? Непорядок. Что же делать? Волшебство нуждается в защите от ошибки.

Защитное волшебство

Речь, конечно, пойдёт не о заклятьях Гарри Поттера и не о «Властелине колец». Я хочу рассказать об удивительных методах — геттерах и сеттерах.

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

Чтобы этого избежать, мы сделаем вот что: припишем к каждой переменной идентификатор, который запретит её использование везде, кроме собственного класса.
private x = 9;
private y = 10;
private z = 11;

Теперь никто со стороны не сможет наши данные изменить. Это называется инкапсуляцией. Ну а для нас пока это — волшебство. Попробуем в нём разобраться.
Чтобы создать новый объект, нам самим, другим программистам или иным частям программы, всё равно потребуется получить данные из нашего класса. И что же тогда делать? Ведь они закрыты? Именно для этих случаев и нужны геттеры. Но это волшебство мы должны создавать сами, прописав всю логику этих методов. Get — значит «читать». Чтобы прочитать наши данные в другом классе, нужно их скопировать и передать.

В классе OneClass напишем, например, для переменной x метод-геттер следующего содержания:
public int getX () {
return x;
}
В классе TwoClass вызываем этот метод:
class cl = new class();
cl. getX ();
Можем напечатать наше выражение и убедиться, что получена именно наша переменная.
System.out.println (cl. getX ());
Можно и записать что-то нужное в переменную класса. Для этого потребуется сеттер.
public void setY (newY) {
y =  newY;
}
Надпись, private int y = 10; останется на своем месте. Но получить из недр класса можно будет только то значение, которое записано в newY. Магия – да и только!
С помощью сеттеров мы можем, например, легко «приструнить» пользователя, принимая лишь числа больше нуля и меньше 10. Для этого в сеттер нужно записать несложную логику:
public void setY (newY) {
if (newY>0&& newY<10) {
y =  newY;
}

Магический конструктор

Конструктор — ещё одна магическая единица ООП. Его видно при создании нового объекта класса — например, OneClass cl = new OneClass(); Круглые скобки как раз и скрывают конструктор. Правда, в нашем случае он не имеет параметров, поскольку создается автоматически. Значит, что переменных у нашего объекта может быть любое количество всех типов. Но если мы напишем свой конструктор, то объект не только будет привязан к каким-то параметрам, которые нельзя изменить, но и может получить начальные значения.
Допустим, игровой юнит «дракон» по нашей задумке должен обладать скоростью, высотой полета, временем нахождения в воздухе и должен иметь несколько абстрактных единиц силы. Создадим соответствующий класс и конструктор:

public class Dragon {
Dragon(){
int speed = 120;
int flight_altitude = 1000;
int timeAir = 7;
int power = 10;
}
}

Итак, у нас есть дракон, который может мчаться со скоростью 120 километров в час на высоте 1000 метров целых семь минут! Вдобавок, он обладает 10 единицами силы, а это значит, что его противник должен будет очень долго с ним сражаться.
Вот так легко мы создали своего собственного дракона и инициализировали его по-настоящему волшебными свойствами. Добавить ему уже ничего нельзя, так как если конструктор с параметрами написан, то пустой сам по себе не создается. Но те свойства, что мы придали дракону, отнять невозможно. Теперь с нашим конструктором можно создавать целые полчища драконов, которые расчистят нам путь к сокровищам. Чудеса!

Волшебная перегрузка

Но и это — ещё не всё. И конструкторы, и методы поддерживают так называемую перегрузку — то ещё волшебство. Допустим, нам нужно создать из нашего класса Dragon не только дракона, а ещё и «берсеркера». У него не будет высоты и времени полёта — а скорость и сила останутся. Второй конструктор будет таким:
Dragon(int v, int c){
}

В классе, где будет расположен метод main для всех игровых юнитов,  инициализируем новые переменные другими значениями и добавим их в новый объект:
int speed = 60;
    int power = 7;
    Dragon berserker = new Dragon(speed, power);

Ап! У нас появился берсеркер!
Ещё одно маленькое чудо «на закуску». Если у нас в классе уже есть поля x, y, z, то в своем конструкторе или методе мы вполне можем создать такие же переменные с теми же именами и передать им значения полей. Причем наши переменные не перепутаются. Для этого только нужно вставить перед ними маленькое слово  с точкой, вот так:  this.
int x = 4;
int y = 6;
int z = 12;
Dragon(int v, int c, int z){
this.x = x;
this.y = y;
this.z = z;
}

В программировании полно вещей, которые можно назвать если и не магическими, то уж чудесными — абсолютно точно. И эти чудеса вполне под силу освоить любому желающему.

Эдуард Трошин

Версия для печатиВерсия для печати

Рубрики: 

  • 1
  • 2
  • 3
  • 4
  • 5
Всего голосов: 0
Заметили ошибку? Выделите ее мышкой и нажмите Ctrl+Enter!
 

Комментарии

Страницы

Аватар пользователя mike

Писать на современных языках гораздо удобнее, чем, допустим, на языке C.

А вот и нет. Всё зависит от решаемой задачи. Если надо написать относительно несложную программу для микроконтроллера, то только глупцу  влетит в голову писать её посредством ООП. Или очень сложную программу, где нет подобных среди функций.

Короче, чтобы кодить, надо кодить.

Привет! Может лучше функциональную парадигму затроньте?

С ООП у вас неплохо выходит Laughing.

Аватар пользователя mike

Это вы кому, мне или автору?

Аватар пользователя Petro45

Если надо написать относительно несложную программу для микроконтроллера, то только глупцу  влетит в голову писать её посредством ООП.

Я долго ждал, что скажет Капитан очевидность. Спасибо, кэп!:-) Про это же и шла речь - про контроллеры:-).

Привет! Может лучше функциональную парадигму затроньте?

В следующей статье будет небольшая учебная прога, написанная в двух версиях - на С и на ООП Java. Можно будет посмотреть, сравнить. Статья уже в редакции.

Аватар пользователя mike

Я долго ждал, что скажет Капитан очевидность. Спасибо, кэп!:-)

Рад, что кое-что вам уже очевидно.

...Шла речь - про контроллеры:-).

Где?

В следующей статье будет небольшая учебная прога, написанная в двух версиях - на С и на ООП Java.

Вероятно, чтобы посрамить С++. Впрочем, вас спросили про функциональную парадигму.

Аватар пользователя Petro45

Вероятно, чтобы посрамить С++. Впрочем, вас спросили про функциональную парадигму.

Это про это? Не знаю, не пробовал. Ничего я не хочу срамить. Мало каши ел.

Аватар пользователя mike

Это про это?

Про это, про это. На C++ можно писать в функциональном стиле  легче, чем на Java. Кстати, на Хабре пару лет назад была статья. 

Аватар пользователя Petro45

Там целый хаб есть про это. Там десятки статей и все не очень понятно. 

Аватар пользователя mike

Потому что слишком много вкладывают в это понятие. А на самом деле программа в функстиле -- это множество функций, аргументы которых -- функции, входящие в это же множество. Результат работы функции -- выполнение (не вызов!)  функции, являющейся одним из аргументов. Программа выглядит, как список.

И да, функции могут динамически порождаться из шаблонов (ООП!), а могут быть и статическими, это зависит от того, как напишешь.

Программисты используют сразу несколько парадигм  при написании софта. По разумению и задачам.

Аватар пользователя leo3

Что-то я ничего не понял... типизация переменных в разных языках решается по-разному. Есть строго типизированные языке, есть нетипизированные... Прямого отношения к ООП эта проблематика не имеет, она эффективно была решена и в функциональных языках... Вспоминаю забавный курьезный случай из практики. В одном очень солидном ИТ институте в период СССР нач. отдела (слишком переучившийся, видимо) поручил сотрудникам писать ввод значений переменных через процедуру преобразования текстовой строки (например 23037) в число... Когда я им показал, что оператор ввода в данном языке делает это автоматически, если переменная описана как число - то выглядел магом-волшебником:)))

Самым настоящим чудом в ООП является тот факт, что программу можно писать не одним бесконечным листингом, как в C, а разбить её на файлы и блоки

Разбиение программы на блоки и файлы было основой концепции функционального программирования...

Это называется инкапсуляцией.

Инкапсуляцией называется не это.

Страницы