SlideShare a Scribd company logo
Java再入門
市岡岳彦
2020/6/3
© 2020 SG Corporation 2
なんとなくで書いているコードをもうちょっとなんとかしたい。
Java再入門
© 2020 SG Corporation 3
Javaの古くからある機能を一度振り返っておこう。
Java再入門
© 2020 SG Corporation 4
サンプルコード
https://www.sgnet.co.jp/edu/2020050701_java.zip
Java再入門
© 2020 SG Corporation 5
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
© 2020 SG Corporation 6
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
© 2020 SG Corporation 7
Javaのスコープはpublic,private,protectedだよね。
ん?なんか忘れてないか。
スコープ再考
© 2020 SG Corporation 8
 アクセス修飾子がないメソッド
スコープ再考
int foo() …
© 2020 SG Corporation 9
 なぜ、Javaのデフォルトスコープがパッケージなのか?
 パッケージを小さなライブラリと考える。
 パッケージ内のクラスが協働して小さな問題を解決する。
スコープ再考
© 2020 SG Corporation 10
 なぜ、Javaのデフォルトスコープがパッケージなのか?
 パッケージ外からは見えないけど、中からは見える。
 1クラス中のprivateメソッドが多くなってきたら、パッケージプ
ライベートなクラスを作るべきサイン。
 テスト容易性という副産物も。
スコープ再考
© 2020 SG Corporation 11
問題1
 ファクトリメソッドでオブジェクトを構築した後に値を変更した
いけど、外部からはアクセスさせたくない。
スコープ再考
© 2020 SG Corporation 12
問題1 解答例の概要
従業員管理
 従業員はチームに所属する。
 従業員は複数チームに所属してもいい。
 チームは固定とする。
 従業員にどのチームに所属しているかを問い合わせるインター
フェースがある。
 ある従業員を後からチームに追加したいが、勝手に個々の従業員
オブジェクトにチームを追加されるのも困る。
スコープ再考
© 2020 SG Corporation 13
問題1 解答例の概要
 クラス
 Book 管理簿
 Employee 従業員
 Team チーム
スコープ再考 サンプルコード:1_scope
© 2020 SG Corporation 14
問題1解答編
 パブリック
 😄後からチームを追加することができる。
 😓管理簿内の従業員を外から変更できてしまう。
スコープ再考
public final class Employee {
private final EnumSet<Team> teams;
…
public boolean addTeam(Team team) {
return teams.add(team);
}
サンプルコード:1_scope/1_1
© 2020 SG Corporation 15
問題1解答編
 防御的コピー
 😄後からチームを追加しても、管理簿内の従業員は変わらない。
 😓逆に、管理簿に反映されていない!という誤解を生む。
→インターフェースがデザインの動機を表していない。
スコープ再考
public final class Book {
private final Map<Integer, Employee> employees =
…
public void add(Employee employee) {
employees.put(employee.getID(),
Employee.create(employee.getID(),
employee.getTeams()));
サンプルコード:1_scope/1_2
© 2020 SG Corporation 16
問題1解答編
 パッケージプライベート
 😄チーム追加は必ず管理簿を通して行うことが明確になる。
 😓パッケージプライベートにしている意図を分かっていないと、
改修時にパブリックにしがち。
スコープ再考
public final class Employee {
public boolean addTeam(Team team) {
…
public final class Book {
public boolean addEmployeeTeam(int id, Team team) {
…
employee.addTeam(team);
サンプルコード:1_scope/1_3
© 2020 SG Corporation 17
 スコープでメソッドの意図を伝える one more step
 公開APIと拡張ポイントが一緒になっている…
スコープ再考
public abstract int foo();
© 2020 SG Corporation 18
 スコープでメソッドの意図を伝える one more step
 公開APIと拡張ポイントを明示する。
 オーバーライド側はこれ以上継承されたくなければ、finalにする。
スコープ再考
public final int foo() {
return fooImpl();
}
protected abstract int fooImpl();
© 2020 SG Corporation 19
 スコープでクラスの意図を伝える one more step
 クラス自体は公開したいが、継承はパッケージ内のみとしたい…
スコープ再考
public class Foo {
…
}
© 2020 SG Corporation 20
 スコープでクラスの意図を伝える one more step
 継承をパッケージ内のみに許可する。
 コンストラクタをパッケージプライベートにすることによって、
実質的finalにする。
スコープ再考
public class Foo {
Foo() {
…
}
…
}
© 2020 SG Corporation 21
 補足:コンストラクタチェーン
 コンストラクタが呼び出されたとき何が起きるか?
 コンストラクタ内で明示的にsuperを呼び出さない場合は、暗黙
的にスーパークラスの引数なしのコンストラクタが呼び出される。
 このとき、スーパークラスのコンストラクタにアクセス不可能だ
と、コンパイル時にエラーになる。
 明示的にsuperを呼び出した場合も同じ。
スコープ再考
© 2020 SG Corporation 22
まとめ
 パッケージを小さなライブラリと考える
 パッケージ内のクラスが協働して小さな問題を解決する
 修飾子でクラスデザインの意図を伝える
 メソッドのオーバーライド可能性に注意する
スコープ再考
© 2020 SG Corporation 23
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
© 2020 SG Corporation 24
 抽象クラスと抽象メソッドをどう使うかは悩みどころ。
 ふるまいを一般化する場合は、基本的にはインターフェースを選
択するけど、抽象クラスはどういうときに使うか。
abstract…?
抽象クラスと抽象メソッド
© 2020 SG Corporation 25
 protectedメソッドを呼ぶpublicメソッドでメソッド間の関係性を
保証する。
 例)java.util.AbstractList, java.util.AbstractSequentialList
 どのような実装であれ、リストとして期待される動作を保証する。
抽象クラスと抽象メソッド
© 2020 SG Corporation 26
問題2
 多態性は持たせたいが、自由に継承はさせたくない。
 関係のあるメソッド間の契約を保証したい。
抽象クラスと抽象メソッド
© 2020 SG Corporation 27
問題2 解答例の概要
給与計算
 基本給と手当がある。
 給与は 基本給+手当 とする。
 手当は 単価×時間 とするが、計算式が異なる可能性がある。
 手当の計算式が異なっても、給与=基本給+手当 は保証する。
抽象クラスと抽象メソッド サンプルコード:2_abstract
© 2020 SG Corporation 28
問題2 解答例の概要
 クラス
 Salary 給与
抽象クラスと抽象メソッド サンプルコード:2_abstract
© 2020 SG Corporation 29
問題2解答編
 パブリックコンストラクタ
 😄わかりやすい。
 😓勝手に継承される。
抽象クラスと抽象メソッド
public class Salary {
public Salary(int base, int wage) {
サンプルコード:2_abstract/2_1
© 2020 SG Corporation 30
問題2解答編
 パッケージプライベートコンストラクタ
 😄継承をパッケージ内のみにとどめる。
 😓オーバーライドするポイントが分からない。
抽象クラスと抽象メソッド
public class Salary {
public Salary(int base, int wage) {
…
public static Salary create(int base, int wage) {
return new Salary(base, wage);
}
サンプルコード:2_abstract/2_2
© 2020 SG Corporation 31
問題2解答編
 abstractメソッド
 😄オーバーライドするポイントが分かる。
 😓意図したオーバーライドポイント以外もオーバーライドできて
しまう。
抽象クラスと抽象メソッド
public abstract class Salary {
public abstract int getBase();
…
public abstract int getWage(int hour);
サンプルコード:2_abstract/なし
© 2020 SG Corporation 32
問題2解答編
 finalメソッド
 😄オーバーライド不可なポイントを守ることにより、メソッド間
の契約を保証できる。
抽象クラスと抽象メソッド
public abstract class Salary {
public final int getBase() {
return getBaseImpl();
}
protected abstract int getBaseImpl();
…
public final int total(int hour) {
return getBase() + getWage(hour);
}
サンプルコード:2_abstract/2_3
© 2020 SG Corporation 33
問題2 いったんまとめ
 abstractメソッドは最小限にする。publicメソッドはabstractメ
ソッド同士の関係性を定義する。
 拡張性を公開しないのであれば、公開するべきではない。
 公開ポイントと拡張ポイントは区別する。
抽象クラスと抽象メソッド
© 2020 SG Corporation 34
問題2解答編
 公開クラスと拡張クラスを分ける。
 😄拡張ポイントを分割・明示できる。
抽象クラスと抽象メソッド
public final class Salary {
private final SalaryImpl salaryImpl;
Salary(int base, int wage, SalaryImpl salaryImpl) {
…
this.salaryImpl = salaryImpl;
static interface SalaryImpl {
サンプルコード:2_abstract/2_4
© 2020 SG Corporation 35
問題2解答編
 拡張クラスを公開する。
 😄拡張ポイントのみを別個に公開できる。
抽象クラスと抽象メソッド
public final class Salary {
private final SalaryImpl salaryImpl;
Salary(int base, int wage, SalaryImpl salaryImpl) {
…
this.salaryImpl = salaryImpl;
public interface SalaryImpl {
サンプルコード:2_abstract/2_5
© 2020 SG Corporation 36
「疎結合としてのサービス」
で、もうちょっとやります。
抽象クラスと抽象メソッド
© 2020 SG Corporation 37
まとめ
 抽象クラスは継承クラスを容易に作成するためのヘルパー
 公開メソッドは抽象メソッド同士の関係性を定義する
 公開メソッドはオーバーライド不可とする
抽象クラスと抽象メソッド
© 2020 SG Corporation 38
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
© 2020 SG Corporation 39
Javaの制御構造はもちろんわかってるよ。
ifとかforとか…
制御構造再考
© 2020 SG Corporation 40
問題3
 単位処理数ごとのバッチ処理、コントロールブレイクをどう実現
するか。
 例)
 1000件ごと(バッチ)
 何かグループ化の条件が変わった場合(コントロールブレイク)
制御構造再考
© 2020 SG Corporation 41
問題3解答編
 そのままループ
 😄シンプルな解決策
 😓外側のループと内側のループでiを共有しているので分かりにく
い。
制御構造再考
for (int i = 0; i < employees.size(); i += 1000) {
for (int id : employees.subList(i,
Math.min(i + 1000, employees.size()))) {
for (int i = 0; i < employees.size();) {
for (int from = i; !isBreakIndex(employees, from, i); i++) {
サンプルコード:3_control/3_1
© 2020 SG Corporation 42
問題3解答編
 そのままループ その2(バッチ)
 😄外側と内側のループを分けた。
制御構造再考
for (int i = 0, n = 0; i < employees.size(); i += n) {
n = batchLoopSub(employees, i);
…
private int batchLoopSub(List<Employee> employees,
int fromIndex) {
int count =
Math.min(fromIndex + 1000, employees.size());
for (int id : employees.subList(fromIndex, count)) {
…
return count - fromIndex;
サンプルコード:3_control/3_2
© 2020 SG Corporation 43
問題3解答編
 そのままループ その2 (コントロールブレイク)
 😄外側と内側のループを分けた。
制御構造再考
for (int i = 0, n = 0; i < employees.size(); i += n) {
n = controlBreakLoopSub(employees, i);
…
private int controlBreakLoopSub(List<Employee> employees,
int fromIndex) {
int i = fromIndex;
for (; !isBreakIndex(employees, fromIndex, i); i++) {
…
return i - fromIndex;
サンプルコード:3_control/3_2
© 2020 SG Corporation 44
問題3解答編
なんか似てないか、これ
制御構造再考
© 2020 SG Corporation 45
問題3解答編
 そのままループ その2
 😕ここ以外同じだな…
制御構造再考
int count =
Math.min(fromIndex + 1000, employees.size());
for (int id : employees.subList(fromIndex, count)) {
int i = fromIndex;
for (; !isBreakIndex(employees, fromIndex, i); i++) {
サンプルコード:3_control/3_2
© 2020 SG Corporation 46
問題3解答編
 そのままループ その3
 バッチのループ継続条件をメソッド化
 😳同じ構造になった!
制御構造再考
int i = fromIndex;
for (; !isBatchIndex(employees, fromIndex, i); i++) {
private boolean isBatchIndex(List<Employee> employees,
int from, int index) {
return index == Math.min(from + 1000, employees.size());
}
サンプルコード:3_control/3_3
© 2020 SG Corporation 47
問題3解答編
同じ構造のものはまとめよう
制御構造再考
© 2020 SG Corporation 48
問題3解答編
 ループ用クラス
制御構造再考
public final class Loop<E> {
@FunctionalInterface
public static interface ListBreak<E> {
boolean test(List<E> list, int from, int index);
}
private final ListBreak<E> listBreak;
public Loop(ListBreak<E> listBreak) {
this.listBreak = listBreak;
}
サンプルコード:3_control/3_3
© 2020 SG Corporation 49
問題3解答編
 ループ用クラス 続き
制御構造再考
…
public void loop(List<E> list) {
for (int i = 0, n = 0; i < list.size(); i += n) {
n = loopSub(list, i);
…
private int loopSub(List<E> list, int fromIndex) {
int i = fromIndex;
for (; !listBreak.test(list, fromIndex, i); i++) {
…
return i - fromIndex;
…
サンプルコード:3_control/3_3
© 2020 SG Corporation 50
問題3解答編
 ループ用クラス 呼び出し側
 😄バッチとコントロールブレイクでループを共通化できた。
 😓ループの中身である処理本体は同じことしかできない。
制御構造再考
Loop<Employee> batch =
new Loop<>(Main::batchBreakIndex);
batch.loop(employees);
Loop<Employee> control =
new Loop<>(Main::controlBreakIndex);
control.loop(employees);
サンプルコード:3_control/3_3
© 2020 SG Corporation 51
問題3解答編
 ビューのリストに変形する
制御構造再考
public final class Loop<E> {
…
public List<List<E>> listView(List<E> list) {
List<List<E>> view = new ArrayList<>();
for (int i = 0, n = 0; i < list.size(); i += n) {
n = loopSub(list, i);
view.add(list.subList(i, i + n));
}
return view;
}
サンプルコード:3_control/3_4
© 2020 SG Corporation 52
問題3解答編
 ビューのリストに変形する 呼び出し側
 😄ループの処理本体を外に出せた。
 😄呼び出し側は一般的な拡張for文のみで対応できる。
 😓一時的ではあるが、リストの生成が必要になる。
制御構造再考
Loop<Employee> batch =
new Loop<>(Main::batchBreakIndex);
for (List<Employee> list : batch.listView(employees)) {
for (Employee e : list) {
サンプルコード:3_control/3_3
© 2020 SG Corporation 53
問題3解答編
 ビューを返すイテレータを作る
制御構造再考
public final class Loop<E> implements Iterable<List<E>> {
…
private final List<E> list;
public Loop(List<E> list, ListBreak<E> listBreak) {
…
}
@Override public Iterator<List<E>> iterator() {
return new Iterator<List<E>>() {
…
}
}
サンプルコード:3_control/3_4
© 2020 SG Corporation 54
問題3解答編
 ビューを返すイテレータを作る 呼び出し側
 😄ブレイク条件のみを定義すれば、後はイテレータがやってくれ
る。
 😓初見ではわかりづらい。
制御構造再考
Loop<Employee> batch =
new Loop<>(employees, Main::batchBreakIndex);
for (List<Employee> list : batch) {
for (Employee e : list) {
サンプルコード:3_control/3_4
© 2020 SG Corporation 55
問題3解答編
せっかくなので、ブレイク条件も一般化しよう
制御構造再考
© 2020 SG Corporation 56
問題3解答編 one more step
 バッチブレイク条件
制御構造再考
static <E> ListBreak<E> batchBreakIndex(int size) {
return (list, from, index) ->
index == Math.min(from + size, list.size());
}
Loop<Employee> batch =
new Loop<>(employees, batchBreakIndex(1000));
サンプルコード:3_control/3_5
© 2020 SG Corporation 57
問題3解答編 one more step
 コントロールブレイク条件
制御構造再考
static <E> ListBreak<E> controlBreakIndex(
BiFunction<E, E, Boolean> test) {
return (list, from, index) -> {
if (index == list.size()) return true;
if (index == from) return false;
return !test.apply(list.get(index - 1), list.get(index));
};
}
Loop<Employee> control = new Loop<>(employees,
controlBreakIndex((a, b) -> Objects.equals(a, b)));
サンプルコード:3_control/3_5
© 2020 SG Corporation 58
 問題がデータ構造であるのなら、データ構造をプログラミングす
る。
 ビューとコピーを区別する。コピーが必要であればコピーする。
 問題が制御構造であるのなら、制御構造をプログラミングする。
 コピーが必要なければ、ビューで対応する。
 使う側は動作をプログラミングしなくてもいい。
 宣言型プログラミング
制御構造再考
© 2020 SG Corporation 59
 とは言え、そのプロジェクトで頻出のイディオムであるという場
合に使うべき。
 素直にループを書いたほうがシンプルな解決策であることには変
わりはない。
 簡明さは失われてしまった。
制御構造再考
© 2020 SG Corporation 60
 補足:ラムダ式・メソッド参照
 Java8以降、メソッド参照とラムダ式が使えるようになった。
 オーバーライド用のメソッドを1つだけ持つインターフェースを
実装する手間を減らす。
 これにより、ラッパークラスを作らなくても、シグネチャが合致
するメソッドをそのまま渡すことができる。
制御構造再考
@FunctionalInterface
public interface Runnable {
void run();
}
© 2020 SG Corporation 61
 補足:ラムダ式・メソッド参照
制御構造再考
public static void main(String ... args) {
ExecutorService service = Executors.newSingleThreadExecutor();
// 無名クラス
service.execute(new Runnable() {
public void run() { print(); }
});
// メソッド参照
service.execute(Main::print);
// ラムダ式
service.execute(() -> print());
service.shutdown();
}
private static void print() {
System.out.println("run.");
}
© 2020 SG Corporation 62
まとめ
 問題がデータ構造であるのなら、データ構造をプログラミングす
る
 問題が制御構造であるのなら、制御構造をプログラミングする
 再利用性と簡明さはトレードオフ(の場合がある)
制御構造再考
© 2020 SG Corporation 63
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
© 2020 SG Corporation 64
オブジェクトにはcloseとかfreeとかないよね…
オブジェクトのライフサイクル
© 2020 SG Corporation 65
 Javaは明示的にメモリ領域を管理しない。
 オブジェクトのライフサイクルを気にしなくていいということで
はない。
オブジェクトのライフサイクル
© 2020 SG Corporation 66
問題4
 登録されたイベントリスナーをイベント発生時に呼び出す。
 リスナーが途中でいなくなっても、登録された側で保持している
と、GCで回収されない。
オブジェクトのライフサイクル
© 2020 SG Corporation 67
問題4 解答例の概要
従業員管理
 管理簿に従業員の追加を依頼する。
 追加依頼は非同期に終了する。
 追加完了時にリスナーにコールバックがかかる。
オブジェクトのライフサイクル サンプルコード:4_lifecycle
© 2020 SG Corporation 68
問題4 解答例の概要
 クラス
 Book 管理簿
オブジェクトのライフサイクル サンプルコード:4_lifecycle
© 2020 SG Corporation 69
問題4解答編
 リスナーの登録のみをする
オブジェクトのライフサイクル
public final class Book {
@FunctionalInterface
public static interface Listener {
void action(Employee employee);
}
private final Map<Listener, Object> listeners =
new ConcurrentHashMap<>();
public void addListener(Listener listener) {
listeners.put(listener, new Object());
}
サンプルコード:4_lifecycle/4_1
© 2020 SG Corporation 70
問題4解答編
 リスナーの登録のみをする 続き
オブジェクトのライフサイクル
public void addAsync(Employee employee) {
addService.execute(new Runnable() {
@Override public void run() {
add(employee);
actionAddEvent(employee);
}
});
}
サンプルコード:4_lifecycle/4_1
© 2020 SG Corporation 71
問題4解答編
 リスナーの登録のみをする 続き
 😓リスナーがいなくなっても、オブジェクトがGCで回収されない。
オブジェクトのライフサイクル
private void actionAddEvent(Employee employee) {
for (Listener l : listeners.keySet()) {
l.action(employee);
}
}
サンプルコード:4_lifecycle/4_1
© 2020 SG Corporation 72
問題4解答編
 削除APIも公開する
 😄削除メソッドが呼ばれれば、GCで回収される。
 😓公開APIだと、削除メソッドが呼ばれる保証がない。
オブジェクトのライフサイクル
public void removeListener(Listener listener) {
listeners.remove(listener);
}
サンプルコード:4_lifecycle/4_2
© 2020 SG Corporation 73
問題4解答編
 弱い参照
 マップ内のリスナーを弱い参照(WeakReference)で保持する。
オブジェクトのライフサイクル
public final class Book {
private final Map<WeakReference<Listener>, Object>
listeners = new ConcurrentHashMap<>();
public void addListener(Listener listener) {
listeners.put(new WeakReference<>(listener),
new Object());
}
サンプルコード:4_lifecycle/4_3
© 2020 SG Corporation 74
問題4解答編
 弱い参照 続き
オブジェクトのライフサイクル
private void actionAddEvent(Employee employee) {
for (Iterator<WeakReference<Listener>> itr =
listeners.keySet().iterator(); itr.hasNext(); ) {
Listener l = itr.next().get();
if (l == null) {
itr.remove();
continue;
}
l.action(employee);
}
}
サンプルコード:4_lifecycle/4_3
© 2020 SG Corporation 75
問題4解答編
 弱い参照
 😄通常の参照が0件になれば、GCで回収される。
 😄オブジェクトのライフサイクルを気にしていないことを明示で
きる。
 😓API仕様で、弱い参照をしていることを明示しておく必要があ
る。
オブジェクトのライフサイクル
© 2020 SG Corporation 76
 ブロードキャストする側はオブジェクトのライフサイクルを気に
しない。
 登録と登録解除を提供してもいいけど、ユーザーが登録解除を呼
ぶ保証はない!
 オブジェクトのライフサイクルを気にしていないということを明
示する。
オブジェクトのライフサイクル
© 2020 SG Corporation 77
 オブジェクト生成 one more step
 派生型が絡むと多少厄介…
オブジェクトのライフサイクル
© 2020 SG Corporation 78
 リストをコピーするメソッドのデザインとして、2通りが考えら
れる。
アウトパラメーターとする
戻り値とする
オブジェクトのライフサイクル
<E> void copyList(List<E> dest, List<E> src)
<E> List<E> copyList(List<E> src)
© 2020 SG Corporation 79
 アウトパラメーター
 呼び出し元がコピー先リストを生成する
 リストの具象クラスを呼び出し元が決めたい場合
 呼び出し先は実行時クラスが何かを気にしていない
 メソッドがアルゴリズムを表している場合はこのパターン
オブジェクトのライフサイクル
<E> void copyList(List<E> dest, List<E> src)
© 2020 SG Corporation 80
 戻り値
 呼び出し先がコピー先リストを生成する
 リストの具象クラスを呼び出し先が決めたい場合
 呼び出し元は実行時クラスが何かを気にしていない
 メソッドがファクトリーを表している場合はこのパターン
オブジェクトのライフサイクル
<E> List<E> copyList(List<E> src)
© 2020 SG Corporation 81
まとめ
 オブジェクトのライフサイクルを気にしなくていいということで
はない
 オブジェクトの生成消滅に対する責務をどこが担っているかを表
現する
 メソッドがアルゴリムなのか、ファクトリーなのかでも責務が変
わってくる
オブジェクトのライフサイクル
© 2020 SG Corporation 82
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
© 2020 SG Corporation 83
突然ですが、MySQLに接続しよう…
疎結合としてのサービス
© 2020 SG Corporation 84
 JDBCドライバ(というかコネクション)を取得する例
 😳“jdbc:mysql://〜”を渡すとMySQLのドライバが取得できる!
疎結合としてのサービス
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testdb",
"user", "pass");
© 2020 SG Corporation 85
JDBCドライバのような機構はどのように実現されているのか?
疎結合としてのサービス
© 2020 SG Corporation 86
問題5
 状態を保存したいが、保存先をプロパティファイル、XML、DB等
で切り替えたい。
疎結合としてのサービス
© 2020 SG Corporation 87
問題5 解答例の概要
従業員管理
 従業員管理簿を外部に保存する。
 外部保存の実装は継承で拡張できる。
疎結合としてのサービス サンプルコード:5_service
© 2020 SG Corporation 88
問題5 解答例の概要
 クラス
 Book 管理簿
 BookHelper 管理簿と保存機能の橋渡し
 Persistance 保存機能
疎結合としてのサービス サンプルコード:5_service
© 2020 SG Corporation 89
問題5解答編
 ファクトリメソッドで文字列から作成
疎結合としてのサービス
public abstract class Persistance {
public static Persistance create(String persist) throws
IOException {
switch (persist) {
case "xml":
return new XMLPersistance();
case "db":
return new DBPersistance();
default:
throw new IOException("No suitable provider found for ”
+ persist);
…
サンプルコード:5_service/5_1
© 2020 SG Corporation 90
問題5解答編
 ファクトリメソッドで文字列から作成
 😄難しい仕組みがいらない。
 😓コンパイル時に実装クラスが必要。
疎結合としてのサービス
© 2020 SG Corporation 91
問題5解答編
 リフレクションで実現
 😄仕組みがまあまあ簡潔。
 😓インスタンス生成するクラスが公開サービスなのかが不明確。
 😓パッケージ名とサービス名が直接結びついてしまう。
疎結合としてのサービス
public abstract class Persistance {
public static Persistance create(String persist) throws … {
return
Class.forName(persist).asSubclass(Persistance.class).
getDeclaredConstructor().newInstance();
}
サンプルコード:5_service/5_2
© 2020 SG Corporation 92
問題5解答編
 サービスで実現
疎結合としてのサービス
public interface PersistanceService {
boolean acceptName(String name);
void read(BookHelper book) throws IOException;
void write(BookHelper book) throws IOException;
}
サンプルコード:5_service/5_3
© 2020 SG Corporation 93
問題5解答編
 サービスで実現 続き
 😄仕組みがまあまあ簡潔。
 😓インスタンス生成するクラスが公開サービスなのかが不明確。
 😓パッケージ名とサービス名が直接結びついてしまう。
疎結合としてのサービス
public final class Book {
private final PersistanceService service;
public static Book createPersistance(String persist) throws
IOException {
PersistanceService found = findPersistanceService(persist);
if (found == null) {
throw new IOException("No suitable provider found for "
+ persist);
}
return new Book(found);
}
サンプルコード:5_service/5_3
© 2020 SG Corporation 94
問題5解答編
 サービスで実現 続き
疎結合としてのサービス
private static PersistanceService findPersistanceService(
String persist) {
for (PersistanceService service :
ServiceLoader.load(PersistanceService.class)) {
if (service.acceptName(persist)) return service;
}
return null;
}
サンプルコード:5_service/5_3
© 2020 SG Corporation 95
問題5解答編
 サービスで実現 続き
 😄サービスとして公開していることを明示できる。
 😄パッケージ名とサービス名を間接的な結びつきにできる。
 😓若干、仕組みが面倒。
疎結合としてのサービス
公開するサービスプロバイダー名を記載した
META-
INF/services/jp.co.sgnet.hrp.skillup202001.hr.spi.PersistanceService
を配置
jp.co.sgnet.hrp.skillup202001.hr.spi.XMLPersistanceServiceProvider
サンプルコード:5_service/5_3
© 2020 SG Corporation 96
 APIとSPI
 API : Application Programming Interface
 SPI : Service Provider Interface
 APIとSPIのクラス階層は分けておく。
 API側の実装クラスが構築時にSPIのオブジェクトをもらって、中
にオブジェクトとして持つ形式。
 APIそのものが継承されることを前提としていることは少ないは
ず。
疎結合としてのサービス
© 2020 SG Corporation 97
 補足: Java9のモジュール機構
 Java9以降、アクセス修飾子とは別にjarの外側に公開する/しな
いを制御することができるようになった。
疎結合としてのサービス
© 2020 SG Corporation 98
 補足: Java9のモジュール機構
 module-info.javaにクラスの公開/非公開を記載する。
疎結合としてのサービス
module local.api {
exports local.api;
uses local.api.MyService;
}
公開側: local.apiパッケージ
module local.app {
requires local.api;
uses local.api.MyService;
}
利用側: local.appパッケージ
module local.provider {
requires local.api;
provides local.api.MyService with
local.provider.MyServiceProvider1;
}
実装側: local.providerパッケージ
© 2020 SG Corporation 99
 補足: Java9のモジュール機構
疎結合としてのサービス
private static PersistanceService findPersistanceService(
String persist) {
for (PersistanceService service :
ServiceLoader.load(PersistanceService.class)) {
if (service.acceptName(persist)) return service;
}
return null;
}
© 2020 SG Corporation 100
まとめ
 APIとSPIは分けておく
 APIは継承を前提としない
 SPIは継承を前提とする
疎結合としてのサービス
© 2020 SG Corporation 101
◍ Effective Java 第3版
Joshua Bloch (著), 柴田 芳樹 (翻訳)
丸善出版
◍ APIデザインの極意 Java/NetBeansアーキテクト探究ノート
Jaroslav Tulach (著), 柴田 芳樹 (翻訳)
インプレス
参考文献
© 2020 SG Corporation 102
◍ SGソフトウェア開発ブログ
https://blog.sgnet.co.jp
◍ SlideShare公開資料
https://www.slideshare.net/t_ichioka_sg/
こちらもどうぞ
© 2020 SG Corporation 103
顧客と社員に信頼されるエス・ジー

More Related Content

What's hot

Spring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装するSpring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装する
Rakuten Group, Inc.
 
Clustering _ishii_2014__ch10
Clustering  _ishii_2014__ch10Clustering  _ishii_2014__ch10
Clustering _ishii_2014__ch10
Kota Mori
 
はじめての人のためのDeep Learning
はじめての人のためのDeep Learningはじめての人のためのDeep Learning
はじめての人のためのDeep Learning
Tadaichiro Nakano
 
PRML 5章 PP.227-PP.247
PRML 5章 PP.227-PP.247PRML 5章 PP.227-PP.247
PRML 5章 PP.227-PP.247
Tomoki Hayashi
 
TensorFlowとCNTK
TensorFlowとCNTKTensorFlowとCNTK
TensorFlowとCNTK
maruyama097
 
Convolutionl Neural Network 入門
Convolutionl Neural Network 入門Convolutionl Neural Network 入門
Convolutionl Neural Network 入門
maruyama097
 
Triplet Loss 徹底解説
Triplet Loss 徹底解説Triplet Loss 徹底解説
Triplet Loss 徹底解説
tancoro
 
【2016.05】cvpaper.challenge2016
【2016.05】cvpaper.challenge2016【2016.05】cvpaper.challenge2016
【2016.05】cvpaper.challenge2016
cvpaper. challenge
 
PythonによるDeep Learningの実装
PythonによるDeep Learningの実装PythonによるDeep Learningの実装
PythonによるDeep Learningの実装
Shinya Akiba
 

What's hot (9)

Spring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装するSpring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装する
 
Clustering _ishii_2014__ch10
Clustering  _ishii_2014__ch10Clustering  _ishii_2014__ch10
Clustering _ishii_2014__ch10
 
はじめての人のためのDeep Learning
はじめての人のためのDeep Learningはじめての人のためのDeep Learning
はじめての人のためのDeep Learning
 
PRML 5章 PP.227-PP.247
PRML 5章 PP.227-PP.247PRML 5章 PP.227-PP.247
PRML 5章 PP.227-PP.247
 
TensorFlowとCNTK
TensorFlowとCNTKTensorFlowとCNTK
TensorFlowとCNTK
 
Convolutionl Neural Network 入門
Convolutionl Neural Network 入門Convolutionl Neural Network 入門
Convolutionl Neural Network 入門
 
Triplet Loss 徹底解説
Triplet Loss 徹底解説Triplet Loss 徹底解説
Triplet Loss 徹底解説
 
【2016.05】cvpaper.challenge2016
【2016.05】cvpaper.challenge2016【2016.05】cvpaper.challenge2016
【2016.05】cvpaper.challenge2016
 
PythonによるDeep Learningの実装
PythonによるDeep Learningの実装PythonによるDeep Learningの実装
PythonによるDeep Learningの実装
 

Similar to Java class design

Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
JustSystems Corporation
 
規格書で読むC++11のスレッド
規格書で読むC++11のスレッド規格書で読むC++11のスレッド
規格書で読むC++11のスレッド
Kohsuke Yuasa
 
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
Masatoshi Tada
 
SQLQL は GraphQL にとってなんなのか
SQLQL は GraphQL にとってなんなのかSQLQL は GraphQL にとってなんなのか
SQLQL は GraphQL にとってなんなのか
yancya
 
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2Masatoshi Tada
 
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会Koichi Sakata
 
jjugccc2018 app review postmortem
jjugccc2018 app review postmortemjjugccc2018 app review postmortem
jjugccc2018 app review postmortem
tamtam180
 
Spock's world
Spock's worldSpock's world
Spock's world
Takuma Watabiki
 
Apexコアデベロッパーセミナー(Apexコード)071010
Apexコアデベロッパーセミナー(Apexコード)071010Apexコアデベロッパーセミナー(Apexコード)071010
Apexコアデベロッパーセミナー(Apexコード)071010
stomita
 
エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2010/9/7エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2010/9/7Tetsuya Morimoto
 
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)Akihiro Kuwano
 
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
normalian
 
Example of exiting legacy system
Example of exiting legacy systemExample of exiting legacy system
Example of exiting legacy system
TakamchiTanaka
 
JJUG CCC 2016 fall バイトコードが君のトモダチになりたがっている
JJUG CCC 2016 fall バイトコードが君のトモダチになりたがっているJJUG CCC 2016 fall バイトコードが君のトモダチになりたがっている
JJUG CCC 2016 fall バイトコードが君のトモダチになりたがっている
Koichi Sakata
 
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
lalha
 
20220914_MySQLでDevOps!
20220914_MySQLでDevOps!20220914_MySQLでDevOps!
20220914_MySQLでDevOps!
Machiko Ikoma
 
Azure DevOps Online Vol.3 - Inside Azure Pipelines
Azure DevOps Online Vol.3 - Inside Azure PipelinesAzure DevOps Online Vol.3 - Inside Azure Pipelines
Azure DevOps Online Vol.3 - Inside Azure Pipelines
Kazushi Kamegawa
 
Rpscala2011 0601
Rpscala2011 0601Rpscala2011 0601
Rpscala2011 0601
Hajime Yanagawa
 
Asp Net Mvc 基礎のキソ
Asp Net Mvc 基礎のキソAsp Net Mvc 基礎のキソ
Asp Net Mvc 基礎のキソ
Yoshitaka Seo
 
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
シスコシステムズ合同会社
 

Similar to Java class design (20)

Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
 
規格書で読むC++11のスレッド
規格書で読むC++11のスレッド規格書で読むC++11のスレッド
規格書で読むC++11のスレッド
 
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
 
SQLQL は GraphQL にとってなんなのか
SQLQL は GraphQL にとってなんなのかSQLQL は GraphQL にとってなんなのか
SQLQL は GraphQL にとってなんなのか
 
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
 
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会
 
jjugccc2018 app review postmortem
jjugccc2018 app review postmortemjjugccc2018 app review postmortem
jjugccc2018 app review postmortem
 
Spock's world
Spock's worldSpock's world
Spock's world
 
Apexコアデベロッパーセミナー(Apexコード)071010
Apexコアデベロッパーセミナー(Apexコード)071010Apexコアデベロッパーセミナー(Apexコード)071010
Apexコアデベロッパーセミナー(Apexコード)071010
 
エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2010/9/7エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2010/9/7
 
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)
 
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
 
Example of exiting legacy system
Example of exiting legacy systemExample of exiting legacy system
Example of exiting legacy system
 
JJUG CCC 2016 fall バイトコードが君のトモダチになりたがっている
JJUG CCC 2016 fall バイトコードが君のトモダチになりたがっているJJUG CCC 2016 fall バイトコードが君のトモダチになりたがっている
JJUG CCC 2016 fall バイトコードが君のトモダチになりたがっている
 
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
 
20220914_MySQLでDevOps!
20220914_MySQLでDevOps!20220914_MySQLでDevOps!
20220914_MySQLでDevOps!
 
Azure DevOps Online Vol.3 - Inside Azure Pipelines
Azure DevOps Online Vol.3 - Inside Azure PipelinesAzure DevOps Online Vol.3 - Inside Azure Pipelines
Azure DevOps Online Vol.3 - Inside Azure Pipelines
 
Rpscala2011 0601
Rpscala2011 0601Rpscala2011 0601
Rpscala2011 0601
 
Asp Net Mvc 基礎のキソ
Asp Net Mvc 基礎のキソAsp Net Mvc 基礎のキソ
Asp Net Mvc 基礎のキソ
 
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
 

Recently uploaded

NIST Cybersecurity Framework 2.0の変更点整理をしよう
NIST Cybersecurity Framework 2.0の変更点整理をしようNIST Cybersecurity Framework 2.0の変更点整理をしよう
NIST Cybersecurity Framework 2.0の変更点整理をしよう
You&I
 
アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)
アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)
アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)
You&I
 
CO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdf
CO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdfCO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdf
CO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdf
yamamotominami
 
Grokking Simplicity探訪
Grokking Simplicity探訪Grokking Simplicity探訪
Grokking Simplicity探訪
Yoshitaka Kawashima
 
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)
fisuda
 
受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf
受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf
受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf
ooishi1
 

Recently uploaded (6)

NIST Cybersecurity Framework 2.0の変更点整理をしよう
NIST Cybersecurity Framework 2.0の変更点整理をしようNIST Cybersecurity Framework 2.0の変更点整理をしよう
NIST Cybersecurity Framework 2.0の変更点整理をしよう
 
アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)
アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)
アジャイルの30年(Tree Decades of Agileというブログ記事に関する要約)
 
CO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdf
CO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdfCO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdf
CO2排出量見える化・削減・報告クラウド「アスエネ」サービス紹介_Saleshub.pdf
 
Grokking Simplicity探訪
Grokking Simplicity探訪Grokking Simplicity探訪
Grokking Simplicity探訪
 
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)
FIWARE Orion Context Broker コンテキスト情報管理 (Orion 4.0.0対応)
 
受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf
受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf
受発注バスターズ説明資料  株式会社batton Saleshub掲載用.pdf
 

Java class design

  • 2. © 2020 SG Corporation 2 なんとなくで書いているコードをもうちょっとなんとかしたい。 Java再入門
  • 3. © 2020 SG Corporation 3 Javaの古くからある機能を一度振り返っておこう。 Java再入門
  • 4. © 2020 SG Corporation 4 サンプルコード https://www.sgnet.co.jp/edu/2020050701_java.zip Java再入門
  • 5. © 2020 SG Corporation 5 ◍ スコープ再考 ◍ 抽象クラスと抽象メソッド ◍ 制御構造再考 ◍ オブジェクトのライフサイクル ◍ 疎結合としてのサービス 目次
  • 6. © 2020 SG Corporation 6 ◍ スコープ再考 ◍ 抽象クラスと抽象メソッド ◍ 制御構造再考 ◍ オブジェクトのライフサイクル ◍ 疎結合としてのサービス 目次
  • 7. © 2020 SG Corporation 7 Javaのスコープはpublic,private,protectedだよね。 ん?なんか忘れてないか。 スコープ再考
  • 8. © 2020 SG Corporation 8  アクセス修飾子がないメソッド スコープ再考 int foo() …
  • 9. © 2020 SG Corporation 9  なぜ、Javaのデフォルトスコープがパッケージなのか?  パッケージを小さなライブラリと考える。  パッケージ内のクラスが協働して小さな問題を解決する。 スコープ再考
  • 10. © 2020 SG Corporation 10  なぜ、Javaのデフォルトスコープがパッケージなのか?  パッケージ外からは見えないけど、中からは見える。  1クラス中のprivateメソッドが多くなってきたら、パッケージプ ライベートなクラスを作るべきサイン。  テスト容易性という副産物も。 スコープ再考
  • 11. © 2020 SG Corporation 11 問題1  ファクトリメソッドでオブジェクトを構築した後に値を変更した いけど、外部からはアクセスさせたくない。 スコープ再考
  • 12. © 2020 SG Corporation 12 問題1 解答例の概要 従業員管理  従業員はチームに所属する。  従業員は複数チームに所属してもいい。  チームは固定とする。  従業員にどのチームに所属しているかを問い合わせるインター フェースがある。  ある従業員を後からチームに追加したいが、勝手に個々の従業員 オブジェクトにチームを追加されるのも困る。 スコープ再考
  • 13. © 2020 SG Corporation 13 問題1 解答例の概要  クラス  Book 管理簿  Employee 従業員  Team チーム スコープ再考 サンプルコード:1_scope
  • 14. © 2020 SG Corporation 14 問題1解答編  パブリック  😄後からチームを追加することができる。  😓管理簿内の従業員を外から変更できてしまう。 スコープ再考 public final class Employee { private final EnumSet<Team> teams; … public boolean addTeam(Team team) { return teams.add(team); } サンプルコード:1_scope/1_1
  • 15. © 2020 SG Corporation 15 問題1解答編  防御的コピー  😄後からチームを追加しても、管理簿内の従業員は変わらない。  😓逆に、管理簿に反映されていない!という誤解を生む。 →インターフェースがデザインの動機を表していない。 スコープ再考 public final class Book { private final Map<Integer, Employee> employees = … public void add(Employee employee) { employees.put(employee.getID(), Employee.create(employee.getID(), employee.getTeams())); サンプルコード:1_scope/1_2
  • 16. © 2020 SG Corporation 16 問題1解答編  パッケージプライベート  😄チーム追加は必ず管理簿を通して行うことが明確になる。  😓パッケージプライベートにしている意図を分かっていないと、 改修時にパブリックにしがち。 スコープ再考 public final class Employee { public boolean addTeam(Team team) { … public final class Book { public boolean addEmployeeTeam(int id, Team team) { … employee.addTeam(team); サンプルコード:1_scope/1_3
  • 17. © 2020 SG Corporation 17  スコープでメソッドの意図を伝える one more step  公開APIと拡張ポイントが一緒になっている… スコープ再考 public abstract int foo();
  • 18. © 2020 SG Corporation 18  スコープでメソッドの意図を伝える one more step  公開APIと拡張ポイントを明示する。  オーバーライド側はこれ以上継承されたくなければ、finalにする。 スコープ再考 public final int foo() { return fooImpl(); } protected abstract int fooImpl();
  • 19. © 2020 SG Corporation 19  スコープでクラスの意図を伝える one more step  クラス自体は公開したいが、継承はパッケージ内のみとしたい… スコープ再考 public class Foo { … }
  • 20. © 2020 SG Corporation 20  スコープでクラスの意図を伝える one more step  継承をパッケージ内のみに許可する。  コンストラクタをパッケージプライベートにすることによって、 実質的finalにする。 スコープ再考 public class Foo { Foo() { … } … }
  • 21. © 2020 SG Corporation 21  補足:コンストラクタチェーン  コンストラクタが呼び出されたとき何が起きるか?  コンストラクタ内で明示的にsuperを呼び出さない場合は、暗黙 的にスーパークラスの引数なしのコンストラクタが呼び出される。  このとき、スーパークラスのコンストラクタにアクセス不可能だ と、コンパイル時にエラーになる。  明示的にsuperを呼び出した場合も同じ。 スコープ再考
  • 22. © 2020 SG Corporation 22 まとめ  パッケージを小さなライブラリと考える  パッケージ内のクラスが協働して小さな問題を解決する  修飾子でクラスデザインの意図を伝える  メソッドのオーバーライド可能性に注意する スコープ再考
  • 23. © 2020 SG Corporation 23 ◍ スコープ再考 ◍ 抽象クラスと抽象メソッド ◍ 制御構造再考 ◍ オブジェクトのライフサイクル ◍ 疎結合としてのサービス 目次
  • 24. © 2020 SG Corporation 24  抽象クラスと抽象メソッドをどう使うかは悩みどころ。  ふるまいを一般化する場合は、基本的にはインターフェースを選 択するけど、抽象クラスはどういうときに使うか。 abstract…? 抽象クラスと抽象メソッド
  • 25. © 2020 SG Corporation 25  protectedメソッドを呼ぶpublicメソッドでメソッド間の関係性を 保証する。  例)java.util.AbstractList, java.util.AbstractSequentialList  どのような実装であれ、リストとして期待される動作を保証する。 抽象クラスと抽象メソッド
  • 26. © 2020 SG Corporation 26 問題2  多態性は持たせたいが、自由に継承はさせたくない。  関係のあるメソッド間の契約を保証したい。 抽象クラスと抽象メソッド
  • 27. © 2020 SG Corporation 27 問題2 解答例の概要 給与計算  基本給と手当がある。  給与は 基本給+手当 とする。  手当は 単価×時間 とするが、計算式が異なる可能性がある。  手当の計算式が異なっても、給与=基本給+手当 は保証する。 抽象クラスと抽象メソッド サンプルコード:2_abstract
  • 28. © 2020 SG Corporation 28 問題2 解答例の概要  クラス  Salary 給与 抽象クラスと抽象メソッド サンプルコード:2_abstract
  • 29. © 2020 SG Corporation 29 問題2解答編  パブリックコンストラクタ  😄わかりやすい。  😓勝手に継承される。 抽象クラスと抽象メソッド public class Salary { public Salary(int base, int wage) { サンプルコード:2_abstract/2_1
  • 30. © 2020 SG Corporation 30 問題2解答編  パッケージプライベートコンストラクタ  😄継承をパッケージ内のみにとどめる。  😓オーバーライドするポイントが分からない。 抽象クラスと抽象メソッド public class Salary { public Salary(int base, int wage) { … public static Salary create(int base, int wage) { return new Salary(base, wage); } サンプルコード:2_abstract/2_2
  • 31. © 2020 SG Corporation 31 問題2解答編  abstractメソッド  😄オーバーライドするポイントが分かる。  😓意図したオーバーライドポイント以外もオーバーライドできて しまう。 抽象クラスと抽象メソッド public abstract class Salary { public abstract int getBase(); … public abstract int getWage(int hour); サンプルコード:2_abstract/なし
  • 32. © 2020 SG Corporation 32 問題2解答編  finalメソッド  😄オーバーライド不可なポイントを守ることにより、メソッド間 の契約を保証できる。 抽象クラスと抽象メソッド public abstract class Salary { public final int getBase() { return getBaseImpl(); } protected abstract int getBaseImpl(); … public final int total(int hour) { return getBase() + getWage(hour); } サンプルコード:2_abstract/2_3
  • 33. © 2020 SG Corporation 33 問題2 いったんまとめ  abstractメソッドは最小限にする。publicメソッドはabstractメ ソッド同士の関係性を定義する。  拡張性を公開しないのであれば、公開するべきではない。  公開ポイントと拡張ポイントは区別する。 抽象クラスと抽象メソッド
  • 34. © 2020 SG Corporation 34 問題2解答編  公開クラスと拡張クラスを分ける。  😄拡張ポイントを分割・明示できる。 抽象クラスと抽象メソッド public final class Salary { private final SalaryImpl salaryImpl; Salary(int base, int wage, SalaryImpl salaryImpl) { … this.salaryImpl = salaryImpl; static interface SalaryImpl { サンプルコード:2_abstract/2_4
  • 35. © 2020 SG Corporation 35 問題2解答編  拡張クラスを公開する。  😄拡張ポイントのみを別個に公開できる。 抽象クラスと抽象メソッド public final class Salary { private final SalaryImpl salaryImpl; Salary(int base, int wage, SalaryImpl salaryImpl) { … this.salaryImpl = salaryImpl; public interface SalaryImpl { サンプルコード:2_abstract/2_5
  • 36. © 2020 SG Corporation 36 「疎結合としてのサービス」 で、もうちょっとやります。 抽象クラスと抽象メソッド
  • 37. © 2020 SG Corporation 37 まとめ  抽象クラスは継承クラスを容易に作成するためのヘルパー  公開メソッドは抽象メソッド同士の関係性を定義する  公開メソッドはオーバーライド不可とする 抽象クラスと抽象メソッド
  • 38. © 2020 SG Corporation 38 ◍ スコープ再考 ◍ 抽象クラスと抽象メソッド ◍ 制御構造再考 ◍ オブジェクトのライフサイクル ◍ 疎結合としてのサービス 目次
  • 39. © 2020 SG Corporation 39 Javaの制御構造はもちろんわかってるよ。 ifとかforとか… 制御構造再考
  • 40. © 2020 SG Corporation 40 問題3  単位処理数ごとのバッチ処理、コントロールブレイクをどう実現 するか。  例)  1000件ごと(バッチ)  何かグループ化の条件が変わった場合(コントロールブレイク) 制御構造再考
  • 41. © 2020 SG Corporation 41 問題3解答編  そのままループ  😄シンプルな解決策  😓外側のループと内側のループでiを共有しているので分かりにく い。 制御構造再考 for (int i = 0; i < employees.size(); i += 1000) { for (int id : employees.subList(i, Math.min(i + 1000, employees.size()))) { for (int i = 0; i < employees.size();) { for (int from = i; !isBreakIndex(employees, from, i); i++) { サンプルコード:3_control/3_1
  • 42. © 2020 SG Corporation 42 問題3解答編  そのままループ その2(バッチ)  😄外側と内側のループを分けた。 制御構造再考 for (int i = 0, n = 0; i < employees.size(); i += n) { n = batchLoopSub(employees, i); … private int batchLoopSub(List<Employee> employees, int fromIndex) { int count = Math.min(fromIndex + 1000, employees.size()); for (int id : employees.subList(fromIndex, count)) { … return count - fromIndex; サンプルコード:3_control/3_2
  • 43. © 2020 SG Corporation 43 問題3解答編  そのままループ その2 (コントロールブレイク)  😄外側と内側のループを分けた。 制御構造再考 for (int i = 0, n = 0; i < employees.size(); i += n) { n = controlBreakLoopSub(employees, i); … private int controlBreakLoopSub(List<Employee> employees, int fromIndex) { int i = fromIndex; for (; !isBreakIndex(employees, fromIndex, i); i++) { … return i - fromIndex; サンプルコード:3_control/3_2
  • 44. © 2020 SG Corporation 44 問題3解答編 なんか似てないか、これ 制御構造再考
  • 45. © 2020 SG Corporation 45 問題3解答編  そのままループ その2  😕ここ以外同じだな… 制御構造再考 int count = Math.min(fromIndex + 1000, employees.size()); for (int id : employees.subList(fromIndex, count)) { int i = fromIndex; for (; !isBreakIndex(employees, fromIndex, i); i++) { サンプルコード:3_control/3_2
  • 46. © 2020 SG Corporation 46 問題3解答編  そのままループ その3  バッチのループ継続条件をメソッド化  😳同じ構造になった! 制御構造再考 int i = fromIndex; for (; !isBatchIndex(employees, fromIndex, i); i++) { private boolean isBatchIndex(List<Employee> employees, int from, int index) { return index == Math.min(from + 1000, employees.size()); } サンプルコード:3_control/3_3
  • 47. © 2020 SG Corporation 47 問題3解答編 同じ構造のものはまとめよう 制御構造再考
  • 48. © 2020 SG Corporation 48 問題3解答編  ループ用クラス 制御構造再考 public final class Loop<E> { @FunctionalInterface public static interface ListBreak<E> { boolean test(List<E> list, int from, int index); } private final ListBreak<E> listBreak; public Loop(ListBreak<E> listBreak) { this.listBreak = listBreak; } サンプルコード:3_control/3_3
  • 49. © 2020 SG Corporation 49 問題3解答編  ループ用クラス 続き 制御構造再考 … public void loop(List<E> list) { for (int i = 0, n = 0; i < list.size(); i += n) { n = loopSub(list, i); … private int loopSub(List<E> list, int fromIndex) { int i = fromIndex; for (; !listBreak.test(list, fromIndex, i); i++) { … return i - fromIndex; … サンプルコード:3_control/3_3
  • 50. © 2020 SG Corporation 50 問題3解答編  ループ用クラス 呼び出し側  😄バッチとコントロールブレイクでループを共通化できた。  😓ループの中身である処理本体は同じことしかできない。 制御構造再考 Loop<Employee> batch = new Loop<>(Main::batchBreakIndex); batch.loop(employees); Loop<Employee> control = new Loop<>(Main::controlBreakIndex); control.loop(employees); サンプルコード:3_control/3_3
  • 51. © 2020 SG Corporation 51 問題3解答編  ビューのリストに変形する 制御構造再考 public final class Loop<E> { … public List<List<E>> listView(List<E> list) { List<List<E>> view = new ArrayList<>(); for (int i = 0, n = 0; i < list.size(); i += n) { n = loopSub(list, i); view.add(list.subList(i, i + n)); } return view; } サンプルコード:3_control/3_4
  • 52. © 2020 SG Corporation 52 問題3解答編  ビューのリストに変形する 呼び出し側  😄ループの処理本体を外に出せた。  😄呼び出し側は一般的な拡張for文のみで対応できる。  😓一時的ではあるが、リストの生成が必要になる。 制御構造再考 Loop<Employee> batch = new Loop<>(Main::batchBreakIndex); for (List<Employee> list : batch.listView(employees)) { for (Employee e : list) { サンプルコード:3_control/3_3
  • 53. © 2020 SG Corporation 53 問題3解答編  ビューを返すイテレータを作る 制御構造再考 public final class Loop<E> implements Iterable<List<E>> { … private final List<E> list; public Loop(List<E> list, ListBreak<E> listBreak) { … } @Override public Iterator<List<E>> iterator() { return new Iterator<List<E>>() { … } } サンプルコード:3_control/3_4
  • 54. © 2020 SG Corporation 54 問題3解答編  ビューを返すイテレータを作る 呼び出し側  😄ブレイク条件のみを定義すれば、後はイテレータがやってくれ る。  😓初見ではわかりづらい。 制御構造再考 Loop<Employee> batch = new Loop<>(employees, Main::batchBreakIndex); for (List<Employee> list : batch) { for (Employee e : list) { サンプルコード:3_control/3_4
  • 55. © 2020 SG Corporation 55 問題3解答編 せっかくなので、ブレイク条件も一般化しよう 制御構造再考
  • 56. © 2020 SG Corporation 56 問題3解答編 one more step  バッチブレイク条件 制御構造再考 static <E> ListBreak<E> batchBreakIndex(int size) { return (list, from, index) -> index == Math.min(from + size, list.size()); } Loop<Employee> batch = new Loop<>(employees, batchBreakIndex(1000)); サンプルコード:3_control/3_5
  • 57. © 2020 SG Corporation 57 問題3解答編 one more step  コントロールブレイク条件 制御構造再考 static <E> ListBreak<E> controlBreakIndex( BiFunction<E, E, Boolean> test) { return (list, from, index) -> { if (index == list.size()) return true; if (index == from) return false; return !test.apply(list.get(index - 1), list.get(index)); }; } Loop<Employee> control = new Loop<>(employees, controlBreakIndex((a, b) -> Objects.equals(a, b))); サンプルコード:3_control/3_5
  • 58. © 2020 SG Corporation 58  問題がデータ構造であるのなら、データ構造をプログラミングす る。  ビューとコピーを区別する。コピーが必要であればコピーする。  問題が制御構造であるのなら、制御構造をプログラミングする。  コピーが必要なければ、ビューで対応する。  使う側は動作をプログラミングしなくてもいい。  宣言型プログラミング 制御構造再考
  • 59. © 2020 SG Corporation 59  とは言え、そのプロジェクトで頻出のイディオムであるという場 合に使うべき。  素直にループを書いたほうがシンプルな解決策であることには変 わりはない。  簡明さは失われてしまった。 制御構造再考
  • 60. © 2020 SG Corporation 60  補足:ラムダ式・メソッド参照  Java8以降、メソッド参照とラムダ式が使えるようになった。  オーバーライド用のメソッドを1つだけ持つインターフェースを 実装する手間を減らす。  これにより、ラッパークラスを作らなくても、シグネチャが合致 するメソッドをそのまま渡すことができる。 制御構造再考 @FunctionalInterface public interface Runnable { void run(); }
  • 61. © 2020 SG Corporation 61  補足:ラムダ式・メソッド参照 制御構造再考 public static void main(String ... args) { ExecutorService service = Executors.newSingleThreadExecutor(); // 無名クラス service.execute(new Runnable() { public void run() { print(); } }); // メソッド参照 service.execute(Main::print); // ラムダ式 service.execute(() -> print()); service.shutdown(); } private static void print() { System.out.println("run."); }
  • 62. © 2020 SG Corporation 62 まとめ  問題がデータ構造であるのなら、データ構造をプログラミングす る  問題が制御構造であるのなら、制御構造をプログラミングする  再利用性と簡明さはトレードオフ(の場合がある) 制御構造再考
  • 63. © 2020 SG Corporation 63 ◍ スコープ再考 ◍ 抽象クラスと抽象メソッド ◍ 制御構造再考 ◍ オブジェクトのライフサイクル ◍ 疎結合としてのサービス 目次
  • 64. © 2020 SG Corporation 64 オブジェクトにはcloseとかfreeとかないよね… オブジェクトのライフサイクル
  • 65. © 2020 SG Corporation 65  Javaは明示的にメモリ領域を管理しない。  オブジェクトのライフサイクルを気にしなくていいということで はない。 オブジェクトのライフサイクル
  • 66. © 2020 SG Corporation 66 問題4  登録されたイベントリスナーをイベント発生時に呼び出す。  リスナーが途中でいなくなっても、登録された側で保持している と、GCで回収されない。 オブジェクトのライフサイクル
  • 67. © 2020 SG Corporation 67 問題4 解答例の概要 従業員管理  管理簿に従業員の追加を依頼する。  追加依頼は非同期に終了する。  追加完了時にリスナーにコールバックがかかる。 オブジェクトのライフサイクル サンプルコード:4_lifecycle
  • 68. © 2020 SG Corporation 68 問題4 解答例の概要  クラス  Book 管理簿 オブジェクトのライフサイクル サンプルコード:4_lifecycle
  • 69. © 2020 SG Corporation 69 問題4解答編  リスナーの登録のみをする オブジェクトのライフサイクル public final class Book { @FunctionalInterface public static interface Listener { void action(Employee employee); } private final Map<Listener, Object> listeners = new ConcurrentHashMap<>(); public void addListener(Listener listener) { listeners.put(listener, new Object()); } サンプルコード:4_lifecycle/4_1
  • 70. © 2020 SG Corporation 70 問題4解答編  リスナーの登録のみをする 続き オブジェクトのライフサイクル public void addAsync(Employee employee) { addService.execute(new Runnable() { @Override public void run() { add(employee); actionAddEvent(employee); } }); } サンプルコード:4_lifecycle/4_1
  • 71. © 2020 SG Corporation 71 問題4解答編  リスナーの登録のみをする 続き  😓リスナーがいなくなっても、オブジェクトがGCで回収されない。 オブジェクトのライフサイクル private void actionAddEvent(Employee employee) { for (Listener l : listeners.keySet()) { l.action(employee); } } サンプルコード:4_lifecycle/4_1
  • 72. © 2020 SG Corporation 72 問題4解答編  削除APIも公開する  😄削除メソッドが呼ばれれば、GCで回収される。  😓公開APIだと、削除メソッドが呼ばれる保証がない。 オブジェクトのライフサイクル public void removeListener(Listener listener) { listeners.remove(listener); } サンプルコード:4_lifecycle/4_2
  • 73. © 2020 SG Corporation 73 問題4解答編  弱い参照  マップ内のリスナーを弱い参照(WeakReference)で保持する。 オブジェクトのライフサイクル public final class Book { private final Map<WeakReference<Listener>, Object> listeners = new ConcurrentHashMap<>(); public void addListener(Listener listener) { listeners.put(new WeakReference<>(listener), new Object()); } サンプルコード:4_lifecycle/4_3
  • 74. © 2020 SG Corporation 74 問題4解答編  弱い参照 続き オブジェクトのライフサイクル private void actionAddEvent(Employee employee) { for (Iterator<WeakReference<Listener>> itr = listeners.keySet().iterator(); itr.hasNext(); ) { Listener l = itr.next().get(); if (l == null) { itr.remove(); continue; } l.action(employee); } } サンプルコード:4_lifecycle/4_3
  • 75. © 2020 SG Corporation 75 問題4解答編  弱い参照  😄通常の参照が0件になれば、GCで回収される。  😄オブジェクトのライフサイクルを気にしていないことを明示で きる。  😓API仕様で、弱い参照をしていることを明示しておく必要があ る。 オブジェクトのライフサイクル
  • 76. © 2020 SG Corporation 76  ブロードキャストする側はオブジェクトのライフサイクルを気に しない。  登録と登録解除を提供してもいいけど、ユーザーが登録解除を呼 ぶ保証はない!  オブジェクトのライフサイクルを気にしていないということを明 示する。 オブジェクトのライフサイクル
  • 77. © 2020 SG Corporation 77  オブジェクト生成 one more step  派生型が絡むと多少厄介… オブジェクトのライフサイクル
  • 78. © 2020 SG Corporation 78  リストをコピーするメソッドのデザインとして、2通りが考えら れる。 アウトパラメーターとする 戻り値とする オブジェクトのライフサイクル <E> void copyList(List<E> dest, List<E> src) <E> List<E> copyList(List<E> src)
  • 79. © 2020 SG Corporation 79  アウトパラメーター  呼び出し元がコピー先リストを生成する  リストの具象クラスを呼び出し元が決めたい場合  呼び出し先は実行時クラスが何かを気にしていない  メソッドがアルゴリズムを表している場合はこのパターン オブジェクトのライフサイクル <E> void copyList(List<E> dest, List<E> src)
  • 80. © 2020 SG Corporation 80  戻り値  呼び出し先がコピー先リストを生成する  リストの具象クラスを呼び出し先が決めたい場合  呼び出し元は実行時クラスが何かを気にしていない  メソッドがファクトリーを表している場合はこのパターン オブジェクトのライフサイクル <E> List<E> copyList(List<E> src)
  • 81. © 2020 SG Corporation 81 まとめ  オブジェクトのライフサイクルを気にしなくていいということで はない  オブジェクトの生成消滅に対する責務をどこが担っているかを表 現する  メソッドがアルゴリムなのか、ファクトリーなのかでも責務が変 わってくる オブジェクトのライフサイクル
  • 82. © 2020 SG Corporation 82 ◍ スコープ再考 ◍ 抽象クラスと抽象メソッド ◍ 制御構造再考 ◍ オブジェクトのライフサイクル ◍ 疎結合としてのサービス 目次
  • 83. © 2020 SG Corporation 83 突然ですが、MySQLに接続しよう… 疎結合としてのサービス
  • 84. © 2020 SG Corporation 84  JDBCドライバ(というかコネクション)を取得する例  😳“jdbc:mysql://〜”を渡すとMySQLのドライバが取得できる! 疎結合としてのサービス Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/testdb", "user", "pass");
  • 85. © 2020 SG Corporation 85 JDBCドライバのような機構はどのように実現されているのか? 疎結合としてのサービス
  • 86. © 2020 SG Corporation 86 問題5  状態を保存したいが、保存先をプロパティファイル、XML、DB等 で切り替えたい。 疎結合としてのサービス
  • 87. © 2020 SG Corporation 87 問題5 解答例の概要 従業員管理  従業員管理簿を外部に保存する。  外部保存の実装は継承で拡張できる。 疎結合としてのサービス サンプルコード:5_service
  • 88. © 2020 SG Corporation 88 問題5 解答例の概要  クラス  Book 管理簿  BookHelper 管理簿と保存機能の橋渡し  Persistance 保存機能 疎結合としてのサービス サンプルコード:5_service
  • 89. © 2020 SG Corporation 89 問題5解答編  ファクトリメソッドで文字列から作成 疎結合としてのサービス public abstract class Persistance { public static Persistance create(String persist) throws IOException { switch (persist) { case "xml": return new XMLPersistance(); case "db": return new DBPersistance(); default: throw new IOException("No suitable provider found for ” + persist); … サンプルコード:5_service/5_1
  • 90. © 2020 SG Corporation 90 問題5解答編  ファクトリメソッドで文字列から作成  😄難しい仕組みがいらない。  😓コンパイル時に実装クラスが必要。 疎結合としてのサービス
  • 91. © 2020 SG Corporation 91 問題5解答編  リフレクションで実現  😄仕組みがまあまあ簡潔。  😓インスタンス生成するクラスが公開サービスなのかが不明確。  😓パッケージ名とサービス名が直接結びついてしまう。 疎結合としてのサービス public abstract class Persistance { public static Persistance create(String persist) throws … { return Class.forName(persist).asSubclass(Persistance.class). getDeclaredConstructor().newInstance(); } サンプルコード:5_service/5_2
  • 92. © 2020 SG Corporation 92 問題5解答編  サービスで実現 疎結合としてのサービス public interface PersistanceService { boolean acceptName(String name); void read(BookHelper book) throws IOException; void write(BookHelper book) throws IOException; } サンプルコード:5_service/5_3
  • 93. © 2020 SG Corporation 93 問題5解答編  サービスで実現 続き  😄仕組みがまあまあ簡潔。  😓インスタンス生成するクラスが公開サービスなのかが不明確。  😓パッケージ名とサービス名が直接結びついてしまう。 疎結合としてのサービス public final class Book { private final PersistanceService service; public static Book createPersistance(String persist) throws IOException { PersistanceService found = findPersistanceService(persist); if (found == null) { throw new IOException("No suitable provider found for " + persist); } return new Book(found); } サンプルコード:5_service/5_3
  • 94. © 2020 SG Corporation 94 問題5解答編  サービスで実現 続き 疎結合としてのサービス private static PersistanceService findPersistanceService( String persist) { for (PersistanceService service : ServiceLoader.load(PersistanceService.class)) { if (service.acceptName(persist)) return service; } return null; } サンプルコード:5_service/5_3
  • 95. © 2020 SG Corporation 95 問題5解答編  サービスで実現 続き  😄サービスとして公開していることを明示できる。  😄パッケージ名とサービス名を間接的な結びつきにできる。  😓若干、仕組みが面倒。 疎結合としてのサービス 公開するサービスプロバイダー名を記載した META- INF/services/jp.co.sgnet.hrp.skillup202001.hr.spi.PersistanceService を配置 jp.co.sgnet.hrp.skillup202001.hr.spi.XMLPersistanceServiceProvider サンプルコード:5_service/5_3
  • 96. © 2020 SG Corporation 96  APIとSPI  API : Application Programming Interface  SPI : Service Provider Interface  APIとSPIのクラス階層は分けておく。  API側の実装クラスが構築時にSPIのオブジェクトをもらって、中 にオブジェクトとして持つ形式。  APIそのものが継承されることを前提としていることは少ないは ず。 疎結合としてのサービス
  • 97. © 2020 SG Corporation 97  補足: Java9のモジュール機構  Java9以降、アクセス修飾子とは別にjarの外側に公開する/しな いを制御することができるようになった。 疎結合としてのサービス
  • 98. © 2020 SG Corporation 98  補足: Java9のモジュール機構  module-info.javaにクラスの公開/非公開を記載する。 疎結合としてのサービス module local.api { exports local.api; uses local.api.MyService; } 公開側: local.apiパッケージ module local.app { requires local.api; uses local.api.MyService; } 利用側: local.appパッケージ module local.provider { requires local.api; provides local.api.MyService with local.provider.MyServiceProvider1; } 実装側: local.providerパッケージ
  • 99. © 2020 SG Corporation 99  補足: Java9のモジュール機構 疎結合としてのサービス private static PersistanceService findPersistanceService( String persist) { for (PersistanceService service : ServiceLoader.load(PersistanceService.class)) { if (service.acceptName(persist)) return service; } return null; }
  • 100. © 2020 SG Corporation 100 まとめ  APIとSPIは分けておく  APIは継承を前提としない  SPIは継承を前提とする 疎結合としてのサービス
  • 101. © 2020 SG Corporation 101 ◍ Effective Java 第3版 Joshua Bloch (著), 柴田 芳樹 (翻訳) 丸善出版 ◍ APIデザインの極意 Java/NetBeansアーキテクト探究ノート Jaroslav Tulach (著), 柴田 芳樹 (翻訳) インプレス 参考文献
  • 102. © 2020 SG Corporation 102 ◍ SGソフトウェア開発ブログ https://blog.sgnet.co.jp ◍ SlideShare公開資料 https://www.slideshare.net/t_ichioka_sg/ こちらもどうぞ
  • 103. © 2020 SG Corporation 103 顧客と社員に信頼されるエス・ジー