More Related Content
PPTX
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O... PPTX
ACL 2015 読み会 @ 小町研 "Gated Recursive Neural Network for Chinese Word Segmentat... PPTX
PDF
ICCV 2019 論文紹介 (26 papers) PDF
PDF
クラウド・アプリケーション・モデリングへのアプローチ PDF
オンライン広告入札システムとZGC ( JJUG CCC 2021 Spring ) PDF
人が注目する箇所を当てるSaliency Detectionの最新モデル UCNet(CVPR2020) What's hot
PDF
Spring Social でソーシャルログインを実装する PDF
Clustering _ishii_2014__ch10 PPTX
PDF
PPTX
PPTX
Convolutionl Neural Network 入門 PPTX
PDF
【2016.05】cvpaper.challenge2016 PDF
PythonによるDeep Learningの実装 Similar to Java class design
PPTX
PDF
Javaセキュアコーディングセミナー東京第4回講義 PDF
Javaセキュアコーディングセミナー東京第1回 講義 PDF
Javaセキュアコーディングセミナー東京第1回演習の解説 PDF
PPTX
PDF
PDF
PDF
PPTX
PDF
PDF
PDF
PDF
PDF
PPTX
PDF
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8 PDF
PDF
PDF
Java class design
- 1.
- 2.
© 2020 SGCorporation 2
なんとなくで書いているコードをもうちょっとなんとかしたい。
Java再入門
- 3.
© 2020 SGCorporation 3
Javaの古くからある機能を一度振り返っておこう。
Java再入門
- 4.
© 2020 SGCorporation 4
サンプルコード
https://www.sgnet.co.jp/edu/2020050701_java.zip
Java再入門
- 5.
© 2020 SGCorporation 5
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
- 6.
© 2020 SGCorporation 6
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
- 7.
© 2020 SGCorporation 7
Javaのスコープはpublic,private,protectedだよね。
ん?なんか忘れてないか。
スコープ再考
- 8.
© 2020 SGCorporation 8
アクセス修飾子がないメソッド
スコープ再考
int foo() …
- 9.
© 2020 SGCorporation 9
なぜ、Javaのデフォルトスコープがパッケージなのか?
パッケージを小さなライブラリと考える。
パッケージ内のクラスが協働して小さな問題を解決する。
スコープ再考
- 10.
© 2020 SGCorporation 10
なぜ、Javaのデフォルトスコープがパッケージなのか?
パッケージ外からは見えないけど、中からは見える。
1クラス中のprivateメソッドが多くなってきたら、パッケージプ
ライベートなクラスを作るべきサイン。
テスト容易性という副産物も。
スコープ再考
- 11.
© 2020 SGCorporation 11
問題1
ファクトリメソッドでオブジェクトを構築した後に値を変更した
いけど、外部からはアクセスさせたくない。
スコープ再考
- 12.
© 2020 SGCorporation 12
問題1 解答例の概要
従業員管理
従業員はチームに所属する。
従業員は複数チームに所属してもいい。
チームは固定とする。
従業員にどのチームに所属しているかを問い合わせるインター
フェースがある。
ある従業員を後からチームに追加したいが、勝手に個々の従業員
オブジェクトにチームを追加されるのも困る。
スコープ再考
- 13.
© 2020 SGCorporation 13
問題1 解答例の概要
クラス
Book 管理簿
Employee 従業員
Team チーム
スコープ再考 サンプルコード:1_scope
- 14.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 17
スコープでメソッドの意図を伝える one more step
公開APIと拡張ポイントが一緒になっている…
スコープ再考
public abstract int foo();
- 18.
© 2020 SGCorporation 18
スコープでメソッドの意図を伝える one more step
公開APIと拡張ポイントを明示する。
オーバーライド側はこれ以上継承されたくなければ、finalにする。
スコープ再考
public final int foo() {
return fooImpl();
}
protected abstract int fooImpl();
- 19.
© 2020 SGCorporation 19
スコープでクラスの意図を伝える one more step
クラス自体は公開したいが、継承はパッケージ内のみとしたい…
スコープ再考
public class Foo {
…
}
- 20.
© 2020 SGCorporation 20
スコープでクラスの意図を伝える one more step
継承をパッケージ内のみに許可する。
コンストラクタをパッケージプライベートにすることによって、
実質的finalにする。
スコープ再考
public class Foo {
Foo() {
…
}
…
}
- 21.
© 2020 SGCorporation 21
補足:コンストラクタチェーン
コンストラクタが呼び出されたとき何が起きるか?
コンストラクタ内で明示的にsuperを呼び出さない場合は、暗黙
的にスーパークラスの引数なしのコンストラクタが呼び出される。
このとき、スーパークラスのコンストラクタにアクセス不可能だ
と、コンパイル時にエラーになる。
明示的にsuperを呼び出した場合も同じ。
スコープ再考
- 22.
© 2020 SGCorporation 22
まとめ
パッケージを小さなライブラリと考える
パッケージ内のクラスが協働して小さな問題を解決する
修飾子でクラスデザインの意図を伝える
メソッドのオーバーライド可能性に注意する
スコープ再考
- 23.
© 2020 SGCorporation 23
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
- 24.
© 2020 SGCorporation 24
抽象クラスと抽象メソッドをどう使うかは悩みどころ。
ふるまいを一般化する場合は、基本的にはインターフェースを選
択するけど、抽象クラスはどういうときに使うか。
abstract…?
抽象クラスと抽象メソッド
- 25.
© 2020 SGCorporation 25
protectedメソッドを呼ぶpublicメソッドでメソッド間の関係性を
保証する。
例)java.util.AbstractList, java.util.AbstractSequentialList
どのような実装であれ、リストとして期待される動作を保証する。
抽象クラスと抽象メソッド
- 26.
© 2020 SGCorporation 26
問題2
多態性は持たせたいが、自由に継承はさせたくない。
関係のあるメソッド間の契約を保証したい。
抽象クラスと抽象メソッド
- 27.
© 2020 SGCorporation 27
問題2 解答例の概要
給与計算
基本給と手当がある。
給与は 基本給+手当 とする。
手当は 単価×時間 とするが、計算式が異なる可能性がある。
手当の計算式が異なっても、給与=基本給+手当 は保証する。
抽象クラスと抽象メソッド サンプルコード:2_abstract
- 28.
© 2020 SGCorporation 28
問題2 解答例の概要
クラス
Salary 給与
抽象クラスと抽象メソッド サンプルコード:2_abstract
- 29.
© 2020 SGCorporation 29
問題2解答編
パブリックコンストラクタ
😄わかりやすい。
😓勝手に継承される。
抽象クラスと抽象メソッド
public class Salary {
public Salary(int base, int wage) {
サンプルコード:2_abstract/2_1
- 30.
© 2020 SGCorporation 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 SGCorporation 31
問題2解答編
abstractメソッド
😄オーバーライドするポイントが分かる。
😓意図したオーバーライドポイント以外もオーバーライドできて
しまう。
抽象クラスと抽象メソッド
public abstract class Salary {
public abstract int getBase();
…
public abstract int getWage(int hour);
サンプルコード:2_abstract/なし
- 32.
© 2020 SGCorporation 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 SGCorporation 33
問題2 いったんまとめ
abstractメソッドは最小限にする。publicメソッドはabstractメ
ソッド同士の関係性を定義する。
拡張性を公開しないのであれば、公開するべきではない。
公開ポイントと拡張ポイントは区別する。
抽象クラスと抽象メソッド
- 34.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 36
「疎結合としてのサービス」
で、もうちょっとやります。
抽象クラスと抽象メソッド
- 37.
© 2020 SGCorporation 37
まとめ
抽象クラスは継承クラスを容易に作成するためのヘルパー
公開メソッドは抽象メソッド同士の関係性を定義する
公開メソッドはオーバーライド不可とする
抽象クラスと抽象メソッド
- 38.
© 2020 SGCorporation 38
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
- 39.
© 2020 SGCorporation 39
Javaの制御構造はもちろんわかってるよ。
ifとかforとか…
制御構造再考
- 40.
© 2020 SGCorporation 40
問題3
単位処理数ごとのバッチ処理、コントロールブレイクをどう実現
するか。
例)
1000件ごと(バッチ)
何かグループ化の条件が変わった場合(コントロールブレイク)
制御構造再考
- 41.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 44
問題3解答編
なんか似てないか、これ
制御構造再考
- 45.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 47
問題3解答編
同じ構造のものはまとめよう
制御構造再考
- 48.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 55
問題3解答編
せっかくなので、ブレイク条件も一般化しよう
制御構造再考
- 56.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 58
問題がデータ構造であるのなら、データ構造をプログラミングす
る。
ビューとコピーを区別する。コピーが必要であればコピーする。
問題が制御構造であるのなら、制御構造をプログラミングする。
コピーが必要なければ、ビューで対応する。
使う側は動作をプログラミングしなくてもいい。
宣言型プログラミング
制御構造再考
- 59.
© 2020 SGCorporation 59
とは言え、そのプロジェクトで頻出のイディオムであるという場
合に使うべき。
素直にループを書いたほうがシンプルな解決策であることには変
わりはない。
簡明さは失われてしまった。
制御構造再考
- 60.
© 2020 SGCorporation 60
補足:ラムダ式・メソッド参照
Java8以降、メソッド参照とラムダ式が使えるようになった。
オーバーライド用のメソッドを1つだけ持つインターフェースを
実装する手間を減らす。
これにより、ラッパークラスを作らなくても、シグネチャが合致
するメソッドをそのまま渡すことができる。
制御構造再考
@FunctionalInterface
public interface Runnable {
void run();
}
- 61.
© 2020 SGCorporation 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 SGCorporation 62
まとめ
問題がデータ構造であるのなら、データ構造をプログラミングす
る
問題が制御構造であるのなら、制御構造をプログラミングする
再利用性と簡明さはトレードオフ(の場合がある)
制御構造再考
- 63.
© 2020 SGCorporation 63
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
- 64.
© 2020 SGCorporation 64
オブジェクトにはcloseとかfreeとかないよね…
オブジェクトのライフサイクル
- 65.
© 2020 SGCorporation 65
Javaは明示的にメモリ領域を管理しない。
オブジェクトのライフサイクルを気にしなくていいということで
はない。
オブジェクトのライフサイクル
- 66.
© 2020 SGCorporation 66
問題4
登録されたイベントリスナーをイベント発生時に呼び出す。
リスナーが途中でいなくなっても、登録された側で保持している
と、GCで回収されない。
オブジェクトのライフサイクル
- 67.
© 2020 SGCorporation 67
問題4 解答例の概要
従業員管理
管理簿に従業員の追加を依頼する。
追加依頼は非同期に終了する。
追加完了時にリスナーにコールバックがかかる。
オブジェクトのライフサイクル サンプルコード:4_lifecycle
- 68.
© 2020 SGCorporation 68
問題4 解答例の概要
クラス
Book 管理簿
オブジェクトのライフサイクル サンプルコード:4_lifecycle
- 69.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 71
問題4解答編
リスナーの登録のみをする 続き
😓リスナーがいなくなっても、オブジェクトがGCで回収されない。
オブジェクトのライフサイクル
private void actionAddEvent(Employee employee) {
for (Listener l : listeners.keySet()) {
l.action(employee);
}
}
サンプルコード:4_lifecycle/4_1
- 72.
© 2020 SGCorporation 72
問題4解答編
削除APIも公開する
😄削除メソッドが呼ばれれば、GCで回収される。
😓公開APIだと、削除メソッドが呼ばれる保証がない。
オブジェクトのライフサイクル
public void removeListener(Listener listener) {
listeners.remove(listener);
}
サンプルコード:4_lifecycle/4_2
- 73.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 75
問題4解答編
弱い参照
😄通常の参照が0件になれば、GCで回収される。
😄オブジェクトのライフサイクルを気にしていないことを明示で
きる。
😓API仕様で、弱い参照をしていることを明示しておく必要があ
る。
オブジェクトのライフサイクル
- 76.
© 2020 SGCorporation 76
ブロードキャストする側はオブジェクトのライフサイクルを気に
しない。
登録と登録解除を提供してもいいけど、ユーザーが登録解除を呼
ぶ保証はない!
オブジェクトのライフサイクルを気にしていないということを明
示する。
オブジェクトのライフサイクル
- 77.
© 2020 SGCorporation 77
オブジェクト生成 one more step
派生型が絡むと多少厄介…
オブジェクトのライフサイクル
- 78.
© 2020 SGCorporation 78
リストをコピーするメソッドのデザインとして、2通りが考えら
れる。
アウトパラメーターとする
戻り値とする
オブジェクトのライフサイクル
<E> void copyList(List<E> dest, List<E> src)
<E> List<E> copyList(List<E> src)
- 79.
© 2020 SGCorporation 79
アウトパラメーター
呼び出し元がコピー先リストを生成する
リストの具象クラスを呼び出し元が決めたい場合
呼び出し先は実行時クラスが何かを気にしていない
メソッドがアルゴリズムを表している場合はこのパターン
オブジェクトのライフサイクル
<E> void copyList(List<E> dest, List<E> src)
- 80.
© 2020 SGCorporation 80
戻り値
呼び出し先がコピー先リストを生成する
リストの具象クラスを呼び出し先が決めたい場合
呼び出し元は実行時クラスが何かを気にしていない
メソッドがファクトリーを表している場合はこのパターン
オブジェクトのライフサイクル
<E> List<E> copyList(List<E> src)
- 81.
© 2020 SGCorporation 81
まとめ
オブジェクトのライフサイクルを気にしなくていいということで
はない
オブジェクトの生成消滅に対する責務をどこが担っているかを表
現する
メソッドがアルゴリムなのか、ファクトリーなのかでも責務が変
わってくる
オブジェクトのライフサイクル
- 82.
© 2020 SGCorporation 82
◍ スコープ再考
◍ 抽象クラスと抽象メソッド
◍ 制御構造再考
◍ オブジェクトのライフサイクル
◍ 疎結合としてのサービス
目次
- 83.
© 2020 SGCorporation 83
突然ですが、MySQLに接続しよう…
疎結合としてのサービス
- 84.
© 2020 SGCorporation 84
JDBCドライバ(というかコネクション)を取得する例
😳“jdbc:mysql://〜”を渡すとMySQLのドライバが取得できる!
疎結合としてのサービス
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testdb",
"user", "pass");
- 85.
© 2020 SGCorporation 85
JDBCドライバのような機構はどのように実現されているのか?
疎結合としてのサービス
- 86.
© 2020 SGCorporation 86
問題5
状態を保存したいが、保存先をプロパティファイル、XML、DB等
で切り替えたい。
疎結合としてのサービス
- 87.
© 2020 SGCorporation 87
問題5 解答例の概要
従業員管理
従業員管理簿を外部に保存する。
外部保存の実装は継承で拡張できる。
疎結合としてのサービス サンプルコード:5_service
- 88.
© 2020 SGCorporation 88
問題5 解答例の概要
クラス
Book 管理簿
BookHelper 管理簿と保存機能の橋渡し
Persistance 保存機能
疎結合としてのサービス サンプルコード:5_service
- 89.
© 2020 SGCorporation 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 SGCorporation 90
問題5解答編
ファクトリメソッドで文字列から作成
😄難しい仕組みがいらない。
😓コンパイル時に実装クラスが必要。
疎結合としてのサービス
- 91.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 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 SGCorporation 96
APIとSPI
API : Application Programming Interface
SPI : Service Provider Interface
APIとSPIのクラス階層は分けておく。
API側の実装クラスが構築時にSPIのオブジェクトをもらって、中
にオブジェクトとして持つ形式。
APIそのものが継承されることを前提としていることは少ないは
ず。
疎結合としてのサービス
- 97.
© 2020 SGCorporation 97
補足: Java9のモジュール機構
Java9以降、アクセス修飾子とは別にjarの外側に公開する/しな
いを制御することができるようになった。
疎結合としてのサービス
- 98.
© 2020 SGCorporation 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 SGCorporation 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 SGCorporation 100
まとめ
APIとSPIは分けておく
APIは継承を前提としない
SPIは継承を前提とする
疎結合としてのサービス
- 101.
© 2020 SGCorporation 101
◍ Effective Java 第3版
Joshua Bloch (著), 柴田 芳樹 (翻訳)
丸善出版
◍ APIデザインの極意 Java/NetBeansアーキテクト探究ノート
Jaroslav Tulach (著), 柴田 芳樹 (翻訳)
インプレス
参考文献
- 102.
© 2020 SGCorporation 102
◍ SGソフトウェア開発ブログ
https://blog.sgnet.co.jp
◍ SlideShare公開資料
https://www.slideshare.net/t_ichioka_sg/
こちらもどうぞ
- 103.