Skip to content

Instantly share code, notes, and snippets.

@omas
Last active February 17, 2017 09:54
Show Gist options
  • Save omas/f6bdb56f8b9b3d54279fba603862e29f to your computer and use it in GitHub Desktop.
Save omas/f6bdb56f8b9b3d54279fba603862e29f to your computer and use it in GitHub Desktop.

オブジェクト指向によるJavaプログラミング

オブジェクト指向とは

オブジェクト指向プログラミング(英: object-oriented programming, OOP)とは、相互にメッセージ (message) を送りあうオブジェクト (object) の集まりとしてプログラムを構成する技法である。

オブジェクト指向プログラミングとは、一般的に以下の機能や特徴のいくつか、あるいは多くを活用したプログラミング技法である。

  • カプセル化(振る舞いの隠蔽とデータ隠蔽)
  • インヘリタンス(継承)
  • ポリモフィズム(多態性、多相性)
  • ダイナミックバインディング(動的束縛)

オブジェクト指向が難解なわけ

  • 用語の曖昧な定義にまつわる問題(クラス,インスタンス,広義のオブジェクト)
  • 設計側のオブジェクト指向(概念)と実装側(どうすればプログラムがかけるのか)のずれ
  • 現実世界をモデリングしたと言いながら,抽象(abstruct)と具象(concrete)の分離
  • そもそも学習コストが高い(プログラム構文 + 提供クラス理解 + ベストプラクティス(デザインパターン))

そもそも,オブジェクト指向が出てきた背景には,プログラムやデータの責務を適切にすることにより,再利用や拡張および補修を簡便にするという構想があった。よってそれらを活用しない場合には,冗長で面倒に思われるであろう。また入門本においては,そもそもオブジェクト指向的でないサンプルが大多数を占めており,通常はプログラム言語の構文を学んだのちにオブジェクト指向の書き方を学ぶことが多い。

プログラム言語の歴史

Hands On

さて,実際にオブジェクト指向的なプログラムを見ていきたいと思います。 題材は,おなじみのHelloWorldです。みなさんサクッとコードを書いてみてください。

  • ファイル名: HelloWorld.java
  • コンソールアプリケーション上で画面に’HelloWorld'を出力する。

入門書のHelloWorld

HelloWorld.java

class HelloWorld {
  public static void main(String...args) {
    System.out.println("HelloWorld!");
  }
}

  • Javaアプリケーションテンプレート
  • クラス名TestRunner.java
interface インターフェース名 {
  abstract 型名 メソッド名();
}
class クラス名 implements インターフェース名 {
  クラス名() {} // コンストラクタ
  @override
  public 型名 メソッド名() {
    // 実装メソッド
  }
}
class クラス名TestRunner {
  public static void main(String...args) {
    インターフェース名 インスタンス名 = new クラス名();
    インスタンス名.メソッド名();
  }
}

オブジェクト指向HelloWorld

ConsoleApp.java

class HelloWorld {
  private final String content = "HelloWorld";
  // オーバーライド
  @Override
  public String toString() {
    return content;
  }
  public String getContent() {
    return content;
  }
}

interface Screen {
  // 抽象メソッド定義
  abstract void setContent(Object o);
  abstract void draw();
}

class CUIScreen implements Screen {
  // コンポジション
  private Object content;
  @Override
  public void setContent(Object content) {
    this.content = content;
  }
  @Override
  public void draw() {
    System.out.println(content.getContent());
  }
}

// インターフェース
interface Application {
  // 抽象メソッド定義
  abstract void run();
}

// Applicationインターフェース継承
public class ConsoleApp implements Application {
  // シングルトン
  private static final Application application = new ConsoleApp();
  // コンストラクタ
  private ConsoleApp() {}
  public static Application getInstance() {
    return application;
  }

  // 具象メソッド定義
  @Override
  public void run() {
    HelloWorld hello = new HelloWorld();
    Screen screen = new CUIScreen();
    screen.setContent(hello);
    screen.draw();
  }
  // メイン
  public static void main(String...args) {
    // インスタンス生成
    Application app = ConsoleApp.getInstance();
    app.run();
  }
}
設計手法

[コンソールアプリケーション]上で[画面]に[’HelloWorld']を出力する。

  • アプリケーション(抽象) <- コンソールアプリケーション(具象)
    • 起動する(run)
  • 画面(抽象) <- CUIの画面(具象)
    • 画面内容をセットする(setContent)
    • 画面に描画する(draw)
  • コンテント <- HelloWorld
    • 内容を取得する(getContent)

次は,並べ替えのアルゴリズム(ソート)をかいてみよう

  • ファイル名: BubbleSort.java
  • ファイル名: MergeSort.java
  • 点数(数値)のリストを昇順に並び替える

並べ替え(手続き型プログラミング)

BubbleSort.java

class BubbleSort {
  public static void main(String...args) {
    int[] scores = {5, 3, 7};
    for (int i = 0; i < scores.length - 1; i++) {
      for (int j = scores.length - 1; j > i; j--) {
        if (scores[j] < scores[j - 1]) {
          int tmp = scores[j];
          scores[j] = scores[j - 1];
          scores[j - 1] = tmp;
        }
      }
    }
    // Display 処理
  }
}

MergeSort.java

class MergeSort {

  static void merge(int[] low, int[] high, int[] integrate) {
    int i = 0, j = 0;
    while (i < low.length || j < high.length) {
      if (j >= high.length || (i < low.length && low[i] < high[j])) {
        integrate[i + j] = low[i];
        i++;
      } else {
        integrate[i + j] = high[j];
        j++;
      }
    }
  }

  static void sort(int[] array) {
    if (array.length > 1) {
      int m = array.length / 2;
      int n = array.length - m;
      int[] left  = new int[m];
      int[] right = new int[n];
      for (int i = 0; i < m; i++) {
        left[i] = array[i];
      }
      for (int i = 0; i < n; i++) {
        right[i] = array[m + i];
      }
      sort(left);
      sort(right);
      merge(left, right, array);
    }
  }
  // 表示
  static void display(String message, int[] scores) {
    System.out.println(message);
    String s = "";
    for (int i = 0; i < scores.length; i++) {
      s += scores[i];
      if (i < scores.length - 1) {
        s += ", ";
      }
    }
    System.out.println("[" + s + "]");
  }
  // メイン
  public static void main(String...args) {
    int[] scores = {5, 3, 7};
    display("並べ替え前", scores);
    sort(scores);
    display("並べ替え後", scores);
  }
}

並べ替え(Arrays#sortを使ったマージソート シンプル実装)

class MergeSortTestRunner {
  public static void main(String...args) {
    Integer[] scores = {5, 3, 7};
    display("並べ替え前", scores);
    java.util.Arrays.sort(scores);
    display("並べ替え後", scores);
  }
  static void display(String message, Integer[] scores) {
    System.out.println(message);
    System.out.println(java.util.Arrays.toString(scores));
  }
}

次は以下をみてみる

  • Scores.java[手続き型]
  • 配列の最高得点,最低得点,平均値を計算して表示。
class Scores {
  public static void main(String...args) {
    int[] scores = {3,7,5};
    int max = scores[0];
    int min = scores[0];
    int sum = 0;
    for (int i = 0; i < scores.length; i++) {
      sum += scores[i];
      if (scores[i] > max) {
        max = scores[i];
      }
      if (scores[i] < min) {
        min = scores[i];
      }
    }
    System.out.println("最高:" + max);
    System.out.println("最低:" + min);
    System.out.println("平均:" + sum / scores.length);
  }
}
  • ScoreCalcTestRunner.java[コレクションオブジェクトを利用]
  • 配列の最高得点,最低得点,平均値をオブジェクトを利用して表示。
    • Collections.max(List) 最高得点
    • Collections.min(List) 最低得点
    • Stream.average 平均点
import java.util.Collections;
import java.util.Arrays;
import java.util.List;

class ScoreCalc {
  private List<Integer> scores;
  ScoreCalc(List<Integer> scores) {
    this.scores = scores;
  }
  ScoreCalc(Integer[] scores) {
    this(Arrays.asList(scores));
  }
  public int max() {
    return Collections.max(scores);
  }
  public int min() {
    return Collections.min(scores);
  }
  public double average() {
    return scores.stream().mapToInt(v -> v.intValue()).average().orElse(0.0d);
  }
  public String toString() {
    return String.join(System.getProperty("line.separator"), {
      "最高:" + max(),
      "最低:" + min(),
      "平均:" + average()
    });
  }
}

class ScoreCalcTestRunner {
  public static void main(String...args) {
    Integer[] scores = {3, 7, 5};
    ScoreCalc scorecalc = new ScoreCalc(scores);
    System.out.println(scorecalc);
  }
}

最初のクラス

class Student {

}
class StudentTestRunner {
  public static void main(String...args) {
    Student student = new Student();
  }
}

クラスへフィールドを追加

class Student {
  public String firstName; // フィールド
  public String lastName;  // フィールド
}
class StudentTestRunner {
  public static void main(String...args) {
    Student student = new Student();
    student.firstName = "Taro";
    student.lastName = "Yamada";
    //確認
    System.out.printf("%s%s%n", student.lastName, student.firstName);
  }
}

カプセル化, メソッドの追加, コンストラクタメソッド

class Student {
  private String firstName;  //privateへ変更
  private String lastName;   //privateへ変更

  // コンストラクタメソッド
  Student(String lastName, String firstName) {
    this.lastName = lastName;
    this.firstName = firstName;
  }
  // メソッド
  public String getFullName(String sep) {
    return String.join(sep, this.lastName, this.firstName);
  }
}
class StudentTestRunner {
  public static void main(String...args) {
    Student student = new Student("Yamada", "Taro");
    //確認
    System.out.println(student.getFullName(" "));
  }
}

アクセッサメソッド(getter/setter)

class Student {
  private String firstName;  //privateへ変更
  private String lastName;   //privateへ変更
  // コンストラクタ
  Student(String lastName, String firstName) {
    this.lastName = lastName;
    this.firstName = firstName;
  }
  // メソッド
  public String getFullName(String sep) {
    return String.join(sep, this.firstName, this.lastName);
  }

  // アクセッサメソッド
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getFirstName() {
    return this.firstName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getLastName() {
    return this.lastName;
  }
}
class StudentTestRunner {
  public static void main(String...args) {
    Student student = new Student("Taro", "Yamada");
    student.setLastName("Suzuki");
    //確認
    System.out.println(student.getLastName());
    System.out.println(student.getFullName(" "));
  }
}

メソッドの追加

import java.util.Calendar;
class Student {
  private String firstName;  //privateへ変更
  private String lastName;   //privateへ変更
  // コンストラクタ
  Student(String lastName, String firstName) {
    this.lastName = lastName;
    this.firstName = firstName;
  }
  // メソッド
  public String getFullName(String sep) {
    return String.join(sep, this.firstName, this.lastName);
  }

  // アクセッサメソッド
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getFirstName() {
    return this.firstName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getLastName() {
    return this.lastName;
  }

  public String greeting() {
    int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
    String message;
    if (hour < 12) {
      message =  "おはようございます。";
    } else if (hour < 18) {
      message = "こんにちは。";
    } else {
      message = "こんばんは。";
    }
    return message;
  }
  public String goodBy(Student somebody) {
    return "さようなら" + somebody.getFirstName() + "さん。";
  }

}
class StudentTestRunner {
  public static void main(String...args) {
    Student yamada = new Student("Taro", "Yamada");
    Student suzuki = new Student("Ichiro", "Suzuki");
    System.out.println(yamada.greeting());
    System.out.println(suzuki.greeting());
    System.out.println(yamada.goodBy(suzuki));
    System.out.println(suzuki.goodBy(yamada));
  }
}

継承

import java.util.Calendar;
class Student {
  private String firstName;
  private String lastName;
  // コンストラクタ
  Student(String lastName, String firstName) {
    this.lastName = lastName;
    this.firstName = firstName;
  }
  // メソッド
  public String getFullName(String sep) {
    return String.join(sep, this.firstName, this.lastName);
  }

  // アクセッサメソッド
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getFirstName() {
    return this.firstName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getLastName() {
    return this.lastName;
  }
  public String greeting() {
    int hour = Calendar.getInstance().get(Calendar.HOUR);
    String message;
    if (hour < 12) {
      message =  "おはようございます。";
    } else if (hour < 18) {
      message = "こんにちは。";
    } else {
      message = "こんばんは。";
    }
    return message;
  }
  public String goodBy(Student somebody) {
    return "さようなら" + somebody.getFirstName() + "さん。";
  }

}
class ForeignStudent extends Student {
  private String middleName;
  ForeignStudent(String firstName, String middleName, String lastName) {
    super(firstName, lastName);
    this.middleName = middleName;
  }
  public void setMiddleName(String middleName) {
    this.middleName = middleName;
  }
  public String getMiddleName(String middleName) {
    return  this.middleName;
  }
  @Override
  public String getFullName(String sep) {
    return String.join(sep, this.getFirstName()
      , this.middleName, this.getLastName());
  }
}
class StudentTestRunner {
  public static void main(String...args) {
    ForeignStudent student = new ForeignStudent("Kouta", "Samuel", "Nakachi");
    //確認
    System.out.println(student.getFullName(" "));
    // 問題ありのコード
    // Student suzuki = new Student("Ichiro", "Suzuki");
    // System.out.println(suzuki.goodBy(student));
  }
}

リファクタリング

interface Student {
  String greeting();
  String goodBy();
}

class JapaneseStudent implements Student {

}
class ForeignStudent implements Student {

}
class StudentTestRunner {
  public static void main(String...args) {
      Student suzuki = new JapaneseStudent(....);
      Student tapa   = new ForeginStudent(.....);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment