2020年12月にやったこと

AWSのDatabaseの資格とろうかなと思ってRDSのドキュメント読んでたけどまったくやる気がでないので一旦やめてTypeScriptに入門していた

相変わらずこれからやることリストを無視している

仕事ですぐに使えるTypeScript

TypeScriptやるならこのへんがよいというのを社の人に教えてもらったのでこれを読んだ

なんとなくざらっと書けるようになったのでよかった

一通り読んでとりあえずスネークゲームを書いた なんとなくかけた

起動周りめちゃくちゃなのでバグっている

github.com

PICTのクローン書く

冬休みはこれしかやっていない

ほんとうは

を冬休みの宿題にしようと思って12月頭に注文していたが、届かずに年が明けた

PICTは社のブログで知って最近ちょくちょく使うようになった

dev.classmethod.jp

便利

で、CLIじゃなくてブラウザでいじりたい(だいたいテストケース考えてるときはブラウザをみているのとGoogleスプレッドシートといい感じに連携したい)気持ちになった あとしらないサーバーに業務で書くコードのテストケース送りたくないなとか

inabajunmr.hatenablog.com

なのでTypeScriptでまるごとクローンしようと思って書いている

TypeScriptのチュートリアルをやっていたのはこれをやるため

とりあえずX組の因子でテストケースを出す、みたいのはかけた

Constraintsはサポートしてない機能がありつつもなんとなく動く、くらいまでかけたが、Constraintsを実装したらコードがかなり終わった だいたいそんな状況

かいている

github.com

冬休みで一段落させたい

DBS

RDSのドキュメント読んでた

モチベーションが全然でないのでこまった

その他

クソアプリアドベントカレンダーのネタ

inabajunmr.hatenablog.com

Base64エンコードの実装をした

しました

これは多分実際に実装なり何なりをしていたのは10月とか

ポエム

かいた

inabajunmr.hatenablog.com

ソフトウェアデザイン 2021年1月号

レビューの話をかいた

これは多分10月とか11月とかにしていたので12月はとくにしていない

レビューのインセンティブが偏るとレビューする人も偏るよねとかそういうのを書きました

眺めた

kratosが100パーAPIファーストでログインとか提供してるというfeatureが書いてあったのでどうやってんだろと思ってながめていた

ついでにsupertokensもみていた

github.com

github.com

これからやること

  • AWS入門する
    • ECS
  • Webエンジニアが知っておきたいインフラの基
  • 入門監視
  • クリーンアーキテクチャ
  • 実践ハイパフォーマンスMySQL 第3版
  • プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
    • 停滞気味
  • Linuxの仕組み
  • Goならわかるシステムプログラミング
  • 英語
  • 中国語
    • Toeic900超えるまで止める予定
  • ここ最近ほとんど業務でプログラミングをしなくなって、調整とかレビューとかばっかしてるがかなり雰囲気でやってるので、マネジメント系の本を読もうとしている
    • なにがいいのかよくわからん
  • Effective Go - The Go Programming Language
  • パタヘネ
  • OAuth2.0のクライアント書く
  • OAuth 2.0/OIDC関連仕様全部読む
  • WebAuthnのドキュメント読む
  • マイクロサービスパターン
  • コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方
  • DBS

Hannya64

本記事はクソアプリ2 Advent Calendar 2020の24日目の記事です。

Hannya64

https://inabajunmr.github.io/Hannya64/

デモ

f:id:inabajunmr:20201201235121g:plain

Base64の実装

注意

  • 筆者は普段こういうコードを全く書かないので、なんか間違ってたら教えて下さい
  • 効率とかは全然意識してないです
  • パディングとかURLエンコードの話とかはないです

基本的な方針

バイト列を先頭から3バイト取り出し、6ビット4つに変換する。6ビット(0〜63)を、文字列にマッピングしていく。

こちらを参考にさせてもらいました。

Algorithm Implementation/Miscellaneous/Base64 - Wikibooks, open books for an open world

実装

エンコード前のバイト配列を以下とする。

10000000 11000000 11100000 11110000 11111000 11111100

3バイトずつ処理する。最初は先頭の3バイトを処理する。

10000000 11000000 11100000

3バイトをくっつけて24ビットにする。 まず1バイト目を16ビット左シフトする。

10000000 << 16

結果右に0が16個くっついて以下になる。

        <---------------
100000000000000000000000

2バイト目を8ビット左シフトする。

                <-------
000000001100000000000000

3バイト目はそのまま。

000000000000000011100000

1つ目と2つ目をORでつなぐ。

100000000000000000000000 |
000000001100000000000000

ビット演算子のORは、いずれかが1だったら1になるので、以下のようになる。

100000001100000000000000

つないだ結果と3つ目もORでつなぐ。

100000001100000000000000 |
000000000000000011100000

結果、24ビットの列ができる。

100000001100000011100000

24ビットの列を18ビット右シフトして6ビット取り出す。

100000001100000011100000 >> 18

右シフトすると、左から0が詰められて右側のビットはあふれて消えるので、以下のようになる。

----------------->
000000000000000000100000

24ビットの列を12ビット右シフトして次の6ビットを取り出す。

----------->
000000000000100000001100

001100 だけを取り出したいので、111111 とANDで右から6桁だけ取り出す。

000000000000100000001100 &
000000000000000000111111

ANDは両方とも1だった時だけ1になるので以下になる。

000000000000000000001100

同じようにして次の6ビットも取り出す。

100000001100000011100000 >> 6 & 000000000000000000111111

最後の6ビットも同様に取り出す。最初の24ビットのうち、最後の6ビットを取り出せばよいので右シフトは不要となる。

100000001100000011100000 & 000000000000000000111111

この結果、この3バイトが

10000000 11000000 11100000

6ビット4つに変換できる。

100000 001100 000011 100000

6ビットなので、ここで得たそれぞれの値は000000(0)から111111(63)の64パターンになる。 10進数に変換すると以下になる。

32 12 3 32

以下のような文字列を用意する。

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

A が0で、/ が63として文字列のインデックスと数値の対応を変換すると以下になる。

gMDg

残った3バイトも同じ方法で変換していくと以下になる。

gMDg8Pj8

コード

Hannya64/hannya64.js at main · inabajunmr/Hannya64 · GitHub

ブラウザで完結しているGUI付きのPICTがほしい

ほしいメモ

2021 1/4追記 TypeScriptで実装してとりあえずブラウザで動くようにした github.com

なぜ共通化のために継承を使うべきではないかをできるだけ簡単に説明したい4J

多重継承できない言語でなるべく誰でもわかるように共通化のために継承を使うべきではない理由をまとめた。 継承を使うべきでない理由は他にもあるがとりあえず一番簡単に納得できそうな理由について記載した。 多重継承できたとしても、共通化のために継承を使うのは個人的にあまり良くないと思っているが、それについては記載しない。

前提

ある程度の規模感があるJavaのコードは責務ごとにクラスを分割するとコードが読みやすくなる(≒全部mainメソッドに書いたコードは読みづらい)。

そもそもコンストラクタでバリデーションしろよ、とかは無視してもとりあえず言いたいことは伝わる気がするので諦めた。

多重継承できる言語だとこの論理では継承よりもコンポジションを主張できない。 リスコフの置換原則の話とかはしない。

まとめ

Javaで継承による共通化を行うと、多重継承ができないため1つの親クラスに全ての共通処理を入れる必要がある。 つまり処理ごとにクラスを分割することができないし、クラスに適切な名前をつけることもできない(なので多くの場合Baseなんとかクラスになってしまう)。

これを避けるために無理やりクラスを分割すると、全く関連の無いクラス同士を親子関係にする必要が出てくる。 こうなると、あるクラスに手を入れる際に関係ないクラスを破壊していないか意識する必要が出てきて辛い。 関係ないクラス同士に親子関係があると、あとから見たときに混乱するので辛い。

以下のようなコードがあったとする。

public class UserCreator {
    public User createUser(User user) {
        if (user.id != null) {
            throw new IllegalArgumentException("ユーザーIDは必須です。");
        }
        if (user.age < 0) {
            throw new IllegalArgumentException("年齢は0以上である必要があります。");
        }

        // 永続化
        return db.create(user);
    }
}

public class UserUpdater {
    public User updateUser(User user) {
        if (user.id != null) {
            throw new IllegalArgumentException("ユーザーIDは必須です。");
        }
        if (user.age < 0) {
            throw new IllegalArgumentException("年齢は0以上である必要があります。");
        }

        // 永続化
        return db.update(user);
    }    
}

ユーザーを永続化する際にバリデーションを行っているが、このバリデーションはCreaterとUpdaterで重複している。 これを継承で共通化すると以下のようになる。

public abstract class UserPersistentBase {
    public void validateUser(User user) {
        if (user.id != null) {
            throw new IllegalArgumentException("ユーザーIDは必須です。");
        }
        if (user.age < 0) {
            throw new IllegalArgumentException("年齢は0以上である必要があります。");
        }
    }
}

public class UserCreator extends UserPersistentBase {
    public User createUser(User user) {
        validateUser(user);
        return repository.create(user);
    }
}

public class UserUpdater extends UserPersistentBase {
    public User updateUser(User user) {
        validateUser(user);
        return repository.update(user);
    }
}

validateUserがUserPersistentBaseに共通化され、UserCreator、UserUpdaterがすっきりした。 次にユーザーが作成されたり更新した際に、WebhookでHTTPリクエストが飛ぶ、という要件が追加されたとする。

public abstract class UserPersistentBase {
    public void validateUser(User user) {
        if (user.id != null) {
            throw new IllegalArgumentException("ユーザーIDは必須です。");
        }
        if (user.age < 0) {
            throw new IllegalArgumentException("年齢は0以上である必要があります。");
        }
    }
}

public class UserCreator extends UserPersistentBase {
    public User createUser(User user) {
        validateUser(user);
        try {
            HttpClient client = HttpClientFactory.create();
            UserChangeEvent event = new UserChangeEvent(user, WebHookType.CREATE_USER);
            client.post(System.getProperty("WEBHOOK_URL"), event.toString());
        } catch (HttpRequestFailureException e) {
            // webhookのリクエストエラーの場合は通知のみで処理をすすめる
            log.error("failed webhook request", event);
        }
        return repository.create(user);
    }
}

public class UserUpdater extends UserPersistentBase {
    public User updateUser(User user) {
        validateUser(user);
        try {
            HttpClient client = HttpClientFactory.create();
            UserChangeEvent event = new UserChangeEvent(user, WebHookType.UPDATE_USER);
            client.post(System.getProperty("WEBHOOK_URL"), event.toString());
        } catch (HttpRequestFailureException e) {
            // webhookのリクエストエラーの場合は通知のみで処理をすすめる
            log.error("failed webhook request", event);
        }
        return repository.update(user);
    }
}

Webhookのリクエストを行うコードが重複しているので共通化する。

public abstract class UserPersistentBase {
    public void validateUser(User user) {
        if (user.id != null) {
            throw new IllegalArgumentException("ユーザーIDは必須です。");
        }
        if (user.age < 0) {
            throw new IllegalArgumentException("年齢は0以上である必要があります。");
        }
    }

    public void requestWebhook(String message) {
        try {
            HttpClient client = HttpClientFactory.create();
            client.post(System.getProperty("WEBHOOK_URL"), message);
        } catch (HttpRequestFailureException e) {
            // webhookのリクエストエラーの場合は通知のみで処理をすすめる
            log.error("failed webhook request", event);
        }
    }
}

public class UserCreator extends UserPersistentBase {
    public User createUser(User user) {
        validateUser(user);
        requestWebhook(new UserChangeEvent(user, WebHookType.CREATE_USER).toString());
        return repository.create(user);
    }
}

public class UserUpdater extends UserPersistentBase {
    public User updateUser(User user) {
        validateUser(user);
        requestWebhook(new UserChangeEvent(user, WebHookType.UPDATE_USER).toString());
        return repository.update(user);
    }
}

UserPersistentBaseにユーザー永続化時のバリデーションとWebhookの送信処理が追加された。 UserだけではなくItemの作成クラスについても考えてみる。この場合も同様にWebhookの送信が必要である。

public class ItemCreator {
    public Item createItem(Item item) {
        try {
            HttpClient client = HttpClientFactory.create();
            client.post(System.getProperty("WEBHOOK_URL"), new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        } catch (HttpRequestFailureException e) {
            // webhookのリクエストエラーの場合は通知のみで処理をすすめる
            log.error("failed webhook request", event);
        }        
        return repository.create(item);
    }
}

Webhookの送信処理はユーザーの場合と同様であり、共通化できそうだがそのまま共通化するとこうなる。

public class ItemCreator extends UserPersistentBase {
    public Item createItem(Item item) {
        requestWebhook(new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        return repository.create(item);
    }
}

UserPersistentBaseを継承したItemCreatorが出来てしまった。 さらにItemについても更新処理とバリデーションを追加する。

public class ItemCreator extends UserPersistentBase {
    public Item createItem(Item item) {
        validateItem(item);
        requestWebhook(new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        return repository.create(item);
    }
}

public class ItemUpdater extends UserPersistentBase {
    public Item createItem(Item item) {
        validateItem(item);
        requestWebhook(new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        return repository.create(item);
    }
}

UserPersistentBaseには現在以下の処理がある。

  • ユーザーのバリデーション
  • アイテムのバリデーション
  • Webhookの送信

これはもはやUserPersistentBaseという名前にマッチしない。 PersistentBaseに変更しても良いが今後永続化するリソースが増えたり共通化したい処理が増えるたびにPersistentBaseに処理が追加され続けると、PersistentBaseが肥大化する。 この状況はもはや責務ごとに処理が分割されているとは言いがたい。

責務ごとにクラスを分けつつ継承で共通化した場合、以下のようになる。

public abstract class UserValidator extends WebhookRequester {
    public void validateUser(User user) {
    }
}

public abstract class ItemValidator extends WebhookRequester {
    public void validateUser(User user) {
    }
}

public abstract class WebhookRequester {
    public void requestWebhook(String message) {
    }
}

public class UserCreator extends UserValidator {
    public User createUser(User user) {
        validateUser(user);
        requestWebhook(new UserChangeEvent(user, WebHookType.CREATE_USER).toString());
        return repository.create(user);
    }
}

public class UserUpdater extends UserValidator {
    public User updateUser(User user) {
        validateUser(user);
        requestWebhook(new UserChangeEvent(user, WebHookType.UPDATE_USER).toString());
        return repository.update(user);
    }
}

public class ItemCreator extends ItemValidator {
    public Item createItem(Item item) {
        validateItem(item);
        requestWebhook(new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        return repository.create(item);
    }
}

public class ItemUpdater extends ItemValidator {
    public Item createItem(Item item) {
        validateItem(item);
        requestWebhook(new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        return repository.create(item);
    }
}

UserPersistentBaseがUserValidator、ItemValidator、WebhookRequesterに分割された。 コードは確かに分割されたが、UserValidatorにはWebhookRequesterから継承されたrequestWebhookが生えている。

UserValidatorはバリデーションを行うためのクラスだが、バリデーション以外のことをしている。名前と責務が一致していない。

しかも、UserValidatorはWebhookRequesterに依存したり、overrideによってWebhookRequesterの処理に影響を与える事ができる。 例えば、WebhookRequesterの修正によってバリデーションを壊したり、UserValidatorの修正によってWebhookの処理を壊したりする可能性がでてくる。 つまりそれぞれの処理に手を入れる際、親クラスや子クラスを気にしながら手を入れる必要がある。

さらにこの構造では、共通処理が増えるたびに継承の階層が深くなり、何が何を継承しているのかを判断するのが難しくなってくる。 この階層はJavaの制約を満たすためだけに存在する階層で本質的には不要であり、複雑さを増している。

継承をやめてコンポジションで共通化を行うと以下のようになる。

public class UserValidator {
    public void validateUser(User user) {
    }
}

public class ItemValidator {
    public void validateItem(Item item) {
    }
}

public class WebhookRequester {
    public void requestWebhook(String message) {
    }
}

public class UserCreator {
    UserValidator validator = new UserValidator();
    WebhookRequester webhookRequester = new WebhookRequester();
    public User createUser(User user) {
        validator.validateUser(user);
        webhookRequester.requestWebhook(new UserChangeEvent(user, WebHookType.CREATE_USER).toString());
        return repository.create(user);
    }
}

public class UserUpdater {
    UserValidator validator = new UserValidator();
    WebhookRequester webhookRequester = new WebhookRequester();
    public User updateUser(User user) {
        validator.validateUser(user);
        webhookRequester.requestWebhook(new UserChangeEvent(user, WebHookType.UPDATE_USER).toString());
        return repository.update(user);
    }
}

public class ItemCreator {
    ItemValidator validator = new ItemValidator();
    WebhookRequester webhookRequester = new WebhookRequester();
    public Item createItem(Item item) {
        validator.validateItem(item);
        webhookRequester.requestWebhook(new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        return repository.create(item);
    }
}

public class ItemUpdater {
    ItemValidator validator = new ItemValidator();
    WebhookRequester webhookRequester = new WebhookRequester();
    public Item createItem(Item item) {
        validator.validateItem(item);
        webhookRequester.requestWebhook(new ItemChangeEvent(item, WebHookType.CREATE_ITEM).toString());
        return repository.create(item);
    }
}

UserValidator、ItemValidator、WebhookRequesterは完全に独立していて各クラスの名前と処理の剥離も無い。 完全に独立しているため、WebhookRequesterに手を入れる時はWebhookRequesterのことだけを意識すればよい。

上記のような状態になってしまうことを避けたいという点だけでも継承よりコンポジションを選べる状況なのであれば、コンポジションを選ぶべきだと思う。

継承はいつ使えばいいのか

通化ではなく拡張のために使うものだと思っている。思っています。

まとめ

Javaで継承による共通化を行うと、多重継承ができないため1つの親クラスに全ての共通処理を入れる必要がある。 つまり処理ごとにクラスを分割することができないし、クラスに適切な名前をつけることもできない(なので多くの場合Baseなんとかクラスになってしまう)。

これを避けるために無理やりクラスを分割すると、全く関連の無いクラス同士を親子関係にする必要が出てくる。 こうなると、あるクラスに手を入れる際に関係ないクラスを破壊していないか意識する必要が出てきて辛い。 関係ないクラス同士に親子関係があると、あとから見たときに混乱するので辛い。

Webhookまわりのコード、何?

わかんない

2020年11月にやったこと

このまとめに書いてあるこれからやること以外のことばかりしている

【増補改訂】 財務3表一体理解法

PLとかBSとかなんもわからんという話をしたら会社の人に教えてもらった

これは大変によかった

この辺なんもわからんのをなんとかしようと思って最初簿記の本読んでたんだけど、なんとなく財務諸表の作り方はわかるけどなんでこういう構造になってるのかさっぱりわからん状態になったがそのあたりの疑問がかなり解消された

個人的にこの本は複式簿記の勉強を始めるよりも前に読むとその後の学習がかなり効率よくなる気がする 表をいったりきたりしながら読むのがちょっとつらいので前ページの表と比べなくても差分がわかる構成になっているともっと読みやすい気がした 電書だとそのへんが特に辛いかも でもこれはよかった

Javaパフォーマンス

GCなんもわからんやばいという話をしたら会社の人に教えてもらった

JPAあたりからなんとなく興味がどっかいってしまったがGCの下りはわかりやすくてよかった

わーっと読んだだけで全然触ってないので触る必要がある

その他

クソアプリアドベントカレンダー用にクソアプリを書いたりしていた

これからやること

  • AWS入門する
    • ECS
  • Webエンジニアが知っておきたいインフラの基
  • 入門監視
  • クリーンアーキテクチャ
  • 実践ハイパフォーマンスMySQL 第3版
  • プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
    • 停滞気味
  • Linuxの仕組み
  • Goならわかるシステムプログラミング
  • 英語
  • 中国語
    • Toeic900超えるまで止める予定
  • ここ最近ほとんど業務でプログラミングをしなくなって、調整とかレビューとかばっかしてるがかなり雰囲気でやってるので、マネジメント系の本を読もうとしている
    • なにがいいのかよくわからん
  • Effective Go - The Go Programming Language
  • パタヘネ
  • OAuth2.0のクライアント書く
  • OAuth 2.0/OIDC関連仕様全部読む
  • WebAuthnのドキュメント読む
  • マイクロサービスパターン
  • コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方
  • DBS

2020年9月、10月にやったこと

資格関連ばっかやってた あとは社内で全員 OAuth 2.0 ちゃんと理解しよう勉強会をやってた

ウェブ・セキュリティ基礎試験(徳丸基礎試験)

とった inabajunmr.hatenablog.com

AWS Certified Security Specialty

とった inabajunmr.hatenablog.com 1ヶ月でいけるだろと思って予約したら割とギリギリなかんじでこればっかりになってしまった

Javaパフォーマンス

150ページくらいまでよんだ 読み始めてから資格のほうが以外とやばいことに気づいてそっちにシフトしたので停滞していた GCとかJVMの話面白いのでもうちょっと掘り下げたい気持ちがでてきた とりあえず最後まで読む

英語

9月はシャドーイングしたり単語力の衰えを感じたので

www.amazon.co.jp

をやったりしていた 10月は停滞していた

その他

社内で参加者 OAuth 2.0 全員理解するぞ勉強会をやった

dev.classmethod.jp

参加者に質問してもらったやつでなんでだっけ?ってなったのがあったので整理した

inabajunmr.hatenablog.com

あとはなんかポエムかいてた

inabajunmr.hatenablog.com

inabajunmr.hatenablog.com

これからやること

  • AWS入門する
    • ECS
  • Webエンジニアが知っておきたいインフラの基
  • 入門監視
  • クリーンアーキテクチャ
  • Javaパフォーマンス
  • 実践ハイパフォーマンスMySQL 第3版
  • プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
    • 停滞気味
  • Linuxの仕組み
  • Goならわかるシステムプログラミング
  • 英語
  • 中国語
    • Toeic900超えるまで止める予定
  • ここ最近ほとんど業務でプログラミングをしなくなって、調整とかレビューとかばっかしてるがかなり雰囲気でやってるので、マネジメント系の本を読もうとしている
    • なにがいいのかよくわからん
  • Effective Go - The Go Programming Language
  • パタヘネ
  • OAuth2.0のクライアント書く
  • OAuth 2.0/OIDC関連仕様全部読む
  • WebAuthnのドキュメント読む
  • マイクロサービスパターン
  • 財務3表立体理解法