- 投稿日:
コインランドリーで選択すると記事がふわふわになるので、家でもふわふわにしたいなーと思い柔軟剤を買ってみた。
結果としては全くふわふわにならず、干したときにシワになったりタオルがガチガチになるのが気持ちマシになる程度だった。ちゃんと分量を柔軟剤投入口に入れてやってるので、使い方は間違っていないはずだ。
何より匂いがキツく、洗濯機も洗濯物も柔軟剤臭くなるのでこれはダメだなと思い、使うのをやめることにした。せめて無香料があってほしかったが近所のドラッグストアにはなかった(´・ω・`)
取り敢えずは捨てたので洗濯機置き場の匂いが早くなくなってほしい。とにかく無限に臭いので…。香り強化!みたいに書いた洗濯芳香剤が柔軟剤の横に売っていたが柔軟剤でこれだと、一体どんだけ臭いのだろうか…。
- 投稿日:
これは伺か・伺的 [第2会場] Advent Calendar 2024、21日目の記事です。担当は「りこ」こと「Lycolia Rizzim」です。
パソコン黎明期におけるペルソナウェアの登場
時は1998年、この頃のパソコンはまだ高級品で、一家に一台あるかどうかという普及具合だった。用途も年賀状作成くらいで、多くの家庭にとってはオーバースペックだったため、一年の大半を高価な箱として過ごしていた機体も少なくなかっただろう。この当時パソコンをそのような置物にせず、使いこなしていたのは熱心な趣味人や好奇心旺盛なオタク層のような人物だったと思う。要するにこの当時のパソコンはおもちゃだった訳だ。
このときは同時にインターネット黎明期でもあり、その利用者層も相成って、独特のパソコン文化が発展していた。例えば、美少女ゲームの発展や、美少女イラストを公開するサイトの流行はその一つだったと思う。その流れから、キャラクター文化が急速に発展していった。
そして、この当時ペルソナウェアというデスクトップマスコットソフトが生まれた。これはデスクトップにキャラクター(ペルソナ)を常駐させ、ペルソナのおしゃべりや仕草を眺めるといったソフトウェアだった。当時としてはキャラクターがユーザーに話しかけてくるという体験は非常に新鮮なもので、ペルソナを自作できることとも相成って爆発的に流行した。ある意味、伺かの原型ともいえるものである。
ペルソナウェアの衰退と、偽ペルソナウェアと「伺か」の誕生
しかしペルソナウェアの人気は長く続かなかった。わずか2年後、フリーウェアだったペルソナウェアはシェアウェアに舵を切ることになる。有償化告知が出たときに少なくないユーザーは絶望したことだろう。当時はソフトウェアに対価を支払う文化があまりなかったからだ。ぶっちゃけ、今でもそんなないと思うが。
そして時を同じくして出てきたのが、伺かである。当時はまだ「伺か」ではなく、「偽ペルソナウェア with "偽春菜"」という名前だった。これはペルソナウェアへの皮肉を込め、ペルソナウェアと基本ペルソナだった春菜の名前を拝借した名称と思われる。
この後「偽ペルソナウェア with "偽春菜"」は「偽ペルソナウェア・あれ以外の何か」と名前を変えたが、有償化に踏み切ったペルソナウェアの開発元から名称の使用停止要請を受け「あれ以外の何か with "任意"」と更に名前が変わった。偽春菜のことを「任意たん」と呼ぶのはここに由来している。この関係で当時、配布していたホームページの内容が凄まじい勢いで変わっていたのが記憶にある。
ここからの潮流は実はよくわかっていないのだが、Wikipediaによると紆余曲折あり、「何か。(仮)」が生まれ、これが「何か」となり、「伺か」となるという変遷を得て、今の伺かという名称になったようだ。
伺かの変遷、さくらとうにゅうの登場
伺かになると偽春菜はさくらに姿を変え、うにゅうはうにゅうのまま存続した。シェルもすか先生が書き下ろしたものに変わり、より洗練され、魅力的な印象となった。全然関係ない話だが、この当時ネットで活躍していた絵師の方々はその後プロになられることが多かったように思う。
この時が恐らく伺かの最盛期で、秋葉系や、はてな界隈を中心に大きく盛り上がっていたと思う。仕様が公開されていたことにより、互換環境が多く出たがメインストリームは本家伺か(MATERIA)だったと思う。本家の開発者がRO(Ragnarok Online)のプレイヤーで、ツールの開発者でもあったので、RO界隈にもある程度浸透していており、ここから知った人も少なくなかっただろう。
ヘッドライン機能では秋葉系ニュースサイトの新着情報を得られたこともあり、RSSやX、LINEがなかった頃の情報流通の一翼を担っていたこともあったため、特にその手のオタクには重宝されていたと思う。
また当時はCGM(Consumer Generated Media)文化が最盛を極めていたのもあり、ペルソナウェア同様にキャラクター(ゴースト)を開発でき、グラフィック部分がシェルやバルーンといった要素に分割されていたり、スクリプトエンジンである栞も自作できるなど、非常に創作性が高い素地があったため、多くのゴーストやシェル、バルーンが生まれた。
この背景には、フリーシェルと言ってシェルだけ配布する形態もあり、絵が描けない人でもフリーシェルを借りてゴーストを作るということができたのも大きいだろう。
本家の更新停止とSSPの台頭
しかし本家伺かは2002年あたりを最後に更新が止まってしまう。しばらくは問題なかったが利用環境として最大であるWindows OSのアップデートにソフトウェアが追従できなくなっており、年を経るごとに動作状況が悪化していっていた。
そんな中で更新が続いていた環境があった。SSPである。SSPはMATERIAの裏仕様を大体サポートするという狂気により、高い互換性を確保し、その後の伺かの標準になった。SSPは現在でも更新が続いている。ちなみにSSPには本家同様デフォルトゴーストが付属しており、これがさくらに負けず劣らずかわいいので、SSPをインストールしていない人は是非インストールしてみてほしい。
時代の変化と伺かの現在
さて、それから時は流れインターネットの世界もパソコンの世界も変わり、世にはスマートフォンやタブレットなる新しい形態のデバイスも出てきた。ネットの流行は個人サイトやMMORPGから、SNSに移動した。もうパソコンなどのデジタルデバイスからはおもちゃとしての性格が薄れ、仕事や生活で使うものとなり、パソコンを使っていたら遊んでいるという風にみられることもずいぶん減った。
SNSはmixi, Facebook, Twitter, Tumblr, Google+, pixivなどと色々出て、それぞれに栄枯盛衰があり、なくなったのもあれば、経営統合や買収などで性格が激変したものも少なくなく、混沌の時代に突入していた。
そんな中、こういった企業運営の中央集権型のSNSに異を唱えるように分散SNSが登場し、ActivityPubという規格で相互接続ができるようになる。代表的な分散SNSのソフトウェアとしてはMastodonやMisskeyなどがある。
Mastodonと伺か、「うかどん」の登場
そして伺かも、この流れに乗ることになる。SSPの開発者が「うかどん」というMastodonサービスを運営しているのだ。
ここではゴーストの会話ルーチンをLLM化し、交流できる仕組みが構築されており、ゴースト的な何かとおしゃべりすることができる。LLMと遊ぶのは楽しいので私も毎日つついて遊んでいる。
26年の結び
今は2024年、ペルソナウェアが生まれた1998年から数えると26年も経つが、こうして何かしらの地脈というか、系譜が流れていることはすごいと思うし、なんというか、この潮流をリアルタイムに体験できていていいな、と感傷に浸る私なのであった。
おまけ
特に記事本体とは関係ない何か。
伺かが出た当時の画面解像度でさくらを呼び出してみたらめちゃくちゃ邪魔だった件
記事に書いた通り当時のパソコンはおもちゃであり、画面全体を使って作業をすることもそこまで多くなかったような気がするので、これでも成り立っていたと思う。当時としてはPCのメモリを無駄に占有しCPUパワーを無駄に消費するソフトウェアで、要するに真面目に運用するものではなかったので、これでも成り立っていたのだと思う。
ペルソナウェアが出た当時、1998年の画面解像度はVGAやXGAだったので、今より存在感があったと思う。伺かでXGA解像度を再現してみるとこんな感じ。
記憶が確かならミドルスペックレベルでもノートPCだと結構きつかったと思う。スペックがだいぶ上がったWindows XPの時代でも決して軽くはなかった記憶がある。それでもメモリクリーナーやごみ箱掃除など、意外に有用な機能はあったので、遊びでよく使っていた。Windows 11だと恩恵が薄いが、キャプションいじりも好きだった。
現在主流と思われるFHD解像度で呼び出しすと以下のようになり、画面占有率はだいぶ下がる。これを動かしている端末はかなりスペックがいいため、伺か程度でリソース不足になることはなく、PCのメモリを無駄に占有しCPUパワーを無駄に消費できなくなってしまったのは残念だ。
ペルソナウェアの思い出
ペルソナウェアが登場した当時は一部のネット界隈では結構な人気を誇っていて、キャラクター(ペルソナ)を自分で作れることから自作する人も当時としては多くいた。私は「魔法星記 グランスノア」のパルテノン=ワッハーというペルソナが好きだった。
かつてフロッピーにパルテノン=ワッハーの画像をいっぱい保存していたのだが、学生時代に処分してしまい残っていないのが悔やまれる。(当時はストレージ容量が少なく、安価な拡張手段としてはフロッピーくらいしかなかったが、余りに古いものなので中身を見ずに処分してしまったという…)
作者の葵カナン氏はTINAMIにアカウントがあり、流石に時が経ちすぎて当時とだいぶ絵柄が変わっているが、現在でもパルテのイラストを見ることができる。
かつて一番使ったゴースト
過去の使ってるぞグラフが残ってないのでアレだが、千郷&魚匠&御子神一家だったと思う。おそらくその次はさくら、三位は陽子&飯網&千早だったと思う。
千郷&魚匠&御子神一家は巫女さんなのがいいし、気が強いのもいい。
さくらはキャプションいじりが楽しいし、純粋に見てて癒される。
陽子&飯網&千早は関西弁いいよねというのと、こう、むっちりしていいよねみたいな()男の子はスケベなのである。
最近推しのゴースト
百道さんと注射器だ。博多弁でしゃべるのがいいし、トークのこの懐かしい感じがいい。今時こんなのはなかなかないと思う。勿論おさわりリアクションもしっかりある。公式サイトのイラストも死ぬほどかわいいのでそちらも必見だ。
今の使ってるぞグラフ
MATERIA時代のログをSSPに継承できていないため、これはSSP時代のグラフになる。
さくら一強感がすごいが、他が起動されていなさすぎるので百道さんの追い上げもすごい。
- 投稿日:
単体テストは、コードの品質を向上させ、開発効率を高めるために欠かせないものだ。本記事では、単体テストを導入することで得られるメリットとその理由について解説する。
単体テストを書くことについて
単体テストを書くことには賛否両論あると思うが、私は書いたほうが良いと考えている。これは単にテストコードを書くことによってデグレが起きづらくなるからだけではなく、可読性の向上やコンフリクト頻度の減少など、様々な恩恵があるからだ。
この記事では、単体テストを書くことで得られるメリットについて書いていく。特に、関数を活用した開発(クラスを使わないスタイル)、TypeScriptでReactの関数コンポーネントを書くようなケースを前提に話を進める。
関数が小さくなる
単体テストの目的は、関数やクラスといった単位でその振る舞いを検証することだ。しかし、もし関数が1000行に及び、クロージャや分岐、ループが複雑にネストされていると、テストは非常に困難になる。つまり、これを回避しようとすると、自然と関数のサイズは小さくなり、責務が一つにまとまる設計となる。
関数が小さくなることでスコープが短くなり、見通しが良くなるほか、変数名を簡略化できる利点も生まれる。長すぎる変数名は基本的に有害だ。例えば以下のようなコードは誰も見たくないはずだ。(しかもよく見ると罠のようにも見えるコードだ)
const loooooooooooooooooooongVariableNameExtraResult = `${loooooooooooooooooooongVariableNameExtraParameter1} ${loooooooooooooooooooongVariableNameExtraParameter3} ${shortVar} ${loooooooooooooooooooongVariableNameExtraParameter2}`;
例えば、Go言語では一文字変数が一般的に受け入れられているが、これは関数のスコープが非常に短く、簡潔なコードが成立しやすい文化があるからだ。
疎結合な設計にしやすくなる
密結合なコードはテストがしづらい。そのため、単体テストを書こうとすると、自然と疎結合な設計になりやすくなる。具体的には、依存性逆転の原則に従った設計を採用するようになる。
例えば、次のようなコードはDate ()
を直接使用しており、テストが困難だ。このような場合、Date ()
をモックするために専用のライブラリやハックを使うことが多くなり、プログラムの保守性が悪化しがちだ。しかし、依存を外部から注入する設計にすれば、そういった心配は不要になる。
const isOneDayLater = (time: number) => {
return (+new Date() - time) > 86_400_000;
};
しかし、以下のように外部から値を注入すればモックしなくともテストコードを書くのが容易になる。第二引数に値を入れればいいからだ。
const isOneDayLater = (time: number, baseEpochTime: number) => {
return (baseEpochTime - time) > 86_400_000;
};
コンフリクトが起きづらくなる
Gitを利用した並行開発においてはブランチをマージするときにコンフリクト、つまりコードの競合が起きることがしばしばある。しかしこれまでに書いたように責務別にモジュール化された簡素なコードではコンフリクトの規模が自然に小さくなり、コンフリクト自体が減ったり、万一起きた時でも影響範囲が限定されるようになる。
コンフリクトの解消ミスがあり本番障害が出た、後続のQAフェーズなどで差し戻しが起きたみたいなのはよくある光景だと思うが、単体テストの導入による設計の改善があれば、それが起きた時のコストを減らすのに貢献することが可能だ。
コードの見通しが良くなる
適切に単体テストを意識してコードを書くと、自然と「べた書き」から「モジュール化」されたコードに変わる。
べた書きされたコードの例
例えば次のような関数は冗長だ。この程度の長さならまだしも、これが500行、1000行と増えていくと読むのが大変になる。現実ではもっとネストが酷かったり、前後に長大関数がいて、注意深く読まないと解読不能ということもままある。そしてこの関数をテストするのは書いてある処理すべてを検査する必要があるので、テストコードも長くなり、しんどい。
export const registerUser = async (user: User) => {
if (!/^\d+$/.test(user.id)) {
throw new Error('ユーザーIDの書式が不正です。');
}
if (user.firstName === '') {
throw new Error('姓が入力されていません。');
}
if (user.lastName === '') {
throw new Error('名が入力されていません。');
}
if (user.gender === '') {
throw new Error('性別が入力されていません。');
}
if (user.address === '') {
throw new Error('住所が入力されていません。');
}
const resp = await fetch('https://example.com/api/v1/user', {
method: 'POST',
body: JSON.stringify({
id: Number(user.id),
name: `${user.firstName} ${user.lastName}`,
gender:
user.gender === '0' ? 'male' : user.gender === '1' ? 'female' : 'other',
address: user.address,
}),
})
.then(async (resp) => await resp.json())
.catch((e) => e);
if (resp.status === 200) {
return resp;
} else {
const payl = resp.json();
return {
errorMessage: payl.errorMessage,
};
}
};
モジュール化されたコードの例
次のように書けば34行あった関数が9行にまで減り、見通しが良くなる。
export const registerUser = async (user: User) => {
validateUser(user);
const payload = createRegisterUserPayload(user);
const resp = await requestRegisterUser(payload);
return validateRegisterUserResponse(resp);
};
関数の中で何をしているかわからないという声もあるだろうが、関数名から内容を読み取れるようにしておけば問題にならない。読み取れない処理を入れなければいいのだ。こういう風にべた書きを一定の粒度で関数化することをモジュール化と呼ぶ。
この時、それぞれの関数(validateUser()
など)は単一責務を持つため、単体テストのスコープが明確になる。親となる関数(この場合registerUser()
)のテストでは、子関数をモックすることで簡潔なテストが可能だ。この場合、親となる関数は子関数を呼び出すことが責務になる。
再テストの効率化
コードを直すことはよくあることで、コードを書いているときに一度書いたコードを直したり、レビュー指摘で直したりする。この時に、関係する部分を手動でテストするのは非効率だ。しかし、単体テストがあれば、その工数を大幅に削減できる。
特に分岐の網羅性が重要な場合、単体テストがあるとないとでは開発者の苦痛が大幅に変わるだろう。例えば、4つの分岐があるロジックでさえ、全数網羅では16パターンにも達するため、これを手動で見るのは手間である。しかしテストコードがあれば、自動テストを蹴るだけでいい。心理的負担も時間もこちらの方がローコストで現実的だ。
実装者が設計した時の意図がある程度読み取れる
コードを見ていて「こんなケースはないだろう」と思うことはしばしばあると思うが、そんな時にもしテストコードが存在すればケース名が書いてあるはずだ。それによって当時どのような意図で実装されたのかを知ることができる。これはコードの理解をより深く助ける手助けとなり、「負債に見えたから消したら不具合が出た」といったケースを減らすことに貢献することができる。
手動テストはなくならない
単体テストが充実していても、手動テストが完全になくなるわけではない。特に初回実装時は、プログラムが正しく動作するかどうかを確認する必要があるため、手動テストで動作確認を行い、その上で単体テストを信頼できる状態にするのが基本だ。
単体テストの限界と現実的な運用
さて、ここまで持ち上げてきた単体テストだが、決して銀の弾丸ではない。単体テストを入れてもバグを完全にゼロにすることは出来ないし、正しくないテストコードを書いたり、レビューが不十分であればテストが不正に通過する可能性もある。
正しくないテストコードを書かないための工夫としては、テストコードを書いた後に実装側を修正し、意図的にテストが落ちるようにしたときに、テストが落ちることを確認することが有用だ。これによって、実装が正しくない状態になったときにテストが正しく落ちることが確認できる。逆に落ちなかった場合、そのテストは機能していないと言える。
またテストを全数書くというのも非現実的であるため、TypeScriptの開発であれば型を騙すas
のようなものは使わず、極力正しい型を検証できるように静的解析に任せるのが良い。戻り値の型指定も場合によっては型を騙すのに使えるケースがあるため、バグを生み出す元になる場合もあり、気を付けたほうが良い。
何より単体テストで見れるのは単体であり、結合は見れないため、あくまで単体テストはベースのロジックを担保するものとして使い、全体面は結合テストやe2eテスト、手動テストで見ていくのが良いだろう。
理想的には単体テストで網羅し、正常系と異常系のそれぞれ1パターンを結合で、初回実装時のみ手動テストというのがよいと考えている。これは結合テストで網羅ケースを書いてしまうと単体テストを書く意味がないうえに、重複テストが大量に発生し、実行速度が遅くなるし、規模が大きい分メンテナンスが大変になるからだ。
e2eテストは決済などのクリティカルケースにはあったほうが良いと思うが、コストの重さからしてそれ以外にはあまりなくてよいと思う。大抵のケースで手動の方が楽だろう。手動テストでは結果の誤りが出る可能性は残るが、些末な処理で100点を目指しても仕方がないので、多少の不具合は許容で良いのではないかと思う。
単体テストが有効に機能しづらいケース
単体テストは銀の弾丸ではないため、機能しないケースが当然ある。
元から単体テストがないプロジェクトに導入するケース
元々単体テストが導入されていないプロジェクトでは多くの困難を伴う。
例えば、関数やクラスがべた書きされており、責務が曖昧なまま複雑に絡み合っているコードでは、単体テストを書くこと自体が非常に難しい。分解しようにも目に見えている範囲だけを直せないケースも少なくない。これは他の機能がその機能と強く結合しており、根幹となる部分から直していかないと上手くいかないケースがあるからだ。
この状態の関数に対するテストを書いても、リファクタリングで分割された時の動作保証がやりづらい。何故ならこの手のものは大抵きれいに分割できず、再集計になるまでに紆余曲折をたどるからだ。
単体テストに対する理解が異なるケース
「単体テスト」の「単体の単位」が上手く認識されていない場合、本来あるべき単体テストの意義や役割が意識されていないことがある。これは結合テストやe2eテストが単体テストとして扱われているケースだ。
例えば画面の単体テストの定義が、実際に画面を操作し、APIをコールし、その結果に基づいた動作の確認だったり、複数の関数を呼び出す関数を単体として考えているケースでは、単体に対する意識が異なるため、本来あるべき単体テストの導入が難しいことがある。
有効な開発標準やコーディング規約が存在しないケース
有効な開発標準やコーディング規約がなく、コードの裁量がメンバー個々人に委ねられているケースでは、チームメンバーがそれぞれ独自のスタイルでコードを書いていることもあり、より問題を複雑化させることもある。
こういったケースではありとあらゆるコードが想定外の振る舞いをするため、単体テストの導入を阻む障壁となる。
破壊的変更が多いケース
これは要件や仕様などの上流部分が固まらず、日々変動するようなプロジェクトの話だ。
いつ仕様が固まるかわからないのでコードだけは書き続ける必要があり、日々変わるので動作確認もおろそかになる。このようなプロジェクトでは書いたコードが無価値化することが日常であるため、テストコードを書くという行為自体が陳腐化していきがちだ。
単体テストを書く文化がないケース
プロジェクトの方針として単体テストを書くことを推奨していなかったり、認めていない場所がある。こういった場所では、単体テストの重要性が理解されていないことも少なくなく、テストを書くことがむしろ「余計な作業」として扱われることがある。そういったシーンで単体テストを書くと、プロジェクト内で孤立するリスクがあり、難しい。
まとめ
単体テストを書くことで自然とSOLID的な設計に近づき、コードが読みやすくなり、ある程度変更に強く、開発速度も上がり、一定の品質も担保出来るため、単体テストを書くことによる恩恵は少なくない。これは間違いなくメリットだ。
しかし一方で、単体テストは銀の弾丸ではなく、密結合でテストしにくいコードや、頻繁に仕様変更が発生するプロジェクト、文化的に単体テストが認められていない環境では上手く機能しないケースもある。
- 投稿日:
ちっとも眠気が来ないのでトップページを弄くり回していたのだが、トップページが概ね完成した以来の大規模更新となる更新を行った結果、Lighthouseのスコアが100になった。
主な更新としては2カラムを1カラムに落とし、職歴系の記述をガっと消した感じ。趣味のサイトなのに仕事のことあんま書きたくないなというのと、書いててもあんま意味ないなと思ったので消した感じだ。
前回 | 今回 |
---|---|
![]() |
![]() |
以前のSPビューでの結果 | 今回のSPビューでの結果 |
---|---|
![]() |
![]() |
以前のPCビューでの結果 | 今回のPCビューでの結果 |
---|---|
![]() |
![]() |
改善点としては、以前リンクの隙間が狭いといわれていたので微妙に広げてみたくらいで、後はほとんど特に何もしていないが、100点になってしまった。シングルカラムになったからかもしれない。前回の結果を保存していないので詳細は分からないけど、100点になったのでいっかという感じ。
- 投稿日:
例えば以下のようなコード(例示用のコードなので動作的な意味は特にない)
<>
{isHoge && obj?.payload?.username !== undefined && (
<div>新規:{obj.payload.username}</div>
)}
{isPiyo && obj?.payload?.username !== undefined && (
<div>更新:{obj.payload.username}</div>
)}
{isFuga && obj?.payload?.acceptTerm !== undefined && (
<div>退会:{obj.payload.acceptTerm}</div>
)}
</>
ifやswich文で分岐処理を書く場合はIntelliSenseで漏れが分かるが、JSXに埋め込んで書くと書き漏れがあった場合に気づきづらい。また上記程度ならまだいいと思うが、ネストされていたり、三項演算子を使った複雑な分岐があったりすると読みづらく、何が起きるのかわかりづらいし、テストも書きづらいので避けたほうが良いと感じる。
このような場合はifやswich文で分岐してJSXを返すコンポーネントを別に作り、それを埋め込んだ方が良いと思う。ページ用のtsxファイルの中にこの手のものが大量にいると、大変見通しが悪いと感じる。
そもそも上記の条件はisHoge, isPiyo, isFugaの関連性が分からないため、MECEでなく、条件分岐としてもよくない。