Классы

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

Класс как объединение данных и методов. Объекты как экземпляры класса

Давайте рассмотрим пример. Предположим, что вы работаете с локальной картой - оперируете координатами точек на карте и вычисляете расстояния между ними. Вы, конечно, можете запомнить два вещественных значения x и y для каждого местоположения и написать функцию distance(x1, y1, x2, y2), которая будет вычислять расстояния между двумя парами координат. Но куда более элегантным будет создать новую сущность, которая будет объединять вместе координаты и метод, вычисляющий расстояние до другой точки. В объектно-ориентированном программировании такая сущность называется классом. Объявление Java класса для точки местоположения на карте будет выглядеть таким образом:

class Location {

//конструктор: создает объект Location с заданными координатами
Location( double xcoord, double ycoord ) {
x = xcoord;
y = ycoord;
}

//два поля типа double
double x; //x координата местоположения
double y; //y координата местоположения

//метод (функция): вычисляет расстояние от данного места до другого
double distanceTo( Location other ) {
double dx = other.x - x;
double dy = other.y - y;
return sqrt( dx*dx + dy*dy );
}

}

Как вы можете видеть, класс включает в себя данные и методы, работающие с этими данными.

Задав такой класс, мы можем написать очень простой и читаемый код для работы с картой, например, такой:

Location origin = new Location( 0, 0 ); //создаем первое местоположение
Location destination = new Location( 250, 470 ); //создаем второе
double distance = origin.distanceTo( destination ); //вычисляем расстояние между ними

Точки origin и destination являются объектами и экземплярами класса Location. Выражение new Location( 250, 470 ) является вызовом конструктора, оно создает и возвращает новый экземпляр класса Location с заданными координатами. Выражение origin.distanceTo( destination ) является вызовом метода: просит объект origin вычислить расстояние до другого объекта destination.

Если вы объявите переменную непримитивного типа (т.е. класса) и не проинициализируете ее, то ее значение будет задано равным null (null является специальной литеральной константой Java, обозначающей "ничего"). Иногда вы явно присваиваете null переменной, чтобы "забыть" тот объект, на который ссылалась эта переменная и показать, что объект не задан или недоступен.

Location target; //переменная объявлена без инициализации и равна null
target = warehouse; //переменной target присвоен объект (на который ссылается переменная) warehouse
//теперь target и warehouse указывают на один и тот же объект

target = null; //target "забывает" о warehouse и снова равен null

Наследование. Подкласс и суперкласс

Теперь предположим, что некоторые из этих точек, заданных вами в двумерном пространстве, соответствуют городам. У города есть название и численность населения. Чтобы эффективно управлять городами, мы расширим класс Location, добавив в него еще два поля: name и population. Давайте назовем этот новый класс City. В Java это будет выглядеть следующим образом:

class City extends Location { //объявление класса City, расширяющего класс Location

//конструктор класса City
City( String n, double x, double y ) {
super( x, y ); //вызов конструктора суперкласса с параметрами x и y
name = n;
//поле population не инициализируется в конструкторе, это будет сделано впоследствии
}

//поля класса City
String name;
int population;

}


Классы и наследование

City в данном примере является подклассом класса Location, а Location - суперклассом (или базовым классом) для класса City. City наследует все свойства класса Location и добавляет новые. Посмотрите, как элегантно выглядит код, определяющий крупнейший город в радиусе 100 километров от заданной точки point класса Location (мы предполагаем, что существует коллекция cities, в которой хранятся все города):

int pop = 0; //здесь мы будем запоминать самую большую численность населения, найденную к данному моменту
City biggestcity = null; //здесь мы будем запоминать наилучший город, найденный к данному моменту

for( City city : cities ) { //для каждого города в коллекции cities
if( point.distanceTo( city ) < 100 && city.population > pop ) { //если этот город лучше, чем найденные ранее
biggestcity = city; //запоминаем его
pop = city.population; //и запоминаем численность его населения
}
}
traceln( "Крупнейший город в радиусе 100 км " + city.name ); //распечатываем результат поиска

Обратите внимание, что хотя city является объектом класса City, который "больше", чем Location, при необходимости (например, при вызове метода distanceTo() класса Location) с ним можно по-прежнему работать как с объектом класса Location.

Это общее правило: вы всегда можете работать с объектом подкласса как с объектом его базового класса.

А что, если наоборот? вы можете объявить переменную класса Location и присвоить ей объект класса City (подкласс класса Location):

Location place = laJolla;

С этим проблем не возникнет. Но если вы затем попробуете получить доступ к численности населения (полю population объекта place), то Java выдаст ошибку:

int p = place.population; //error: "population cannot be resolved or is not a field"

Это происходит потому, что Java не знает о том, что place в действительности является объектом класса City. Чтобы устранить такое "непонимание", нужно сделать следующее:

Типичный образец кода:

if( place instanceof City ) {
City city = (City)place;
int p = city.population;

}

Классы и объекты в моделях AnyLogic

В сущности все элементы, используемые вами при построении моделей, являются объектами Java классов AnyLogic. Вы можете найти имена Java классов, соответствующие всем этим элементам, в Справочнике классов АnyLogic. Если вы захотите узнать, какие методы предоставляет Java для того или иного типа элемента, вам нужно будет просто найти нужный класс в Справочнике и ознакомиться со списком поддерживаемых этим классом полей и методов.