Практика ООП в Java

Помечено: ,

  • В этой теме 0 ответов, 1 участник, последнее обновление 1 месяц, 2 недели назад сделано Васильев Владимир Сергеевич.
Просмотр 0 веток ответов
  • Автор
    Сообщения
    • #6847
      @admin
      StudLance.ru

      В этой статье мы рассмотрим дополнительные примеры к уроку «Объектно-ориентированное программирование в Java«.

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

      Пример 1 — Простейший класс

      Создайте класс Test1 с двумя переменными. Добавьте метод вывода на экран и методы изменения переменных. Добавьте метод, который находит сумму значений этих переменных, и метод, который находит наибольшее значение из этих двух переменных.

      Решение

      В этой задаче у класса не предусмотрен конструктор и методы установки значений переменных, однако, чтобы проверить корректность работы функций sum и max эти значения надо изменять. Поэтому эти переменные объявлены публичными (public):

      Класс Test1 (в файле Test1.java):

      class Test1 {
        public int a, b;
        public int max() {
          if (a > b)
            return a;
          return b;
        }
        public int sum() {
          return a+b;
        }
      }

      Соответствующая UML диаграмма классов приведена на рисунке:

      Для проверки работы этого класса был создан класс Main (файл Main.java):

      public class Main {
       public static void main(String[] args) {
        Test1 obj = new Test1();
      
        System.out.println("a: " + obj.a);
        System.out.println("b: " + obj.b);
        System.out.println("max: " + obj.max());
        System.out.println("sum: " + obj.sum());
        System.out.println("------------");
      
        obj.a = 123;
        obj.b = 43;
      
        System.out.println("a: " + obj.a);
        System.out.println("b: " + obj.b);
        System.out.println("max: " + obj.max());
        System.out.println("sum: " + obj.sum());
      
        obj.b = 433;
      
        System.out.println("a: " + obj.a);
        System.out.println("b: " + obj.b);
        System.out.println("max: " + obj.max());
        System.out.println("sum: " + obj.sum());
      
       }
      }
      

      Запустить программу можно в онлайн-компиляторе.

      Пример 2 — Конструкторы

      Создайте класс Test2 с двумя переменными. Добавьте конструктор с входными параметрами. Добавьте конструктор, инициализирующий члены класса по умолчанию. Добавьте set- и get- методы для полей экземпляра класса.

      Решение

      В этой задаче, класс имеет конструкторы и поля установки/получения значений переменных, поэтому переменные класса можно объявить в приватной секции – за счет этого все обращения к полям «извне» будут всегда выполняться через эти методы, это может помочь в обеспечении безопасности. Например, если в качестве полей использовались бы минуты и секунды, то методы установки значений могли бы выполнять валидацию значений.

      UML диаграмма классов приведена на рисунке:

      Исходный код класса Tast2 (файл Test2.java):

      class Test2 {
          private int a, b;
          public Test2() {
            a = 1; // Значения по умолчанию
            b = 5;
          }
          public Test2(int a_, int b_) {
            a = a_;
            b = b_;
          }
          public int get_a() {
              return a;
          }
          public int get_b() {
              return b;
          }
          public void set_a(int a_) {
            a = a_;
          }
          public void set_b(int b_) {
            b = b_;
          }
      }

      Конструктор без параметров (по умолчанию) устанавливает полям значения 1 и 5 (могли быть любые другие).

      Для проверки работы этого класса был создан класс Main (файл Main.java):

      public class Main {
       public static void main(String[] args) {
        Test2 obj = new Test2();
      
        System.out.println("a: " + obj.get_a());
        System.out.println("b: " + obj.get_b());
        System.out.println("------------");
      
        obj.set_a(123); //obj.a = 123;
        obj.set_b(43); //obj.b = 43;
      
        System.out.println("a: " + obj.get_a());
        System.out.println("b: " + obj.get_b());
        System.out.println("------------");
      
        obj = new Test2(4, 3);
      
        System.out.println("a: " + obj.get_a());
        System.out.println("b: " + obj.get_b());
        System.out.println("------------");
      
       }
      }

      Запустить программы можно в онлайн-компиляторе.

      Пример 3 — Более сложный класс

      Создайте класс с именем Student, содержащий поля: фамилия и инициалы, номер группы, успеваемость (массив из пяти элементов). Создайте массив из десяти элементов такого типа. Добавьте возможность вывода фамилии и номеров групп студентов, имеющих оценки, равные только 9 или 10.

      Решение

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

      Уже в предыдущей задаче большой объем в функции Main занимал вывод элементов на экран, но в этой задаче таких элементов 10 и каждый из них имеет более сложную внутреннюю структуру, поэтому в класс добавлен метод print, выводящий содержимое объекта на консоль.

      Оценки помечены спецификатором доступа private, чтобы у пользователя не было прямого доступа к ним – иначе пользователь мог бы, например, попробовать добавить шестую оценку (хотя память выделена под 5 элементов). Поэтому в класс также добавлен метод isGood, который возвращает true если все оценки студента равны 9 или 10.

      Исходный код класса:

      class Student {
       private String name;
       private String initials;
       private int group;
       private int grades[];
       private int nGrades;
      
       public Student(String name_, String initials_, int group_) {
        name = name_;
        initials = initials_;
        nGrades = 0;
        grades = new int[5];
       }
      
       boolean addGrade(int grade) {
        if (nGrades >= 5) {
         return false;
        }
        grades[nGrades] = grade;
        nGrades++;
        return true;
       }
      
       boolean isGood() {
        for (int i = 0; i < nGrades; ++i)
         if (grades[i] != 9 && grades[i] != 10)
          return false;
      
        return true;
       }
      
       void print() {
        System.out.print(name + " " + initials);
        System.out.print(". Group: " + Integer.toString(group) + ". Grades: ");
        for (int i = 0; i < nGrades; ++i)
         System.out.print(Integer.toString(grades[i]) + " ");
        System.out.print("\n");
       }
      }

      Главная функция создает 10 объектов типа Student. Затем, в цикле каждому из них генерируется 2 случайных оценки. Для генерации оценок используется функция стандартный модуль java.util.Random. Подробное описание работы ним в статье «Java — Генерация случайного числа в заданном диапазоне«.

      Далее, все студенты выводятся на экран, а затем – выводятся только отличники. Исходный код файла Main.java:

      import java.util.Random;
      
      public class Main {
       public static void main(String[] args) {
        int n = 10;
        Student students[] = new Student[n];
      
        students[0] = new Student("Petrov", "A.I.", 1);
        students[1] = new Student("Petrov", "B.I.", 1);
        students[2] = new Student("Ivanov", "A.S..", 1);
        students[3] = new Student("Pushkin", "A.S.", 2);
        students[4] = new Student("Dostoevskii", "F.M.", 2);
        students[5] = new Student("Shishkin", "I.I.", 3);
        students[6] = new Student("Aivazovskii", "I.K.", 3);
        students[7] = new Student("Mate", "V.V.", 3);
        students[8] = new Student("Preobrazhenkii", "M.T.", 3);
        students[9] = new Student("Solncev", "F.G.", 3);
      
        Random rnd = new Random();
        for (int i = 0; i < n; ++i) {
         students[i].addGrade(rnd.nextInt(6) + 5);
         students[i].addGrade(rnd.nextInt(6) + 5);
        }
      
        System.out.println("все студенты: ");
        for (int i = 0; i < n; ++i) {
         students[i].print();
        }
      
        System.out.println("хорошие студенты: ");
        for (int i = 0; i < n; ++i) {
         if (students[i].isGood())
          students[i].print();
        }
      
       }
      }

      Запустить программу можно в онлайн-компиляторе. Результаты работы программы:

      Пример 4 — Наследование, Компараторы, Сортировка объектов

      Создайте класс Train, содержащий поля: название пункта назначения, номер поезда, время отправления. Создайте массив из пяти элементов типа Train, добавьте возможность сортировки элементов массива по номерам поездов. Добавьте возможность вывода информации о поезде, номер которого введен пользователем, добавьте возможность сортировки массива по пункту назначения, причем поезда с одинаковым пунктом назначения должны быть упорядочены по времени отправления.

      Решение

      В этой задаче, в отличии от предыдущей:

      • необходимо организовать ввод данных с консоли, для этого в модуле Main используется класс java.util.Scanner. Его метод nextInt() возвращает считанное с консоли целое число;
      • необходимо выполнять сортировку массива, причем по различным критериям. В решении используется готовый алгоритм сортировки Arrays.sort(), на вход которого подается компаратор (java.util.Comparator);
      • компаратор – это класс, отвечающий за сравнение наших объектов, он должен наследоваться от Comparator и реализовывать функцию int compare(Train left, Train right);
      • для сравнения объектов типа Train, компаратор должен получать доступ к внутренним полям класса Train, однако они, в соответствии с принципом инкапсуляции, должны объявляться закрытыми – поэтому класс компаратора является вложенным в класс Train.

      Зависимости между классами показаны на рисунке:

      Исходный код модуля Train.java:

      import java.util.Comparator;
      
      class Train {
       private String destination;
       private int number;
       private String departure_time;
      
       public Train(String destination_, int number_, String departure_time_) {
        destination = destination_;
        number = number_;
        departure_time = departure_time_;
       }
      
       public static class ByNumberComparator implements Comparator < Train > {
        @Override
        public int compare(Train left, Train right) {
         return left.number - right.number;
        }
       }
      
       public static class ByDestinationAndTimeComparator implements Comparator < Train > {
        @Override
        public int compare(Train left, Train right) {
         if (left.destination == right.destination)
          return left.departure_time.compareTo(right.departure_time);
      
         return left.destination.compareTo(right.destination);
        }
       }
      
       public void print() {
        System.out.println(number + " -> " + destination + " : " + departure_time);
       }
      }

      Исходный код модуля Main.java:

      import java.util.Scanner;
      import java.util.*;
      
      public class Main {
       public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
      
        int n = 5;
        Train trains[] = new Train[n];
      
        trains[0] = new Train("Msk", 122, "12:42");
        trains[1] = new Train("Msk", 123, "12:45");
        trains[2] = new Train("Spb", 34, "23:08");
        trains[3] = new Train("Msk", 33, "12:42");
        trains[4] = new Train("Spb", 156, "05:44");
      
        while (true) {
         System.out.println(
          "Выберете пункт меню:" + "\n" +
          "1. сортировка по номерам поездов" + "\n" +
          "2. вывод информации о поезде по номеру" + "\n" +
          "3. сортировка по пункту назначения" + "\n" +
          "4. вывод всех поездов" + "\n" +
          "5. выход" + "\n"
         );
         int choice = scanner.nextInt();
         if (choice == 5)
          break;
         if (choice < 1 || choice > 5) {
          System.out.println("выбран неправильный пункт меню, повторите ввод.");
          continue;
         }
      
         switch (choice) {
          case 1:
           Arrays.sort(trains, new Train.ByNumberComparator());
           break;
          case 2:
           System.out.println("введи индекс поезда: ");
           int pos = scanner.nextInt();
           if (pos < 0 || pos >= trains.length)
            System.out.println("выбран неправильный номер элемента массива");
           else
            trains[pos].print();
           break;
          case 3:
           Arrays.sort(trains, new Train.ByDestinationAndTimeComparator());
           break;
          case 4:
           for (int i = 0; i < trains.length; ++i) {
            System.out.print(i + " ");
            trains[i].print();
           }
           break;
         }
        }
       }
      }

      Попробовать этот код в деле можно тут.

      Пример 5 — Композиция

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

      Решение

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

      Треугольник задается тремя точками — имеет смысл выделить отдельный класс Point. Так как в этом классе не имеет смысла валидация полей (координаты могут быть любыми), а интерфейс из set- и get- функций полностью раскроет детали реализации – то целесообразно поля x и y этого класса сделать публичными. Тем не менее, конструктор и метод вывода точки и метод ввода координат с консоли повысят удобство и сократят дублирование кода. Кроме того, в класс Point включен статический метод distance. Статический, значит, что для его вызова не требуется создавать объект – в языке С++ такой метод эквивалентен свободной функции, сложенной в пространство имен. На рисунке приведена диаграмма соответствующая диаграмма классов:

      Исходный код модуля Point.java:

      import java.util.Scanner;
      class Point {
       public double x, y;
      
       public Point(double x_, double y_) {
        x = x_;
        y = y_;
       }
      
       public Point() {
        this(0, 0);
       }
      
       public static double distance(Point a, Point b) {
        double dx = a.x - b.x;
        double dy = a.y - b.y;
        return Math.sqrt(dx * dx + dy * dy);
       }
      
       public void print() {
        System.out.println("x: " + x + ", y: " + y);
       }
      
       public void read(Scanner scanner) {
        System.out.print("x: ");
        x = scanner.nextDouble();
        System.out.print("y: ");
        y = scanner.nextDouble();
       }
      }

      Тут в метод read передается объект java.util.Scanner, который будет создаваться в классе Main.

      Модуль Triangle.java:

      class Triangle {
       private Point a, b, c;
      
       public Triangle(Point a_, Point b_, Point c_) {
        a = a_;
        b = b_;
        c = c_;
       }
      
       public double square() {
        // формула Герона
        double ab = Point.distance(a, b);
        double bc = Point.distance(b, c);
        double ac = Point.distance(a, c);
      
        double p = (ab + bc + ac) / 2;
      
        return Math.sqrt(p * (p - ab) * (p - bc) * (p - ac));
       }
      
       public double perimeter() {
        double ab = Point.distance(a, b);
        double bc = Point.distance(b, c);
        double ac = Point.distance(a, c);
        return ab + bc + ac;
       }
      
       public Point median_crossing() {
        return new Point((a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3);
       }
      
       public void print() {
        System.out.print("A: ");
        a.print();
        System.out.print("B: ");
        b.print();
        System.out.print("C: ");
        c.print();
       }
      }

      В модуле Main.java создаются три точки, а затем – из них конструируется треугольник. Вычисляемая медиана также является точкой:

      import java.util.Scanner;
      public class Main {
       public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
      
        Point a = new Point();
        Point b = new Point();
        Point c = new Point();
      
        System.out.println("enter A: ");
        a.read(scanner);
      
        System.out.println("enter B: ");
        b.read(scanner);
      
        System.out.println("enter C: ");
        c.read(scanner);
      
        Triangle triangle = new Triangle(a, b, c);
      
        System.out.println("square: " + triangle.square());
        System.out.println("perimeter: " + triangle.perimeter());
        System.out.print("median_crossing: ");
        triangle.median_crossing().print();
       }
      }

      Запустить программу можно в онлайн-компиляторе.

      Результаты работы программы:

      Аналогичные вычисления, выполненные аналитически:

      Пример 6 — Композиция (класс Автомобиль)

      Создать объект класс Автомобиль, используя классы Колесо, Двигатель. Методы: ехать, заправляться, менять колесо, вывести на консоль марку автомобиля.

      Решение

      В соответствии с заданием разработана структура системы, показанная на рисунке. Машина включает в себя один двигатель и 4 колеса.

      Двигатель имеет номер и мощность, а также методы для ввода параметров двигателя с консоли и преобразования в строку (для вывода на экран). Модуль Engine.java:

      import java.util.*;
      class Engine {
       private int number;
       private double power;
      
       public Engine(int number_, double power_) {
        number = number_;
        power = power_;
       }
      
       public String toString() {
        String string = "engine: " + number + ", power:" + power;
      
        return string;
       }
      
       public static Engine get(Scanner scanner) {
        Engine object = new Engine(0, 0);
      
        System.out.print("Введи номер двигателя: ");
        object.number = scanner.nextInt();
      
        System.out.print("Введи мощность двигателя: ");
        object.power = scanner.nextDouble();
        return object;
       }
      }

      Класс Колесо имеет тип (зимнее/летнее), диаметр и марку. Помимо методов для ввода и вывода, позволяет получить диаметр – это нужно, так как машина не должна ехать с колесами разных диаметров. Содержимое Wheel.java:

      import java.util.*;
      class Wheel {
        public enum Type { Winter, Summer }
        
        private double diameter;
        private String brand;
        private Type type;
        
        public Wheel(String brand_, double diameter_, Type type_) {
          brand = brand_;
          diameter = diameter_;
          type = type_;
        }
        
        public String toString() {
          String string = "";
          
          string += brand + "(" + diameter + "): ";
          if (type == Type.Winter)
            string += "Winter";
          else
            string += "Summer";
            
          return string;
        }
        
        public static Wheel get(Scanner scanner) {
          Wheel object = new Wheel("", 0, Type.Winter);
          
          System.out.print("Введи бренд: ");
          object.brand = Common.readNotEmptyString(scanner);
          
          System.out.print("Введи диаметр: ");
          object.diameter = scanner.nextDouble();
          
          System.out.print("Введи тип (1) - зимние, (2) - летние: ");
          while (true) {
            int value = scanner.nextInt();
            if (value == 1) {
              object.type = Type.Winter;
              break;
            }
            if (value == 2) {
              object.type = Type.Summer;
              break;
            }
            System.out.print("Введено неправильное значение, повтори ввод.");
          }
          return object;
        }
        
        public double get_diameter() {
          return diameter;
        }
      }

      Класс автомобиль, помимо колес и двигателя, содержит марку, расход топлива на 100км и текущий уровень топлива. Помимо методов ввода/вывода имеет методы move, add_fuel и change_wheel. Метод move – движение автомобиля, проверяет может ли автомобиль двигаться (наличие топлива и равенство диаметров всех колес), и производит пробег автомобиля до окончания топлива, на экран выводится пройденное расстояние. Содержимое файла Car.java:

      import java.util.*;
      class Car {
       private Wheel wheels[];
       private Engine engine;
       private String brand;
       private double consumption;
       private double fuel_level;
      
       public Car(String brand_, double consumption_,
        Engine engine_, Wheel wheels_[], double fuel_level_) {
        brand = brand_;
        consumption = consumption;
        engine = engine_;
        wheels = wheels_;
        fuel_level = fuel_level_;
       }
      
       public String toString() {
        String string = "";
      
        string += brand + engine.toString() + ";" + consumption + "L/100km { " + fuel_level + "}; [";
        for (int i = 0; i < 4; ++i) {
         string += wheels[i].toString() + " ";
        }
      
        return string + "]";
       }
      
       public static Car get(Scanner scanner) {
        Car object = new Car("", 0, new Engine(0, 0), new Wheel[4], 0);
      
        System.out.print("Введи марку автомобиля: ");
        object.brand = Common.readNotEmptyString(scanner);
      
        System.out.print("Введи расход топлива на 100км: ");
        object.consumption = scanner.nextDouble();
      
        System.out.print("Введи текущий уровень топлива:");
        object.fuel_level = scanner.nextDouble();
      
        System.out.println("Введи колеса:");
        for (int i = 0; i < 4; ++i) {
         System.out.println("колесо #" + i);
         object.wheels[i] = Wheel.get(scanner);
        }
      
        object.engine = Engine.get(scanner);
      
        return object;
       }
      
       public void move() {
        if (fuel_level <= 0) {
         System.out.println("Нет бензина, машина не может двигаться");
         return;
        }
      
        for (int i = 1; i < 4; ++i) {
         if (wheels[i].get_diameter() != wheels[i - 1].get_diameter()) {
          System.out.println("Колеса разных диаметров, машина не может двигаться");
          return;
         }
        }
      
        double distance = fuel_level / consumption;
        System.out.println("машина проехала " + distance * 100 + "км.");
        fuel_level = 0;
       }
      
       public void add_fuel(double value) {
        fuel_level += value;
       }
      
       public void change_wheel(Scanner scanner) {
        System.out.println("Введи номер колеса [0-3]:");
        int number;
        while (true) {
         number = scanner.nextInt();
         if (number >= 0 && number <= 3)
          break;
         System.out.println("Неправильный номер, повтори ввод");
        }
      
        wheels[number] = Wheel.get(scanner);
       }
      }

      Содержимое модуля Main.java:

      import java.util.Scanner;
      public class Main {
       public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
      
        Car car = Car.get(scanner);
      
        while (true) {
         System.out.println(
          "Выберете пункт меню:\n" +
          "0. выход\n" +
          "1. ехать\n" +
          "2. заправляться\n" +
          "3. менять колесо\n" +
          "4. вывести информацию об авто (марку тоже)\n" +
          ": "
         );
         int choice = scanner.nextInt();
         if (choice == 0)
          break;
         if (choice < 1 || choice > 4) {
          System.out.println("выбран неправильный пункт меню, повторите ввод.");
          continue;
         }
      
         switch (choice) {
          case 1:
           car.move();
           break;
          case 2:
           System.out.println("Введи количество топлива:");
           double fuel = scanner.nextDouble();
           car.add_fuel(fuel);
           break;
          case 3:
           car.change_wheel(scanner);
           break;
          case 4:
           System.out.println(car.toString());
           break;
         }
        }
       }
      }

      Сразу после старта программы у пользователя запрашивается информация об автомобиле, затем интерфейс позволяет нам получить текущее состояние автомобиля, «ехать» или менять колесо.

      Ввод начальной информации об автомобиле:

      Вывод информации об автомобиле:

      Замена колеса:

      Запустить программу можно в онлайн-компиляторе.

      StudLance.ru

Просмотр 0 веток ответов
  • Для ответа в этой теме необходимо авторизоваться.
×