Hama Blog

Hama Blog

主にtech関連の記録

単一責任原則(SRP)について考えてみた

概要

単一責任原則(Single Responsibility Principle, SRP)についての理解がいまいちだったので、自分なりに勉強した結果、考えたことをまとめてみた。

※ この記事ではサンプルコードを使うなどしておらず、具体的な解決方法については触れていません🙏

最初に結論

  • SOLID原則のなかで、パット見、一番簡単そうに見えるが、実は一番奥が深いのかもしれない。

  • 何についての責任なのかが大事である。

    • 機能(目的)について、もしくはクラスの変更理由について、の2通りに分けて考える必要がありそうと思った。

そもそも単一責任原則(SRP)とは

Wikipediaの説明を拝借すると、微妙に違う2つの説明がされているように思った。

単一責任の原則 - Wikipedia

以下は、単一の機能についての責任ということであった。

単一責任の原則 (たんいつせきにんのげんそく、英: single-responsibility principle) は、プログラミングに関する原則であり、モジュール、クラスまたは関数は、単一の機能について責任を持ち、その機能をカプセル化するべきであるという原則である。モジュール、クラスまたは関数が提供するサービスは、その責任と一致している必要がある[1]

以下は、クラスの変更理由が一つであり、それがアクターについてのものだとなっている。

※ アクターとは、そのシステムを利用するユーザーのことだと思う。

単一責任の原則は、ロバート・C・マーティン(英語版)によって定義された。この原則について、彼は、「クラスを変更する理由は、ひとつだけであるべきである」[1] と表し、「変更する理由」に関して、「この原則は、人についてのものである」と述べ[2]、アクターについてのものであると補足した[3]

上記の説明で思ったこと

おそらく、多くの人が単一責任の原則について取り上げていることとしては、1つ目の説明に出てくる「単一の機能」という部分なのかなと思った。

しかも、この「機能」というものを、おそらく振る舞いとか処理内容とかという具体な部分に絞っていそうかなとも思った。

たしかにそこだけ考えると楽に思えるのだが、そこだけに着目しすぎると、極端ではあるが、一つのクラスには一つの振る舞い、にするというような勘違いも起こりえそう。
実際にそう捉えられかねない内容が書かれた記事もあった。

この「機能」について個人的に思ったこととしては、振る舞いとか処理内容とかということではなく、もう少し抽象化した「目的」ということだと考えてみると腹落ちするような気がした。

その目的を見出し、どこに落とし所を見つけるかという作業も難しいことではあるが、単一責任なクラスを設計するうえではそうやって考えるのがよさそうなのかもしれない。

また、2つ目の説明とごっちゃにして考えるのではなく、それはそれで考えたほうがよさそうとも思った。

というか、アクターを基準にクラスを分けるのは現実的にはなかなか難しそうな気がするので、まずは1つ目の説明の内容をコードに反映するだけでも十分なのかもしれない。
(まだまだそこの概念についての理解が足りていないというのもあったり、特に自分の場合はRailsを使っているというのもあるが、、)

とにかく、単一責任の原則は奥が深いことがわかった。

あと、細かいところではあるが、1つ目の説明は関数にも触れているが、2つ目の説明では関数には触れていないのも気になったりしたので、そういったことも含め、もっと勉強してみようと思った。

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技 を読むのがいいのかな?

余談

(その1)記事にしようと思った背景

先日、Object-Oriented Conference 2024というカンファレンスに参加してきたのだが、度々SOLID原則についての話題があがっていた。

ooc.dev

SOLID原則自体はもちろん聞いたことはあるけど、説明できるほど理解はしていないし、普段からきちんと意識できているわけでもなかった。

そして、いい機会なのでSOLID原則のそれぞれについて、ちゃんと勉強しようという気にさせてもらえた。
(カンファレンスから少し日が経過してしまったが、、)

あと、単純にSOLID原則のそれぞれの概要だけだとつまらないので、一つ一つの原則について少しだけ丁寧目に理解をしていきたかったので、とりあえずS: 単一責任原則から勉強して、記事にしてみようと思った。
(とはいいつつ、そんなに深堀れているわけではないが。。)

(その2)Railsのモデルに単一責任の原則を適用するのって難しそう

Wikipediaの2つ目の説明にあるような、変更理由がひとつである部分について、サンプルコードを書いてみようかと思ったが、自分には難しく書けなかった。

特に、自分は普段Railsを触っているので、最初はRailsActiveRecordのモデルを例にサンプルコードを書いてみようと思ったが、なおさら難しくて諦めてしまった。

STIを使うべきなのか、テーブルレベルで分割すべきなのか、それとも別のアプローチで解決すべきなのかがわからなかった。。

DDDやクリーンアーキテクチャについて学んでいけばヒントがあるのかもしれないので、それはそれで追々勉強してみようと思う。

書籍「具体と抽象」を読んだ感想

概要

具体と抽象という書籍を読んだのだが、とても学びがあり、他の人にもすごくおすすめしたいなと思ったので、個別の記事として感想を書いてみた。

全体的な感想

抽象化のメリット、デメリット、使い方、具体とセットで考える、など、すごく学びになることが多かった。

現代のようなインターネット社会で、様々な情報が飛び交うからこそ、そのなかで抽象的に考えられ、本質を見抜ける力を身につけるためにも、とても有意義な書籍になっていると思う。

また、自分はソフトウェアエンジニアをしているのだが、日常生活や仕事でのコミュニケーションだけでなく、プログラミングの文脈でも大事な思考なので、そういう面でもこの本に出会えてよかった。

どういう人におすすめな内容か

  • 抽象化について気になっている人
  • 他人とのコミュニケーションにおいて、話が噛み合わないことがあると感じたことがあるが、もやもやしたままの人
  • 固定観念に縛られやすいと自覚がある人

特に気になった章の感想

全部の章で学びがあったのだが、そのなかでも特に気になった部分をもとに感想を書いてみた。

序章 抽象化なくして生きられない

「具体=わかりやすい」「抽象=わかりにくい」というのが一般的に認知されている
...(中略)...
具体=善、抽象=悪という印象はとんでもなく大きな誤解です。

コンテキストにもよるが、恥ずかしながら僕も上記のように考えてしまっていた時期があった。

このことに気づけるだけでも、この本を読む価値があるように思える。

第1章 数と言葉

たとえば、「鮪」は「魚」の具体でもあり、個別の鮪の抽象でもあるというように、あくまでも相対的にどうみるかで具体か抽象かが決まる

当たり前のことだけど、たしかに!と思った。

なので、他人とコミュニケーションを取るときに、自分が抽象化して話していると思っていても(そう勘違いしていても)、実は抽象の目線(レベル)が合っていない、というようなことが起こり得るのかもしれない。

例えば、相手が「クロマグロが、〜。キハダマグロが、〜。」というようなことを話していて、自分は「(あ〜、鮪のことね)」と思っていても、実は魚全体のことを話していた、とかがあるのかもしれない、と思った。

第2章 デフォルメ

抽象化とは一言で表現すれば、「枝葉を切り捨てて幹を見ること」といえます。

第1章にあったように、抽象は相対的にどうみるかで考えるということなので、言い換えると、その場その場での本質を捉えることが大事なのかなと思った。

自分は細かいことが気になる性格なので、どうしても枝葉を見てしまいがちなのだが、抽象化思考を鍛えるという面では、幹を見れるように、いい意味で細かいことを気にしないようにしていきたい。

第4章 法則とパターン認識

抽象化の最大のメリットとして、

複数のものを共通の特徴を以てグルーピングして「同じ」と見なすことで、一つの事象における学びを他の場面でも適用することが可能になることです。つまり「一を聞いて十を知る」(実際には、十どころか百万でも可能)です。

とあった。

たしかに、知らず知らずのうちにやっていることもあるとは思うけど、それこそが抽象化の最大のメリットであり、武器なんだなとはっきりと知ることができた。

プログラミングの文脈だと、デザインパターンとかが、そのメリットを活かした手法なのかもしれない。

第8章 本質

世の「永遠の議論」の大部分は、「どのレベルの話をしているのか」という視点が抜け落ちたままで進むため、永遠に噛み合わないことが多いのです。

これは、第1章で出てきたように、相対的にどう見るかで具体と抽象が決まる、ということだとも思った。

X(旧: Twitter)とかで議論していることの大半って、まさにそういうことだよなあ、とも思える。

僕はそういう争いごとが嫌いなので、そういうのを見ると、ただただ嫌な気持ちになることが多かったのだが、この本のおかげで、そういうのを見ても「あ、抽象レベルがずれているな」と気づけるようになったり、その議論の幹は何だろうと考えるための抽象化思考を鍛えるトレーニングにもなったりするので、別目線で見るとそれはそれで面白いコンテンツになり得るんだなと思えるようになった。

具体レベルでしか相手の言うことをとらえていないと、少しでも言うことが変わっただけで、「心変わり」ととらえてしまいます。

この本の例でも出てくるが、上司と部下の関係がまさにそうなんだということがわかる。

しかし、これはどうしても経験の差だったり、ポジションの差だったり、というところで、部下(具体レベルでしか捉えられない人)が、一律に悪い、というわけでもないと思った。(別に、この本にはそういう人が一律に悪い、と書かれているわけではない)

上司(抽象レベルで考えられる人)が、部下にそう思わせないようにするための努力をするなどの、責務は負うべきだとも考えられる。

第11章 量と質

抽象の世界での「単純化」は、短絡的思考とはまったく対極のもので、対象が複雑であればあるほどよく、それをいかにシンプルにするか、まさに具体と抽象とのギャップの大きさ」を追求することです。

これはまさにプログラミングの文脈で、自分が特に身につけていきたいことだと思った。

難しいことをシンプルに考える、実装する、といったことができる人は、設計力が特に強い印象がある。

第15章 階層

抽象化して話せる人は、「要するに何なのか?」をまとめて話すことができます。

これもとても大事で、身につけたいこと。

僕はなるべく「要するに何なのか?」を意識することは多いけど、いざ話すとなるとそれはそれでとても難しかったりするので、要約したものを言葉にして出す、という能力はまた別なのかなとも思ったりした。

あと、抽象化思考ができる人とそうでない人を簡単に見分けるには、この方法が一番判断しやすいのかなと思った。

第16章 バイアス

ルールや理論、法則は、大抵の場合は具体的に起こっている事象の「後追い」の知識だったはずです。ところが、一度固定化された抽象度の高い知識(ルールや法則等)は固定観念となって人間の前に立ちはだかり、むしろそれに合わない現実のほうが間違いで、後付けだったはずの理論やルールに現実を合わせようとするのは完全な本末転倒といえます。

いわゆる、ブラック校則とかもそうなのかなと思った。

そういった凝り固まった考えを自分ももし持ってしまった場合には、改めて抽象化思考をしなければいけない。

第18章 マジックミラー

具体の世界と抽象の世界は、いってみればマジックミラーで隔てられているようなものです。本書のピラミッドで言うと、上(抽象側)の世界が見えている人には下(具体側)の世界は見えるが、具体レベルしか見えない人には上(抽象側)は見えないということです。

このピラミッドを下から上に行くのって、本当に難しいよなあと思う。

自分が下にいることがわかっていて、上の世界が存在するのもわかっているけど、その見えていない部分を見ようとすることが難しく、見れないことがむず痒いかは、割と経験したりする。

この場合は、下の人が上を見えていないことを上の人に適切に伝え、上の人は下の人が上に行くのを手伝ってあげるのが、理想なんだよなあと思う。(難しいことだけど)

自分には理解できないレベルの抽象を前にすると、私たちは「わからない」と批判の対象にしてしまうのです。

プログミングの文脈だと、概念のような抽象的なことが多いので、よくあることのように思った。

そう考えると、自分の場合は(たぶん他の人も)それに関する具体的なことをいくつか学んでから、なんとなく抽象化していき、理解する手法を取ることが多いので、そういった方法でマジックミラーの向こう側に行ける方法を知らないうちに試していたんだなあと思う。

第19章 一方通行

前章で、「具体抽象」のピラミッドには、片側からしか見えない「マジックミラー」があると書きましたが、このピラミッドには、別の「一方通行」があります。 「一度上ったら簡単には下りられない」ということです。
...(中略)...
要は、状況と相手に応じてちょうどよいちょうどよい抽象度でコミュニケーションすることが重要です。「抽象的だからわかりにくい」ということがクローズアップされがちですが、じつは「具体的すぎてわかりにくい」こともあるのです。

「具体的すぎてわかりにくい」というのは、なるほど!と思った。

たしかに、抽象化されたもの=シンプルなもの、ということでもあるから、わざわざ具体化されたもの=複雑なもの、でコミュニケーションを取ったり、考えたりするのは煩わしいという側面があるように思った。
(ハイコンテクストか、ローコンテクストか、みたいなこともそうだと思う)

第20章 共通と相違

高い抽象レベルの視点を持っている人ほど、一見異なる事象が「同じ」に見え、抽象度が低い視点の人ほどすべてが「違って」見えます。したがって抽象化して考えるためにはまず、「共通点はないか」と考えてみることが重要です。当然ここでいう共通点は「抽象度の高い共通点」です。
...(中略)...
表面的な類似性でなく、関係性や構造レベルでの共通点と相違点に目を向けること、そして「要するに何が大事なのか」という本質レベルで共通点や相違点に目を向けること、それができれば抽象化というツールを最大限に生かすことができます。

この章の別の箇所にも書かれているのだが、これらの力をつけていくためには、やはりいろんな経験や、読書や映画鑑賞などの疑似経験を積み、自分の裾野を広げることが大事なので、あまり好き嫌いせずにものごとに取り組んでいける姿勢を持つことが抽象化思考を鍛えるためにも大きなことなんだなと思う。

さいごに

ほんとに全部の章がとても学びになるくらい、とても素晴らしい書籍でした。

いつも本を読むときは、気になった箇所をメモするのですが、ビジネス書の中ではダントツでメモした量が多かったです。

個人的テック系情報収集方法まとめ

概要

僕はソフトウェアエンジニアを職業としていることもあり、普段からテック系(IT系)の情報収集をしている。

たまに情報収集の方法をまとめている記事を見ることがあり、面白そうだなあと思ったので自分もまとめてみた。

能動的に収集しているものもあれば、受動的に収集できるようにしているものもあるため、それぞれ紹介していきたい。

能動的に収集している方法

X (旧: Twitter)

本当にこれがないと、個人的にはエンジニア人生を損してしまうレベルだと思っている。

毎日のようにいろんな新しい情報を仕入れることができてありがたい。

僕の観測範囲に存在するエンジニアは、仕事に関係する技術的なことや、日常でも役立つ技術的なことを呟いてくれることが多いので、けっこうそれが勉強になっている。

フォローさせてもらっている方が多すぎるので個別の紹介はできないが、以下の方をフォローさせていただくことが多い。

  • 自分が携わっていたり、興味のある技術領域での著名人

  • たまたま見つけ、参考になった技術記事などを書いた人 (だいたいXでも有用なことを呟いてくれている方が多い)

  • フォローさせてもらっている方がリポストした内容に自分が興味を持ったときに、それをポストした人

ポッドキャスト

(能動的に収集しているものに分類したが、半受動的とも言えるかもしれない)

今までこのような音声コンテンツに興味を持つことはなかったが、聴き始めてみたら面白いものばかりで、今では毎日のように聴いている。

ちなみに、自分が現在聴いているものと、今後聴いてみたいのは以下。
(リンクにしていますが、リンク先が間違っていたらすみません🙇‍♂️)

拝聴中 (名前順)

今後拝聴したい (名前順)

技術書

Software Designを定期購読しているのと、不定期でいくつか技術書を買っているので、年に12冊 + 数冊の技術書を読んでいるという感じ。

主に設計関連のことを学びたいときに技術書を買うことが多いかもしれない。

YouTube

たまに観る程度。

connpassとか経由で開催されたイベントをアップロードしてくれていたりするので、それをたまに観ることがある。

受動的に収集している方法

RSS

いろんなRSSリーダーがあるが、個人的にはSlackが一番楽なので、Slackで受け取れるようにしている。

企業テックブログRSS

yamadashy.github.io

各企業のテックブログのRSSフィードをSlackで受け取れるようになる。

これは本当に最高のサービス。
(開発者様ありがとうございます!)

もし追加したい企業があれば、https://github.com/yamadashy/tech-blog-rss-feed でissueかPRを出せば追加していただけるみたい。

自分の専門外、興味の範囲外のものも多々あるが、Slack上は以下のような概要レベルのものが流れてくるだけなので、流し読みして気になる記事だけ該当ページに行って読んでいる。

Qiita

気になったユーザーや組織のRSSをSlackで受け取れる。

僕は今のところ、運営者ギルドさんのメンバーが投稿された記事を受け取れるようにしている。 qiita.com

メルマガ

Qiita

Qiitaが、とかではなく、世の中のメルマガって基本的に不要なものが多い印象だけど、このメルマガはいいねが多かった投稿ベスト20などを週次で配信してくれて便利。

僕は、このランキングから気になった記事を見に行くようにしている。

さいごに

他のエンジニアもだいたいこんなものなのかなあ、と気になるところ。

いろいろな方法で情報収集しているが、半分趣味なのであまり無理をして収集している感は感じていない。

PullRequestのレビュー時に主に気をつけていること(レビュアー/レビューイとして)

概要

3年くらいGitHubのPullRequestでコードレビューをしている経験があるため、いつも個人的に気にしていることをまとめようと思った。
(技術的な面は少なめで、主にマナーだったり気持ち的な面が多いです)

レビュアー、レビューイの両方の立場で気をつけていることをまとめてみた。

レビュアーとして気をつけていること

感謝の気持ちを持つ(できれば伝える)

レビュアーからすると、できあがったものを確認するだけになることが多いので忘れがちだが、PRはレビューイが時間をとって対応してくれたものである。

また、レビューイが取り組んでくれたPRは、もしかしたら自分が対応しないといけなかった可能性は少なからずあるので、代わりに対応してくれてありがとう、という気持ちを持つようにしている。

ただ、それだけだと自分の心の中だけになってしまうので、「対応ありがとうございました!LGTM!」みたいな感じで、Approve時のコメントで伝えたりするようにしている。

コメントするときは冷たい表現にしない

よく言われることだし、相手との関係性とか、効率面とか、人によって考え方の幅が大きく違いそうなところではあるが、やっぱりテキストベースだとどうしても冷たい表現になりがちなので、レビューイからするとちょっと攻撃的というか怒られているように思えてしまう場面がある。

例えば、前後の文脈にもよるが、「〜に変更してください!」とかとコメントするより、「〜に変更してもらえると🙏」などのように柔らかい表現、プラス、文脈によっては絵文字を使うと、少なくとも僕は傷つかないのでいいと思っているw

意図が読み取れない部分はそのままにせず質問する

よくわからないまま Approve しても、理解することを諦めた(妥協する)ことになり、自分のためにも相手のためにもならないので。

それに、意図が読み取れないということは、よくない実装の可能性もあるので。

質問するほどではないけれど気になったところはコメントする

上記のように意図が読み取れない、とまではいかないが、なんか気になったり、理解に時間がかかってしまった部分は一応コメントを残しておく。

自分が「こう理解していますよ」というような証明にもなるし、今後誰かがgit blameでコミットを遡ってそのPRに辿り着いたときに、「なるほど、そういうことか」となったりすることもあるので。
(それなら実装を修正してもらったり、コードにコメントを残してもらうべきということもあるが、、、)

指摘するときは意図も伝える

「ここは◯◯を使ってください」というコメントだけだと、ハイコンテクストなコメントになりかねず、レビューイからすると「なぜ?」となることもある。

その意図を把握するのにコメントが往復してしまったり、レビューイが頭をフル回転させ、その意図を推測しないといけなかったりもする。

なので、ちゃんと指摘の意図も付け足して、レビューイが感じる「なぜ?」を極力減らす。

なるべくサンプルコードを添える

上記の+αで、「◯◯を使ったほうが、〜の理由でよさそうです!」みたいな指摘コメントだけよりかは、「例えばこのような書き方です!」みたいに サンプルコードも一緒に書いてあげると、レビューイとしても「◯◯を使う?どういう書き方をすればいいんだ?こういうこと?」みたいなことになりにくいので。

なる早で確認する

自分の現在のタスク優先度や、そもそも急ぎではないPRだったら、、、とかにもよるが、基本的には依頼が来たその日中か翌営業日中には確認する。

ちょっと時間が経ってしまうと、レビュアーが忘れているのかな?とか、何かよくない実装があってそれの確認に時間がかかっているのかな?、とかと思って、レビューイがソワソワしてしまうため。

もし時間がかかりそうなら、理由をつけてその旨を連絡したり、「一旦ここまでは確認しました!残りはこれから確認します!」など、途中経過を伝えるのでもよいと思う。

レビューイとして気をつけていること

レビューしてもらったらお礼を言う

たまに、Approveをもらっても、何のコメントもなく、そのままマージする人がいる。

基本的には、「ありがとうございます」とコメントしたほうが個人的にはいいなと思う。

逆に、「ありがとうございます」だけの通知がうっとおしいと思う人もいるので、なんとも言えない部分ではあるが。

せめて「LGTM!」とコメントされていたりしたら、そこに絵文字マークをつけるだけでもしたほうがいいように思う。

GitHub上でもセルフレビューは必ずする

コミット時にdiffを確認していた場合でも、実際にGitHubでPRを作ってFiles changedを見ると、「何だこれ」と思ったり、「ここわかりにくいなあ」と自分で気づけることが往々にしてある。(少なくとも僕はある)

なので、必ずセルフレビューをして、逆に自分がレビュアーならここツッコむかもと思うところをなくしてからレビュー依頼したほうが、レビュアーにとって「これは何だ?」と思う場面がその分減るはず。

自分の能力不足等により妥協してしまった部分はコメントで意思表示する

事前に実装の相談とかできれば別だが、何かしらの都合でどうも納得いかない実装をしてしまったときに、その点に何も触れないままレビュー依頼しても、高確率でどうせ指摘が入るので。

あとは、コードベースで実装の相談ができるので。

自分でもわかりにくいと思った部分はコメントで説明する

上記と似ているが、自分がわかりにくいと思っているということは、レビュアーはもっとわかりにくいと思っているはずなので。

参考資料は該当箇所にコメントする

とある資料に基づいて実装したり、別の箇所で参考にしたコードがある場合などは、今回修正したコードの該当箇所にリンクを貼ったり、引用をつけるなどして、レビュアーに資料を探させなくて済むようにする。

できるだけ、スクショや動画を貼って視認性をあげる

画面の追加やデザイン変更などは、コードだけ見てもちゃんと実装できているのか、いまいちわからないので。

もちろん時間があれば、レビュアーがローカルとか実際に動かせる環境でも確認してくれることもあるが、スクショとかを適切に貼っていると、レビュアーの動作確認時の負担も減ると思うので。

適切な粒度になるようにPRを分割する

実装機能のボリュームとかにもよるが、レビュアーの(主に脳の)負担にならないように、できるだけPRを小さくする。

逆に、全体像が見えにくくもなるので、どの粒度にすべきかは個人的にはけっこう迷うところではあるが。。

さいごに

もちろん上記以外にも、状況や実装内容によっていろいろと気にしていることはありますが、ざっと思い当たるところだとこんな感じでした。

僕自身もですが、ついつい疎かにしがちな部分なのと、若干理想論的な部分も含みますが、できるだけレビュアー/レビューイがお互いにストレス少なくレビューに臨めるといいなと思っています。

キーボードに無関心だった人がKeychron Q11を買ってみた

概要

自分はソフトウェアエンジニアをやっているため毎日PCを触っているのだが、少し前まではキーボードには無関心でMacBookの内蔵キーボード (US配列) をそのまま使っているだけだった。

だが、諸事情で分割キーボードが必要になり、Keychron Q11を買ってみたので、せっかくなので分割キーボード初心者なりにレビュー記事を書いてみた。

↓こんな感じに使っている


ちなみに、パームレストは木製なのだが、手首に当たる感触が気持ち悪かったので、100均のフェルト生地を貼り付けたら、偶然キーボードに合った色合いになった。

キーボード詳細

買ってみてよかったこと、悪かったこと

よかったこと

  • 見た目がかっこいい。

  • 打鍵感が気持ちいい。

    今までキーボードの打鍵感なんてどうでもいいと思っていたが、初めてメカニカルキーボードを使ってみて、「ああ、、これが打鍵感というやつか」という感じになった。
    ※ 打鍵音もそれなりにするので、オフィスで仕事する場合だと気を使うかもしれないです。

  • MacのUS配列のキーボードに配列がかなり近い。

    ちなみに、左下のFn, Control, Option, Commandキーは微妙に違う(他も微妙に違うところはある)が、Keychron Q11の場合はVIA(https://usevia.app/) というブラウザで使えるソフトを使えば、キーマップを変えられるし、キーキャップ自体も4つのキーはそれぞれのサイズが同じで簡単に取り替えられるので、4つのキーはMacのUS配列と同じにすることができる。

    デフォルト
    (公式画像から拝借)
    キーキャップ変更後
  • キーの打ち方が矯正された。

    自分はこれまで本来この指で打つべきと言われているキーを別の指でタイピングすることが多かったのだが、分割したことでそこらへんが矯正された。
    特にYとHはついつい左手で打っていたのだが、右手に矯正できた。

  • キーボードに少し詳しくなった。

    Keychron Q11を購入したから、というわけではないが、購入に至るまでの検討段階などで色々調べていたら少し知識が増えた。

悪かったこと

  • スイッチングコストがめっちゃ高い。

    Keychron Q11はMacのUS配列のキーボードと配列がかなり似ていて移行はしやすそうだと思ったのだが、分割であることやストロークの深さの違いなどで、かなりタイポしてしまう。
    慣れてしまえば気にはならなそう。

    (追記) 3週間くらいで慣れました!もう他のキーボードには乗り換えられないくらい最高になりました!

  • あやうくキーボードの沼にハマりそうだった。

    今回、茶軸にしたのだが、赤軸も使ってみたい、や他のモデルや他のメーカーのも欲しくなったりしてしまった。(なんとかこらえた)

なぜ分割キーボードなのか

そもそもなぜキーボードに無関心だったのに、いきなりスイッチングコストが高いと言われる分割キーボードにしたのか理由・経緯を書きたい。

理由・経緯

なんか短く書けず、気持ちを吐き出していたら長くなりましたw

まあ一言でいうと、ケンジントンのSlimBladeと組み合わせて使いたかったから。(結局SlimBladeは自分には合わず、MX ERGOを使うことにしたw)

↓は詳細ですが、長いので興味がありましたら読んでいただければと🙏

  • MacBookの内蔵キーボードが別に嫌いになったわけじゃない。

  • 内蔵トラックパッドが嫌になっただけ。

  • トラックパッドは、手汗などで指が湿っているときに、急激に使い勝手が悪くなる。

  • ずっと我慢してきたが、そろそろ限界を迎えた。

  • じゃあ、キーボードに不満はないから、マウスを買ってみるかと思った。

  • トラックパッドの操作に慣れている身からすると、普通のマウスは使い勝手がよくないので、トラックボールマウスにしようと思った。

  • トラックボールでよさそうだと思ったのは以下あたり。

  • 上記のうち、トラックパッドの操作に一番近しい(好きな指でカーソル移動、クリックできる)と思うものは、ケンジントンのSlimBladeだった。

  • もしそれを買うとなると、どう置こうかと考えたときに、自分はずっとMacBookの操作に慣れていたため、キーボードの手前にマウスがあるという配置にしたかった。

  • そうすると、MacBookの内蔵キーボードとの併用は難しそうだと考えた。

  • じゃあ、キーボードも買っちゃえとなったが、自分の腕の伸ばし具合とかを考えたときに今使っているデスクの広さだと、普通のキーボードとSlimBladeの縦置きの組み合わせでは、ちょっと無理そうだった。

  • なので、分割キーボードにして、その間にSlimBladeを挟むことで解決できそうと思って、分割キーボードを購入することを決意した。

購入先

Keychronの公式ストアで購入。

Keychron Q11 QMK Custom Mechanical Keyboard – Keychron | Mechanical Keyboards for Mac, Windows and Android

(あまりよくわかっていないが)日本版ページ?(日本語、円表記)でも買えるみたいだったが、購入したときは存在に気づかず上記の英語版ページ(英語、$表記)で購入した。

Keychron Q11 QMK カスタムメカニカルキーボード(US ANSI 配列) – Keychron Japan

が、英語版ページで$で買ったほうが、送料とか含めても結果的に1,000円くらい安かった気がする。(自分の気のせいの可能性もあります🙇‍♂️)

日本版ページで買うとどうなるかはわからないが、英語版ページで買ったらDHLで発送された。

関税はかからなかったと思う。(正直よくわかっていませんが、後から別途請求とかはなかったです)

DHLで困ったこと

DHLを使ったのが初めてだったので不安だったが、結果的には購入してから3日くらいで届いた。(中国からの発送だった)

ただ、当初の配達予定日は1日過ぎていた。

原因としては、途中Naritaで処理保留中となったまま半日以上動きがなかったため、と思われる。

不安になって、DHL 処理保留中 とかでググってみると、いろんな理由があるらしく一概には原因がわからないため、問い合わせてみるのが吉だということがわかったので問い合わせてみた。

余談だが、フリーダイヤルにかけ、自動音声に従い、問い合わせ内容にあった番号を押していくと、オペレーターに繋げてくれるパターンがあったのだが、そのパターンがめっちゃわかりづらく何度も自動音声だけで終了する結末を迎えた。
具体的なパターンは忘れたが、どこかの質問でその他の問い合わせを選択すると、おそらくオペレーターに繋がると思う。

で、オペレーターに処理保留中の原因を確認すると、自分の説明だとうまく伝えられなかったのか、処理保留中の原因は教えてくれず、既に近くの配達営業所?にいます、とのことだった。
(自分は東京在住ではないのに、なぜかDHLの追跡ページを見るとNarita(成田空港)になっていた)
(オペレーターの方は丁寧な感じの人でした)

「それじゃあ今日届く感じですか?」と聞いたら、「今日の希望ですか?、それでしたら今日届けるように指示を出しておきます」、とのことで、よくわからない感じになったが、その日に届いてよかった。
(問い合わせしなかったらいつ配達されていたんだろう?w)

まあ結果的に、慣れない海外サイトでの購入や初めてのDHLからの配送というものを経験でき、次からは不安を少なくできる糧になったのでよかった。

Railsで複数カラムの組み合わせでuniquenessバリデーションを指定するときの指定順について

概要

Railsuniqueness 1 バリデーションは単一のカラムだけでなく、複数のカラムに対しても、scope オプションを使うことで validates :book_id, uniqueness: { scope: :user_id } のように設定できるのだが、これってテーブルにも複合ユニーク制約を設定している場合は順番を同じにしないとインデックスが効かないよね?と疑問に思ったので調べてみた。

記事執筆時の環境

結論

少なくとも上記の環境・バージョンでは、
テーブルに実際に設定した複合ユニーク制約と順番を揃えなくてもよさそう。

例えば、以下のような場合は validates :book_id, uniqueness: { scope: :user_id } でも validates :user_id, uniqueness: { scope: :book_id } でもインデックスが効く。

  • BookReview という書籍に対するレビューを投稿できるモデルがある。
  • 書籍1つにつき1人1レビューまでという制約をつけたい。
  • book_reviews テーブルには book_id, user_id の順番で複合ユニーク制約を設定している。
class BookReview < ApplicationRecord
  belongs_to :book
  belongs_to :user

  validates :book_id, uniqueness: { scope: :user_id }
  # validates :user_id, uniqueness: { scope: :book_id } でもインデックスが効く
end

調査内容

uniqueness バリデーションの実行ログ確認

まず、上記の例をもとに、uniqueness バリデーションで2パターンの指定をしたときの実行ログを確認してみた。

(前提) BookReview に既に book_id = 1, user_id = 1 のレコードが存在する。

  • validates :book_id, uniqueness: { scope: :user_id } のとき

      [7] pry(main)> BookReview.create!(book_id: 1, user_id: 1, title: 'テスト')
        TRANSACTION (0.7ms)  BEGIN
        Book Load (0.3ms)  SELECT "books".* FROM "books" WHERE "books"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
        User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
        BookReview Exists? (0.5ms)  SELECT 1 AS one FROM "book_reviews" WHERE "book_reviews"."book_id" = $1 AND "book_reviews"."user_id" = $2 LIMIT $3  [["book_id", 1], ["user_id", 1], ["LIMIT", 1]]
        TRANSACTION (0.3ms)  ROLLBACK
    
  • validates :user_id, uniqueness: { scope: :book_id } のとき

      [9] pry(main)> BookReview.create!(book_id: 1, user_id: 1, title: 'テスト')
        TRANSACTION (0.5ms)  BEGIN
        Book Load (0.4ms)  SELECT "books".* FROM "books" WHERE "books"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
        User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
        BookReview Exists? (0.5ms)  SELECT 1 AS one FROM "book_reviews" WHERE "book_reviews"."user_id" = $1 AND "book_reviews"."book_id" = $2 LIMIT $3  [["user_id", 1], ["book_id", 1], ["LIMIT", 1]]
        TRANSACTION (0.3ms)  ROLLBACK
    

BookReview Exists? を確認すると、

前者は WHERE "book_reviews"."book_id" = $1 AND "book_reviews"."user_id" = $2
後者は WHERE "book_reviews"."user_id" = $1 AND "book_reviews"."book_id" = $2

となっており、validates に指定した順番のとおりに WHERE 句が指定されている。

SQL 実行計画の確認

book_reviews テーブルに対して WHERE 句に book_id と user_id を指定して、SELECT 文を EXPLAIN してみた。

PostgreSQL

  • book_id, user_id の指定順のとき

  • user_id, book_id の指定順のとき

どちらも Index Scan となっている⁉️

MySQL

  • book_id, user_id の指定順のとき

  • user_id, book_id の指定順のとき

こちらも、両方とも type が const なので、ユニークインデックスがちゃんと効いていた。

所感

てっきり、複合キーの指定順に WHERE 句でも順番を指定しないとインデックスが効かないものだと思っていたので驚いた。
(DB の知識は浅いのでもうちょっとちゃんと調べたいところではある)

なので、少なくとも今回の調査環境では、指定順を気にしなくてよさそうではあったのだが、個人的には Railsuniqueness バリデーションでは以下の方針で書こうと思った。

  • できる限り、テーブルの複合ユニーク制約の指定順と同じ順番にする。

    今回の調査でわかった仕様を知らない人が見ても、同じような疑問を抱かないために。

  • バリデーションメッセージを工夫したい場合は、テーブルの複合ユニーク制約の指定順と異なる順番にしてもよい。

    Rails の仕様的に、カラム名がバリデーションメッセージの主語になるはずなので、今回の例だと主語を「ユーザー」にしたい場合は以下のように指定する。

      validates :user_id, uniqueness: { scope: :book_id, message: 'は1つの書籍につき1つのレビューしか投稿できません' }
      # 「ユーザーは1つの書籍につき1つのレビューしか投稿できません」というメッセージになる。
    

    (上記だとバリデーションメッセージとしては少し不自然な気がするのと、普通に validates :book_id, uniqueness: { scope: :user_id, message: '1つにつき、1人1つまでしか投稿できません' } として、「書籍1つにつき、1人1つまでしか投稿できません」というメッセージのほうが自然な文章な気がするので、上記は例が悪いが。。)

connpassで自分が所属しているグループのイベントをGASを使ってSlackに定期的に通知する

概要

connpass でいくつかのグループに所属しているのだが、グループによっては毎日のようにイベントを開催していて、開催通知的なメールが多いと、そんなに通知いらないな、、、と思うことがある。

かといって通知をなくすのも嫌なので、直近1週間にあるイベントを週に1回 Slack に通知できるようにしてみた。

今回は Google Apps Script (GAS) と connpass API 1 を使ってみた。

※ connpass の API は会員登録不要で無料で使えます。

(2024/4/14追記)

※ 2024/5/23をもって、イベントサーチAPIの無料提供を廃止されるようです。

また、基本的には法人向けらしいです。個人でももしかしたら使えるかもしれないですが、2024/4/14時点で個人向けの料金は公表されていませんでした。


API を利用する場合は、リファレンスの注意書きにもあるように、robots.txt を遵守するよう注意してください。

> ※過度な検索やクローリングに対しては、アクセス制限を施す可能性があります。robots.txt を遵守してください。

成果物

こんな感じで、グループごとのイベントにまとめて通知される。

コードはこちら

設定手順

※ 今回は具体的なコードは紹介しません。成果物に記載したものを参照してください。

新しく Google Apps Script を作成する。(clasp でもいいと思う)

作成したばかりだと以下の関数が用意されているため、成果物に記載したコードに置き換える。

Slack の Webhook URLと connpass の所属グループの series_id が必要なので、それぞれスクリプトプロパティに設定する。

上記を設定すると、PropertiesService.getScriptProperties().getProperty('SLACK_WEBHOOK_URL'); みたいに GAS のコード上で取得することができる。

  • Slack の Webhook URL は、こちらから作成できる。

  • (他にも方法があるかもしれないが) connpass の所属グループの series_id の取得は少し面倒で、グループページの HTML ソースから series で検索して取得した。

    • たとえば Findy さんの場合だと、グループページ (https://findy.connpass.com) にアクセスして、HTML ソースを見ると以下のように series (series_id) を取得できる。

ここまでできたら、実際に動かして Slack に通知することができる。

あとは定期実行できるようにするだけなので、トリガーを好きな時間に発火するように設定して完了。

その他

  • Slack のメッセージ内容は、Block Kit Builder を使うことで、プレビューしながら簡単に組み立てることができる。

  • connpass の API で取得できるイベント開催日時 (started_at) 等を Date オブジェクトを使ってフォーマットしているのだが、GAS のプロジェクトのタイムゾーンに注意が必要。