절차 지향과 객체 지향 비교 문서의 구현 코드다.

// 실행: docker run --rm -v "$PWD":/w -w /w eclipse-temurin:21-jdk java 13_procedural-vs-oop_impl.java
// (JDK 21. 파일 이름이 숫자로 시작해 public 클래스를 두지 않는다. 맨 위 클래스의 main이 돈다.)
// 문서 「절차 지향과 객체 지향 비교」의 두 코드를 실행해 출력을 비교한다.
// Student에 getName()이 없으면 student1.getName()을 부르는 줄이 컴파일되지 않는다. 그래서 getName()을 둔다.

class ProceduralVsOopCheck {
    // 절차 지향: 자료(배열)와 그 자료를 다루는 순서가 main 한곳에 놓인다
    static void procedural() {
        int[] grades = {90, 85, 88, 75, 80, 82};

        double sum = 0;
        for (int grade : grades) {
            sum += grade;
        }
        double average = sum / grades.length;
        System.out.println("Average grade: " + average);
    }

    // 객체 지향: 학생 객체가 자기 점수와 평균 내는 법을 함께 갖는다
    static void oop() {
        int[] student1Grades = {90, 85, 88};
        int[] student2Grades = {75, 80, 82};

        Student student1 = new Student("Alice", student1Grades);
        Student student2 = new Student("Bob", student2Grades);

        System.out.println("Average grade for " + student1.getName() + ": " + student1.calculateAverageGrade());
        System.out.println("Average grade for " + student2.getName() + ": " + student2.calculateAverageGrade());
    }

    // 절차 지향으로 학생 둘을 다루면 같은 반복문을 두 번 쓴다(문서의 "학생이 늘면" 대목)
    static void proceduralTwoStudents() {
        int[] aliceGrades = {90, 85, 88};
        int[] bobGrades = {75, 80, 82};
        double sumA = 0;
        for (int g : aliceGrades) sumA += g;
        double sumB = 0;
        for (int g : bobGrades) sumB += g;
        System.out.println("Alice: " + sumA / aliceGrades.length);
        System.out.println("Bob: " + sumB / bobGrades.length);
    }

    public static void main(String[] args) {
        check("procedural", capture(ProceduralVsOopCheck::procedural), "Average grade: 83.33333333333333\n");
        check("oop", capture(ProceduralVsOopCheck::oop),
              "Average grade for Alice: 87.66666666666667\nAverage grade for Bob: 79.0\n");
        check("procedural, two students", capture(ProceduralVsOopCheck::proceduralTwoStudents),
              "Alice: 87.66666666666667\nBob: 79.0\n");
        System.out.println("ALL CHECKS PASSED");
    }

    static String capture(Runnable r) {
        java.io.PrintStream old = System.out;
        java.io.ByteArrayOutputStream buf = new java.io.ByteArrayOutputStream();
        System.setOut(new java.io.PrintStream(buf));
        try { r.run(); } finally { System.setOut(old); }
        return buf.toString();
    }

    static void check(String name, String got, String want) {
        if (!got.equals(want)) throw new AssertionError(name + "\n--- got ---\n" + got + "--- want ---\n" + want);
        System.out.print("[" + name + "]\n" + got);
    }
}

class Student {
    private String name;
    private int[] grades;

    public Student(String name, int[] grades) {
        this.name = name;
        this.grades = grades;
    }

    public String getName() {   // 없으면 student1.getName()이 컴파일되지 않는다
        return name;
    }

    public double calculateAverageGrade() {
        double sum = 0;
        for (int grade : grades) {
            sum += grade;
        }
        return sum / grades.length;
    }
}