wrongwrongな開発日記

しんまいさんの忘備録

【Kotlin】ありえないエラーが出てハマった話【BeanValidation】

以下のように、「エラー条件を満たした場合、値をメッセージに入れて返す」的な処理をクラス内に実装していたところ、通常では考えられないバグが出てハマりました。

@Null(message = "\${validatedValue}は異常値です。")
fun getHoge(): String? = when (cond) {
    true -> hoge.value
    false -> null
}

エラー内容

エラー内容は以下の通りです。

  • condを満たしていないのにアノテーションが反応する
  • ${validatedValue}に入る値がhoge.valueじゃない(バリデーション対象オブジェクトのクラス名が入っていた)

対処法

メソッド定義の順番を変えた所治りました。

原因の考察

SpringBootValidatorKotlinコンパイル結果等々絡む要素が多すぎて特定はできませんでした。
一応デコンパイル結果も見ましたが、特に何も問題無いように見えました。

同名のプライベートフィールドやら拡張関数やら色々と入っているファイルだったので、Kotlinによるゲッター生成関連のどこかでコンパイルが壊れるのかなと思っています。

【日記】はじめてのBuzzり

自分の書いた以下の記事がBuzzったので感想を書きます。

qiita.com

何故バズったか

多分やめ太郎さんに拡散していただいたことが大きいと思います。
このおかげでGitKrakenを多くの方に知っていただけました、ありがとうございました。

Contribution数がすごい伸びる

9/30の公開からランキングから外れる10/4朝までの間で101いいねも貰いました。
この記事を除いて1年で貯めたがContributionが150弱だったことを考えると凄まじい数です。

10月2日の朝起きた時には46とか通知が溜まっていてびっくりしました。
スクショ撮ってなかったり、99+まで貯めたりしなかったのはちょっともったいなかったですね……。

日中もQiitaを確認する度に通知が来ていて、「通知が壊れるとはこういう感覚か……」などと知ったような気分でした。
千とかバズる方々の通知欄はヤバそうですね。

実はQiita向けに書いた内容じゃなかった

この記事は、元は会社の技術ブログ用の内容でしたが、社内調整の結果公開が先になりそうで、バージョンアップも頻繁に来ていたため、取り下げて個人で公開したという経緯が有りました。
個人的には会社のブログでバズってくれた方が嬉しいかなとか色々思いましたが、それとは別にいい意味でバズると気分がいいものですね。

最後に

GitKrakenは2016年末からずっと使っている思い入れのあるツールであるため、この記事がAxosoftさんとGitKrakenに対してプラスに働いてくれたら嬉しく思います。
記事の末尾に書いたとおり、この記事では語り尽くせない良さが沢山あるので、是非皆さんもGitKrakenを使ってみて頂けるとありがたいです。

【日記】DDD的な設計完全に理解したメモ【プログラミング】

マサカリ避け

本読んでないです。
経験則です。
DDDチョットデキル人になりたいです。

前書き

「大量の相関チェック(しかもDBアクセスなど層をまたぐ処理や複数オブジェクト間の相関チェックを含む)とその後の登録が発生する状況」を想定して書きます。
言語はJava(SpringBoot)想定です。

最近作っているプロダクトで、DDD的な考えに触れて自分が考えたことを設計に取り込んだ経験から書きます。

ドメインを見極める

ドメインとは、複雑になりそうな処理内容を切り出したものです。
想定した状況の場合、まずバリデーション処理は物凄く複雑になるでしょう。

この「複雑な何か」、「まともにやりあったら複雑すぎてコードと自分が爆発四散しそうなもの」がドメインです。
ここではドメインが明確になったものとして話を続けます(でもDDDで一番むずかしいのはドメインの見極めだとは思います)。

実現する

次に実際の処理の作り方です。

まず書いてみる

どんな処理であっても、何を実現するのか、そのために何が必要なのかを理解するために努力していれば、「何かこうやったらうまくいきそう」という想像ができると思います。
そのぼんやりした想像を、図に書いたり、コードに書いたり、何でもいいから書き出してみて、その上で分からないことが有れば聞いたりはっきりさせていきながら書いたり捨てたりしていくのが良いでしょう。
そうして「処理とフィールドの塊」を作っていきます。

今回はドメインの持つべきフィールドの内容が以下の3クラスにまとまったものとして話を進めます(ゲッターや内部処理等は省略)。
内容は、DomainRootがDomainChildとDomainNodeを持っているというものです。

public class DomainRoot {
    private final DomainChild domainChild;
    private final DomainNode domainNode;
}
public class DomainChild {
    private final ChildValueObject childValueObject;
}
public class DomainNode {
    private final NodeValueObject nodeValueObject;
}

そうして全体の構造をはっきりさせていくと、今度はドメイン内部で用いる処理が3種類に分けられるようになります。

  • 単体のオブジェクト内で処理が完結する内容
  • DomainRoot内の情報をDomainChildやDomainNodeが必要とする内容
  • レイヤーをまたぎ、ドメインオブジェクト外に処理を書く必要がある内容

この内、単体のオブジェクト内で完結しない内容について具体的な実装のコツを書きます。

単体のオブジェクト内で完結しない内容をどう書くか

個人的には、以下3つの方法があると思っています。

  • 関数型インターフェースで処理を受け取る
  • インターフェースを用意してその実装を受け取る
  • abstractクラスを作り、その実装を受け取る

この内まず試すべきなのが関数型インターフェースで処理を受け取る方法で、数が多かったり、上の方のクラスで継承するなどの理由が有ればインターフェースを用意してその実装を受け取る方法に順次シフトするのが良いと思います。

abstractクラスを使うやり方は、Javaでは多重継承ができないなどの制約が有り、そもそも要求する内容はフィールド等を要求しないため、やらない方がいいと思います。

DomainRoot内の情報をDomainChildやDomainNodeが必要とする内容

DomainChild.childValueObjectの検証にDomainNode.nodeValueObjectの内容が必要な場合を、 関数型インターフェースで処理を受け取る例で書きます。

public class DomainChild {
    private final ChildValueObject childValueObject;
    // 外部に公開しないように保持する
    // この関数をコンストラクタで渡してもらう
    private final Predicate<ChildValueObject> predicate;

    // バリデーション関数
    private boolean isValid() {
        return predicate.test(childValueObject);
    }
}

ここで要求される関数を、DomainRoot側では以下のように実装します。

public class DomainRoot {
    private final DomainChild domainChild;
    private final DomainNode domainNode;

    public DomainRoot() {
        // 引数や途中の処理は省略

        this.domainChild = new DomainChild(
                childValueObject,
                it -> /* domainNodeの絡むような相関チェック */;
        )
    }
}

重要なのは、外の内容を必要とする(DomainChild)側は「引数を渡したら検証結果が返ってくる」ような関数を要求することです。
これによって、DomailChildは外の処理を知らずにそれが正しいかを検証することができるようになります。
また、この相関チェックは本質的にルートが知っておくべき処理なので、それをDomainRootに書くこともできています。

レイヤーをまたぎ、ドメインオブジェクト外に処理を書く必要がある内容

レイヤーをまたぎ、ドメインオブジェクト外に処理を書く必要がある内容を、インターフェースを用意してその実装を受け取る例で書きます。

具体的には、以下のように実装します(前節で追記した内容は書くと邪魔になるので排除しています)。

public class DomainChild {
    public interface Delegates {
        HogeDto getHogeDto(Integer childId);
    }

    private final Delegates delegates; // 移譲はコンストラクタで受け取る
    private final ChildValueObject childValueObject;
}
public class DomainNode {
    public interface Delegates {
        FugaDto getFugaDto(Integer nodeId);
    }

    private final Delegates delegates; // 移譲はコンストラクタで受け取る
    private final NodeValueObject nodeValueObject;
}
public class DomainRoot {
    // ルート側では特にインターフェースを要求しなかった場合を想定
    public interface Delegates extends DomainChild.Delegates, DomainNode.Delegates {
    }

    private final DomainChild domainChild;
    private final DomainNode domainNode;

    public DomainRoot(Delegates delegates) {
        domainChild = new /* delegatesを渡す */;
        domainNode = new /* delegatesを渡す */;
    }
}

これによって、「ドメインに対してその他処理が依存している状態」を作り出すことができます。
補足として、今回は「このオブジェクトはDBから取ってくるものだ」ということを強調するために『なんたらDto』というような名前を付けていますが、これに関しては必要な情報をドメイン層に定義した上でそれをDBから取ってくるという形としなければ、「ドメインに対してその他処理が依存している状態」にはなりません。

なぜこう書くときれいだと言えるのか

ここまで長々と書いてきましたが、今書いたコツは一見するとかなり複雑なことをやっているように見えます。
なぜこう書くときれいだと言えるのか、自分なりの答えは以下3点にまとまります。

  • どのドメインオブジェクトでも「そのオブジェクトの中でやるべきこととそうでないこと」が明確に分かれている
  • どのドメインオブジェクトでもDBなど「Javaの外側の世界」/「具体的な実装」を意識せずに実装できている
  • どのドメインオブジェクトも使い方・使われ方が明示されているため、それに従うだけで処理を生やしていくことができる

細かく言い出すとキリが有りませんが、対象の処理が複雑なのにこれらが成り立っていない部分の有る設計は大体どこかで破綻すると思います。

まとめ

まとまってませんが、とりあえずこれを意識して設計することで、以下のような利点が有ると思っています。

  • 外側の変更に強いシステムを作ることができる
  • 「どこに何を書くか問題」がドメイン別に整理されることでスッキリする
  • ドメインファーストで設計することで、複雑さを切り離して楽になることができる

【Java】interfaceでinterfaceをimplementsしたかった(extendsが正しかった)話

やりたかったこと

以下のようにinterfaceが2つあるとき、この2つのinterfaceを継承したinterfaceを作ろうとしました。

public interface Interface1 {
    String hoge();
}
public interface Interface2 {
    int fuga();
}
やろうとしたこと

interfaceなので、以下のようにimplementsすればいいと考えましたが、これでは動きませんでした。

public interface Interface3 implements Interface1, Interface2 {
}
正しいやり方

extendsを指定するのが正しかったです。

public interface Interface3 extends Interface1, Interface2 {
}
public class Impl implements Interface3 {
    @Override
    public String hoge() {
        return null;
    }

    @Override
    public int fuga() {
        return 0;
    }
}

【日記】「エンジニアが不要とされる瞬間」について考えたこと【意見】

wrongwrong163377.hatenablog.com

上記記事に関して、「じゃあ未来永劫エンジニアが必要とされ続けるか」と言われるとそうでもないと思っている話を書きます。
自分は主に以下の状況でエンジニアが不要とされるだろうなと思っています。

  • 人格と社会的な不確実性まで再現するAIが出てきたとき
  • 戦争になったとき
  • 核戦争になったとき

人格と社会的な不確実性まで再現するAIが出てきたとき

これに関しては、もう人間そのものが不要になるだろうなあと思っています。

人間の有利な点はその汎化能力と言われていますが、人生を無限にエミュレートするようなことをされてしまえば、どんなこともAIには敵わなくなるでしょう。
特に机上でアレコレやるのが主な仕事となるようなエンジニアは真っ先に置き換えられてしまいそうな気がします。

ただ、これに関してはもはや人類共通の問題となってしまうので、そこまで行ったらもう諦めるしかないんじゃねとも思っています。

戦争になったとき

これについてはほぼ日本しか見ていない話です。

色々きな臭い世の中になっていますが、今の日本の状況で戦争になったとき、エンジニアの技能は全く大切にされないだろうと思っています。
第二次大戦でも重要な技能者を徴兵で溶かして兵器の生産力を下げるなんてことをやらかしていましたが、現代でもそうだろうなあという諦めが有ります。

まず上に立っている人間がIT音痴すぎるので、エンジニアという道具の用途なんて考えられないでしょう。
また、国民の方も「後ろで何やってるか分からない奴なんて前線に出したほうがいい!」という声で応えそうだなと思います。

状況が不安定になったとき、エンジニアの立場は多分弱いです。

核戦争になったとき

直接的に電子機器が壊れることもそうですし、ライブラリ・パッケージの配信システムがやられたり、クラウド環境が消し飛んだりすれば結構なエンジニアが仕事できなくなるだろうと思います。
少なくとも色んなサービスが止まることでしょう。

まあ、こんなことになればやっぱり人類共通の問題となってしまうので、そこまで行ったらもう諦めるしかないですね。

終わりに

今の世の中は物凄くきな臭いことになっていて、全方面「なんかあったら社会的にも人生的にも終わるかもなあ」で囲まれてしまっている感が有ります。
日本という国の将来に希望を見いだせないというか、人類という種の将来そのものに希望を見出せません。

宇宙植民さえ軌道に乗れば人類は未来永劫やっていけるんじゃないかと思うので、できればそういう世界線に収束して欲しいものです。

【日記】「AIがエンジニアを不要にする」という言説について考えたこと【意見】

「AIがエンジニアの仕事を完全に置き換えるからエンジニアが不要になる論」について、上記ツイートを元に考えたことをまとめます。
そもそもの話として、「〇〇だからエンジニアは不要になる論」はどうやら自分が生まれる位から繰り返されているようなので、ここでは「何らかの自動化によりエンジニアが不要になる論」全体について書きます。

直近十年に対する結論

まずはじめに、直近十年でエンジニアが不要になることはほぼ無いと自分は考えています。
理由は以下の3点です。

  • 自動化のための仕事と自動化できない部分は必ず残る
  • 自動化しただけでは環境変化に対応できない
  • エンジニアの仕事はほぼ無限に有るので需要が無くならない

以下、それぞれについて主張を書きます。

自動化のための仕事と自動化できない部分は必ず残る

そもそも自動化を行うためには自動化を行える状況である必要があります。
一方、現実はそう単純なものではなく、沢山の歴史的経緯や考慮不足によって自動化を行える状況になっていない方が普通です。
つまり、自動化のためにはまず自動化できる状況を整えるための仕事が発生します。

この自動化のための仕事というのは「業務フローから見直したほうがベター」と言われる程度には大規模になることが殆どです。
また、自動化のための仕事をやっている最中にも本業は進めていかなければなりません。
ここで何が起きるかというと、自動化が完全に終わる前にライフサイクルが一周してしまい、「新しい技術を使って昔自動化した部分をより効率化する」的な仕事が発生していきます。
つまりエンジニアがやらなきゃならない仕事は残ってしまいます。

更に、仮に自動化を完遂したとしても、やはり自動化できない部分は確実に残ります。
開発にエッジパターンや考慮漏れが発生しないなんてことは有りえません。
高度に練られたフレームワークを使っていてさえかゆい所に手が届かない場面が発生することがいい例です。

そのような部分に対応するためには、結局エンジニアの手作業が発生するでしょう。
やはりエンジニアの仕事は残ってしまいます。

自動化しただけでは環境変化に対応できない

仮に自動化可能な業務を全て自動化したとしても、エンジニアの仕事は無くなりません。
前段で少し触れましたが、時間経過に伴って業務を取り巻く環境は変化していくからです。

パッと思いつくだけでもエンジニアが対応しなければならない環境変化はこれだけあります。

環境が変化しないなんてことは有りえません。
そして、自動化は今現時点で見える範囲に対してしか行うことはできません。
環境の変化に対応してやっていくためには、やはりエンジニアが必要とされます。

エンジニアの仕事はほぼ無限に有るので需要が無くならない

近年のツールや開発環境の進歩というのは凄まじく、IDEによる補完といった個人の生産性を向上させるものから、クラウド技術のように組織全体のアレコレを効率化するものまで、多様かつ圧倒的な改善が見られます。
これらを使いこなすエンジニアの生産性は、年単位で飛躍的に向上していると言って良いでしょう。
一方、高度に自動化された最先端の環境においてエンジニアの労働時間が減ったかと言えば必ずしもそうとは言えません。

この理由を、自分は「エンジニアの仕事はほぼ無限に有る」からだと考えています。

人間が計画して実行するアレコレでは「何をやらないか」を決めることが重要とされていますが、逆に言えばやりたいことはそれだけ有るという訳です。
エンジニアにおいてもそれは例外ではなく、もっとこうしたいだとか、機能追加をしたいだとか、とにかくエンジニアが携わる必要のあるタスクは無限に有るわけです。
表に出ている分かりやすい事実として、webページは表現がどんどんリッチになっています。

要するに、現状では多少何かが改善されてもそのスペースに滑り込んでくる仕事が有るという訳です。

終わりに

今の所自動化はアキレスと亀で、もブレイクスルーが無いならどれだけ進歩してもエンジニアが不要になるという状況に辿り着くことは無いだろうというのが自分の結論です。

というか「何らかの自動化によりエンジニアが不要になる論」って、エンジニアが生産性向上のために日々どれだけ努力しているかを軽視している感が有ると思っています。
毎日毎日業務と向き合い自動化の可能性の模索を行う立場の人間を無くすことができるとなぜ考えられるのか、割と不思議だな、と。

……割とこういった言説を真に受けていた自分も想像力不足でしたね。
まとまりませんがここで記事を切ります。

【レビュー】東京マルイ ステアーHC レビュー

過去記事で何度か触れていましたが、改めてステアーHC単体でレビューしていきます。

wrongwrong163377.hatenablog.com
wrongwrong163377.hatenablog.com
wrongwrong163377.hatenablog.com

記事内で提示するデータに関する注釈は以下の通りです

  • マルイ純正ミニSバッテリーを使うものと仮定し、その重量は181gとして計算する
  • 参照させていただいたデータについて、出典は記事末尾にまとめる

他銃との比較

結論から言うと、実用性だけ考えてハイサイクルカスタムを買うならH&K MP5A5 HCをオススメします。
理由は、素の状態で弾速・マガジン装弾数・重量のバランスが一番良く、実用面でも不便が少ないからです。

以下、スペック面と実用面の両面から、H&K MP5A5 HCとの比較を通してレビューしていきます。

スペック面

まずはステアーHCとH&K MP5A5 HCをスペック面から比較します。
両者の主だったスペックは以下のとおりです、ただしH&K MP5A5 HCはストックを何段階かで調整できます。

ステアーHC H&K MP5A5 HC
平均弾速(m/s) 85.13 81.88
装弾数 330 400
弾以外の重量(g) 2,861 1,800
全長(mm) 610 660(ストックの最大展開時)

こうして見ると分かる通り、ステアーHCは弾速以外のスペックで劣ってしまっています。
また、その弾速についても誤差レベルと言って良いでしょう。

実用面

実用面では、以下3点で比較を行います。

  • 装弾数
  • 重量
  • AIMしやすさ
装弾数

装弾数に関してはプレイスタイル・フィールドによって活きたり活きなかったりしますが、沢山撃つフィールドでは両者の70発の差は結構大きいです。
また、弾をあまり撃たないフィールドでも、補給の手間が減るのは楽です。

重量

重量に関しても、ステアーHCが重いというわけではありませんが、やはりH&K MP5A5 HCの方が軽いです。
場合によっては丸一日エアガンを持って走り回ることになる訳で、1Kgの差は大きいです。

AIMしやすさ

ここが一番大きな差です。
ステアーHCはマスクをした状態だとAIMしにくいです。

この点について、マック堺さんの動画(比較対象がH&K G3 SAS HCに変わりますが、結論は変わらないのでこちらを使わせて頂きます)からキャプチャして解説します。
www.youtube.com

画像は17秒付近(左)と23秒付近(右)です。見て分かる通り、ステアーHCの方がより多くの頬肉が乗っています。

f:id:wrongwrongwrongwrong163377:20190808224906p:plainf:id:wrongwrongwrongwrong163377:20190808224829p:plain

普通の状態で覗くのであればこれでもAIMに十分かもしれませんが、硬いマスクをして覗こうと思うと大分無理やり覗かなければなりません。
ドットサイトを使うのであれば、マウントベースで嵩上げすることで多少マシになりますが、銃身とスコープが離れることを考えるとやはり狙いやすいのはH&K MP5A5 HCの方だと思います。

本体に関して

ここからは単体でのレビューです。

他の方のレビューでも見かけましたが、ステアーHCはVグリップの付いているあたりがガタつきます。
また、まだ2戦しか使えていませんが、アッパーのレール部分も若干歪んでいました。
以上の通り、剛性には若干難が有るのかなと思います。

まだ特に問題が出たわけではありませんが、若干不安感は有ります。

まとめ

「ステアーHCを買うメリット有るの?」と聞かれると、「正直他のやつ買ったほうがいいよ」と言うと思います。

とは言えスタイルの独特さ(特に他人と銃がかぶる確率は低そう)は魅力的なので、「どうしてもこれがいい!」という人にはかなりオススメできます(本音を言うとHCならFA-MASが欲しかったマン)。
ボロクソ書いた自覚は有りますが、自分はこの銃を結構気に入っていますし、フィールドでも割とまともに戦っていける程度には使えています。

まとめるとロマン銃ということですね(アレ、結局擁護できてない感が)。