システム開発で思うところ

Javaで主にシステム開発をしながら思うところをツラツラを綴る。主に自分向けのメモ。EE関連の情報が少なく自分自身がそういう情報があったら良いなぁということで他の人の参考になれば幸い

Interfaceのdefaultで多重継承(下準備編)

はじめに

こちらの続きです。
コードの全量のリンクをこちらの記事に書いているので全量を見たい方はこちらを参照してください。

vermeer.hatenablog.jp

まずは多重継承の話の前に必要な土台作りがメインです。
Objectクラスを、ほんの少し(?)拡張するInterfaceの追加をします。

Objectを拡張からはじめる

Interfaceのdefaultを使った実装をするにあたって、どこから手を付けようと思ったか?についてから。

どのような型であれ、フロントから取得して、DBで永続化するにあたって

  • フロントからは任意項目で受け取りたい
  • DBのカラムの定義がnull許容

というのがある以上、DTOのメンバとしては Nullableであることを起きえます。
フラグなどを設ければ避けられるのですが、その分の手間がかかります。

そこで、Nullを安全に扱うのに便利なのがOptionalです。

//  dto.getName() の戻り値の型が Stringだと

if(Objects.isNull(dto.getName()){
   // なにかしら
} 

// これをOptionalにすれば、こういう感じの実装ができる
if(dto.getName().isEmpty(){
   // なにかしら
}

ということで、ObjectをNullableに扱いやすい拡張を一番初めにしました。

このメソッドがプロパティを扱うためのエントリーポイント的なものになります。

実装と説明

public interface SinglePropertyObjectType<T> {

  Optional<T> getNullableValue();

}

はプリミティブ相当のプロパティの型を指定するだけです。
インスタンスがNullableなものだという拡張をしただけですね。

NotEmptyを扱う

Nullableとは逆にNonNullを強制をするものも下準備しておきます。

Objectがベースになるので、Nullableなものが先にあって Nullを許容しないという制約宣言を追加する、というイメージで使用する想定です。

public interface NotEmptyType<T> {

  T getValue();

}

<T>は、SinglePropertyObjectType<T><T>と同じプリミティブな型を指定します。
具体的な使い方は次の「文字列編」で示す予定です。

インスタンスの生成

ここでやりたいことは newの振舞いを定義する、です。

せっかくプリミティブをラップした独自型のための準備をしたのですからメソッドの戻り値がプリミティブというのだと少々残念です。
また、booleanを戻り値とする判定ロジックであれば特にいらないのですが、加工/編集をした結果をインスタンスとした戻り値にしたいです。
その辺りの作法的なところをInterfaceとして定義します。

実装と説明

public interface NoArgumentNewInstance<R> {

  /**
   * 引数なしでインスタンスを生成します.
   *
   * @return 生成したインスタンス
   */
  public R newInstance();

}
public interface SingleArgumentNewInstance<T, R> {

  /**
   * 引数ありでインスタンスを生成します.
   *
   * @param value インスタンスのプロパティ
   * @return 生成したインスタンス
   */
  R newInstance(T value);

}

これを具象クラスで実装すれば編集や加工をしたインスタンスをInterfaceのdefault内で生成することが出来るようになります。

さいごに

こんな感じで、Interfaceのdefaultを実装していく話がこれから続いていきます。

次回はStringの拡張をテーマにした「文字列編」を予定。

Interfaceのdefaultで多重継承(はじめに)

はじめに

広くはこちらの続き

vermeer.hatenablog.jp

Interfaceのdefaultでやろうと思って、やっぱりやめたというのを、やっぱりやってみたという感じです。

先に断っておきますが、以降はOOPLの話ではありません。
あくまで Javaの言語仕様で実現できる実装パターンの話です。

一般的な批判

おそらく以下で紹介する実装は一般的に「推奨されない」ものです。
OOPLにおける「継承よりも委譲」に反しているためです。
また多重継承による継承パズルも良くないというのが一般的だと思います。

こちらの記事などを参考にされると良いと思います。

インターフェースのdefaultメソッド、ちゃんと使えていますか? #Java - Qiita

多重継承が禁止されている理由

プログラミング応用a 第14回 『インタフェイス(interface )』 -01〜多重継承の問題点

何が嬉しいの?

嬉しいの?というか、なんでこんなことをやろうと思ったの?というのを振り返りから。

基本は継承より委譲

ユーティリティを使うと

String hoge = "abc";
String upperHoge= StringUtils.toUppercase(hoge);

これをStringをプロパティにする文字列クラス(例えば「Text」)の場合

Text hoge = Text.of("abc");
Text upperHoge = hoge.toUppercase();

こういう感じになります。

Textみたいな汎用的なものを使い回すのではなくて、もうちょっとドメインに特化したようなクラスがあって、そこでも「toUppercase()」を設けたい場合、委譲になるんだけど同じようなことを書くのが面倒です。

では親クラス定義して継承を使えば良いんじゃないか?と考えがちです。
ようは「委譲よりも継承」という逆アプローチ。

例えば、COBOLからJavaを知った僕自身が「おぉ、これは冗長なロジックを回避できるので良い!」と思って継承地獄(?)にハマりました*1

まずやってみて困ったことは「親への依存が強すぎて使いにくい」です。
使う必要のないユーティリティも常に巻き込んでいるのでやりたいことに対してやれることが多すぎます。
開放したくないメソッドがあった場合に、サブクラス側でブラックリストのようにUnsupportedOperationExceptionで蓋閉じをすることも出来なくは無いですが、もはや本末転倒です。

Interfaceに対する個人的なイメージ

Interfaceは 操作の属性 というように個人的には思っています。
CloneableSerializable などがその例になります。
クラスにimplements ととして宣言されている属性といえばいいでしょうか。
上記のInterfaceは属性だけだったりしますが、多態をつかった実装をしていると「クラスは操作の属性の組み合わせで出来ている」というように僕は感じるようになりました。
そういうイメージをするようになって、試しに多態を使用した実装*2をするようになって、よりこのイメージが強くなりました。

ここまではInterfaceだけの話です。

ここからはJavaの言語仕様で、できるようになったことの話です。
Interfaceという型(操作の属性)としては、これで十分だったのですが、ここでJavaの言語仕様として InterfaceのdefaultがJava8から使えるようになりました。
実装が持てるという事は、this.getValue() というような実装をすれば インスタンスのプロパティ値を参照することも出来るという事だったりします。
Interface自身ではプロパティを持つことは出来ませんが、プロパティへのアクセスをするメソッドを経由してインスタンスのプロパティ値を参照することは出来ます。

「あー、、できるかぁ・・・」

このボンヤリとした「言語仕様として出来ること」と「クラスは操作の属性の組み合わせで出来ている」というイメージが僕の中でモヤモヤと。。

Interfaseのdefaultを使ってみようと考えるまでの経緯

委譲で実装

元々のキッカケとなったのはBigDecimalの計算を「単価クラス」を例にします。

委譲のやり方は以前の記事を抜粋しつつ、例えば

public interface Plus<T> extends CalculatorBase {

    public T plus(T... other);

}

でInterfaceを作っておいて、実装で

public class Price implements Plus<Price> {

    private final BigDecimalCalculator calculator;

    private Price(BigDecimal value) {
        this.calculator = BigDecimalCalculator.builder(value).build();
    }

    public static Price of(BigDecimal value) {
        return new Price(value);
    }

    @Override
    public final Price plus(Price... other) {
        return Price.from(this.calculator.plus(other));
    }
}

という感じにしました。 PlusはInterfaceなので、他の属性を追加できて親クラスを持った継承でもありません。

他に数値を扱うクラスで加算を行う場合は同じように機能を割り当てる感じで実装をします。

そこで思うわけです「冗長だなぁ」と。

ユーティリティで対応

(実装例は省略)

BigDecimalの加算は独自性も無く単純なユーティリティを呼ぶだけです。
そういうものはユーティリティクラスでカバーするという方法もあります。 欠点はドメインオブジェクトのプロパティを外部へ公開しなければいけません。
(内部で内包したパターンは委譲と同じなので割愛)

Interfaceのdefaultを使った実装へ

Interfaceと委譲の組み合わせを使った実装は

  • 型による統一的な操作の属性を宣言的に実施
  • ユーティリティメソッドを呼ぶだけの実装をする

という繰り返しだったりします。

この「繰り返し」をしていると、Interfaceのdefaultを使う「操作の属性の標準実装」と何が違うのだろう? と思い至ります。

一般的に「やらないこと」という理解はしつつ、それをもって思考停止するのも良くないな、と。
せっかくJavaの言語仕様としてできることなのだから、やってみて、良いところ、気を付けるところ、みたいなのを自分なりに考えてみても良いのではないだろうか?と。

やってみて分かったこと

良かったところ、気を付けた方が良さそうなところですが、明確な学術的な論拠ではなく あくまで僕の肌感覚です。

気を付けた方が良さそうなところの記載が多いですが、僕としてはネガティブな意味合いではなく気を付けさえすれば、それなりに使えるんじゃないかな?という意図で書いています。

良かったところ

属性を宣言するだけで合成できる

一番やりたかったところ。
基本操作の属性の組み合わせで汎用的なロジックで充足するところは完結できます。
冗長なコードは無くなりました。

メンバ変数値の所在が実装クラスだけ

メンバ変数が実装クラスだけで宣言するので、this.getValue()の取得場所は1つだけです。
実装継承だと親クラスのメンバ変数かサブクラスのメンバ変数か分かりにくくなったりすることがありますがInterfaceにはメンバ変数の宣言ができないので thisは確実に実装クラスだけになります。

プリミティブな型の拡張がやりやすい

やりやすいというか、向いているという言い方の方が良いかもしれません。

気を付けた方が良さそうなところ

親が子のメンバ変数を参照する

良かったところの逆で、Interfaceが親クラス的な位置付けだとして、thisという子クラス的なメンバ変数の値を参照するということに違和感があるといえばあります。
馴染んでしまえばそういうものと僕は割り切れましたが、嫌な人には嫌な感じはすると思います。

ドメインオブジェクト的なものに限った方が良さそう

IOなどの副作用が伴う実装はやるべきではないと思います。
あくまでPOJOというか、副作用のないものに限った方が良いというニュアンスとして「ドメインオブジェクト的なもの」としました。

プロパティは1つ(もしくは少数)が良さそう

上述の例はインスタンスの複製をしていませんが実装例としてはインスタンスの複製も行います。
ここでコンストラクタをリフレクションで実行するのですが引数の組み合わせや順序まで考慮した汎用的な仕組みを組み込むとパズルがより複雑になります。
さすがにそれはやりすぎかなぁと思います。

プリミティブな型の拡張に留めておいた方が良さそう

プロパティは1つとほぼ同じです。
プリミティブな型(もしくはそれに相当するもの)を拡張するくらいに収めておけるものであれば、まだ使う側も作る側も扱える範囲かな?と思います。

合成を前提に1つ1つは動詞単位で小さく作る

思っているよりも細かい粒度でInterfaceは作った方が良いです。
たとえば「計算」というInterfaceを作る場合は「加算」「減算」「乗算」「除算」に分割をするという感じです。
操作の属性の粒度なので、名詞ではなく動詞を単位に分割しておいた方が合成がやりやすいです。

実装の継承は避けた方が良さそう

型を組み合わせたクラス(実装)を継承するというのは止めた方が良いと思います。
あくまで型の合成(もしくは型の継承)の範囲で留めておくのが良いと思います。
ただでさえ継承ツリーで複雑なところに依存の強制まで入ってくると飼いならす(?)のはかなり困難な印象があります。

委譲の方が楽

楽というか継承ツリーを意識しながら実装するのはInterfaceを作る側としては手間がかかります。
テストもInterfaceの実装をテストするためクラスのテストを作るのと比べると一般的ではないので慣れないと違和感があります。
シンプルな分かりやすさとしては委譲が良いと思います。

GitHub(コード全量)

Release interface.default.3 · vermeerlab/domains · GitHub

さいごに

ボイラーなコードもコピペとIDEの力を借りれば簡単に(?)解消できたり、これからはAIがコードを量産してくれる時代なので面倒なことはAIに任せるのが良いでしょう*3

ただ、あくまで個人的には 副作用を伴わない、POJOドメインオブジェクトだったら使っても良いんじゃないかな?と思います。
思いますが、おそらく反対の声が多いことも予想されます。
ご利用は計画的に(?)、用法用量を守って(?)試してみても良いかもしれません。*4*5

「はじめに」としてはここまでです。
具体的なコードとそこに至った感想や説明を今後書いていく予定です。
上述のコード全量を順番に説明をしていくだけなので、そんな説明とかいらない人は そちらを参照していただければと思います。

記事リスト

記事の索引的なもの

Interfaceのdefaultで多重継承(下準備編) - システム開発で思うところ

Interfaceのdefaultで多重継承(文字列編) - システム開発で思うところ

Interfaceのdefaultで多重継承(数値編) - システム開発で思うところ

Interfaceのdefaultで多重継承(単位編) - システム開発で思うところ

Interfaceのdefaultで多重継承(日時編) - システム開発で思うところ

Interfaceのdefaultで多重継承(ファーストクラスコレクション編) - システム開発で思うところ

*1:「継承よりも委譲」といわれている所以の一例ですね

*2:いわゆるGoFデザインパターンのステートパターンやストラテジーパターン

*3:知らんけど

*4:なお僕自身は自分だけコード資産では使うつもりです

*5:お仕事で行う実装では使う予定はありません。言うまでもなく反発されるのが必至ですし、他の人にとって書きにくく癖が強いものだろうというのは分かっているので

Developers Summit 2024

はじめに

event.shoeisha.jp

へ行ってきました。
参加したのは2月15日(木)だけ。
聴講しながら書き込んだメモとか感想を無加工で放流。

ソフトウェアセキュリティ

チャットによるUIがイノベーションになった

たしかに、IFとしてチャットというか自然言語を使えるのは大きいと思う。

RAG

LLMへの問い合わせにProxy的に情報を付加するみたいな感じかな? (あとで調べる)

プロンプトインジェクション的な攻撃

「これまで言ったことを忘れて」

LLMのセキュリティリスクTOP10

これを見ると何が攻撃対象になるのかというのがわかるので目を通すと良い

自動化による過度の信頼

「なぜ、それをしたのか」というところが分からないことをしてしまうことがありえる。。 勝手な商品購入とか、メインルータを止めてしまうとか 「いや、そこまでやったらだめ」ということは、AIとは別のところできちんとブロックする必要はありそう。

全部をAIだけではなくて、セーフティーネットはハード側の設定で変更不可なところで止めるみたいなことをあわせ技でできる仕組みをきちんと設ける必要性

Human In the Loop

チェック、改善の中に「人」を入れることを忘れないように、ということ

リスクベースアプローチ

考える必要あり

WebシステムやモバイルアプリにおけるUIからの自動テスト事例3選

自動テストの山は2つ

「はじめる」「継続する」

初期構築がすぐにできない

テストをリファクタリングできない(テストのメンテンスで疲弊)

環境の変化

テストはバグを見つけるものではなくフィードバックを得る手段

技術の変遷

大きいのはテストの実行環境
実機からWorkspaces、Dockerへ

テストケース管理はエクセルが結局王道(?)

何を自動テストする?

リグレッションテストの充足

基本的な機能を確認する

落ちると致命的なもの(重要度が高いもの)

手動テストでコストが高いところ

テストピラミッド

Unit:Service:UI = 7:2:1

この「1」を満たすことを目標にする

最初のテストケースが回るようになるまで3Week

慣れている専業でも対象の選定から実行までそのくらい時間かかるというのは1つの目安になりそう。

UIのテストを増やすのではなく、Integrationテストを増やすように

UIを増やす方向にすると破綻しやすい

(UIとIntegrationテストの違いを学ぶ必要もありそう?)

テストケースは適切に分割

基準は「時間」
10分を超えるテストは分割すること

テストツールで拾える情報を実装に組み込む

Appiumで認識できるIDを入れる

いれないと、場所の特定で失敗してしまってテストが安定しない

自動テストは属人化がキモ

担当者をちゃんと決めないと「誰もやらない」になって継続ができない

不安定なテストは能動的に捨てる

自動化が安定しないものを延々とメンテナンスするのでは、結局「手動」のほうがハンドリングしやすいということ

ケースを最適化をする

「ただ追加していく」ではなく、適切な規模をキープするように精査(減らす)というメンテナンスも大事

サービスの危機に立ち向かうリーダーシップ

PagerDuty

インシデント全体を分析と改善する

インシデントコマンダー

SREと同じくらいの位置づけになると思っている

言葉の整理

インシデントは「状態」not = 「障害」

参照

事業価値とエンジニアリング・リソース効率性とフロー効率性
リクルートの公開資料)

インシデントコマンダーボトルネックになる

これはなるべくしてなる

PageDutyで可視化して、俯瞰力をツールで補完する

PagerDutyでWar Roomを作って情報を集める

コミュニケーションは、ブロードキャスト型にする

インシデントコマンダーはCEO/CIO(すーてくホルダー)に対するメンバーの壁役

対応者の手を止めないのが大事

RUNDECK

自動実行スクリプトエンジン

ポストモーテム

きちんとまとめて組織としての成長につながるものまでやるのが大事

インシデントコマンダーになれる人

(後で資料で) イメージとしてはSMに近い印象をもった

教育・育成についてもPagerDutyで公開しているので参考にすると良い

AIの利用

データが集約されているので、そこからLLMで「文章化」してレポートを読めるという仕組み

要約を作るのはLLMの得意分野だと思うので有効活用できるところだと思う

知識ゼロから学ぶAIのテスト(信頼できないソフトウェアとそのテストのために)

書籍「ソフトウェアテスト」で扱えなかったAIテストについて

30年でテストの費用は減っていない

工数内の46%がテスト費用
おそらくAIで更に増えるかも?

Copilot活用

生産性は上がる、コードの凝縮度も上がるけど、コードレベルは良くないかも
テストの生産性は上がるわけではない

AI自体をテストするのは困難

シフトレフト

(初耳だった・・・) 原因工程で不具合を見つけると、後工程で見つけるよりも費用は低くなるよ
だから「シフトレフトしよう」という話

シフトライトせざるを得ない背景

AIは事後教育の結果でテスト結果が変わってしまうので「初期の想定」と 違う結果が起きるのは自然な話ではある

シフトライトは以前からある話

パフォーマンステストとか、出来上がらないとできないテストは以前からある

AIのテストの難しさ

サンプリングデータによるバイアスが発生しがち
=白人データに偏りがち

さて、、これを「バグ」というべき?みたいな。 かとって、人口比率にあわせても、たぶん「求めているもの」とは違うかも。

非常にウェットなものなので、理系学問というよりも文系学問に近しい分野

ADAS

自動運転による危機回避はトロッコ問題的なものもAIのテストの難しさ

AIのテストは自動化が難しい

IOが安定しない(期待値が安定しない)

AIによるバイアス問題は、機能テストでも非機能テストでもない

AIテスト手法

ニューロンカバレッジ

役に立たないわけではないが、気休めよりもちょっとマシくらいのいうところ

タモリフィックテスト

これが現時点でもまともなテスト手法

データポイソニン

モデルデータに対する攻撃
起こり得ることとしては、道路標識にノイズを入れて「通行止め」を「通行可」にしてしまうことができてしまう。

テストの自動化が成功する確率はそもそも低い(by Rex Black)

プラスのROIを達成していないというのが実態とのこと

AIのテストはリリースしたところが「始まり」

あくまで初期設定に近くて、その結果を分析・改修を繰り返すことになるから

AIを活用した誰でもテストが自動化できるプラットフォームの実現に向けて

モバイルのテストは画像に依存する

これはAMのセクションでも言っていた。
このあたりはテストをE2Eで実行するときに覚えておいたほうが良いかも

LLMの登場で抽象度の高い作業ができるようになった

これまでできていたのは「操作」に対してのテスト
これからは要件分析やテスト計画といったところをテスト出来るようになってきた!

LLMの問題

ハルシネーション(嘘を付く)などなど。
結局のところ最終的には「人」によるレビューは必須
あくまで作成は自動化はできるけど、それをチェックするのは必要
そして、それには高いスキルが必要というジレンマ

LLMの活用:テストすべきことの提案

これであれば「何をテストすべき?」を考えることをLLMで補完するアプローチ

これならLLM問題よりも安全な使い方

これまでテスト経験知でカバーしていたところをAIに補完してもらうアプローチ

LLMの活用:テストのシナリオの要約

テストケースはどうしても人間には読みにくい
LLMによる要約によって「人間に読みやすい文章化」をしてもらう

これはインシデントコマンダーのところでもあったところ
この「要約する」というのはLLMの強みと考えて良さそう

LLMとこれまでの機械学習の差

LLMは抽象度の高い問題解決(文系的なもの?)
機械学習は特化型の問題解決(理系的なもの?)
という強みの違いがある

レガシーモダナイゼーション

モダナイゼーションの提案はほぼジオゲッサー

よくわからないところに落とされて、ヒントを探して推測してゴールに到達する

推測が間違っていると…

現在地が違うと課題もずれる

課題がずれると解決できない

戦略的ドメイン駆動

アーキテクチャとかVOは戦術的DDD
コンテキスト分割とかドメイン分析が戦略的な方

戦略的DDDは地図に例えるとわかりやすい

逆に言うと全体の課題(システムとか関係なく)を把握しないと、解決したい課題に正しくたどり着けない

「線引の外」が結局線引の中の問題解決に繋がらずデスゲーム

地図に山や川に名前をつけるように、名前をつけることで初めて「地図」になる
名前がないと「経度緯度」
これでは誰もわからない

戦略的DDDで得られること

全体像を全員で把握

適切な名前付け

適切な属人性の把握(=ステークホルダー

アプリケーションシンプリファイ

ルール駆動開発
これ、製品がなくなったのでこの表現はつかえなくなったみたい(?)

死にロジックを捨てるためのアプローチともいえる

なので、既存のソースコードを見ない

7年間1000件の生涯事例から分かった障害対応の改善ポイント

まずできる改善ポイント

大事なこと

サービス視点(システム視点ではない)

アクション(事象ではない)

情報の質(量ではない)

サービス視点

使う側にとって、何が必要?に対して適切なアンサーをすることが大事

アクション

事象はシステム毎に多岐にわたるけれど、すべきアクションはパターン化されていることが多い

情報の質

有象無象の情報ではなく、処理できる量に収めないと解決ができない

30分以内 or 以上 になるの?みたいなのが判断できる情報に整理

小さな対処をトレーニングとして大きな障害に対応できるようにする

運用設計は後回しとかやっていないとかアルアル

まず「大規模なシステム障害」の定義とパターンに応じた関連組織を決める

週2〜3時間をつかってやれる活動
判断基準をまず作る
基準がない一人ひとりが判断もできない(情報の質) そして、誰に伝えればいいのかわからないと広報もできない(サービス視点、Action) 担当者自身にとっても安心につながる

改善を進ませるための工夫(もしくは進まない原因)

アラート多すぎ

まず扱えるように減らすことから

障害対応にユーザー企業と協力体制を作る

アイディアとしては「サービスの質の維持」をキーワードにすれば、ユーザーにとっても「自分事」になるのでユーザーを巻き込める文脈になるかな?

GenAI in Production

弁護士ドットコムはIBMワトソンから使っていた

LLMの前から使っていた
課題としては「正しい質問立て」 それを解消できたのが「ChatGPT(自然言語による質問立てができる)」

RAGをつかって質問の精度を上げている

質問に対して、そのキーワードになる情報から関連書籍などをベクター検索して、その情報を質問に加えてChatGPTに投げて回答の精度を上げるている

「RAG」という言葉はAMの登壇で知った言葉
さっそくその理解が活きているのはデブサミならではという感じで良い

RAGに関するTIPS的なもの

Llamaindex Blog

専門性があるからこそ

フレームワークによる抽象化のブラックボックス化が逆に扱いにくい

このあたりはLLMの結果を精査する主体ならではだと思う

何を持って改善という?

LLMならではの不確実性
品質基準の難しさ

このあたりは「知識ゼロから…」で扱っていたところの課題で言っていたところ

結局、それを解消するのは弁護士による検証しかない
学習データによる学び直しを継続していく必要がある

データの品質大事

Data is the new oil

ビッグデータが流行ったときの言葉

ブランク・キャンバス症候群

「なんでもできる」だと、逆に「何をしたらいいのかわからない」になる
適切な問いかけや枠組みがあると、より質の良いデータを集めることが出来る

人間の役割はチェックだけで良い?

レビュー・修正するためには、実装経験のような育成がないと行き詰まってしまうよね?

AIの限界は、結局 人側の成長分に依存するよね?という話
伝統工芸の技術の継承の話みたい

むしろ、そのあたりをかなり意識的にやっていかないと緩やかにAIによって人は退化(?)するかも?とか思ったりもした
実際は「課題を解決する」ということになるんだろうけど
コードを書くこと自体がロストテクノロジーになるのは、まぁそう遠くないのかもしれないとは思ったりもする

世界標準のソフトウェア工学知識系SWEBOKGuide最新第4版を通じた開発アップデート

エンジニアリングに対する知識の体系がSWEBOK Guide

エンジニアリングは科学に裏打ちされたもの

体系的、規律的、定量的なアプローチ

顧客満足、開発者満足も「定量化」の範囲だと考えている、とのこと

正当なエンジニアリング(の定義)

コミュニティ(独りよがりではない)

科学的基盤に則っている(実証性がないのはNG)

実質的な価値を形成する(趣味じゃない)

体系とは、点と点を線で結んだもの

ラクティスとパターンは、体系を作り込む実践

SQuBOK(品質体系)

ソフトウェアエンジニアリングに対する共有理解のための手引(=SWEBOK)

枯れた技術を体系立てて基盤として整理したものがSWEBOK

ぎゃくにいうと、SWEBOKで扱っているものは「エンジニアリングというのであれば、やっているのが当たり前」という扱いでもあるとも言える

V3までに意外にもなかったんだと思うところ

アーキテクチャ

システムは複雑で、基盤も多岐にわたる

運用

そもそも入っていなかったの?とも思える

セキュリティ

悪意への想定も重要な品質

アジャイルが入らなかった理由

これは、体系の1つというよりも、横断的なものであり、マインドになるものという扱いにした

AIが入らなかった理由

まだ新しすぎて、知識領域としてまとめるには成熟していない

セキュリティは「ドメインに特化したもの」まで扱う

ドメイン(Cloud、IoT、ML)

正しい用語の定義がSWEBOKの意義

AIについては「言及」まで

LLMのムーブメントがちょうど起きてしまったので入れた

機械学習デザインパターン

SWEBOKの開発への役立て方

ポータル

エンジニアリングの地図

辞書

ソフトエンジニアリングほど言葉がブレる業界はない…
ちゃんと用語を合わせよう

文献ガイド

Kotlinのメモ

基礎学習

プログラマー向け Kotlin ブートキャンプ  |  トレーニング コース  |  Android Developers

Kotlinでのインスタンス生成!10選の実践的なサンプルコード | Japanシーモア

GoF

Kotlinでのシングルトンのマスター8ステップ! | Japanシーモア

環境構築

Gradle/Kotlinで開発する私的ベストプラクティス2022 - Kengo's blog

Android開発のメモ

パッケージ/構成

Android開発におけるアーキテクチャー設計について | プロダクト開発部Blog

AndroidアプリのApplicationクラスについて - くま's Tech系Blog

【Android】2020年からの MVVM【実践】 #Android - Qiita

開発中のアプリをMVVMからMVCに戻した理由 MVVM×RxSwiftでの開発で得られた教訓 - ログミーTech

Androidアプリのパッケージ構成の個人的ベストプラクティス - kobakei's blog

www.slideshare.net

【Android】初心者が意識すべきMVVMでの開発ルール

EventHandler

[Android] LiveEventまとめ #Android - Qiita

DI

Androidアプリ開発においてなぜDI Containerライブラリが必要になるのか

大規模AndroidアプリのDIをKoinからHiltへ移行する #Android - Qiita

API通信

Retrofitの基本的な使い方 - くま's Tech系Blog

Room

Android Roomのmigrationをやってみた

【Kotlin】MVVM + Room + CalendarView でカレンダーアプリを作成してみた | 株式会社ナディア

Roomの更新監視の方法4種類を比較してみた #Android - Qiita

エラーハンドリング

java - Android exception handling best practice? - Stack Overflow

How to avoid Force Close Error in Android – Hardik Trivedi

Effective Error Handling in Android: Strategies and Best Practices | by Diego Marcher | Stackademic

uncaughtExceptionメソッドでサブスレッドの例外発生をハンドリングする|CodeZine(コードジン)

Androidアプリ開発で例外の発生した場所を特定する | DevelopersIO

【Android】共通したエラーハンドリング行いたい場合の実装まとめ #Android - Qiita

非同期

Androidの非同期処理について自分なりの整理 - Continuity is The Father of Success

AndroidのRoomライブラリを用いたDB非同期処理をHandlerで書いてみた - OPTiM TECH BLOG

スレッド

Android Studio 3.6 ワーカースレッドとしてHandlerThreadを使う - 365連休

AndroidStudio

Android StudioにおけるBuild Outputの文字化けを解消 #Android - Qiita

Androidでlayoutのディレクトリを分けたい | フルスタックな感じで

Test

ArchUnit: How to get gradle to execute the tests? - Stack Overflow

JUnit 5 + Gradle による Java の自動テスト導入 #Java - Qiita

VirtualBox

結論としてはエミュレーターが動かなかったのでWindowsで直接動かすことにした。

VirtualBox で AndroidStudio の Emulator を実行したい #AndroidStudio - Qiita

VirtualBoxにAndroidをインストールしてAndroidStrudioでデバッグ | Project | すきるくえすと

www.youtube.com

アノテーションを使ったアレコレ

Androidのコードを 見やすく! 書きやすく! するアノテーション その2 #Android - Qiita

アノテーションによるコード検査の改善  |  Android Studio  |  Android Developers

lombok

Android Studio 最新版で Lombok plugin が使えなくなったときの対処法 #AndroidStudio - Qiita

こちらからダウンロードして試したらうまくいった [File sharing] Lombok files compatible with Android Studio · Issue #1111 · mplushnikov/lombok-intellij-plugin · GitHub
うまくいったタイミングもあったけど、どうも不安定な感じなので使わないというのが一番安全だと判断

参考

ViewModelからFragmentへ画面遷移イベントを送りたい - 縁側プログラミング

Android Studio覚え書き|Android Studio | IT底辺脱却ブログ

[Android]リリースビルドを実行して、スマホにインストールする方法 #Android - Qiita

第38回 プロジェクトの実行方法について | gihyo.jp

あれこれやっているときのつぶやき

AndroidのLog出力について

はじめに

Androidでの開発をすることになって、研修(カサレアル)を受けたりして事前学習をしていてログ出力を。 なんとなくAndroidStudioのページをみていると

注: アプリを公開する準備ができたら、デバッグ ログメッセージとスタック トレース出力の呼び出しをコードから削除します。そのためには、DEBUG フラグをセットして、条件ステートメント内にデバッグ ログメッセージを配置します。
アプリをデバッグする  |  Android Studio  |  Android Developers

どういうことだろう??
と思って調べてみると、どうやら Log.x はSystem.out.printに近いもの&コンソールログでフィルターしやすいように整形するだけのもののようです。
Androidアプリを開発する上で賢いLogの出力方法(とその確認の仕方) | Genのプログラム日誌

困ったな。。ということでアレコレ調べたメモが以下。

Tips

「はじめに」であった、やり方。
if文で端末のログレベルを都度判定する。

でも、これって端末のログレベルを変えたら開発時に仕込んだデバッグログが出力されるんじゃないかな?
これはちょっとNGかなぁ。

Timber

Androidのログ出力ライブラリ”Timber” #Android - Qiita

そう、リリース後にデバッグログがどうやっても出力されないようにしたいのが目的なので、たぶん、こっちがやりたいこと。

さいごに

stackoverflow すでに書かれていた。
android - Logging best practices and thoughts - Stack Overflow

とはいえ、なんでTimberが良いのか?というのに自分なりに辿り着くことも大事。

BeanParamがOpenAPIで出力されない(未解決)

困っていること

@QueryParamだったら出力できるのに、@BeanParamだと OpenAPIの出力が想定通りに出ない

実行環境

  • Payara Server 5.2022.3
  • Java 11

@QueryParamでやってみる

コントローラー

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Operation(summary = "ユーザー情報を検索します")
  @APIResponse(description = "UserResourse", responseCode = "200")
  public ResponseEntity<UserResourse> getUsersByQuery(
      @Parameter(name = "id", example = "1")
      @QueryParam("id") Integer id,
      @Parameter(name = "name", example = "name")
      @QueryParam("name") String name
  ) {
    var model = searchUser.findById(UserId.of(id)).orElseThrow();
    return ResponseEntity.success(UserResourse.from(model));
  }

OpenAPI(yaml

paths:
  /api/users:
    get:
      summary: ユーザー情報を検索します
      operationId: getUsersByQuery
      parameters:
      - name: id
        in: query
        required: false
        deprecated: false
        allowEmptyValue: false
        allowReserved: false
        schema:
          type: integer
        example: "1"
      - name: name
        in: query
        required: false
        deprecated: false
        allowEmptyValue: false
        allowReserved: false
        schema:
          type: string
        example: name
      responses:
        "200":
          description: UserResourse
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseEntityUserResourse'
      deprecated: false

OpenAPI(画像)

思っている通りに出力されている

@BeanParamでやってみる

コントローラー

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Operation(summary = "ユーザー情報を検索します")
  @APIResponse(description = "UserResourse", responseCode = "200")
  public ResponseEntity<UserResourse> getUsersByQuery(@BeanParam UserQueryParam userQueryParam) {
    var model = searchUser.findById(UserId.of(userQueryParam.getId())).orElseThrow();
    return ResponseEntity.success(UserResourse.from(model));
  }

クエリ―クラス

/**
 * UserQueryParam.
 */
@lombok.Data
@lombok.NoArgsConstructor
@lombok.AllArgsConstructor
public class UserQueryParam {

  @Parameter(name = "id", example = "1")
  @QueryParam("id")
  private Integer id;

  @Parameter(name = "name", example = "name")
  @QueryParam("name")
  private String name;

}

OpenAPI(yaml

paths:
  /api/users:
    get:
      summary: ユーザー情報を検索します
      operationId: getUsersByQuery
      responses:
        "200":
          description: UserResourse
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseEntityUserResourse'
      deprecated: false

OpenAPI(画像)

思っている通りに出力されない(困った)