持論:Application Layerがシンプルなコードは読みやすい
DDDでは全てのビジネスロジックはドメインクラスに、とのことなので当然プログラムの肝はドメインクラスなのですが、
コードの読みやすさは8割がたアプリケーションレイヤで決まると思っています。
Springで言えば、Controllerから呼ばれるServiceクラスの、はじめに呼ばれるメソッドです。
ここがいかにスッキリ、整然と、必要なことのみが書かれているかで、プログラム全体の可読性が決まると言っても過言ではないと思います。
具体的には、このメソッドの行数が10行を超えていたら、既に随分怪しい香りがしている…と思います。
何故ならば、このクラスは(一般的なWebページ、あるいはWeb APIを想定した時に)、
そのURLに対して実行されるふるまいが書かれる箇所であり、普通そのふるまいは、せいぜい3-4ステップで説明できるからです。
例えば、
1. 条件に合う「A」を(DBなどから)検索
2. 同じく、条件に合う「B」を検索
3. AとBをルールに合わせて結合・加工
4. 出来上がった結果を返却
と言った感じです。
(細かく見れば内部はもっと複雑でしょうが、この程度の長さで説明できないふるまいであれば、そもそも一つのURLで扱うには重すぎです)
さて、例えば上記のように4ステップで表されるふるまいであれば、アプリケーションレイヤの(最初のメソッドの)コードは理想的には4行で書かれるべきです。
人がコードを通しで読む場合、普通は頭から読んでいくケースが多いと思います。
その際、アプリケーションの先頭がスッキリ書かれていることで、そのエンドポイント全体の振る舞いを把握することができ、それ以降の理解がしやすくなると思います。
【追記】
1. 「普通は頭から読んでいくケースが多いと思います」ということで、当然一番上のコントローラがスッキリしていることも重要です。ただし、コントローラはサービスクラスと違って明確にロールが決められているため、そこまで酷いことにはなりにくいように思われます。*1
2. 主役たるドメインクラスはもちろん重要です。…が、適切な命名がなされたメソッドがあれば*2、最悪中が多少複雑だったりゴチャゴチャしていても概要をつかむことは可能です。一方、外側が整理されていないと、まず概要をつかむことができません。
『現場で役立つシステム設計の原則』 増田亨さん を読んだ。「データしか持たないデータクラスは作らない!」
こちらの本を読んででのレポートです。
現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法【電子書籍】[ 増田亨 ] |
複雑な業務ロジックは「適切な境界」で分離されていれば理解しやすくなる!
…が最近の自分の持論なのですが、じゃあ「適切な境界」って何よ? …と言われると、うまく言語化説明できない。
そんな私にたくさんヒントを与えてくれた本でした。
なかなか消化しきれていないところもあるのですが、腹落ちさせるためにもブログに落としてみます。
このブログに書いたことをまとめると・・・
- 修正が大変なのはロジックが分散しているから
- データとロジックをひとつのクラスにまとめるとロジックが分散しない!
- じゃあWeb APIでサービス間連携するときも、データを持つほうにロジックを寄せるべき… と思ったら、そうでもない?
この本のすごいところ
こういうコードは修正が大変ですよね。
だから風にリファクタリングしましょう!
…と、身近なイケていないコードのリファクタリングの話を読んでいたはずが、
いつの間にかドメイン駆動な設計の話に移っていきます。
ドメイン駆動設計の部分はエリック・エヴァンスの所謂DDD本をベースにしており、
エリック・エヴァンスのドメイン駆動設計【電子書籍】[ エリック・エヴァンス ] |
「あー あの本で見た。よくわからんかったけどとりあえず見た。」
という事柄が、随分噛み砕かれ、理解しやすく書かれています。
なのでエリック・エヴァンスに勝てなかった方には大変オススメ。
もちろんエリック・エヴァンス 誰それ? って方にもおすすめの一冊です。
ソフトウェアの変更が大変なのは「設計」が悪いから (第1章より)
ソフトウェアの変更は大変。
- どこに何が書いてあるかを理解するまでに、調査に時間がかかる
- ちょっとの修正なのに、変更すべき箇所があちこちに散らべっている
- 慎重に修正したのに、思わぬ副作用に苦しむ
こういった変更が大変なプログラムは以下のような特徴がある。
- メソッドが長い
- クラスが大きい
- 引数が多い
ならばリファクタリングしましょう。
- わかりやすい名前を使う
- 目的ごとに変数を用意する
- メソッドを独立させる
- 異なるクラスの重複したコードをなくす
- 狭い関心事に特化したクラスにする
ここまではおそらく、ほぼ万人に受け入れられる、「当たり前のこと」ではないでしょうか。
うんうんそうだよね、と読める内容。
ところがここからじわりじわりと、人によっては「お!?」と思うような内容が書かれています。
値オブジェクト(Value Object)を用意する。基本データ型を使わない。
- 数量を扱うために、Quantityクラスを作り、1以上100以下の様に制約も記述する。マイナスや非常に大きな値もの取りうるintをそのまま使わない
- 電話番号を扱うのに、フォーマットのルールまで定めたTelephonNumberクラスを作る。Stringをそのまま使わない
- 値オブジェクトを作ることで、業務の用語がプログラム中に現れるため、変更の際の調査が用意になる。『業務に関係するデータとロジックを閉じ込めておくことができる』
- 値オブジェクトはImmutableに。1つの変数を異なる目的に使うような、変数の引き回しを防げる
そんないちいちクラスを作るなんてコード量が多くなるだけ… という意見が出てきそうなところです。
実際、私が今まで仕事で見てきたプログラムでは、ここまで徹底して値オブジェクトが作られているものは見たことがありませんでした。
データとロジックを一緒に置く (第3章より)
この章の主張は
- データとロジックを別のクラスに分けることがわかりにくさを生む
- データと機能が分離していると…
- 同じ業務ロジックがあちこちに重複して書かれる
- どこに業務ロジックが書いてあるか見通しが悪くなる
という内容。
データとロジックはセットで。 という縛りを作ることで、自然にロジックが書かれる場所が定まり、どこに何が書いてあるのかわからなくなる事自体を防ぐ、という話です。
でも、Javaにおいて、データだけ持つデータクラス(=DTO、Entityクラス、JavaBeans、…)はよく見る手法…なのですが、
と、なかなか衝撃的なことが。StrutsはともかくJ2EEまで否定されるともはや、「Javaらしさとはなにか」というのもよくわからなくなりますが(笑)
※ とはいえ、ここまではっきり言っていただいたところで、始めてStrutsにふれた時に
「俺の思ってるオブジェクト指向となんか違う…」
「Logicクラスって何??」
と感じた原因がわかったように思えます。
メソッドをロジックの置き場にする(=ロジックのないメソッド禁止)
- getterのように単に値を返すメソッドはダメ。メソッドはもっと役に立つもので無くてはならない
- もしgetterを見つけたら、getterを呼び出している側に注目。getした値を使って何かの「判断/加工/計算」をしているはず。その「何か」ごとデータを持つクラスに移動してみる
このようなリファクタリングを繰り返していくことで、徐々にロジックが「データを持つクラス」に集約され、ロジックが重複して実装されることがなくなる、としています。
なお本書の特徴として、はじめから上に挙げたような、完璧なオブジェクト指向で実装されることを要求していません。
実装当初はデータクラスや、ただのgetterを作るのは問題ない(そちらのほうが書きやすい場合がある)。一旦動くようになったあとで、このようなリファクタリングを繰り返していくことで、ソフトウェアの変更容易性を善くすることができる
と主張されています。
TDDと根底の考え方は一緒ですね。まず動かす。そしてリファクタリングする。
なお、TDDで一つ思ったのですが、はじめ、このようにクラスをまたいだリファクタリングを積極的に行う場合、
「『メソッドの振る舞いを記述したユニットテスト』は、テストそのものがリファクタリングの邪魔になるのでは?」
と、感じました。
しかし、もう一度考えてみると、
- リファクタリング中に、ふるまいが壊されていないことが確認できる → 大きなメリット
- ユニットテストが書かれている時点でメソッドは小さく、テストしやすいもののはず。概ね、そのメソッドがそのままデータのあるクラスに移動するだけなので、テストの修正量は小さい → 小さなデメリット
と、メリットがあったり、テストの修正が必要になるにしてもさほど大きな修正ではなさそう… ということに気づきました。
メソッドは必ずインスタンス変数を使う
インスタンス変数を使っていないメソッドはそのクラスのメソッドとして不適切。ロジックの置き場を再検討するべき。
これは先程の、「データとメソッドは同じクラスに置く」と逆のことをいっています。
例えばこのようなメソッドです。
BigDecimal total(BigDecimal unitPrice, BigDecimal quantity) { BigDecimal total = unitPrice.multiply(quantity); return total.setScale(0, ROUND_HALF_UP); }
このメソッドは引数から全ての値を得ており、インスタンス変数を全く使っていないため、このクラスに存在している明確な理由がないのだそうです。
このような考え方はしたことがなかったので、衝撃的でした。
Web APIの作り方(第8章より)
ここまでは一貫して、
- ロジックはデータの近くに置く
- はじめから完璧なプログラムは書けない。徐々にリファクタリングして良い設計にしていく
ということが主張されてきました。
ということは、Web APIによってサービス間を連携する場合も、以下のような主張になる と思っていました。
しかし、どうやらそう単純ではないようです。
例えば、データ上存在している誕生日から年齢を計算する といった、計算ロジックをどちらに置くか という問題について、
- どちらに置くにしてもロジックを持つ側の負担が増える
- ロジックをどちらのアプリケーションが管理すべきかで判断する
と書かれており、「データを持っている方に置くべき!」という論調ではありません。
それぞれのサービスを担当する人が違うことによって、一つのアプリケーション内で閉じた話とは、格段に難しさが増しているようです。
とはいえ、
例えば誕生日から年齢を計算する例の場合
- 基本データ(ここでは生年月日)の形式や内容の変更が利用する側のコードに大きく影響する
- 逆に、導出結果を渡すAPIでは、基本データの形式や内容に変更があっても利用する側への影響は少ない
とあります。
基本データを変更することになるのは普通、提供側の都合(あるいは他の利用者の都合)だと思うので、ロジックを提供側に閉じ込めておいたほうが修正が早く、またテストもしやすくなると、個人的には思います。
また、本書で一貫して述べられてきた、データとロジックを一緒にすることでロジックの分散を防ぐ、という内容に沿わせることを考えると、やはり、データを持っている側のサービスにロジックを書くほうが全体として適切な構成になるのではないでしょうか。
(逆にこれで統一しないのであれば、提供する側は徹底してデータしか提供せずロジックは持たない、というルールにするべきだと思います。
しかしこの場合、年齢が複数のサービスから使われる時に、ロジックの重複を避ける方法がないのです)
追記
著者の増田さんご本人からコメントいただきました!
ありがとうございます。APIのところは、私も、まだ悩みながらやっています。APIの利用者が特定できる少数の場合と、不特定の多数を想定する場合では、違う設計になりそうですね。
— 増田 亨. (@masuda220) 2017年11月5日
ご本人がまとめられている書評一覧を発見したのでメモ。他の方の意見が参考になります。
たくさんの書評、ありがとうございます | システム設計日記
『一生、エンジニアで食っていこう』漆原茂さん 楽天テクノロジーカンファレンス2017 レポート #rakutentech
楽天テクノロジーカンファレンスで聞いた、漆原茂さんの『一生、エンジニアで食っていこう』というセッションのレポートです。
私的に刺さった話
0x30歳ころ 何歳でも技術で仕事したい!…と思った。
そのためには、自分自身の「進化」が大きな課題だった。
「昔はこれこれ項で苦労したんだぞ」的な「技術おじさん」にはなりたくなかった。
若返りながら進化できる唯一の生物を探したら……
一つだけ見つけた。ドラキュラ!(笑)
「若者のエキスを吸う」ために
・若いコミュニティに飛び込む
・若い人をメンターにつける
最先端に居続ける
外を知るために、イケてる人に会いに行く。
SNSで探してメッセージを送ったり、メールを送って直接話したり。
セミナーは話を聞くためではなく、偉い人と仲良くなるために行く。
会いに行くコツ
質問しに行かない。言いたいことを言いに行く。
ビジネスネタとLTを準備しよう!
イケてる人と繋がれると、その人達からの情報で技術トレンドが読める。
さらに技術は掛け算で進化する。イケてる人同士を繋げる!
私は思いました
良いエンジニアでいるためには… コミュニケーション、すごく大事じゃん!
いやわかってるんですよ。
でもね、“自分みたいな”、コミュニケーション苦手な人間にはほんとつらい………
と思いましたが、わざわざこういう場で話されるくらいだから、みんな少しだけ恥ずかしかったり辛かったりするんですよね。
自分だけじゃない。
なので、そのちょっとだけの心理的なハードルを超えることで、
楽しい楽しいエンジニアリングを仕事として続けられるんだと思います。
なので私も… 若い頃の苦労話おじさんにならないように(既に時々怪しい…)…
- 若い人に学ぶ
- そもそもそのために若いコミュニティに飛び込む
- イケてる人に話しかける! イケてる人にこそプレゼンする!
- どんどん自分からアウトプットする!
頑張ります!
「楽しい」は最強!!
追記
若い人に混ざっていく、というのは以前参加したこちらでも聞いた。
http://taichiw.hatenablog.com/entry/2015/01/29/230538
情熱プログラマには、一番下手くそになる、というのがある。
スゴイ人たちは自らそういう環境に行くし、本当に謙虚に話を聴かれる。
スゴイと思う。
『未来を創るプレゼン術』:澤円さん 仙台IT文化祭レポート1 #sendaiitfes
今年発足した、仙台IT文化祭というイベントに参加してきました。
この記事は澤円さんの『未来を創るプレゼン術』という発表のレポートです。
プレゼンは相手に行動させるためにするべし
私にはこの言葉が最も響きました。
これまで、いろんな場でLTや発表をさせてもらってきましたが、この大切な点が分かっているようで分かっていなかったのです。
私は、IT系のカンファレンスや勉強会で話される、「すごい人の話」が好きです。
大変な環境や困難な状況を、ご本人の努力で切り抜けられた
という類の発表を聞いて、刺激を受け、真似られるところは真似しようとしてきました。
そして、自分も発表する際は、そんな発表がしたいと思い、
自分のストーリーを中心にして、聞き手の人に元気を与えられるような発表をしてきたつもりでいました。
しかし。
今日のプレゼンを聞いて、相手の立場にたったストーリー作りができてないことに気付かさせられました。
スライドの主語は?
今日のプレゼン内で
全てのスライドの主語を「みなさん」に置き換えて話せるか?
という確認方法を紹介していただきました。
聞き手にとって利益のある話なら、主語を「みなさん」に置き換えられるはず。置き換えられないのであれば、オーディエンスに何の利益もないか、話し手が内容の価値を理解できていない。
なるほど、と思い、試しに昨日したばかりのLTのスライドを一枚ずつ見直してみました。
だいたいの私の発表の構成はこうです。
- こんなに大変な状況だったけど
- こんなふうに切り抜けて頑張ってきた
- パッションは熱いです!
- (さあ君もなんか持ち帰って真似してみて!)
改めて見てみると、自分の話しかしていませんでした。
「こんなに大変な状況だったけど」
→ 聞き手は、似たような問題を抱えているのか?という観点の考察が足りていない。
「こんなふうに切り抜けて頑張ってきた」
→ 聞き手の方に、どの部分を持って帰ってもらうのか。願わくば実践してもらうのかという観点が欠けている。
逆に言えば、上記の観点をプレゼン作成時によく考えてみることで、
「聞き手の方に共感してもらい、あわよくば私の事例から何かを感じ取って実践してもらう」
そんなプレゼンにできそうです。
以下、印象的だったトピックのメモです。
「プレゼンは相手に行動させるためにするべし」につながる話
素晴らしいプレゼンは「何かをしたくなるプレゼン」
世の中には2つのプレゼンしか無い
- 素晴らしいプレゼン
- 残念すぎるプレゼン
では、素晴らしいプレゼンとは
- 何かを始めたくなる
- 何かをやめたくなる
のように、何かをしたくなるプレゼンである。
(つまり、何かをしたくならないプレゼンは、残念過ぎるプレゼン)
プレゼンを構成する上で一番最初に考えることは 「参加者は誰か」
何を伝えたいかではない!!
残念過ぎるプレゼンをしている人は、だいたい「参加者は誰か」が抜け落ちている。
セッションの参加者が誰か、言語化する。出来る限り具体的にイメージしてみる。
事前に分かっている情報(どういう参加者層か) という情報に加え、
当日、アイスブレイクも兼ねて質問から始める。
・普段、プレゼンしてますか?
・残念なプレゼンを聞いたことはありますか?
付随効果として、参加者は「話しかけられている」と感じることができる。
その他テクニック的なこと等
思いついたキーワードで画像検索してみる
・プレゼン資料用の写真を探すわけじゃなくて、プレゼンを広げるために探す。
例)セキュリティの話をしよう → 検索 → ガードマンの絵が見つかる
→ 「ガードマンを中心に話が組み立てられるかも!」
※なるべく英語で検索すること。15万倍の情報量。
プレゼン用の写真を見つけられる、おすすめサイト
Photo AC (無料)
Adove Stock(有料)
オーディエンスは味方です
みんな、どんなおもしろい話が聞けるんだろうってワクワクしてきている。誰も発表者を貶めようと思っていない。
最後に。このブログを書く時に意識したこと
このブログは、
・私が感じた自分のこれまでのプレゼンに対するショックを伝える。
→ 同じように澤さんの発表内容を聞いたら愕然とされる方がいるのでは?
・「主語を変えてみる」というTryと結果を共有。
→ 良いと思って試してくれる人が一人でもいたら嬉しい
ということを考えて書きました。
表現されている内容は結局いつもとあまり変わらない感じ… ということはまだまだ不足していることがあるのかもしれません。
が、今後プレゼンをする際は今日の話を思い出しながら、相手に行動してもらえるような発表を作り、喋りたいと思います。
(中身まだ無いです) spring-cloud-sockets と言うものを教えてもらったんだけど
GitHub - viniciusccarvalho/spring-cloud-sockets: Reactive socket communication for microservices
これとSpring WebFlux の関係… というかそもそもWebFluxって何 的なのがさっぱりわかってないので後でちゃんと調べる調べる…… きっと。
多分これも関係あるからリンクだけ貼っとく手抜き。
JavaDayTokyo 2017 Report 2 : Reactive Stream - エンジニア的なネタを毎週書くブログ
Maven vs Java9のModule(Project Jigsaw) : 依存はどっちが勝つの? (追記:あんまりMaven関係なかった)
無事Moduleを使ったビルドがMavenでできたところで、本当にやりたかった検証へ。
Mavenで複数プロジェクトが同一プロジェクトに依存していると、「どれか一つ」が使われることになります。
全て同じバージョンなら問題にならないのですが、それぞれ異なるバージョンのプロジェクトを使用していると、結果、意図せぬ不具合を引き起こすことになります。
もしかしてmoduleはこの問題も解決したのでは? と期待したのですが… 結果、ダメそうでした!
検証内容
こういう依存関係のものを作ります。
MultiVersion.java
package multi.version; public class MultiVersion { public static String version () { return "1"; } }
multi.version/pom.xml(一部)
<groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>1.0</version>
World.java
package org.astro; import multi.version.MultiVersion; public class World { public static String name() { return "world" + MultiVersion.version(); } }
org.astro/pom.xml (一部)
<dependencies>
<dependency>
<groupId>multiversion</groupId>
<artifactId>multi.version</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
Main.java
package com.greetings; import org.astro.World; public class Main { public static void main(String[]args){ System.out.format("Greetings %s!%n", World.name()); } }
ビルドして実行すると
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world1!
ここまではOK.
さてここで、
multi.versionモジュールのversion2.0を作ります。
依存を追加するだけで、特にrequiresやimportはしません。
MultiVersion.java
package multi.version; public class MultiVersion { public static String version () { return "2"; } }
multi.version/pom.xml
<groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>2.0</version>
そして com.greetingsのpom.xmlにversion 2.0のmultiversionへのdependencyを追加します。
<dependencies> <dependency> <groupId>groupId</groupId> <artifactId>org.astro</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>2.0</version> </dependency> </dependencies>
GitHub - taichiw/moduleTry at 3.0_module_tukattemo_dame
これで実行すると…
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world2!
2が返ってきてしまいました。
たとえModuleを使っても、Mavenビルド時に、「より外側」に書かれたプロジェクトが使われてしまうことに変わりはないようです。
これだと、Moduleは、Module提供者が意図とした環境での実行が行われるとは限らないことになります。
さらに、MultiVersionクラスのメソッド名を変えてみます。
MultiVersion.java
package multi.version; public class MultiVersion { // changed method name version() -> version3() public static String version3 () { return "3"; } }
これでもビルドは通りますが…
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Exception in thread "main" java.lang.NoSuchMethodError: multi.version.MultiVersion.version()Ljava/lang/String; at org.astro.World.name(World.java:6) at com.greetings.Main.main(Main.java:7)
実行時エラーになってしまいました。
残念。
追記 (2017/10/05)
Mavenが悪いみたいな書き方したけど、そもそも現行のModuleの仕様の問題っぽい気がする。
mavenで作られたjarファイルを1ディレクトリに集めて…
$ ls mlib com.greetings-1.0-SNAPSHOT.jar multi.version-1.0.jar org.astro-1.0-SNAPSHOT.jar
javaコマンドでmoduleとして実行すると
$ java -p mlib -m com.greetings/com.greetings.Main Greetings world1!
multi.versionを2.0に置き換えて実行すると
$ cp multiversion/target/multi.version-2.0.jar mlib/ $ mv mlib/multi.version-1.0.jar mlib/multi.version-1.0.jar_ $ ls mlib/ com.greetings-1.0-SNAPSHOT.jar multi.version-1.0.jar_ multi.version-2.0.jar org.astro-1.0-SNAPSHOT.jar $ java -p mlib -m com.greetings/com.greetings.Main Greetings world2!
2になっちゃう。
moduleをビルドするときには--module-versionコマンドでバージョン指定しながらモジュールが作れるので、特定のバージョンに対しての依存も貼れそうなんだけど…
ひとまず方法が見つからず。
追記
今日、JavaOneでセッションがあったようで… あとで資料 or tweet or なんか無いか探してみよう。
MavenでJava9のModule(Project Jigsaw)を使ってみた
探し方が悪いのか、わざわざ書くほどのことでもないのか、mavenでJava9のmoduleを使うとどうなるか という記事が英語でも日本語でもWebで見つからなかったので、試してみました&備忘録としてブログに残しておきます。
Project Jigsaw: Quick Start Guide
こちらのQuick Startのソースを拝借しました。
結論から言うと、「module-info.javaを置いておけばmoduleになる」っぽい。
(言葉の使い方が正しいのか怪しい…)
1. module使わない版
とりあえず比較用に、"Java9で導入されたmodule" ではない、ただの独立したJavaリポジトリが2つの状態。
GitHub - taichiw/moduleTry at 0.1_no_module
World.java
package org.astro; public class World { public static String name() { return "world"; } }
Main.java
package com.greetings; import org.astro.World; public class Main { public static void main(String[]args){ System.out.format("Greetings %s!%n", World.name()); } }
com.greetings/pom.xml (一部)
<dependencies> <dependency> <groupId>groupId</groupId> <artifactId>org.astro</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
org.astro -> mvn install
com.greetings -> mvn package
でビルド成功。
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world!
2. org.astro (libとして提供される側)だけmoduleにしてみる
org.astro/module-info.java
module org.astro { exports org.astro; }
これでも普通にビルドが通る。
3. org.astroをただのjar, com.greetingsをmoduleにしてみる
何もrequiresしない場合
com.greetings/module-info.java
module com.greetings { }
これはビルドエラー。
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project com.greetings: Compilation failure: Compilation failure: [ERROR] ~/moduleTry/com.greetings/src/main/java/com/greetings/Main.java:[3,17] package org.astro does not exist [ERROR] ~/moduleTry/com.greetings/src/main/java/com/greetings/Main.java:[7,54] cannot find symbol [ERROR] symbol: variable World [ERROR] location: class com.greetings.Main
module-info.javaを書いた瞬間にmodule扱いになる → requiresなしでは外部パッケージが使えなくなる
ということらしい。
requiresを足してみたら
com.greetings/module-info.java
module com.greetings { requires org.astro; }
org.astroというmoduleは存在しないにも関わらずビルドが通ってしまった…
IDE上では思いっきり赤線が引かれているのでなんか間違ってるかも。
4. org.stroもcom.greetingsもmoduleに。
org.astroパッケージをexportする。
GitHub - taichiw/moduleTry at 1.0
これはきれいにビルド成功。