Объектно Ориентированное Программирование Java ООП

Давайте поговорим о том, как мы пишем код. Скорей всего все начинающие программисты вспомнят свой первый более менее сложный код, который выглядел одной портянкой в мейн методе, что напоминал больше процедурный язык (сделать сначала первое, потом второе и т.д.). Изначально мыслить в парадигме ООП непросто. Итак, что это такое? Это стиль, если так можно сказать, программирования, в котором все предствляется объектом. Точно так же как и в нашем мире все является объектом — машина, магазин, продавец, кофеварка, дата, время. И у каждого объекта есть свои свойства и функции, т.е. признаки и возможности — у машины есть цвет, марка, тип двигателя (свойства) и функции — перемещаться в пространстве, возить вещи и др.

Все выше сказанное можно написать на Java в виде простого класса:

public abstract class Car {
@NotNull
private String color;
@NotNull
private final String model;
@NotNull
@EngineType
private final String engineType;
protected Car(@NotNull String color, @NotNull String model, @NotNull @EngineType String engineType) {
this.color = color;
this.model = model;
this.engineType = engineType;
}
protected abstract void move();
public String getColor() {
return this.color;
}
public void setColor(@NotNull String color) {
if (color.length > 0) this.color = color;
}
public String getModel() {
return this.model;
}
}

И на этом простом примере (упрощенном) мы сейчас рассмотрим все 3 базовых принципа ООП: инкапсуляция, наследование, полиморфизм.

Суть инкапсуляции в том, чтобы скрывать некоторые детали от пользователя (в нашем случае пользователем будет программист, который унаследуется от этого класса). Здесь мы видим, что цвет может определиться в конструкторе и позднее переопределиться через сеттер. Но суть инкапсуляции не просто в том, чтобы сделать приватным поле и публичные геттеры и сеттеры, а в том, чтобы не определять переменную как вздумается, а наложить некоторые условия (их видим в сеттере — т.е. если передать пустую строку, то цвет машины не должен измениться).

Также мы видим, что у нас неизменяемая переменная марка автомобиля, ее необходимо определить при создании и единственное, что можно получить ее название (для пущей закрытости можно применить Defensive Copy в геттере создается новый объект и наружу отдается его свойство, а не текущего объекта).

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

Теперь вкратце поговорим про наследование. Наш класс является абстрактным, т.е. нельзя создать экземпляр абстрактной машины. Все особенности должны быть определены в наследнике. Давайте рассмотрим маленький пример.
public class MustangShelbyGt500 extends Car {
public MustangShelbyGt500(@NotNull String color) {
super(color, "MustangShelbyGt500", ENGINE_TYPE_PATROL);
}
public void move() {
// implement here
}
}

Здесь мы видим конкретный класс Мустанг Шелби, которому можно лишь определить цвет при создании, так как марка уже определена в конструкторе и тип двигателя бензиновый. Также необходимо определить абстрактный метод перемещения. Суть наследования именно в том, чтобы каждый раз не писать одни и те же вещи, такие как цвет, модель и др. Можно создать абстрактный класс Машина и в нем хранить все общие свойства и методы. Это упрощает написание кода и понимание его.  Также можно было вынести метод move() в интерфейс interface Moveable и имплементить у абстрактного класса, чтобы явно указать, что данный класс имеет свойство перемещаться и этот метод нужно определить. т.е.

public abstract class Car implements Moveable{ ... }

public interface Moveable {
void move();
}

О налседовании можно говорить долго, но думаю суть ясна. Поэтому перейдем к полиморфизму. Итак, мы определили интерфейс для передвижения. И он реализуется классом машина. Но также этот интерфейс можно применить и к другим классам, например самолет, собака, человек и все, что может иметь метод перемешаться. Единственное отличие каждого класса от другого в том, что он этот метод будет реализовывать по-своему. Например:
class Human implements Moveable {
public void move() {
System.out.println("I am a human and I'm walking");
}

class Airplane implements Moveable {
public void move() {
System.out.println("The airplane is flying");
}

class Dog implements Moveable {
public void move() {
System.out.println("The dog is running!");
}

Но самое интересное происходит тогда, когда мы вызываем один и тот же метод у каждого объекта и видим различный результат — именно в этом и суть полиморфизма.

class Polymorphysm {
public static void main(String[] args) {
List moveables = new ArrayList();
moveables.add(new Human());
moveables.add(new Airplane());
moveables.add(new Dog());

for (Moveable moveable : moveables) {
moveable.move()
}
}

В результате мы видим что вызывется один и тот же метод, но так как у них различная реализация, то на выходе будет различный результат.

I am a human and I'm walking
The airplane is flying
The dog is running!

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

p.s. в данной статье не были освещены все детали ООП, но на мой взгляд, важные аспекты были рассмотрены

Запись опубликована в рубрике Программирование Java. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *