<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>AichiLog</title>
	<atom:link href="https://aichi.blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://aichi.blog</link>
	<description>学びて富み　富みて学ぶ</description>
	<lastBuildDate>Sun, 17 Aug 2025 04:42:07 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://aichi.blog/wp-content/uploads/2021/12/cropped-915AB649-D1E9-4810-9658-CB8CE1B605FD.JPEG-2-32x32.jpeg</url>
	<title>AichiLog</title>
	<link>https://aichi.blog</link>
	<width>32</width>
	<height>32</height>
</image> 
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/>
<atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/>
<atom:link rel="hub" href="https://websubhub.com/hub"/>
<atom:link rel="self" href="https://aichi.blog/feed/"/>
	<item>
		<title>アプリ開発で失敗しない！アイデアを検証する 3 ステップフレームワーク</title>
		<link>https://aichi.blog/app-idea-validation/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=app-idea-validation</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Sun, 17 Aug 2025 04:36:28 +0000</pubDate>
				<category><![CDATA[個人開発]]></category>
		<category><![CDATA[アプリ開発]]></category>
		<category><![CDATA[チェックポイント]]></category>
		<category><![CDATA[開発]]></category>
		<guid isPermaLink="false">https://aichi.blog/app-idea-validation/</guid>

					<description><![CDATA[<p>「アプリ開発をしたけど、全くユーザーが集まらなかった…」 そんな失敗談は珍しくありません。実際、90%のアプリは「誰も欲しくないものを作ってしまう」ことが原因で失敗しています。 そこで本記事では、アプリ開発初心者やスター [&#8230;]</p>
<p>The post <a href="https://aichi.blog/app-idea-validation/">アプリ開発で失敗しない！アイデアを検証する 3 ステップフレームワーク</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>「アプリ開発をしたけど、全くユーザーが集まらなかった…」<br />
そんな失敗談は珍しくありません。実際、<strong>90%のアプリは「誰も欲しくないものを作ってしまう」ことが原因で失敗</strong>しています。</p>
<p>そこで本記事では、アプリ開発初心者やスタートアップの方に向けて、<strong>開発前にアイデアを検証できる「3 ステップフレームワーク」</strong>を解説します。</p>
<p>この方法を使えば、半年や 1 年かけて無駄な開発をする前に、<strong>数週間で「本当に需要があるか」を確認</strong>できます。</p>
<p><h2><span id="toc1">アプリ開発が失敗する最大の理由</span></h2>
</p>
<p>多くの人がやってしまうのが、<strong>「自分の欲しいもの＝みんなも欲しいもの」</strong>と考えてしまうことです。</p>
<ul>
<li>需要がない</li>
<li>競合が強すぎる</li>
<li>お金を払うユーザーがいない</li>
</ul>
<p>こうした理由でせっかくのアイデアも失敗に終わります。</p>
<p>では、どうやって失敗を防ぐのか？<br />
答えはシンプルで、<strong>作る前に「本当に必要か」をテストすること</strong>です。</p>
<p><h2><span id="toc2">アプリアイデア検証の 3 ステップ</span></h2>
</p>
<p><h3><span id="toc3">ステップ 1：市場規模の分析（マーケットサイズ）</span></h3>
</p>
<p>アプリ開発でまず確認すべきは「市場の大きさ」です。</p>
<ul>
<li><strong>B2C アプリ（消費者向け）</strong></li>
</ul>
<p>  → 潜在ユーザー数が 1,000 万人以上（実際に使うのは 1%と仮定）</p>
<ul>
<li><strong>B2B アプリ（企業向け）</strong></li>
</ul>
<p>  → 10 万社以上の企業をターゲットにできるか</p>
<p>さらに競合調査も重要です。</p>
<ul>
<li>競合が全くいない → 需要がない可能性</li>
<li>競合が多すぎる → 差別化ポイントが必要</li>
</ul>
<p>&#x1f449; <strong>アプリのレビューを調べると、ユーザーの不満や改善点が見つかります。</strong></p>
<p><h3><span id="toc4">ステップ 2：課題（Problem）の妥当性を確認</span></h3>
</p>
<p>「問題はあるけど、お金を払うほどではない」ケースは意外に多いです。</p>
<p><h4>課題認識をチェックする方法</h4>
</p>
<ul>
<li><strong>Google トレンド</strong>で検索需要を確認</li>
<li><strong>SNS や掲示板</strong>でユーザーの不満を探す</li>
<li><strong>競合アプリの口コミ</strong>を分析</li>
</ul>
<h4>支払い意欲をチェックする方法</h4>
</p>
<ul>
<li>B2C → アンケートで「いくらなら払うか」を調査</li>
<li>B2B → 業界のソフトウェア予算をリサーチ</li>
</ul>
<p>&#x1f449; <strong>「困っている」だけでなく「解決にお金を払うか」が重要です。</strong></p>
<p><h3><span id="toc5">ステップ 3：ソリューションのテスト（MVP 検証）</span></h3>
</p>
<p>アプリを作る前に、<strong>小さな実験</strong>で需要を確認します。</p>
<p><h4>ランディングページテスト</h4>
</p>
<ul>
<li>アプリの概要・料金を紹介するページを作成</li>
<li>登録フォームを設置</li>
<li>SNS やコミュニティにシェアして反応を見る</li>
</ul>
<h4>ユーザーインタビュー</h4>
</p>
<ul>
<li>最低 10 人に直接ヒアリング</li>
<li>今はどう解決しているか？</li>
<li>お金を払ってでも解決したいか？</li>
<li>β テスターとして使いたいか？</li>
</ul>
<h2><span id="toc6">検証の成功基準</span></h2>
</p>
<p>検証を通して、以下の基準を満たせば次に進む価値があります。</p>
<ul>
<li>LP のコンバージョン率：5%以上</li>
<li>登録者数：100 人以上</li>
<li>インタビューで 10 人中 7 人が前向き</li>
<li>成功している競合が 3 社以上存在</li>
<li>市場規模が十分にある</li>
</ul>
<h2><span id="toc7">アプリ検証に役立つツール</span></h2>
</p>
<ul>
<li><strong>Google Trends</strong>：検索需要の確認</li>
<li><strong>AppAnnie / SensorTower</strong>：競合アプリの DL 数・収益調査</li>
<li><strong>Facebook Ads Manager</strong>：市場規模の推定</li>
<li><strong>Tally.so</strong>：アンケート作成</li>
<li><strong>Calendly</strong>：ユーザーインタビューの調整</li>
</ul>
<h2><span id="toc8">レッドフラグ（要注意サイン）</span></h2>
</p>
<p>以下に当てはまる場合は、アプリ開発を進める前に見直しが必要です。</p>
<ul>
<li>ユーザーが課題を認識していない</li>
<li>既存の解決策に満足している</li>
<li>顧客獲得コストが高すぎる</li>
<li>季節限定・一回きりの利用しか見込めない</li>
<li>収益モデルが成立しない</li>
</ul>
<h2><span id="toc9">検証後のアクション</span></h2>
</p>
<ul>
<li><strong>成功した場合</strong></li>
</ul>
<ul>
<li>最小限の機能で MVP をリリース</li>
<li>最初の 10 人の有料ユーザーを獲得</li>
<li>フィードバックを元に改善</li>
</ul>
<ul>
<li><strong>失敗した場合</strong></li>
</ul>
<ul>
<li>ターゲット市場を変える</li>
<li>解決策を見直す</li>
<li>新しいアイデアを試す</li>
</ul>
<h2><span id="toc10">まとめ：失敗しないアプリ開発の秘訣</span></h2>
</p>
<p>アプリ開発で失敗する最大の原因は「需要がないものを作ること」です。</p>
<p>そのためには、</p>
<p>1. 市場規模を調べる<br />
2. 課題の妥当性を確認する<br />
3. ソリューションをテストする</p>
<p>この<strong>3 ステップ検証フレームワーク</strong>を実践することで、リスクを最小化できます。</p>
<p>&#x1f449; <strong>数週間の検証で方向性を決めることで、半年や 1 年を無駄にせず、本当に必要とされるアプリを作れるようになります。</strong></p>
<p><h2><span id="toc11">参考文献</span></h2>
</p>
<ul>
<li><a rel="noopener" href="https://amzn.to/3HFA3oa" target="_blank">リーン・スタートアップ　ムダのない起業プロセスでイノベーションを生みだす</a></li>
<li><a rel="noopener" href="https://amzn.to/4mFqG6U" target="_blank">Running Lean 第 3 版 ―リーンキャンバスから始める継続的イノベーションフレームワーク (THE LEAN SERIES)</a></li>
<li><a rel="noopener" href="https://amzn.to/4mFrRDm" target="_blank">SPRINT 最速仕事術――あらゆる仕事がうまくいく最も合理的な方法</a></li>
</ul><p>The post <a href="https://aichi.blog/app-idea-validation/">アプリ開発で失敗しない！アイデアを検証する 3 ステップフレームワーク</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【保存版】ビジネスアイデアを見極める 7 つのチェックポイント｜起業・新規事業に必須のフレームワーク</title>
		<link>https://aichi.blog/green-flags/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=green-flags</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Sun, 17 Aug 2025 04:26:43 +0000</pubDate>
				<category><![CDATA[個人開発]]></category>
		<category><![CDATA[アイデア]]></category>
		<category><![CDATA[チェックポイント]]></category>
		<category><![CDATA[ビジネス]]></category>
		<category><![CDATA[開発]]></category>
		<guid isPermaLink="false">https://aichi.blog/green-flags/</guid>

					<description><![CDATA[<p>はじめに 「ビジネスアイデアを思いついたけど、本当に売れるのか不安…」 「起業のアイデアがあるけど、市場調査をどうすればいいのか分からない」 そんな悩みを持つ方に向けて、この記事では スタートアップや新規事業を始める前に [&#8230;]</p>
<p>The post <a href="https://aichi.blog/green-flags/">【保存版】ビジネスアイデアを見極める 7 つのチェックポイント｜起業・新規事業に必須のフレームワーク</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><h2><span id="toc1">はじめに</span></h2>
</p>
<p>「ビジネスアイデアを思いついたけど、本当に売れるのか不安…」<br />
「起業のアイデアがあるけど、市場調査をどうすればいいのか分からない」</p>
<p>そんな悩みを持つ方に向けて、この記事では <strong>スタートアップや新規事業を始める前に確認すべき 7 つのポイント</strong> を紹介します。<br />
このフレームワークを使えば、思いつきの起業アイデアを、実際に収益を生むビジネスアイデアへとブラッシュアップできます。</p>
<p><h2><span id="toc2">1. 明確な問題</span></h2>
</p>
<p>成功するビジネスの出発点は、<strong>顧客の「解決すべき問題」</strong> にあります。<br />
市場調査をするときは、以下の点を確認しましょう。</p>
<ul>
<li>それは「必要不可欠な痛み」か？それとも「あると便利」程度か？</li>
<li>問題は <strong>緊急・頻発・コストが高い</strong> の 3 条件に当てはまるか？</li>
<li>ユーザーはすでに検索・自作ツール・外注で対策しているか？</li>
<li>もし解決策がなくなったら、多くの人が「困る」と感じるか？</li>
</ul>
<p>&#x1f449; <strong>解決すべき痛みがなければ、需要は生まれません。</strong></p>
<p><h2><span id="toc3">2. ニーズかウォンツか</span></h2>
</p>
<p>ビジネスアイデアを考えるときに大事なのは、<strong>人が「欲しい」と言うもの</strong>ではなく、\*\*実際に「行動して解決しようとしているもの」\*\*です。</p>
<ul>
<li>検索・外注・自作ツールで解決しようとしているか？</li>
<li>それとも「こうなったらいいな」と言うだけで行動しないか？</li>
</ul>
<p>&#x1f449; 人は「欲望」ではなく「必要」にお金を払います。</p>
<p><h2><span id="toc4">3. 支払い能力</span></h2>
</p>
<p>どんなに素晴らしい起業アイデアでも、顧客に <strong>支払い能力がなければビジネスは成立しません</strong>。</p>
<ul>
<li>顧客はすでに同じ領域にお金を使っているか？</li>
<li>この問題解決に予算を組んでいるか？</li>
<li>解決すれば「収益アップ」や「コスト削減」につながるか？</li>
</ul>
<p>&#x1f449; <strong>払えない顧客は、ビジネスではなく趣味対象。</strong></p>
<p><h2><span id="toc5">4. 顧客の明確さと到達性</span></h2>
</p>
<p>ビジネスアイデアを収益化するには、<strong>理想の顧客を明確にし、直接リーチできるかどうか</strong> が重要です。</p>
<ul>
<li>顧客像を 1 文で説明できるか？</li>
<li>彼らはどこに集まっているか？（Instagram、YouTube、Reddit、X など）</li>
<li>広告、SEO、SNS マーケティング、コンテンツ発信でアプローチ可能か？</li>
</ul>
<p>&#x1f449; <strong>顧客に届かなければ、どんな商品も売れません。</strong></p>
<p><h2><span id="toc6">5. 成長市場に乗る</span></h2>
</p>
<p>市場調査の際に必ずチェックすべきが、<strong>その市場が成長しているかどうか</strong> です。</p>
<ul>
<li>Google トレンドで需要が右肩上がりか？</li>
<li>AI や規制緩和、新技術で新しいニーズが生まれているか？</li>
<li>大手企業が参入・買収しているか？</li>
</ul>
<p>&#x1f449; <strong>伸びている市場（ブルーオーシャン）に参入することが成功の近道。</strong></p>
<p><h2><span id="toc7">6. 最初から価値が伝わる</span></h2>
</p>
<p>スタートアップや新規事業は、<strong>最初の印象で勝負が決まることも多い</strong> です。</p>
<ul>
<li>すぐに「すごい！」と感じられる体験があるか？</li>
<li>デモやスクショで直感的に伝わるか？</li>
<li>サービスがなくても「ストーリー」だけで魅力を語れるか？</li>
</ul>
<p>&#x1f449; <strong>価値が一瞬で伝わるサービスは、マーケティングが自然に回ります。</strong></p>
<p><h2><span id="toc8">7. MVP で早く試せる</span></h2>
</p>
<p>新規事業を始めるときは「完璧なサービス」を目指すより、<strong>最小限の MVP（Minimum Viable Product）を早く出すこと</strong>が重要です。</p>
<ul>
<li>2 週間以内に AI やノーコードで試作できるか？</li>
<li>ランディングページやウェイトリストから検証できるか？</li>
</ul>
<p>&#x1f449; <strong>傑作ではなく MVP を作り、早く市場でテストすること。</strong></p>
<p><h2><span id="toc9">まとめ｜アイデアを「起業」につなげる 7 つのポイント</span></h2>
</p>
<p>この記事で紹介した <strong>ビジネスアイデアの 7 つのチェックポイント</strong> を振り返ります。</p>
<p>1. 本当に痛い問題を解決しているか？<br />
2. 欲しいものではなく必要なものか？<br />
3. 支払い能力のある顧客がいるか？<br />
4. 顧客像が明確で、リーチできるか？<br />
5. 成長市場に乗れているか？<br />
6. 最初から価値が伝わるか？<br />
7. MVP で早く試せるか？</p>
<p>このフレームワークを使えば、ただの「思いつきの起業アイデア」を、<strong>収益化できる新規事業</strong> へと進化させられます。</p>
<p><h2><span id="toc10">参考文献</span></h2>
</p>
<ul>
<li><a rel="noopener" href="https://amzn.to/3HFA3oa" target="_blank">リーン・スタートアップ　ムダのない起業プロセスでイノベーションを生みだす</a></li>
<li><a rel="noopener" href="https://amzn.to/4mFqG6U" target="_blank">Running Lean 第 3 版 ―リーンキャンバスから始める継続的イノベーションフレームワーク (THE LEAN SERIES)</a></li>
<li><a rel="noopener" href="https://amzn.to/4mFrRDm" target="_blank">SPRINT 最速仕事術――あらゆる仕事がうまくいく最も合理的な方法</a></li>
</ul><p>The post <a href="https://aichi.blog/green-flags/">【保存版】ビジネスアイデアを見極める 7 つのチェックポイント｜起業・新規事業に必須のフレームワーク</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Laravel で「CORS error」と表示された真犯人を突き止めるまで ―― “dump がヘッダーを確定させていた” という落とし穴と、その対処法</title>
		<link>https://aichi.blog/laravel-cors-error/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=laravel-cors-error</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Mon, 16 Jun 2025 03:53:46 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[CORS]]></category>
		<category><![CDATA[デバッグ]]></category>
		<guid isPermaLink="false">https://aichi.blog/laravel-cors-error/</guid>

					<description><![CDATA[<p>症状 ―― CORS 設定を直しても消えない「CORS error」 フロント（Next.js）から POST /api/entry を呼ぶと DevTools に “CORS error” と表示される。 ネットワーク [&#8230;]</p>
<p>The post <a href="https://aichi.blog/laravel-cors-error/">Laravel で「CORS error」と表示された真犯人を突き止めるまで ―― “dump がヘッダーを確定させていた” という落とし穴と、その対処法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><h2><span id="toc1">症状 ―― CORS 設定を直しても消えない「CORS error」</span></h2>
</p>
<ul>
<li>フロント（Next.js）から <code>POST /api/entry</code> を呼ぶと DevTools に “CORS error” と表示される。</li>
<li>ネットワークタブには <strong>Status 200 / Size 0 B / Content-Type <code>text/html</code></strong> が並ぶ（JSON を返すはずが HTML になっている）。</li>
<li><code>config/cors.php</code> を編集したり、nginx で <code>add_header</code> を加えても改善しない。</li>
</ul>
<p>ここで「<strong>本当に CORS が原因なのか？</strong>」を疑うのがポイントです。</p>
<p><h2><span id="toc2">調査手順と気付き</span></h2>
</p>
<p>1. <strong>curl でエンドポイントを叩く</strong><br />
   まず <code>curl -i -X POST http://localhost/api/entry</code> を実行してみました。すると、ブラウザでは 200 ステータスが返ってきていたにもかかわらず、curl では <strong>500 Internal Server Error</strong> が返ってきました。<br />
   これにより、問題は <strong>CORS ではなくサーバーエラーであること</strong>が判明しました。</p>
<p>2. <strong>ブラウザのネットワークタブを再確認</strong><br />
   ブラウザのネットワークタブを見ると、レスポンスのコンテンツタイプが <strong><code>text/html</code></strong> になっていることに気付きました。本来 API レスポンスであれば <code>application/json</code> のはずです。これは <strong>何かが先に HTML を出力している可能性</strong> を強く示唆していました。</p>
<p>3. <strong>Postman で同じリクエストを送信</strong><br />
   Postman で同じリクエストを送信したところ、レスポンスボディには <strong><code>&lt;pre class="xdebug-var-dump"...&gt;</code> で始まる HTML</strong>（dump の内容）がそのまま返ってきました。これは <strong>dump の出力がそのまま HTTP レスポンスになっていること</strong>を示しています。</p>
<p>4. <strong><code>storage/logs/laravel.log</code> を tail</strong><br />
   <code>storage/logs/laravel.log</code> を tail したところ、外部 API 呼び出し前後でログが途切れていることがわかりました。これは <strong>途中で例外または予期せぬ出力が挟まっている可能性</strong> を示しています。</p>
<p>5. <strong>Xdebug でブレークポイントを設定</strong><br />
   サービス層のメソッド中で <code>dump($xml)</code> が実行されている箇所を発見しました。これは <strong>dump が HTML を即時出力しヘッダーを確定させていること</strong>を示しています。</p>
<p><h2><span id="toc3">原因 ―― dump/dd/echo がヘッダーを先に送信していた</span></h2>
</p>
<p><code>dump()</code> はデバッグ用関数で、呼ばれた瞬間にブラウザへ HTML を送り出します。</p>
<p>レスポンスヘッダーが <strong><code>Content-Type: text/html</code></strong> で確定してしまうため、その後に付与されるはずだった <code>Access-Control-Allow-Origin</code> などの CORS ヘッダーが入らなくなります。</p>
<p>結果、ブラウザからは <strong>「CORS エラー」としか見えません</strong>。</p>
<p><h2><span id="toc4">修正方法 ―― サービス層の dump を Log::debug に差し替える</span></h2>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-php" data-lang="php" data-show-lang="1"><code class="language-php" data-hcb-clip="0">// app/Services/HogeSyncService.php
class HogeSyncService
{
    public function sync(array $payload): array
    {
        $xml = $this-&gt;buildXml($payload);

        // ✗ NG： dump するとヘッダーが確定してしまう
        // dump($xml);

        // 〇 OK： Log に残せばブラウザへは何も送られない
        Log::debug('Hoge XML payload', ['xml' =&gt; $xml]);

        Http::post(config('hoge.endpoint'), $xml);

        return ['status' =&gt; 'ok'];
    }
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;0&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc5">改善ポイントとベストプラクティス</span></h2>
</p>
<p>本番環境のコードには <code>dump</code>、<code>dd</code>、<code>echo</code> などのデバッグ用の出力を残さないようにしましょう。これらの関数はヘッダーが確定する前に呼び出されると、意図しないレスポンスヘッダーが送信されてしまう可能性があります。代わりに、ログ出力を使用するか、デバッグが完了したら完全に削除することをお勧めします。</p>
<p><h2><span id="toc6">まとめ</span></h2>
</p>
<p>1. ブラウザが出す “CORS error” は <strong>必ずしも CORS 設定の誤りを示すわけではない</strong>。<br />
2. Laravel/PHP では <strong>ヘッダー確定前に dump/echo を呼ぶ</strong> と CORS ヘッダーが付けられず、同じエラー表示になる。<br />
3. デバッグ出力は <strong>Log クラスへ切り替え（もしくは削除）</strong>、レスポンスは常に JSON で返すようにすれば解決。</p>
<p><strong>まずは curl と Postman とログを確認する</strong>。それだけで「CORS か、それ以外か」を簡単に切り分けられます。</p><p>The post <a href="https://aichi.blog/laravel-cors-error/">Laravel で「CORS error」と表示された真犯人を突き止めるまで ―― “dump がヘッダーを確定させていた” という落とし穴と、その対処法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>依存関係逆転の法則（Dependency Inversion Principle）: Go 言語で柔軟で強い設計を実現するには？</title>
		<link>https://aichi.blog/dependency-inversion-principle/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=dependency-inversion-principle</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Sat, 31 May 2025 06:30:58 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[依存関係]]></category>
		<category><![CDATA[設計]]></category>
		<guid isPermaLink="false">https://aichi.blog/dependency-inversion-principle/</guid>

					<description><![CDATA[<p>依存関係逆転の法則（Dependency Inversion Principle）とは 依存関係逆転の法則とは、ソフトウェア設計において「上位モジュール（ビジネスロジックなど）が下位モジュール（詳細な実装、たとえばデータ [&#8230;]</p>
<p>The post <a href="https://aichi.blog/dependency-inversion-principle/">依存関係逆転の法則（Dependency Inversion Principle）: Go 言語で柔軟で強い設計を実現するには？</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><h2><span id="toc1">依存関係逆転の法則（Dependency Inversion Principle）とは</span></h2></p>
<p>依存関係逆転の法則とは、ソフトウェア設計において「上位モジュール（ビジネスロジックなど）が下位モジュール（詳細な実装、たとえばデータベースや外部サービスなど）に依存しないようにし、両者とも“抽象（インターフェースや抽象クラス）”に依存するべきだ」という原則です。これにより、システムは柔軟性・拡張性・保守性が高くなり、変更に強くなります。</p>
<p><h2><span id="toc2">2 つのポイント</span></h2></p>
<p><ul><li>上位モジュールも下位モジュールも、具体的な実装ではなく抽象（インターフェースなど）に依存する</li><li>実装の詳細（下位モジュール）が抽象に従うようにする（＝依存の向きを逆転させる）</li></ul>
<hr></p>
<p><h2><span id="toc3">具体例で理解する</span></h2></p>
<p><h3><span id="toc4">1. 具象に依存している悪い例</span></h3></p>
<p>たとえば「メールを送る」機能を考えます。</p>
<div class="hcb_wrap"><pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="0">// EmailSender はメール送信を担当する構造体
type EmailSender struct{}

// SendEmail はメールを送信するメソッド
func (e *EmailSender) SendEmail(message string) {
	// メール送信処理
	fmt.Printf("メールを送信中: %s\n", message)
}

// NotificationService は通知サービスを提供する構造体
type NotificationService struct {
	emailSender *EmailSender
}

// NewNotificationService はNotificationServiceの新しいインスタンスを作成
func NewNotificationService() *NotificationService {
	return &NotificationService{
		emailSender: &EmailSender{},
	}
}

// Send はメッセージを送信するメソッド
func (n *NotificationService) Send(message string) {
	n.emailSender.SendEmail(message)
}

// 使用例
func main() {
	service := NewNotificationService()
	service.Send("テストメッセージです")
}</code></pre><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;0&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p>この例では、<code>NotificationService</code>が<code>EmailSender</code>という具体的な実装に直接依存しています。もし今後、SMS 送信や他の通知手段を追加したい場合、<code>NotificationService</code>のコードを修正しなければなりません。</p>
<p><hr></p>
<p><h3><span id="toc5">2. 抽象に依存させる良い例（依存関係逆転の法則の実践）</span></h3></p>
<div class="hcb_wrap"><pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="1">// MessageSender はメッセージ送信のインターフェース
type MessageSender interface {
	Send(message string)
}

// EmailSender はメール送信を実装する構造体
type EmailSender struct{}

// Send はMessageSenderインターフェースを実装
func (e *EmailSender) Send(message string) {
	// メール送信処理
	fmt.Printf("メールを送信中: %s\n", message)
}

// NotificationService は通知サービスを提供する構造体
type NotificationService struct {
	messageSender MessageSender
}

// NewNotificationService はNotificationServiceの新しいインスタンスを作成
func NewNotificationService(messageSender MessageSender) *NotificationService {
	return &NotificationService{
		messageSender: messageSender,
	}
}

// Send はメッセージを送信するメソッド
func (n *NotificationService) Send(message string) {
	n.messageSender.Send(message)
}

// 使用例
func main() {
	// EmailSenderを作成
	emailSender := &EmailSender{}

	// NotificationServiceに依存性を注入
	service := NewNotificationService(emailSender)

	// メッセージを送信
	service.Send("テストメッセージです")
}</code></pre><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;1&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p>このように、<code>NotificationService</code>は<code>MessageSender</code>という抽象（インターフェース）に依存し、<code>EmailSender</code>はその実装を担います。今後、<code>SmsSender</code>など新しい通知手段を追加しても、<code>NotificationService</code>のコードを変更せずに済みます。</p>
<p>以下は、具体的に、<code>SmsSender</code>メソッドを追加した場合のコードです。</p>
<div class="hcb_wrap"><pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="2">// MessageSender はメッセージ送信のインターフェース
type MessageSender interface {
	Send(message string)
}

// ----------------------- 既存の実装 -----------------------

type EmailSender struct{}

func (e *EmailSender) Send(message string) {
	fmt.Printf("メールを送信中: %s\n", message)
}

// ----------------------- 追加する実装 ----------------------

type SmsSender struct{}

func (s *SmsSender) Send(message string) {
	// SMS 送信処理（ここではダミー）
	fmt.Printf("SMS を送信中: %s\n", message)
}

// -------------------- 共通サービス層 -----------------------

type NotificationService struct {
	messageSender MessageSender
}

func NewNotificationService(sender MessageSender) *NotificationService {
	return &NotificationService{messageSender: sender}
}

func (n *NotificationService) Send(message string) {
	n.messageSender.Send(message)
}

// ------------------------- 使用例 --------------------------

func main() {
	// 1) Email で通知
	emailSender := &EmailSender{}
	emailService := NewNotificationService(emailSender)
	emailService.Send("メールのテストメッセージです")

	// 2) SMS で通知
	smsSender := &SmsSender{}
	smsService := NewNotificationService(smsSender)
	smsService.Send("SMS のテストメッセージです")
}</code></pre><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;2&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><hr></p>
<p><h2><span id="toc6">日常的なたとえ</span></h2></p>
<p><h3><span id="toc7">1&#xfe0f;&#x20e3; 具象（具体的なモノそのもの）に依存している例</span></h3></p>
<p>イメージとしては、 “その道具・方法しか使えない” 状態です。</p>
<p><table class="wp-table">
<thead>
<tr>
<th>シーン</th>
<th>説明</th>
<th>困るポイント</th>
</tr>
</thead>
<tbody>
<tr>
<td>➊ 乾電池式のおもちゃが “単三電池” 専用</td>
<td>取扱説明書には「このおもちゃは単三電池 2 本のみ対応」と書いてある。単四や充電池は入らない。</td>
<td>単三が切れたら遊べない。別規格が増えるたびに製品を買い替え or 改造が必要。</td>
</tr>
<tr>
<td>➋ コーヒーメーカーが “専用カプセル” だけを認識</td>
<td>機械内部のセンサーはメーカー純正カプセルの形状だけを前提に作られている。</td>
<td>新しい味のカプセルが出ても、形が少し違うとエラーになり抽出できない。</td>
</tr>
<tr>
<td>➌ スマートフォン用アクセサリが “Lightning 端子” 固定</td>
<td>ケーブル・ドック・マイク…すべて Lightning でしか給電・通信できない設計。</td>
<td>端末が USB-C に変わった瞬間、全部使えなくなる。</td>
</tr>
</tbody>
</table></p>
<p>「特定の電池・カプセル・コネクタ ＝ 具体的実装」にロックインされていると <strong>道具が増える or 仕様が変わるたびに本体側を作り直さなければならない</strong>。</p>
<p>これが、具象に依存している例です。</p>
<p><hr></p>
<p><h3><span id="toc8">2&#xfe0f;&#x20e3; 抽象（共通ルール・インターフェース）に依存している例</span></h3></p>
<p>イメージとしては、 “ルールさえ守れば何でも OK” な状態です。</p>
<p><table class="wp-table">
<thead>
<tr>
<th>シーン</th>
<th>説明</th>
<th>うれしいポイント</th>
</tr>
</thead>
<tbody>
<tr>
<td>➊ コンセント（AC100 V 50/60 Hz）を使う家電</td>
<td>ドライヤー・炊飯器・PC などは「家庭用 100 V の壁コンセント」というルールだけを前提に作る。</td>
<td>メーカーも形も違う家電を自由に差し替えられる。将来、AI 搭載炊飯器が出てもコンセントはそのまま。</td>
</tr>
<tr>
<td>➋ “交通系 IC カード” 対応改札機</td>
<td>Suica、PASMO、ICOCA… すべて “FeliCa タッチで残高を引く” というインターフェースを共有。</td>
<td>新しい地域カードが増えても改札機はソフト追加だけで対応。ハードを一から作り替えなくて済む。</td>
</tr>
<tr>
<td>➌ 汎用サイズのネジ穴（1/4-20 UNC）の三脚</td>
<td>カメラでもスマホホルダーでも、同じネジ規格を守れば載せ替え可能。</td>
<td>新しいカメラが発売されても三脚本体は継続利用。周辺機器のバリエーションが豊富に。</td>
</tr>
</tbody>
</table></p>
<p>「壁コンセント・IC 乗車券・ネジ規格 ＝ 抽象インターフェース」にロックインされていると <strong>新しい製品が増えても “共通ルール” さえ守れば双方そのまま使える</strong>。</p>
<p>これが、抽象に依存している例です。</p>
<p><hr></p>
<p><h3><span id="toc9">違いを一言で</span></h3></p>
<p><table class="wp-table">
<thead>
<tr>
<th>観点</th>
<th>具象依存</th>
<th>抽象依存</th>
</tr>
</thead>
<tbody>
<tr>
<td>依存先</td>
<td>“単三電池” のように 特定品そのもの</td>
<td>“乾電池 1.5 V” のような 共通仕様</td>
</tr>
<tr>
<td>変化への強さ</td>
<td>容易に壊れる・作り直しが必要</td>
<td>追加・交換が容易</td>
</tr>
<tr>
<td>開発者／利用者の自由度</td>
<td>限定的（ロックイン）</td>
<td>拡張しやすくイノベーションが起きやすい</td>
</tr>
</tbody>
</table></p>
<p>DIP を意識すると日常でもソフトウェアでも「<strong>長く使える・後で組み替えられる</strong>」設計ができ、<strong>“選択肢が増えても主役（高レベル）は迷わない”</strong> 世界が実現します。</p>
<p><hr></p>
<p><h2><span id="toc10">なぜ「逆転」なのか</span></h2></p>
<p><table class="wp-table">
<thead>
<tr>
<th>項目</th>
<th>ふつう（逆転前）</th>
<th>依存関係“逆転”後</th>
</tr>
</thead>
<tbody>
<tr>
<td>コードの層</td>
<td>高レベル（ビジネスロジック）が ↓ 低レベル（具体的処理）を直接呼ぶ</td>
<td>低レベルが ↑ 共通の“ルール（抽象）”に合わせる</td>
</tr>
<tr>
<td>物理的な依存（import / include）</td>
<td>高レベル → 低レベル</td>
<td>低レベル → 抽象 ← 高レベル</td>
</tr>
</tbody>
</table></p>
<p>つまり、本来“上”が“下”を<strong>見に行っていた依存の向き</strong>を、“下”が“上のルール”に<strong>合わせに行く形へひっくり返す</strong>からです。</p>
<p><hr></p>
<p><h2><span id="toc11">まとめ</span></h2></p>
<ul><li><strong>依存関係逆転の法則</strong>とは、「具体的な実装」ではなく「抽象（インターフェース）」に依存することで、柔軟で変更に強い設計を実現する原則です。</li><li>これにより、上位モジュールも下位モジュールも抽象に依存し、実装の入れ替えや拡張が容易になります。</li></ul>
<h3><span id="toc12">参考文献</span></h3>



<a rel="noopener" href="https://qiita.com/k2491p/items/686ee5dd72b4baf9a81a" title="【SOLID】依存関係逆転の原則を完全に理解したい - Qiita" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRmF2YXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tJTJGdSUyRjc2NzYzNTkwJTNGdiUzRDQ_aXhsaWI9cmItNC4wLjAmYXI9MSUzQTEmZml0PWNyb3AmbWFzaz1lbGxpcHNlJmJnPUZGRkZGRiZmbT1wbmczMiZzPWFjNmUyMDFiNzdiZDYwMjQ3ZjMwYjMyZTlmZWQxMDY4%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3D83b61c0cbe5bf96ab1817375e88345b1?ixlib=rb-4.0.0&#038;w=1200&#038;fm=jpg&#038;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9JUUzJTgwJTkwU09MSUQlRTMlODAlOTElRTQlQkUlOUQlRTUlQUQlOTglRTklOTYlQTIlRTQlQkYlODIlRTklODAlODYlRTglQkIlQTIlRTMlODElQUUlRTUlOEUlOUYlRTUlODklODclRTMlODIlOTIlRTUlQUUlOEMlRTUlODUlQTglRTMlODElQUIlRTclOTAlODYlRTglQTclQTMlRTMlODElOTclRTMlODElOUYlRTMlODElODQmdHh0LWFsaWduPWxlZnQlMkN0b3AmdHh0LWNvbG9yPSUyMzFFMjEyMSZ0eHQtZm9udD1IaXJhZ2lubyUyMFNhbnMlMjBXNiZ0eHQtc2l6ZT01NiZ0eHQtcGFkPTAmcz01MDVkMDIwMjk2NDYxNDZlYWNhN2Q2YjkyMDVjMWE4NQ&#038;mark-x=120&#038;mark-y=112&#038;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBrMjQ5MXAmdHh0LWNvbG9yPSUyMzFFMjEyMSZ0eHQtZm9udD1IaXJhZ2lubyUyMFNhbnMlMjBXNiZ0eHQtc2l6ZT0zNiZ0eHQtcGFkPTAmcz04MThhZWM4MDk1MzhkYmZjMDA1NDg3MTg3NjMxNjQ4Ng&#038;blend-x=242&#038;blend-y=480&#038;blend-w=838&#038;blend-h=46&#038;blend-fit=crop&#038;blend-crop=left%2Cbottom&#038;blend-mode=normal&#038;s=c53e353a99d144c4706153e93b44f0c0" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">【SOLID】依存関係逆転の原則を完全に理解したい - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">SOLIDの原則とは？ SOLIDは 変更に強い 理解しやすい などのソフトウェアを作ることを目的とした原則です。 次の5つの原則があります。 Single Responsibility Princi...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://qiita.com/k2491p/items/686ee5dd72b4baf9a81a" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">qiita.com</div></div></div></div></a>





<a rel="noopener" href="https://ja.wikibooks.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87" title="プログラミング/依存性逆転の原則 - Wikibooks" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fja.wikibooks.org%2Fwiki%2F%25E3%2583%2597%25E3%2583%25AD%25E3%2582%25B0%25E3%2583%25A9%25E3%2583%259F%25E3%2583%25B3%25E3%2582%25B0%2F%25E4%25BE%259D%25E5%25AD%2598%25E6%2580%25A7%25E9%2580%2586%25E8%25BB%25A2%25E3%2581%25AE%25E5%258E%259F%25E5%2589%2587?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">プログラミング/依存性逆転の原則 - Wikibooks</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://ja.wikibooks.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">ja.wikibooks.org</div></div></div></div></a>





<a rel="noopener" href="https://qiita.com/hirodragon/items/090467284d19a4cbafa0" title="初学者でも10分で理解できる依存性逆転の原則(Dependency inversion principle) - Qiita" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Fadvent-calendar-ogp-background-7940cd1c8db80a7ec40711d90f43539e.jpg%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnFpaXRhLWltYWdlLXN0b3JlLnMzLmFtYXpvbmF3cy5jb20lMkYwJTJGMjM1OTU4JTJGcHJvZmlsZS1pbWFnZXMlMkYxNTE3Njg3MDU4P2l4bGliPXJiLTQuMC4wJmFyPTElM0ExJmZpdD1jcm9wJm1hc2s9ZWxsaXBzZSZiZz1GRkZGRkYmZm09cG5nMzImcz05NTg5ZTcyNGFiYzcwMDA1ODFmNWNiZTdmYjNiMDcxMw%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3D062f8c9b71a7c72d46e04fba70c36424?ixlib=rb-4.0.0&#038;w=1200&#038;fm=jpg&#038;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9JUU1JTg4JTlEJUU1JUFEJUE2JUU4JTgwJTg1JUUzJTgxJUE3JUUzJTgyJTgyMTAlRTUlODglODYlRTMlODElQTclRTclOTAlODYlRTglQTclQTMlRTMlODElQTclRTMlODElOEQlRTMlODIlOEIlRTQlQkUlOUQlRTUlQUQlOTglRTYlODAlQTclRTklODAlODYlRTglQkIlQTIlRTMlODElQUUlRTUlOEUlOUYlRTUlODklODclMjhEZXBlbmRlbmN5JTIwaW52ZXJzaW9uJTIwcHJpbmNpcGxlJTI5JnR4dC1hbGlnbj1sZWZ0JTJDdG9wJnR4dC1jb2xvcj0lMjMzQTNDM0MmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9NTYmdHh0LXBhZD0wJnM9MmNkN2Q4YWQ1N2QxYTMxNmUyYzBiZDMxNGY3NWVlMDk&#038;mark-x=120&#038;mark-y=112&#038;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBoaXJvZHJhZ29uJnR4dC1jb2xvcj0lMjMzQTNDM0MmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9MzYmdHh0LXBhZD0wJnM9NmJlZDZlYmQ2ZjUyNzdkMjczOWNmODU3ZDhkNmM1YmY&#038;blend-x=242&#038;blend-y=480&#038;blend-w=838&#038;blend-h=46&#038;blend-fit=crop&#038;blend-crop=left%2Cbottom&#038;blend-mode=normal&#038;s=80cc64197e5cc97fa1a2b30b0c0d3f73" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">初学者でも10分で理解できる依存性逆転の原則(Dependency inversion principle) - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">この記事について この記事は YYPHPアドベントカレンダー22日目の記事となります。 内容としては私が主催しているPHPer向けの勉強会 ぺちオブにて開催した、初心者向けのオブジェクト指向勉強会にて...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://qiita.com/hirodragon/items/090467284d19a4cbafa0" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">qiita.com</div></div></div></div></a>





<a rel="noopener" href="https://qiita.com/phenan/items/a8030b164d95a035a5dc" title="令和版: 依存関係逆転の法則の実現方法 - Qiita" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnFpaXRhLWltYWdlLXN0b3JlLnMzLmFtYXpvbmF3cy5jb20lMkYwJTJGMjczNjMlMkZwcm9maWxlLWltYWdlcyUyRjE0NzM2ODQ5OTI_aXhsaWI9cmItNC4wLjAmYXI9MSUzQTEmZml0PWNyb3AmbWFzaz1lbGxpcHNlJmJnPUZGRkZGRiZmbT1wbmczMiZzPWRlM2UzMWJmOWYxNzllOWVlNzU1N2E3NjMyYWMzZDNi%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3D0ad522ebdc1e0f85c54b9906f165e95f?ixlib=rb-4.0.0&#038;w=1200&#038;fm=jpg&#038;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9JUU0JUJCJUE0JUU1JTkyJThDJUU3JTg5JTg4JTNBJTIwJUU0JUJFJTlEJUU1JUFEJTk4JUU5JTk2JUEyJUU0JUJGJTgyJUU5JTgwJTg2JUU4JUJCJUEyJUUzJTgxJUFFJUU2JUIzJTk1JUU1JTg5JTg3JUUzJTgxJUFFJUU1JUFFJTlGJUU3JThGJUJFJUU2JTk2JUI5JUU2JUIzJTk1JnR4dC1hbGlnbj1sZWZ0JTJDdG9wJnR4dC1jb2xvcj0lMjMxRTIxMjEmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9NTYmdHh0LXBhZD0wJnM9ZGQ2OTBmMjFkMTI5MmYzMTIzNWNlYWNlODkwMWMxYmU&#038;mark-x=120&#038;mark-y=112&#038;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBwaGVuYW4mdHh0LWNvbG9yPSUyMzFFMjEyMSZ0eHQtZm9udD1IaXJhZ2lubyUyMFNhbnMlMjBXNiZ0eHQtc2l6ZT0zNiZ0eHQtcGFkPTAmcz0wNTllMzQwM2RmZGM5YWNjZDRkYjdhNTdkMDM4NGNhNA&#038;blend-x=242&#038;blend-y=480&#038;blend-w=838&#038;blend-h=46&#038;blend-fit=crop&#038;blend-crop=left%2Cbottom&#038;blend-mode=normal&#038;s=e3eb3d6af9859bf7a39e899e0d524c15" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">令和版: 依存関係逆転の法則の実現方法 - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">依存関係逆転の法則とは コアのロジックが実装の詳細に依存しないようにして、モジュール間を疎結合にしましょうという原則。 Wikipediaでは以下のように説明されている。 上位モジュールはいかなるもの...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://qiita.com/phenan/items/a8030b164d95a035a5dc" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">qiita.com</div></div></div></div></a>





<a rel="noopener" href="https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87" title="依存性逆転の原則 - Wikipedia" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fja.wikipedia.org%2Fwiki%2F%25E4%25BE%259D%25E5%25AD%2598%25E6%2580%25A7%25E9%2580%2586%25E8%25BB%25A2%25E3%2581%25AE%25E5%258E%259F%25E5%2589%2587?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">依存性逆転の原則 - Wikipedia</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">ja.wikipedia.org</div></div></div></div></a>





<a rel="noopener" href="https://zenn.dev/yoshinani_dev/articles/c743a3d046fa78" title="依存性の逆転のいちばんわかりやすい説明" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://res.cloudinary.com/zenn/image/upload/s--iUsJWgr5--/c_fit%2Cg_north_west%2Cl_text:notosansjp-medium.otf_55:%25E4%25BE%259D%25E5%25AD%2598%25E6%2580%25A7%25E3%2581%25AE%25E9%2580%2586%25E8%25BB%25A2%25E3%2581%25AE%25E3%2581%2584%25E3%2581%25A1%25E3%2581%25B0%25E3%2582%2593%25E3%2582%258F%25E3%2581%258B%25E3%2582%258A%25E3%2582%2584%25E3%2581%2599%25E3%2581%2584%25E8%25AA%25AC%25E6%2598%258E%2Cw_1010%2Cx_90%2Cy_100/g_south_west%2Cl_text:notosansjp-medium.otf_34:Nakano%2520as%2520a%2520Service%2Cx_220%2Cy_108/bo_3px_solid_rgb:d6e3ed%2Cg_south_west%2Ch_90%2Cl_fetch:aHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL3plbm4tdXNlci11cGxvYWQvYXZhdGFyL2ZhMjMyNjAxN2QuanBlZw==%2Cr_20%2Cw_90%2Cx_92%2Cy_102/co_rgb:6e7b85%2Cg_south_west%2Cl_text:notosansjp-medium.otf_30:YOSHINANI%2Cx_220%2Cy_160/bo_4px_solid_white%2Cg_south_west%2Ch_50%2Cl_fetch:aHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EtL0FPaDE0R2lrTHp0c1hkMHVUN3dhQ1FwWTBtQ3JfbzVKbnFJUkE1U1BFV1gxPXM5Ni1j%2Cr_max%2Cw_50%2Cx_139%2Cy_84/v1627283836/default/og-base-w1200-v2.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">依存性の逆転のいちばんわかりやすい説明</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://zenn.dev/yoshinani_dev/articles/c743a3d046fa78" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">zenn.dev</div></div></div></div></a>





<a rel="noopener" href="https://emb-sw-eng.com/solid_d/" title="SOLID原則とは？SOLIDのD、依存関係逆転(依存性逆転)の原則と得られるメリットをわかりやすく解説" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://emb-sw-eng.com/wp-content/uploads/2021/03/SOLID_D_EC-1.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">SOLID原則とは？SOLIDのD、依存関係逆転(依存性逆転)の原則と得られるメリットをわかりやすく解説</div><div class="blogcard-snippet external-blogcard-snippet">現役車載組込みソフトエンジニアの竹です。今回はSOLID原則のD、依存関係逆転の原則についてまとめます。この記事は依存関係逆転の原則の概要を説明するものであり、具体的な実装例については触れない初心者向...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://emb-sw-eng.com/solid_d/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">emb-sw-eng.com</div></div></div></div></a>





<a rel="noopener" href="https://zenn.dev/chida/articles/e46a66cd9d89d1" title="【SOLID原則】依存性逆転の原則 - DIP" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://res.cloudinary.com/zenn/image/upload/s--nTMX5Z2M--/c_fit%2Cg_north_west%2Cl_text:notosansjp-medium.otf_55:%25E3%2580%2590SOLID%25E5%258E%259F%25E5%2589%2587%25E3%2580%2591%25E4%25BE%259D%25E5%25AD%2598%25E6%2580%25A7%25E9%2580%2586%25E8%25BB%25A2%25E3%2581%25AE%25E5%258E%259F%25E5%2589%2587%2520-%2520DIP%2Cw_1010%2Cx_90%2Cy_100/g_south_west%2Cl_text:notosansjp-medium.otf_37:TC%2Cx_203%2Cy_121/g_south_west%2Ch_90%2Cl_fetch:aHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL3plbm4tdXNlci11cGxvYWQvYXZhdGFyLzI5NmE3MDhmYWEuanBlZw==%2Cr_max%2Cw_90%2Cx_87%2Cy_95/v1627283836/default/og-base-w1200-v2.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">【SOLID原則】依存性逆転の原則 - DIP</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://zenn.dev/chida/articles/e46a66cd9d89d1" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">zenn.dev</div></div></div></div></a>





<a rel="noopener" href="https://blog.openreplay.com/ja/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87-%E8%AA%AC%E6%98%8E/" title="依存性逆転の原則とは？簡単に説明" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://blog.openreplay.com/images/dependency-inversion-principle-explained/images/hero.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">依存性逆転の原則とは？簡単に説明</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://blog.openreplay.com/ja/依存性逆転の原則-説明/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">blog.openreplay.com</div></div></div></div></a>





<a rel="noopener" href="https://rookie-programmer.jp/?p=75" title="【SOLID原則】新人エンジニアが教える「依存関係逆転の原則」" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://rookie-programmer.jp/wp-content/uploads/2023/08/608ee7dc1fd91ba151015173d79db8cb.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">【SOLID原則】新人エンジニアが教える「依存関係逆転の原則」</div><div class="blogcard-snippet external-blogcard-snippet">こんにちは。新人プログラマーの岩本です。今回はSOLID原則の1つ「依存関係逆転の原則(Dependency Inversion Principle)」について、自分なりに調べたことをまとめます。プロ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://rookie-programmer.jp/?p=75" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">rookie-programmer.jp</div></div></div></div></a>





<a rel="noopener" href="https://qiita.com/marienplatz/items/1e3fe7597afc75b22399" title="依存性逆転の原則って、結局何が逆転してるの？ - Qiita" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnNlY3VyZS5ncmF2YXRhci5jb20lMkZhdmF0YXIlMkYzMTY1MWNjMzkyZjBhZGRiZWUxODEyYzU5YTYyYmI3Nj9peGxpYj1yYi00LjAuMCZhcj0xJTNBMSZmaXQ9Y3JvcCZtYXNrPWVsbGlwc2UmYmc9RkZGRkZGJmZtPXBuZzMyJnM9M2Q0MmY3NTEwZWI5MGQwODJiOTdkNzljNjQyZTE0ZjM%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3D192529e8ca091202e1ebe1f0ab80190f?ixlib=rb-4.0.0&#038;w=1200&#038;fm=jpg&#038;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9JUU0JUJFJTlEJUU1JUFEJTk4JUU2JTgwJUE3JUU5JTgwJTg2JUU4JUJCJUEyJUUzJTgxJUFFJUU1JThFJTlGJUU1JTg5JTg3JUUzJTgxJUEzJUUzJTgxJUE2JUUzJTgwJTgxJUU3JUI1JTkwJUU1JUIxJTgwJUU0JUJEJTk1JUUzJTgxJThDJUU5JTgwJTg2JUU4JUJCJUEyJUUzJTgxJTk3JUUzJTgxJUE2JUUzJTgyJThCJUUzJTgxJUFFJUVGJUJDJTlGJnR4dC1hbGlnbj1sZWZ0JTJDdG9wJnR4dC1jb2xvcj0lMjMxRTIxMjEmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9NTYmdHh0LXBhZD0wJnM9NTdiODI1YzRiYzBiZDRjMDA1N2E5ODUxOTUwNWJhNjE&#038;mark-x=120&#038;mark-y=112&#038;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBtYXJpZW5wbGF0eiZ0eHQtY29sb3I9JTIzMUUyMTIxJnR4dC1mb250PUhpcmFnaW5vJTIwU2FucyUyMFc2JnR4dC1zaXplPTM2JnR4dC1wYWQ9MCZzPTgzNDIxNmI5NjdhZWU2MjMzOWRjZWE3NmMzNGZkNmRj&#038;blend-x=242&#038;blend-y=480&#038;blend-w=838&#038;blend-h=46&#038;blend-fit=crop&#038;blend-crop=left%2Cbottom&#038;blend-mode=normal&#038;s=00b3eecf2fbbc78b41218a9365d64d42" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">依存性逆転の原則って、結局何が逆転してるの？ - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">bitFlyer Advent Calendar 2022の2日目です。 こんにちは、私はフロントエンド開発部でAndroidエンジニアをしています。 タイトルは、依存性逆転の原則に対する私のファース...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://qiita.com/marienplatz/items/1e3fe7597afc75b22399" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">qiita.com</div></div></div></div></a>





<a rel="noopener" href="https://qiita.com/zizynonno/items/6bf71d73d790e27fb9ee" title="依存関係逆転の原則 - Qiita" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnFpaXRhLWltYWdlLXN0b3JlLnMzLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20lMkYwJTJGMjcwMTMyJTJGcHJvZmlsZS1pbWFnZXMlMkYxNjA4NjMxMjg5P2l4bGliPXJiLTQuMC4wJmFyPTElM0ExJmZpdD1jcm9wJm1hc2s9ZWxsaXBzZSZiZz1GRkZGRkYmZm09cG5nMzImcz1iMGQ1YTIzM2I1YjkxMGMzMjgyMDA4MDIxZTkzNmUyZQ%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3De6041d4fd6b5125fff26231dfd81214a?ixlib=rb-4.0.0&#038;w=1200&#038;fm=jpg&#038;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9JUU0JUJFJTlEJUU1JUFEJTk4JUU5JTk2JUEyJUU0JUJGJTgyJUU5JTgwJTg2JUU4JUJCJUEyJUUzJTgxJUFFJUU1JThFJTlGJUU1JTg5JTg3JnR4dC1hbGlnbj1sZWZ0JTJDdG9wJnR4dC1jb2xvcj0lMjMxRTIxMjEmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9NTYmdHh0LXBhZD0wJnM9OWMwMThjZjMzYmY1MjQ1NjY2MDE3YmNmYmU4ZDZmZGM&#038;mark-x=120&#038;mark-y=112&#038;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDB6aXp5bm9ubm8mdHh0LWNvbG9yPSUyMzFFMjEyMSZ0eHQtZm9udD1IaXJhZ2lubyUyMFNhbnMlMjBXNiZ0eHQtc2l6ZT0zNiZ0eHQtcGFkPTAmcz1mMGM3ZWM5YjM4NDM3ZDgxMTY2NDMxNDNhYmY3YTU4Yw&#038;blend-x=242&#038;blend-y=480&#038;blend-w=838&#038;blend-h=46&#038;blend-fit=crop&#038;blend-crop=left%2Cbottom&#038;blend-mode=normal&#038;s=90753a4f4636a2d6ce9d94f5f0c9b37f" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">依存関係逆転の原則 - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">依存関係逆転の原則（Dependency Inversion Principle、DIP）は、ソフトウェア設計の原則の一つ。高レベルモジュールが低レベルモジュールに直接依存しないように設計することを意...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://qiita.com/zizynonno/items/6bf71d73d790e27fb9ee" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">qiita.com</div></div></div></div></a>





<a rel="noopener" href="https://yuru-uni.com/2023/04/23/solid_principle_d/" title="【C#設計】SOLID原則をUnity公式サンプルで学ぼう～D：依存性逆転の原則～" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://yuru-uni.com/wp-content/uploads/2023/04/thumbnail_solid_d.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">【C#設計】SOLID原則をUnity公式サンプルで学ぼう～D：依存性逆転の原則～</div><div class="blogcard-snippet external-blogcard-snippet">はじめに講座トップに戻るこの講座ではプログラミングの設計を勉強する際に避けては通れない「SOLID原則」について学ぶことができます。SOLID原則は有名ですので名前を知っている人も多いかもしれませんが...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://yuru-uni.com/2023/04/23/solid_principle_d/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">yuru-uni.com</div></div></div></div></a>





<a rel="noopener" href="https://blog.chiyuu.co.jp/2024/06/06/dependency-inversion-principle/" title="SOLID原則の依存性逆転の原則(DIP)についてRubyで解説 | 株式会社CHIYUU 公式ブログ" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://blog.chiyuu.co.jp/wp-content/uploads/2024/05/blog_thumbnail-4.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">SOLID原則の依存性逆転の原則(DIP)についてRubyで解説 | 株式会社CHIYUU 公式ブログ</div><div class="blogcard-snippet external-blogcard-snippet">オブジェクト指向プログラミングにおいて、コードの保守性、可読性、再利用性を高めるために、SOLID原則と呼ばれ</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://blog.chiyuu.co.jp/2024/06/06/dependency-inversion-principle/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">blog.chiyuu.co.jp</div></div></div></div></a>





<a rel="noopener" href="https://plainprogram.com/dependency-inversion-principle/" title="【SOLID】依存性逆転原則～大事なのは、抽象にもとづくこと | プレイン・プログラム" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://plainprogram.com/wp-content/uploads/2023/05/dependency-inversion-principle.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">【SOLID】依存性逆転原則～大事なのは、抽象にもとづくこと | プレイン・プログラム</div><div class="blogcard-snippet external-blogcard-snippet">依存関係逆転原則 Dependency Inversion Principle 上位モジュールは下位モジュールに依存してはならない。両者は抽象に依存すべき。 抽象は詳細に依存してはならない。詳細は抽象...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://plainprogram.com/dependency-inversion-principle/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">plainprogram.com</div></div></div></div></a><p>The post <a href="https://aichi.blog/dependency-inversion-principle/">依存関係逆転の法則（Dependency Inversion Principle）: Go 言語で柔軟で強い設計を実現するには？</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>CloudFormationのスタック削除時に発生する「Role is invalid or cannot be assumed」エラーの解決方法</title>
		<link>https://aichi.blog/cloudformation-role-error/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cloudformation-role-error</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Tue, 01 Apr 2025 07:59:41 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[エラー解決]]></category>
		<guid isPermaLink="false">https://aichi.blog/cloudformation-role-error/</guid>

					<description><![CDATA[<p>AWS CloudFormation でスタックを削除しようとした際に、次のようなエラーが発生することがあります: Role arn:aws:iam::795600592301:role/CFn-cicd-CFnRole [&#8230;]</p>
<p>The post <a href="https://aichi.blog/cloudformation-role-error/">CloudFormationのスタック削除時に発生する「Role is invalid or cannot be assumed」エラーの解決方法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>AWS CloudFormation でスタックを削除しようとした際に、次のようなエラーが発生することがあります:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-" data-lang="" data-show-lang="1"><code class="language-" data-hcb-clip="0">Role arn:aws:iam::795600592301:role/CFn-cicd-CFnRole-yXR5dzw2KIcH is invalid or cannot be assumed</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;0&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p>このエラーは CloudFormation が指定された IAM ロールを &#8220;assume&#8221; (引き受け)ようとした際に発生します。以下、原因と実際の対処方法を解説します。</p>
<p><h2><span id="toc1">原因</span></h2>
</p>
<p>CloudFormation はスタック作成または削除の際に IAM ロールを &#8220;AssumeRole&#8221; する必要があります。しかし、以下のような信頼ポリシーだと CloudFormation はこのロールを引き受けません:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-json" data-lang="json" data-show-lang="1"><code class="language-json" data-hcb-clip="1">{
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::795600592301:root"
  },
  "Action": "sts:AssumeRole",
  "Condition": {}
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;1&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p>これは「同じアカウントの全IAMユーザー/ロールは使える」という意味ですが、CloudFormationサービスは <code>cloudformation.amazonaws.com</code> として書かれるので、上記の設定だけでは足りません。</p>
<p><h2><span id="toc2">解決方法</span></h2>
</p>
<p><h3><span id="toc3">方法1. 信頼ポリシーをCloudFormation対応に修正</span></h3>
</p>
<p>IAM ロールに次のような信頼ポリシーを設定することで、CloudFormation が引き受けるようになります:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-json" data-lang="json" data-show-lang="1"><code class="language-json" data-hcb-clip="2">{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudformation.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;2&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc4">方法2. CLIでの修正例</span></h3>
</p>
<p>上記の信頼ポリシーを AWS CLI を使って適用する方法:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-bash" data-lang="bash" data-show-lang="1"><code class="language-bash" data-hcb-clip="3">aws iam update-assume-role-policy \
  --role-name CFn-cicd-CFnRole-yXR5dzw2KIcH \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "cloudformation.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;3&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc5">方法3. CloudFormation 以外のサービスも使う場合</span></h3>
</p>
<p>このロールを CodePipeline や Lambda も使う場合は、Principal を複数指定して両方に対応させましょう:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-json" data-lang="json" data-show-lang="1"><code class="language-json" data-hcb-clip="4">"Principal": {
  "AWS": "arn:aws:iam::795600592301:root",
  "Service": "cloudformation.amazonaws.com"
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;4&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc6">おわりに</span></h2>
</p>
<p>この問題は、ロールがあるのに assume できないというミスマッチな情報から原因を探るため、初心者にとってはとても難解な障害です。</p>
<p>同じエラーに迷った方は、IAM ロールの信頼ポリシーを確認し、CloudFormation サービスが Assume できるようになっているかどうかをチェックしてみてください。</p>
<p>なお、この設定を改めたあとは CloudFormation スタックの削除が通常どおりに実行できるはずです。</p><p>The post <a href="https://aichi.blog/cloudformation-role-error/">CloudFormationのスタック削除時に発生する「Role is invalid or cannot be assumed」エラーの解決方法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Kaggle】Kaggle Notebook を Docker を用いてローカル環境で構築する手順と作成方法</title>
		<link>https://aichi.blog/kaggle-notebook/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=kaggle-notebook</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Sat, 29 Mar 2025 14:49:42 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Kaggle]]></category>
		<category><![CDATA[ディープラーニング]]></category>
		<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://aichi.blog/kaggle-notebook/</guid>

					<description><![CDATA[<p>今回の記事では、Kaggle Notebook をローカル環境で構築する手順と作成方法を解説します。 実現すること kaggle API を使用可能に（notebook やデータセットのダウンロードなど） 起動後すぐに  [&#8230;]</p>
<p>The post <a href="https://aichi.blog/kaggle-notebook/">【Kaggle】Kaggle Notebook を Docker を用いてローカル環境で構築する手順と作成方法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>今回の記事では、Kaggle Notebook をローカル環境で構築する手順と作成方法を解説します。</p>
<p><h2><span id="toc1">実現すること</span></h2>
</p>
<ul>
<li>kaggle API を使用可能に（notebook やデータセットのダウンロードなど）</li>
<li>起動後すぐに Jupyter を使用可能</li>
<li>ローカルとコンテナ間で<code>/working</code>ディレクトリを同期</li>
</ul>
<h2><span id="toc2">ディレクトリ構成</span></h2>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-" data-lang="" data-show-lang="1"><code class="language-" data-hcb-clip="0">kaggle/
├── Dockerfile
├── docker-compose.yml
├── kaggle.json              # Kaggle API Key（手動で配置）
├── requirements.txt         # 必要なパッケージを記載
├── working/                 # Kaggleプロジェクト作業ディレクトリ
└── input/                   # コンペデータ配置用（自動でコンテナ内にマウント）</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;0&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc3">手順</span></h2>
</p>
<p><h4>1. ディレクトリを作成</h4>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-shell" data-lang="shell" data-show-lang="1"><code class="language-shell" data-hcb-clip="1">mkdir kaggle
cd kaggle

touch Dockerfile docker-compose.yml
mkdir working input</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;1&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h4>2. kaggle API Key を取得</h4>
</p>
<p>Kaggle サイトにアクセスして、API Key を取得してください。<code>.json</code>ファイルがダウンロードされるので、名前を<code>kaggle.json</code>に変更して、<code>kaggle</code>ディレクトリに配置してください。</p>
<p><h4>3. requirements.txt を作成</h4>
</p>
<p>必要なパッケージを記載してください。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-" data-lang="" data-show-lang="1"><code class="language-" data-hcb-clip="2">numpy
pandas
matplotlib
scikit-learn
seaborn</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;2&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h4>4. Dockerfile を作成</h4>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-dockerfile" data-lang="dockerfile" data-show-lang="1"><code class="language-dockerfile" data-hcb-clip="3">FROM gcr.io/kaggle-gpu-images/python:latest

# Kaggle CLI用APIキーを配置
COPY kaggle.json /root/.kaggle/kaggle.json
RUN chmod 600 /root/.kaggle/kaggle.json

# requirements.txt のコピーとインストール
COPY requirements.txt /kaggle/requirements.txt
RUN pip install --no-cache-dir -r /kaggle/requirements.txt

# Jupyter用のカーネルをインストール
RUN pip install ipykernel
RUN python -m ipykernel install --user --name kaggle-env --display-name "Python (Kaggle)"

# デフォルトディレクトリの作成
RUN mkdir -p /kaggle/input /kaggle/working

# 作業ディレクトリを設定（docker exec -it kaggle bash をした際に、このディレクトリが開かれる）
WORKDIR /kaggle/working

# Jupyterを起動できるようにポートを空けておく
EXPOSE 8888

# 起動時にJupyterを自動起動
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--allow-root", "--NotebookApp.token=''", "--NotebookApp.password=''"]</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;3&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h4>5. docker-compose.yml を作成</h4>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-yaml" data-lang="yaml" data-show-lang="1"><code class="language-yaml" data-hcb-clip="4">version: "3.9"

services:
  kaggle:
    platform: linux/amd64 # Apple Siliconで起動する際に必要
    build: .
    container_name: kaggle
    ports:
      - "8888:8888"
    volumes:
      - ./working:/kaggle/working
      - ./input:/kaggle/input
    tty: true
    stdin_open: true</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;4&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h4>6. コンテナを起動</h4>
</p>
<p>ここで注意が必要です。</p>
<p>最初に Docker デスクトップを起動し、歯車アイコンをクリック。<code>Resources</code>タブをクリックし、<code>Virtual Machines</code>の値を変更してください。</p>
<p><img decoding="async" src="https://aichi.blog/wp-content/uploads/2025/05/docker-setting-1.png" alt="docker-setting"></p>
<p>僕の場合、136 にしました。参考までに。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-shell" data-lang="shell" data-show-lang="1"><code class="language-shell" data-hcb-clip="5">docker compose up -d</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;5&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p>コマンドが終了するまで、結構時間がかかりますので、気長に待ちましょう。</p>
<p><h4>7. コンテナに接続</h4>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-shell" data-lang="shell" data-show-lang="1"><code class="language-shell" data-hcb-clip="6">docker exec -it kaggle bash
jupyter notebook --ip=0.0.0.0 --port=8888 --allow-root --NotebookApp.token=''
# --ip=0.0.0.0 : すべてのIPからのアクセスを許可
# --port=8888 : ポート番号を指定
# --allow-root : ルートユーザーでの起動を許可（Dockerでは必要）
# --NotebookApp.token='' : 認証トークンなしでアクセス可（セキュリティ注意）</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;6&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><code>/kaggle/working</code>ディレクトリが開かれます。こちらで、jupyter notebook を作成することができます！</p>
<p><h2><span id="toc4">Kaggle API のコマンド</span></h2>
</p>
<p>以下に、Kaggle API のコマンドをまとめました。<br />
詳しい情報は公式ドキュメントを参照してください。</p>
<p><h3><span id="toc5">コンペデータのダウンロード</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-shell" data-lang="shell" data-show-lang="1"><code class="language-shell" data-hcb-clip="7">kaggle competitions download -c &lt;コンペ名&gt; -p &lt;保存先パス&gt;
kaggle competitions download -c titanic -p /kaggle/input/titanic
unzip ファイル名.zip -d 解凍先パス</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;7&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc6">コンペに提出</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-shell" data-lang="shell" data-show-lang="1"><code class="language-shell" data-hcb-clip="8">kaggle competitions submit -c &lt;コンペ名&gt; -f &lt;提出ファイル&gt; -m "&lt;コメント&gt;"
kaggle competitions submit -c titanic -f submission.csv -m "1st submission"</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;8&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc7">提出履歴</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-shell" data-lang="shell" data-show-lang="1"><code class="language-shell" data-hcb-clip="9">kaggle competitions submissions -c &lt;コンペ名&gt;</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;9&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc8">ノートブックの取得コマンド</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-shell" data-lang="shell" data-show-lang="1"><code class="language-shell" data-hcb-clip="10">kaggle kernels pull &lt;username&gt;/&lt;notebook-name&gt;</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;10&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc9">まとめ</span></h2>
</p>
<p>Kaggle Notebook をローカル環境で構築する手順と作成方法を解説しました。<br />
こちらのプロジェクトで実現できることは、</p>
<ul>
<li>kaggle API を使用可能に（notebook やデータセットのダウンロードなど）</li>
<li>起動後すぐに Jupyter を使用可能</li>
<li>ローカルとコンテナ間で<code>/working</code>ディレクトリを同期</li>
</ul>
<p>です。</p>
<p>参考になれば幸いです。</p><p>The post <a href="https://aichi.blog/kaggle-notebook/">【Kaggle】Kaggle Notebook を Docker を用いてローカル環境で構築する手順と作成方法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>環境変数の効率的な管理方法：os.Getenv vs os.LookupEnv と github.com/caarlos0/env の活用ガイド</title>
		<link>https://aichi.blog/go-env/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=go-env</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Wed, 28 May 2025 09:03:44 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[クロージャー]]></category>
		<category><![CDATA[文法]]></category>
		<category><![CDATA[開発]]></category>
		<guid isPermaLink="false">https://aichi.blog/go-env/</guid>

					<description><![CDATA[<p>TL;DR;Go言語での環境変数管理には主にos.Getenvとos.LookupEnvの2つの方法があります。os.Getenvはシンプルでデフォルト値の設定に適しており、os.LookupEnvは環境変数の存在確認が [&#8230;]</p>
<p>The post <a href="https://aichi.blog/go-env/">環境変数の効率的な管理方法：os.Getenv vs os.LookupEnv と github.com/caarlos0/env の活用ガイド</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><p class="is-style-big_icon_check">TL;DR;<br />Go言語での環境変数管理には主に<code>os.Getenv</code>と<code>os.LookupEnv</code>の2つの方法があります。<code>os.Getenv</code>はシンプルでデフォルト値の設定に適しており、<code>os.LookupEnv</code>は環境変数の存在確認が必要な場合に使います。より高度な環境変数管理には<code>github.com/caarlos0/env</code>ライブラリが推奨され、構造体タグによる型安全な設定が可能です。テスト時は<code>t.Setenv</code>を使用することで、環境変数の一時的な設定と自動復元が簡単に行えます。</p>
</p>
<p>Go言語の環境変数を取得する方法には<code>os.Getenv</code>と<code>os.LookupEnv</code>の2つの方法があります。</p>
<p><h2><span id="toc1">os.Getenv関数</span></h2>
</p>
<ul>
<li><strong>戻り値</strong>: 文字列のみ</li>
<li><strong>動作</strong>: 環境変数が存在しない場合、空文字列<code>""</code>を返す</li>
<li><strong>問題</strong>: 環境変数が存在しないのか、空文字列が設定されているのか区別できない</li>
</ul>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="0">package main

import (
    "fmt"
    "os"
)

func main() {
    // 存在しない環境変数
    value1 := os.Getenv("NON_EXISTENT_VAR")
    fmt.Printf("NON_EXISTENT_VAR: '%s'\n", value1) // 出力: ''

    // 空文字列が設定された環境変数（事前にexport EMPTY_VAR=""で設定）
    value2 := os.Getenv("EMPTY_VAR")
    fmt.Printf("EMPTY_VAR: '%s'\n", value2) // 出力: ''

    // どちらも同じ結果になってしまう
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;0&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc2">os.LookupEnv関数</span></h2>
</p>
<ul>
<li><strong>戻り値</strong>: <code>(string, bool)</code>の2つの値</li>
<li><strong>動作</strong>: 環境変数の値と、存在するかどうかのbool値を返す</li>
<li><strong>利点</strong>: 環境変数の存在を明確に判定できる</li>
</ul>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="1">package main

import (
    "fmt"
    "os"
)

func main() {
    // 存在しない環境変数
    value1, exists1 := os.LookupEnv("NON_EXISTENT_VAR")
    fmt.Printf("NON_EXISTENT_VAR: value='%s', exists=%t\n", value1, exists1)
    // 出力: NON_EXISTENT_VAR: value='', exists=false

    // 空文字列が設定された環境変数
    value2, exists2 := os.LookupEnv("EMPTY_VAR")
    fmt.Printf("EMPTY_VAR: value='%s', exists=%t\n", value2, exists2)
    // 出力: EMPTY_VAR: value='', exists=true

    // 値が設定された環境変数
    value3, exists3 := os.LookupEnv("PATH")
    fmt.Printf("PATH exists: %t\n", exists3)
    // 出力: PATH exists: true
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;1&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc3">実用的な使い分け例</span></h2>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="2">package main

import (
    "fmt"
    "os"
)

func getConfig() {
    // os.Getenvの場合：デフォルト値を設定
    dbHost := os.Getenv("DB_HOST")
    if dbHost == "" {
        dbHost = "localhost" // 空文字列でもデフォルト値を使用
    }

    // os.LookupEnvの場合：存在チェック
    dbPort, exists := os.LookupEnv("DB_PORT")
    if !exists {
        dbPort = "5432"
        fmt.Println("DB_PORT not set, using default")
    } else if dbPort == "" {
        fmt.Println("DB_PORT is set but empty!")
        // 空文字列が明示的に設定された場合の処理
    }

    fmt.Printf("DB_HOST: %s, DB_PORT: %s\n", dbHost, dbPort)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;2&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<ul>
<li><strong><code>os.Getenv</code></strong>: シンプルで、デフォルト値を簡単に設定したい場合に適している</li>
<li><strong><code>os.LookupEnv</code></strong>: 環境変数の存在を明確に判定したい場合や、空文字列と未設定を区別したい場合に適している</li>
</ul>
<p>一般的には、環境変数の存在チェックが重要な場合は<code>os.LookupEnv</code>を、単純にデフォルト値で十分な場合は<code>os.Getenv</code>を使用します。</p>
<p><h2><span id="toc4">サードパーティ：github.com/caarlos0/envの使用</span></h2>
</p>
<p>Web開発をする場合、DBやSaasの接続情報など、複数の環境変数情報が必要になります。</p>
<p>OS パッケージのみを使って環境変数を扱おうとすると環境変数が増えるたびに <code>OS.Getenv</code> 関数を呼び出して変数に値を設定する必要があります。</p>
<p>また、OS パッケージで取得した環境変数の値は string 型なので何らかのスライスや数字型として環境変数を扱いたい場合はそれぞれのパース処理を書く必要も出てきます。</p>
<p>これらを簡略化するためにサードパーティのライブラリ<code>github.com/caarlos0/env</code>を使うと良いです。</p>
<p>このライブラリは構造体のタグを使って環境変数を自動的にマッピングできる便利なツールです。</p>
<p>標準パッケージと比較して次のような優位な点があります。<br />
●<code>Parse</code>関数を一度呼ぶだけで複数の環境変数を読み込むことができる<br />
●構造体への<code>tags</code>で環境変数とフィールドを細付けられる<br />
●<code>string</code>型以外の読み込みができる<br />
●デフォルト値の設定をすることができる<br />
●環境変数未設定の場合は<code>error</code>を返すことを指定できる</p>
<p><h2><span id="toc5">基本的な使用方法</span></h2>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="3">package main

import (
    "fmt"
    "log"

    "github.com/caarlos0/env/v10"
)

type Config struct {
    // 必須の環境変数
    DatabaseURL string `env:"DATABASE_URL,required"`

    // デフォルト値付き
    Port int `env:"PORT" envDefault:"8080"`

    // オプション（デフォルト値なし）
    RedisURL string `env:"REDIS_URL"`

    // bool型
    Debug bool `env:"DEBUG" envDefault:"false"`
}

func main() {
    cfg := Config{}
    if err := env.Parse(&cfg); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Config: %+v\n", cfg)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;3&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc6">os.Getenv/LookupEnvとの比較</span></h2>
</p>
<p><h3><span id="toc7">従来の方法（os.Getenv使用）</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="4">package main

import (
    "fmt"
    "os"
    "strconv"
)

type Config struct {
    DatabaseURL string
    Port        int
    RedisURL    string
    Debug       bool
}

func loadConfigManual() Config {
    cfg := Config{}

    // 必須チェックを手動で行う
    cfg.DatabaseURL = os.Getenv("DATABASE_URL")
    if cfg.DatabaseURL == "" {
        panic("DATABASE_URL is required")
    }

    // 型変換を手動で行う
    portStr := os.Getenv("PORT")
    if portStr == "" {
        cfg.Port = 8080 // デフォルト値
    } else {
        port, err := strconv.Atoi(portStr)
        if err != nil {
            panic("Invalid PORT value")
        }
        cfg.Port = port
    }

    // オプション値
    cfg.RedisURL = os.Getenv("REDIS_URL")

    // bool変換
    debugStr := os.Getenv("DEBUG")
    cfg.Debug = debugStr == "true" || debugStr == "1"

    return cfg
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;4&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc8">envライブラリを使用した方法</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="5">package main

import (
    "log"
    "github.com/caarlos0/env/v10"
)

type Config struct {
    DatabaseURL string `env:"DATABASE_URL,required"`
    Port        int    `env:"PORT" envDefault:"8080"`
    RedisURL    string `env:"REDIS_URL"`
    Debug       bool   `env:"DEBUG" envDefault:"false"`
}

func loadConfigWithEnv() Config {
    cfg := Config{}
    if err := env.Parse(&cfg); err != nil {
        log.Fatal(err)
    }
    return cfg
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;5&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc9">高度な機能の例</span></h2>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="6">package main

import (
    "fmt"
    "log"
    "time"

    "github.com/caarlos0/env/v10"
)

type DatabaseConfig struct {
    Host     string `env:"DB_HOST" envDefault:"localhost"`
    Port     int    `env:"DB_PORT" envDefault:"5432"`
    Username string `env:"DB_USER,required"`
    Password string `env:"DB_PASS,required"`
}

type Config struct {
    // ネストした構造体
    Database DatabaseConfig `envPrefix:"DB_"`

    // スライス型
    AllowedHosts []string `env:"ALLOWED_HOSTS" envSeparator:","`

    // 時間型
    Timeout time.Duration `env:"TIMEOUT" envDefault:"30s"`

    // カスタムパーサー
    LogLevel string `env:"LOG_LEVEL" envDefault:"info"`

    // 環境変数の存在チェック
    SecretKey string `env:"SECRET_KEY,required,unset"`
}

func main() {
    cfg := Config{}

    // パースオプション
    opts := env.Options{
        RequiredIfNoDef: true, // デフォルト値がない場合は必須
    }

    if err := env.Parse(&cfg, opts); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Config: %+v\n", cfg)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;6&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc10">実際の使用例（設定ファイル vs 環境変数）</span></h2>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="7">package main

import (
    "fmt"
    "log"
    "os"

    "github.com/caarlos0/env/v10"
)

type AppConfig struct {
    // サーバー設定
    ServerPort int    `env:"SERVER_PORT" envDefault:"8080"`
    ServerHost string `env:"SERVER_HOST" envDefault:"0.0.0.0"`

    // データベース設定
    DatabaseURL      string `env:"DATABASE_URL,required"`
    DatabasePoolSize int    `env:"DB_POOL_SIZE" envDefault:"10"`

    // 外部サービス
    RedisURL    string `env:"REDIS_URL"`
    RedisPrefix string `env:"REDIS_PREFIX" envDefault:"myapp"`

    // セキュリティ
    JWTSecret string `env:"JWT_SECRET,required"`

    // 機能フラグ
    EnableMetrics bool `env:"ENABLE_METRICS" envDefault:"true"`
    EnableDebug   bool `env:"DEBUG" envDefault:"false"`

    // 配列設定
    TrustedProxies []string `env:"TRUSTED_PROXIES" envSeparator:"," envDefault:"127.0.0.1"`
}

func main() {
    // 環境変数の例を設定
    os.Setenv("DATABASE_URL", "postgres://user:pass@localhost/db")
    os.Setenv("JWT_SECRET", "my-secret-key")
    os.Setenv("TRUSTED_PROXIES", "192.168.1.1,10.0.0.1")

    cfg := AppConfig{}
    if err := env.Parse(&cfg); err != nil {
        log.Fatal("設定の読み込みに失敗:", err)
    }

    fmt.Printf("サーバー: %s:%d\n", cfg.ServerHost, cfg.ServerPort)
    fmt.Printf("データベース: %s\n", cfg.DatabaseURL)
    fmt.Printf("デバッグモード: %t\n", cfg.EnableDebug)
    fmt.Printf("信頼できるプロキシ: %v\n", cfg.TrustedProxies)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;7&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc11">メリットとデメリット</span></h2>
</p>
<p><h3><span id="toc12">envライブラリのメリット</span></h3>
</p>
<ul>
<li><strong>簡潔なコード</strong>: 構造体タグで設定が完結</li>
<li><strong>型安全</strong>: 自動的な型変換とバリデーション</li>
<li><strong>必須チェック</strong>: <code>required</code>タグで必須項目を指定</li>
<li><strong>デフォルト値</strong>: <code>envDefault</code>で簡単に設定</li>
<li><strong>複雑な型対応</strong>: スライス、時間、カスタム型にも対応</li>
</ul>
<h3><span id="toc13">envライブラリのデメリット</span></h3>
</p>
<ul>
<li><strong>外部依存</strong>: サードパーティライブラリに依存</li>
<li><strong>学習コスト</strong>: タグの記法を覚える必要がある</li>
<li><strong>デバッグ</strong>: エラー時の詳細が分かりにくい場合がある</li>
</ul>
<h3><span id="toc14">使い分けの指針</span></h3>
</p>
<ul>
<li><strong>小規模プロジェクト</strong>: <code>os.Getenv</code> / <code>os.LookupEnv</code> で十分</li>
<li><strong>中〜大規模プロジェクト</strong>: <code>env</code>ライブラリで効率的に管理</li>
<li><strong>設定項目が多い</strong>: <code>env</code>ライブラリが有効</li>
<li><strong>型変換が複雑</strong>: <code>env</code>ライブラリが有効</li>
</ul>
<p>このように、<code>env</code>ライブラリは環境変数の管理を大幅に簡素化し、型安全性を提供する優れたツールです。</p>
<p><h2><span id="toc15">環境変数のテスト</span></h2>
</p>
<p><code>t.Setenv</code>メソッドについて詳しく説明します。</p>
<p>実は、Go 1.17以降では<strong><code>t.Setenv</code>の使用が推奨されています</strong>。</p>
<p><h2><span id="toc16">t.Setenvメソッドの基本</span></h2>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="8">func TestWithSetenv(t *testing.T) {
    // Go 1.17以降で利用可能
    t.Setenv("DATABASE_URL", "postgres://test@localhost/testdb")
    t.Setenv("SERVER_PORT", "8080")

    // テスト終了時に自動的に元の値に復元される
    cfg, err := LoadConfig()
    if err != nil {
        t.Fatal(err)
    }

    if cfg.DatabaseURL != "postgres://test@localhost/testdb" {
        t.Errorf("期待値と異なります")
    }
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;8&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc17">従来の方法 vs t.Setenv</span></h2>
</p>
<p><h3><span id="toc18">従来の方法（非推奨になった理由）</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="9">func TestOldWay(t *testing.T) {
    // 手動でバックアップと復元
    original := os.Getenv("DATABASE_URL")
    defer func() {
        if original == "" {
            os.Unsetenv("DATABASE_URL")
        } else {
            os.Setenv("DATABASE_URL", original)
        }
    }()

    os.Setenv("DATABASE_URL", "postgres://test@localhost/testdb")

    // テスト実行
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;9&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc19">t.Setenvを使った方法（推奨）</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="10">func TestNewWay(t *testing.T) {
    // 自動的にバックアップと復元が行われる
    t.Setenv("DATABASE_URL", "postgres://test@localhost/testdb")
    t.Setenv("SERVER_PORT", "8080")
    t.Setenv("DEBUG", "true")

    // テスト実行
    cfg, err := LoadConfig()
    if err != nil {
        t.Fatal(err)
    }

    // アサーション
    if cfg.DatabaseURL != "postgres://test@localhost/testdb" {
        t.Errorf("DATABASE_URL: 期待値=%s, 実際=%s",
            "postgres://test@localhost/testdb", cfg.DatabaseURL)
    }
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;10&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc20">実践的な使用例</span></h2>
</p>
<p><h3><span id="toc21">envライブラリとの組み合わせ</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="11">package config

import (
    "testing"
    "github.com/caarlos0/env/v10"
)

type AppConfig struct {
    DatabaseURL string   `env:"DATABASE_URL,required"`
    ServerPort  int      `env:"SERVER_PORT" envDefault:"8080"`
    Debug       bool     `env:"DEBUG" envDefault:"false"`
    Features    []string `env:"FEATURES" envSeparator:","`
}

func TestAppConfig(t *testing.T) {
    tests := []struct {
        name     string
        envVars  map[string]string
        expected AppConfig
        wantErr  bool
    }{
        {
            name: "すべての環境変数が設定されている",
            envVars: map[string]string{
                "DATABASE_URL": "postgres://user:pass@localhost/testdb",
                "SERVER_PORT":  "3000",
                "DEBUG":        "true",
                "FEATURES":     "auth,logging,metrics",
            },
            expected: AppConfig{
                DatabaseURL: "postgres://user:pass@localhost/testdb",
                ServerPort:  3000,
                Debug:       true,
                Features:    []string{"auth", "logging", "metrics"},
            },
        },
        {
            name: "デフォルト値が使用される",
            envVars: map[string]string{
                "DATABASE_URL": "postgres://user:pass@localhost/testdb",
            },
            expected: AppConfig{
                DatabaseURL: "postgres://user:pass@localhost/testdb",
                ServerPort:  8080, // デフォルト値
                Debug:       false, // デフォルト値
                Features:    nil,
            },
        },
        {
            name: "必須項目が不足",
            envVars: map[string]string{
                "SERVER_PORT": "3000",
            },
            wantErr: true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // t.Setenvで環境変数を設定
            for key, value := range tt.envVars {
                t.Setenv(key, value)
            }

            cfg := AppConfig{}
            err := env.Parse(&cfg)

            if tt.wantErr {
                if err == nil {
                    t.Error("エラーが期待されていましたが発生しませんでした")
                }
                return
            }

            if err != nil {
                t.Fatalf("予期しないエラー: %v", err)
            }

            // 構造体の比較
            if cfg.DatabaseURL != tt.expected.DatabaseURL {
                t.Errorf("DatabaseURL: 期待値=%s, 実際=%s",
                    tt.expected.DatabaseURL, cfg.DatabaseURL)
            }
            if cfg.ServerPort != tt.expected.ServerPort {
                t.Errorf("ServerPort: 期待値=%d, 実際=%d",
                    tt.expected.ServerPort, cfg.ServerPort)
            }
            if cfg.Debug != tt.expected.Debug {
                t.Errorf("Debug: 期待値=%t, 実際=%t",
                    tt.expected.Debug, cfg.Debug)
            }
        })
    }
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;11&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc22">複雑な設定のテスト</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="12">func TestComplexConfig(t *testing.T) {
    t.Run("本番環境設定", func(t *testing.T) {
        t.Setenv("ENV", "production")
        t.Setenv("DATABASE_URL", "postgres://prod@prod-db:5432/proddb")
        t.Setenv("REDIS_URL", "redis://redis-cluster:6379")
        t.Setenv("LOG_LEVEL", "warn")
        t.Setenv("RATE_LIMIT", "1000")

        cfg, err := LoadConfig()
        if err != nil {
            t.Fatal(err)
        }

        if cfg.Env != "production" {
            t.Errorf("環境設定が正しくありません: %s", cfg.Env)
        }
    })

    t.Run("開発環境設定", func(t *testing.T) {
        t.Setenv("ENV", "development")
        t.Setenv("DATABASE_URL", "postgres://dev@localhost:5432/devdb")
        t.Setenv("LOG_LEVEL", "debug")
        t.Setenv("HOT_RELOAD", "true")

        cfg, err := LoadConfig()
        if err != nil {
            t.Fatal(err)
        }

        if cfg.Env != "development" {
            t.Errorf("環境設定が正しくありません: %s", cfg.Env)
        }
        if !cfg.HotReload {
            t.Error("開発環境ではホットリロードが有効になっている必要があります")
        }
    })
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;12&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc23">t.Setenvのメリット</span></h2>
</p>
<p><h3><span id="toc24">1. 自動クリーンアップ</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="13">func TestAutoCleanup(t *testing.T) {
    // テスト前の値
    originalValue := os.Getenv("TEST_VAR")

    t.Setenv("TEST_VAR", "test_value")

    // テスト中
    if os.Getenv("TEST_VAR") != "test_value" {
        t.Error("環境変数が設定されていません")
    }

    // テスト終了後、自動的に元の値に復元される
    // 手動でのクリーンアップは不要
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;13&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc25">2. パラレルテスト対応</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="14">func TestParallelSafe(t *testing.T) {
    t.Run("テスト1", func(t *testing.T) {
        t.Parallel()
        t.Setenv("TEST_VAR", "value1")

        // このテストは他のテストと並列実行されても安全
        if os.Getenv("TEST_VAR") != "value1" {
            t.Error("値が正しくありません")
        }
    })

    t.Run("テスト2", func(t *testing.T) {
        t.Parallel()
        t.Setenv("TEST_VAR", "value2")

        // 他のテストの環境変数設定に影響されない
        if os.Getenv("TEST_VAR") != "value2" {
            t.Error("値が正しくありません")
        }
    })
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;14&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc26">3. シンプルなコード</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="15">func TestSimpleCode(t *testing.T) {
    // 従来の方法（煩雑）
    /*
    original := os.Getenv("DB_HOST")
    defer func() {
        if original == "" {
            os.Unsetenv("DB_HOST")
        } else {
            os.Setenv("DB_HOST", original)
        }
    }()
    os.Setenv("DB_HOST", "test-host")
    */

    // t.Setenvを使用（シンプル）
    t.Setenv("DB_HOST", "test-host")
    t.Setenv("DB_PORT", "5432")
    t.Setenv("DB_NAME", "testdb")

    // テスト実行
    config := loadDatabaseConfig()
    if config.Host != "test-host" {
        t.Error("ホスト名が正しくありません")
    }
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;15&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc27">注意点とベストプラクティス</span></h2>
</p>
<p><h3><span id="toc28">1. Go 1.17以降限定</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="16">//go:build go1.17
// +build go1.17

func TestWithSetenv(t *testing.T) {
    t.Setenv("VAR", "value") // Go 1.17以降でのみ利用可能
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;16&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc29">2. サブテストでの使用</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="17">func TestSubtests(t *testing.T) {
    t.Run("サブテスト1", func(t *testing.T) {
        t.Setenv("ENV", "test1")
        // この設定はサブテスト終了時に自動クリーンアップされる
    })

    t.Run("サブテスト2", func(t *testing.T) {
        t.Setenv("ENV", "test2")
        // 前のサブテストの設定は影響しない
    })
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;17&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p>簡単にまとめると以下の通りです。</p>
<ul>
<li><strong>Go 1.17以降の標準機能</strong></li>
<li><strong>自動クリーンアップ</strong>でメモリリークやテスト間の干渉を防ぐ</li>
<li><strong>パラレルテスト対応</strong></li>
<li><strong>コードが簡潔</strong>で保守しやすい</li>
<li><strong>エラーが起きにくい</strong>（手動復元の忘れがない）</li>
</ul>
<p>Go 1.17以降を使用している場合は、<code>t.Setenv</code>を積極的に使用することを強く推奨します。</p>
<p><h2><span id="toc30">まとめ</span></h2>
</p>
<p>Goにおける環境変数管理は、シンプルなアプローチから高度なサードパーティライブラリの利用まで幅広く対応しており、プロジェクトの規模や要件に応じて適切な方法を選択することが重要です。<code>os.Getenv</code>や<code>os.LookupEnv</code>は標準ライブラリとして軽量で簡易的な方法を提供する一方で、複雑な型の変換や依存関係の管理が必要な場合には<code>github.com/caarlos0/env</code>のような外部ライブラリが非常に有用です。</p>
<p>特に、環境変数を利用するシステムにおいては、環境変数が正しく設定されていない場合にアプリケーションが予期せぬ挙動をするリスクがあります。そのため、環境変数の存在確認やバリデーションを行う仕組みを導入することが推奨されます。例えば、<code>env</code>ライブラリを使用すれば、環境変数が不足している場合にエラーを返す機能や、デフォルト値の設定、型安全な変換を簡単に実現できます。</p>
<p>また、テスト環境においては、<code>t.Setenv</code>を活用することで環境変数の設定やクリーンアップが効率的に行えるため、テストコードの保守性が向上します。特に、複数の環境で動作するアプリケーションを開発する際には、環境変数を適切に管理することで、設定の切り替えやデプロイがスムーズに行えます。</p>
<p>最終的には、以下のような指針に基づいて環境変数管理の方法を選択すると良いでしょう：</p>
<p>1. 環境変数の数が少なく、単純な設定の場合は<code>os.Getenv</code>や<code>os.LookupEnv</code>を活用する。<br />
2. 型変換やバリデーション、デフォルト値が必要な場合は<code>env</code>ライブラリなどのサードパーティツールを利用する。<br />
3. テスト環境では<code>t.Setenv</code>を活用して環境変数の設定・管理を簡略化する。<br />
4. プロジェクトの規模が拡大する場合は、環境変数管理を一元化し、必要に応じて設定ファイルやシークレット管理ツール（例：HashiCorp Vault、AWS Secrets Manager）と組み合わせる。</p>
<p>適切な手法を選択することで、コードの簡潔さと可読性を保ちながら、安全でスケーラブルなアプリケーションの開発が可能になります。</p><p>The post <a href="https://aichi.blog/go-env/">環境変数の効率的な管理方法：os.Getenv vs os.LookupEnv と github.com/caarlos0/env の活用ガイド</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Go 言語のクロージャーとは？ 無名関数の使い方と注意点</title>
		<link>https://aichi.blog/go-closure/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=go-closure</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Wed, 28 May 2025 07:39:52 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[クロージャー]]></category>
		<category><![CDATA[文法]]></category>
		<category><![CDATA[開発]]></category>
		<guid isPermaLink="false">https://aichi.blog/go-closure/</guid>

					<description><![CDATA[<p>Go では func(引数…){ … } のように名前を付けずにその場で関数リテラルを作る書き方を「無名関数（anonymous function または closure）」と呼びます。 即席でハンドラーを定義したい 外 [&#8230;]</p>
<p>The post <a href="https://aichi.blog/go-closure/">Go 言語のクロージャーとは？ 無名関数の使い方と注意点</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Go では <code>func(引数…){ … }</code> のように<strong>名前を付けずに</strong>その場で関数リテラルを作る書き方を「無名関数（anonymous function または closure）」と呼びます。</p>
<ul>
<li><strong>即席でハンドラーを定義したい</strong></li>
<li><strong>外側の変数を包み込んで（= クロージャ）あとで実行したい</strong></li>
</ul>
<p>  ── そんな時に便利です。</p>
<p><h2><span id="toc1">状態をもつ関数</span></h2>
</p>
<ul>
<li>通常、状態を持たせる時は、構造体を用意し、メソッドを作成する必要がある</li>
<li>ただ、構造体自体に意味がないなら、以下のようにクロージャーを定義・使用することで、冗長な構造体を書かずに済む</li>
</ul>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="0">package main

import "fmt"

// クロージャーは、関数が定義されたときの環境（スコープ）を「覚えている」仕組み
// つまり、関数の外側にある変数にアクセスできる関数のこと
func store() func(int) int {
	// 外側の関数の変数
	sum := 0
	// ↓ クロージャー関数
	return func(i int) int {
		// 内側の関数が外側の変数xを参照している
		sum += i
		return sum
	}
}

func main() {
	// クロージャーを変数に束縛する
	s1 := store()
	s2 := store()

	// クロージャーを呼び出す
	fmt.Println(s1(1))
	fmt.Println(s1(2))
	fmt.Println(s1(3))
	fmt.Println("別のクロージャー")
	fmt.Println(s2(4))
	fmt.Println(s2(5))
	fmt.Println(s2(6))
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;0&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc2">ミドルウェアの生成：異なるシグネチャ（型）を揃える</span></h2>
</p>
<p>無名関数を使用することで、用意した関数の型とシグネチャが合わない時も無名関数を使用することで、シグネチャを揃えることができます。</p>
<p><h3><span id="toc3">シナリオ：既存のビジネスロジック関数を HTTP ハンドラーとして使いたい場合</span></h3>
</p>
<p><h3><span id="toc4">既存の関数（シグネチャが合わない）</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="1">// 既存のビジネスロジック関数
func calculatePrice(productID string, quantity int) (float64, error) {
    // 商品価格計算のロジック
    basePrice := 100.0
    total := basePrice * float64(quantity)
    return total, nil
}

func getUserProfile(userID string) (string, error) {
    // ユーザープロファイル取得のロジック
    return fmt.Sprintf("User profile for ID: %s", userID), nil
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;1&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc5">&#x274c; 無名関数を使用しない場合（コンパイルエラー）</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="2">func main() {
    // これはコンパイルエラーになる
    // calculatePriceのシグネチャ: func(string, int) (float64, error)
    // 期待されるシグネチャ: func(http.ResponseWriter, *http.Request)

    http.HandleFunc("/price", calculatePrice) // &#x274c; エラー！
    //                        ^^^^^^^^^^^
    // cannot use calculatePrice (type func(string, int) (float64, error))
    // as type func(http.ResponseWriter, *http.Request) in argument

    http.HandleFunc("/user", getUserProfile) // &#x274c; エラー！
    //                       ^^^^^^^^^^^^^^
    // 同様のエラー
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;2&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc6">&#x2705; 無名関数を使用した場合（正常動作）</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="3">func main() {
    // 無名関数でシグネチャを合わせる
    http.HandleFunc("/price", func(w http.ResponseWriter, r *http.Request) {
        // HTTPリクエストからパラメータを取得
        productID := r.URL.Query().Get("product_id")
        quantityStr := r.URL.Query().Get("quantity")
        quantity, err := strconv.Atoi(quantityStr)
        if err != nil {
            http.Error(w, "Invalid quantity", http.StatusBadRequest)
            return
        }

        // 既存関数を呼び出し
        price, err := calculatePrice(productID, quantity)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        // レスポンスを返す
        fmt.Fprintf(w, "Total price: %.2f", price)
    })

    http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
        // HTTPリクエストからパラメータを取得
        userID := r.URL.Query().Get("user_id")

        // 既存関数を呼び出し
        profile, err := getUserProfile(userID)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        // レスポンスを返す
        fmt.Fprintln(w, profile)
    })

    http.ListenAndServe(":8080", nil)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;3&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc7">より複雑な例：認証付きハンドラー</span></h2>
</p>
<p><h3><span id="toc8">既存の関数</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="4">// 既存の認証関数
func authenticate(username, password string) bool {
    return username == "admin" && password == "secret"
}

// 既存のデータ取得関数
func getSecretData(userID string) (map[string]interface{}, error) {
    return map[string]interface{}{
        "data": "secret information",
        "user": userID,
    }, nil
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;4&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc9">&#x274c; 無名関数なしの場合</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="5">func main() {
    // これらは全てコンパイルエラー
    http.HandleFunc("/login", authenticate)    // &#x274c; エラー
    http.HandleFunc("/secret", getSecretData)  // &#x274c; エラー
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;5&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc10">&#x2705; 無名関数ありの場合</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="6">func main() {
    // ログインハンドラー
    http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
        username := r.FormValue("username")
        password := r.FormValue("password")

        // 既存の認証関数を使用
        if authenticate(username, password) {
            fmt.Fprintln(w, "Login successful")
        } else {
            http.Error(w, "Login failed", http.StatusUnauthorized)
        }
    })

    // シークレットデータハンドラー
    http.HandleFunc("/secret", func(w http.ResponseWriter, r *http.Request) {
        userID := r.Header.Get("User-ID")

        // 既存のデータ取得関数を使用
        data, err := getSecretData(userID)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        // JSONレスポンス
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(data)
    })

    http.ListenAndServe(":8080", nil)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;6&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<table class="wp-table">
<thead>
<tr>
<th>項目</th>
<th>無名関数なし</th>
<th>無名関数あり</th>
</tr>
</thead>
<tbody>
<tr>
<td>コンパイル</td>
<td>&#x274c; エラー</td>
<td>&#x2705; 成功</td>
</tr>
<tr>
<td>既存関数の再利用</td>
<td>&#x274c; 不可能</td>
<td>&#x2705; 可能</td>
</tr>
<tr>
<td>HTTP パラメータ処理</td>
<td>&#x274c; 不可能</td>
<td>&#x2705; 可能</td>
</tr>
<tr>
<td>エラーハンドリング</td>
<td>&#x274c; 不可能</td>
<td>&#x2705; 可能</td>
</tr>
<tr>
<td>レスポンス形成</td>
<td>&#x274c; 不可能</td>
<td>&#x2705; 可能</td>
</tr>
</tbody>
</table>
<p><strong>このように、無名関数を使用することで</strong>、既存のビジネスロジックを変更することなく、HTTP ハンドラーとして利用できるようになります。これにより、コードの再利用性と保守性が大幅に向上します。</p>
<p><h2><span id="toc11">ルーチンで無名関数から外部変数を参照することの問題</span></h2>
</p>
<p><h3><span id="toc12">1. 競合状態（Race Condition）</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="7">package main

import (
    "fmt"
    "sync"
    "time"
)

// &#x274c; 問題のあるコード
func badExample() {
    var counter int
    var wg sync.WaitGroup

    for i := 0; i &lt; 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter++ // 複数のゴルーチンが同じ変数に同時アクセス
        }()
    }

    wg.Wait()
    fmt.Printf("Counter: %d\n", counter) // 期待値1000だが、実際は不定
}

// &#x2705; 改善されたコード
func goodExample() {
    var counter int
    var wg sync.WaitGroup
    var mu sync.Mutex

    for i := 0; i &lt; 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.Lock()
            counter++ // ミューテックスで保護
            mu.Unlock()
        }()
    }

    wg.Wait()
    fmt.Printf("Counter: %d\n", counter) // 正確に1000
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;7&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc13">2. 変数の予期しない共有</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="8">// &#x274c; 問題のあるコード：ループ変数の共有
func badLoopExample() {
    var wg sync.WaitGroup

    for i := 0; i &lt; 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            fmt.Printf("Value: %d\n", i) // 全て同じ値（5）を出力する可能性
        }()
    }

    wg.Wait()
}

// &#x2705; 改善されたコード：値を明示的に渡す
func goodLoopExample() {
    var wg sync.WaitGroup

    for i := 0; i &lt; 5; i++ {
        wg.Add(1)
        go func(val int) { // パラメータとして渡す
            defer wg.Done()
            fmt.Printf("Value: %d\n", val) // 期待通りの値を出力
        }(i)
    }

    wg.Wait()
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;8&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc14">3. スライスの共有による問題</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="9">// &#x274c; 問題のあるコード
func badSliceExample() {
    data := []int{1, 2, 3, 4, 5}
    var wg sync.WaitGroup

    for i := 0; i &lt; len(data); i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            data[i] = data[i] * 2 // 競合状態 + インデックス範囲外エラーの可能性
        }()
    }

    wg.Wait()
}

// &#x2705; 改善されたコード
func goodSliceExample() {
    data := []int{1, 2, 3, 4, 5}
    var wg sync.WaitGroup

    for i := 0; i &lt; len(data); i++ {
        wg.Add(1)
        go func(index int, slice []int) { // 値を明示的に渡す
            defer wg.Done()
            slice[index] = slice[index] * 2
        }(i, data)
    }

    wg.Wait()
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;9&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc15">4. Web アプリケーションでの実例</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="10">// &#x274c; 危険なコード：HTTPハンドラーでの共有変数
func badWebExample() {
    requestCount := 0 // 共有変数

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        go func() {
            requestCount++ // 競合状態
            fmt.Printf("Request count: %d\n", requestCount)
        }()

        fmt.Fprintln(w, "Hello World")
    })
}

// &#x2705; 安全なコード：適切な同期化
func goodWebExample() {
    var requestCount int64

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        go func() {
            atomic.AddInt64(&requestCount, 1) // アトミック操作
            count := atomic.LoadInt64(&requestCount)
            fmt.Printf("Request count: %d\n", count)
        }()

        fmt.Fprintln(w, "Hello World")
    })
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;10&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc16">5. チャネルを使った解決方法</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="11">// &#x2705; チャネルを使った安全なアプローチ
func channelExample() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // ワーカーゴルーチン
    for w := 1; w &lt;= 3; w++ {
        go func(id int) {
            for job := range jobs {
                result := job * 2 // 外部変数に依存しない
                results &lt;- result
            }
        }(w)
    }

    // ジョブを送信
    for j := 1; j &lt;= 9; j++ {
        jobs &lt;- j
    }
    close(jobs)

    // 結果を受信
    for r := 1; r &lt;= 9; r++ {
        &lt;-results
    }
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;11&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h2><span id="toc17">対策方法一覧</span></h2>
</p>
<p><h3><span id="toc18">1. 値渡し</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="12">go func(val int) {
    // valは各ゴルーチンで独立
}(externalVar)</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;12&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc19">2. 同期プリミティブ</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="13">var mu sync.Mutex
go func() {
    mu.Lock()
    // 共有リソースへの安全なアクセス
    mu.Unlock()
}()</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;13&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc20">3. アトミック操作</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="14">go func() {
    atomic.AddInt64(&counter, 1)
}()</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;14&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc21">4. チャネル</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="15">ch := make(chan int)
go func() {
    ch &lt;- computeValue() // チャネル経由で安全に通信
}()</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;15&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p>ゴルーチンで外部変数を参照する際の主な問題：</p>
<p>1. <strong>データ競合</strong>: 複数のゴルーチンが同じメモリ位置に同時アクセス<br />
2. <strong>予期しない共有</strong>: 変数が意図せず共有される<br />
3. <strong>デバッグの困難さ</strong>: 非決定的な動作により再現が困難</p>
<p>これらの問題を避けるため、<strong>値渡し</strong>、<strong>適切な同期化</strong>、<strong>チャネル</strong>などを使用することが推奨されます。</p>
<p><h2><span id="toc22">まとめ</span></h2>
</p>
<p>Go 言語のクロージャーは、柔軟性と再利用性の高いコードを記述するための強力なツールです。特に、無名関数を使用することで、既存の関数をラップし、新しい文脈やシグネチャに適応させることができます。ただし、クロージャーを使用する際には、外部変数の参照に伴う競合状態や予期しない動作に注意が必要です。</p>
<p>競合状態を防ぐためには、値渡し、同期プリミティブ（ミューテックスやアトミック操作）、またはチャネルを活用することが重要です。これにより、ゴルーチンの安全性が確保され、信頼性の高い並行処理が可能になります。</p>
<p>さらに、HTTP ハンドラーのような現実的なシナリオにおいても、無名関数を活用することで、既存のビジネスロジックを効率的に再利用することができます。</p>
<p>このように、Go 言語のクロージャーは、プログラムの簡潔性と保守性を高めるだけでなく、複雑なタスクをより直感的に実現する手段を提供します。適切な注意を払いながら使用することで、その潜在能力を最大限に引き出すことができるでしょう。</p><p>The post <a href="https://aichi.blog/go-closure/">Go 言語のクロージャーとは？ 無名関数の使い方と注意点</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Go言語のJSON処理｜メモリとストリームの違い・使い分け・実装例まで</title>
		<link>https://aichi.blog/go-json-memory-stream/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=go-json-memory-stream</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Sun, 27 Apr 2025 04:48:53 +0000</pubDate>
				<category><![CDATA[Go]]></category>
		<category><![CDATA[文法]]></category>
		<category><![CDATA[開発]]></category>
		<guid isPermaLink="false">https://aichi.blog/go-json-memory-stream/</guid>

					<description><![CDATA[<p>Go言語で JSON を扱う際には、標準パッケージ encoding/json を使用するのが一般的です。 このパッケージには、メモリ上で一括変換を行う方法と、ストリームを通じて順次変換する方法の2通りがあります。 まず [&#8230;]</p>
<p>The post <a href="https://aichi.blog/go-json-memory-stream/">Go言語のJSON処理｜メモリとストリームの違い・使い分け・実装例まで</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Go言語で JSON を扱う際には、標準パッケージ <code>encoding/json</code> を使用するのが一般的です。<br />
このパッケージには、メモリ上で一括変換を行う方法と、ストリームを通じて順次変換する方法の2通りがあります。</p>
<p>まずはじめに、それぞれの違いを簡単にまとめておきましょう。</p>
<hr>
<p><h2><span id="toc1">メモリ処理とストリーム処理の違い（概要）</span></h2>
</p>
<table class="wp-table">
<thead>
<tr>
<th>特徴</th>
<th>メモリ処理</th>
<th>ストリーム処理</th>
</tr>
</thead>
<tbody>
<tr>
<td>処理方法</td>
<td>データを一括で読み書きする</td>
<td>データを少しずつ読み書きする</td>
</tr>
<tr>
<td>使用関数</td>
<td><code>json.Marshal</code> / <code>json.Unmarshal</code></td>
<td><code>json.Encoder</code> / <code>json.Decoder</code></td>
</tr>
<tr>
<td>対象データ</td>
<td><code>[]byte</code>（メモリ上の全データ）</td>
<td><code>io.Reader</code> / <code>io.Writer</code>（データの流れ）</td>
</tr>
<tr>
<td>適する場面</td>
<td>小〜中規模のデータ、簡単な変換</td>
<td>大規模データ、リアルタイム・逐次処理</td>
</tr>
</tbody>
</table>
<p>このあと、それぞれの方法を具体的に見ていきます。</p>
<hr>
<p><h2><span id="toc2">エンコードとデコードとは？</span></h2>
</p>
<p>GoでJSONを扱う際によく出てくる「エンコード」と「デコード」は、次のような意味です：</p>
<table class="wp-table">
<thead>
<tr>
<th>用語</th>
<th>意味</th>
<th>関数例</th>
</tr>
</thead>
<tbody>
<tr>
<td>エンコード</td>
<td>Goの構造体などのデータをJSON形式の文字列に変換する処理</td>
<td><code>json.Marshal</code> / <code>Encode</code></td>
</tr>
<tr>
<td>デコード</td>
<td>JSON形式の文字列をGoの構造体などのデータに戻す処理</td>
<td><code>json.Unmarshal</code> / <code>Decode</code></td>
</tr>
</tbody>
</table>
<p>たとえば、次のような処理がエンコードとデコードの典型です：</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="0">jsonBytes, _ := json.Marshal(myStruct) // ← エンコード（Go → JSON）
json.Unmarshal(jsonBytes, &myStruct)   // ← デコード（JSON → Go）</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;0&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<hr>
<p><h2><span id="toc3">メモリで JSON を処理する</span></h2>
</p>
<p><h3><span id="toc4">json.Marshal / json.Unmarshal の実装例</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="1">package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	// 構造体からJSONへ（エンコード）
	p := Person{Name: "Alice", Age: 30}
	jsonBytes, err := json.Marshal(p)
	if err != nil {
		panic(err)
	}
	fmt.Println("JSON出力:", string(jsonBytes))

	// JSONから構造体へ（デコード）
	var decoded Person
	err = json.Unmarshal(jsonBytes, &decoded)
	if err != nil {
		panic(err)
	}
	fmt.Printf("構造体に戻した結果: %+v\n", decoded)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;1&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc5">メモリ処理の特徴</span></h3>
<li>小〜中規模のデータに適している</li>
<li>データ全体を保持してから処理</li>
<li>簡潔で直感的なコードが書ける</li>
<hr>
<p><h2><span id="toc6">ストリームで JSON を処理する</span></h2>
</p>
<p><h3><span id="toc7">json.NewDecoder / json.NewEncoder の実装例</span></h3>
</p>
<div class="hcb_wrap">
<pre class="prism line-numbers language-go" data-lang="go" data-show-lang="1"><code class="language-go" data-hcb-clip="2">package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type Product struct {
	ID    int     `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
}

func main() {
	// ファイルにJSONを書き出す（エンコード）
	f, err := os.Create("product.json")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	encoder := json.NewEncoder(f)
	product := Product{ID: 1, Name: "Notebook", Price: 1299.99}
	if err := encoder.Encode(product); err != nil {
		panic(err)
	}
	fmt.Println("ファイルにJSONを書き出しました")

	// ファイルからJSONを読み込む（デコード）
	f2, err := os.Open("product.json")
	if err != nil {
		panic(err)
	}
	defer f2.Close()

	decoder := json.NewDecoder(f2)
	var p Product
	if err := decoder.Decode(&p); err != nil {
		panic(err)
	}
	fmt.Printf("読み込んだ構造体: %+v\n", p)
}</code></pre>
<p><button class="hcb-clipboard" data-clipboard-target="[data-hcb-clip=&quot;2&quot;]" data-clipboard-action="copy" aria-label="コードをクリップボードにコピーする"></button></div>
<p><h3><span id="toc8">ストリーム処理の特徴</span></h3>
<li>データサイズが大きい場合や継続的に処理したい場合に有効</li>
<li><code>io.Reader</code> / <code>io.Writer</code> を活用</li>
<li>メモリ消費を抑えつつ処理可能</li>
<hr>
<p><h2><span id="toc9">メモリ処理とストリーム処理の比較</span></h2>
</p>
<table class="wp-table">
<thead>
<tr>
<th>項目</th>
<th>メモリ処理 (<code>Marshal</code> / <code>Unmarshal</code>)</th>
<th>ストリーム処理 (<code>Encoder</code> / <code>Decoder</code>)</th>
</tr>
</thead>
<tbody>
<tr>
<td>対象データ</td>
<td>一括データ（<code>[]byte</code>）</td>
<td>順次データ（<code>io.Reader</code> / <code>Writer</code>）</td>
</tr>
<tr>
<td>適するデータ規模</td>
<td>小〜中規模</td>
<td>大規模・連続データ</td>
</tr>
<tr>
<td>メリット</td>
<td>簡単・高速</td>
<td>柔軟・低メモリ</td>
</tr>
</tbody>
</table>
<hr>
<p><h2><span id="toc10">まとめ</span></h2>
</p>
<li><strong>メモリ処理</strong>：<code>json.Marshal</code> / <code>json.Unmarshal</code> は、データを一括で処理するのに適しており、簡潔なコードが書けます。</li>
<li><strong>ストリーム処理</strong>：<code>json.Encoder</code> / <code>json.Decoder</code> は、データを少しずつ処理したい場合に便利で、大規模データやリアルタイム処理に向いています。</li>
<li><strong>エンコード</strong>は「Go構造体 → JSON文字列」、<strong>デコード</strong>は「JSON文字列 → Go構造体」と覚えておきましょう。</li>
<p>処理するデータの大きさや性質に応じて、最適な方法を選ぶことが、Goでの効率的なJSON処理のポイントです。</p><p>The post <a href="https://aichi.blog/go-json-memory-stream/">Go言語のJSON処理｜メモリとストリームの違い・使い分け・実装例まで</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Python】文字列メソッドの使い方</title>
		<link>https://aichi.blog/python-string-methods/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=python-string-methods</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Fri, 03 Mar 2023 01:46:33 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[プログラミング]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=2783</guid>

					<description><![CDATA[<p>プログラミングで、文字列を扱うことは非常によくあることです。Pythonには、文字列を扱う様々なメソッドが用意されています。この記事では、Pythonの文字列メソッドについて学び、それぞれの使い方について確認していきます [&#8230;]</p>
<p>The post <a href="https://aichi.blog/python-string-methods/">【Python】文字列メソッドの使い方</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>プログラミングで、文字列を扱うことは非常によくあることです。Pythonには、文字列を扱う様々なメソッドが用意されています。この記事では、Pythonの文字列メソッドについて学び、それぞれの使い方について確認していきます。</p>



<h2 class="wp-block-heading"><span id="toc1">startswith()メソッド</span></h2>



<p>Pythonの文字列メソッドの一つに、<code>startswith()</code>があります。<code>startswith()</code>は、指定した文字列で始まるかどうかを判定するメソッドです。このメソッドを使うことで、文字列の先頭が指定した文字列で始まるかどうかを判定できます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-lang="Python" data-show-lang="1"><code>is_start = s.startswith(&#39;My&#39;)
print(is_start) # True
is_start = s.startswith(&#39;X&#39;)
print(is_start) # False</code></pre></div>



<h2 class="wp-block-heading"><span id="toc2">find()メソッド</span></h2>



<p>Pythonの文字列メソッドの中でも、<code>find()</code>はよく使われるメソッドの一つです。<code>find()</code>は、指定した文字列が最初に現れる位置を返します。もし文字列中に指定した文字列が存在しない場合は、<code>-1</code>を返します。</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-python" data-lang="Python"><code>print(s.find(&#39;Mike&#39;)) # 11</code></pre></div>



<p>また、<code>rfind()</code>メソッドは、指定した文字列が最後に現れる位置を返すメソッドです。</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-plain"><code>print(s.rfind(&#39;Mike&#39;)) # 17</code></pre></div>



<h2 class="wp-block-heading"><span id="toc3">count()メソッド</span></h2>



<p><code>count()</code>は、指定した文字列が何回現れるかを返すメソッドです。</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-plain"><code>print(s.count(&#39;Mike&#39;)) # 2</code></pre></div>



<h2 class="wp-block-heading"><span id="toc4">capitalize()メソッド</span></h2>



<p><code>capitalize()</code>メソッドは、文字列の先頭の1文字だけを大文字にして、残りを小文字に変換します。</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-plain"><code>print(s.capitalize()) # My name is mike. hi mike.</code></pre></div>



<h2 class="wp-block-heading"><span id="toc5">title()メソッド</span></h2>



<p><code>title()</code>メソッドは、各単語の先頭文字を大文字に変換します。</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-plain"><code>print(s.title()) # My Name Is Mike. Hi Mike.</code></pre></div>



<h2 class="wp-block-heading"><span id="toc6">upper()メソッド</span></h2>



<p>文字列をすべて大文字に変換するには、<code>upper()</code>メソッドを使用します。</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-plain"><code>print(s.upper()) # MY NAME IS MIKE. HI MIKE.</code></pre></div>



<h2 class="wp-block-heading"><span id="toc7">replace()メソッド</span></h2>



<p>文字列の中にある指定した文字列を、別の文字列に置き換えたい場合は、<code>replace()</code>メソッドを使用します。</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-plain"><code>print(s.replace(&#39;Mike&#39;, &#39;Nancy&#39;)) # My name is Nancy. Hi Nancy.</code></pre></div>



<p>以上が、Pythonでの文字列メソッドの使い方です。この記事を参考に、Pythonで文字列を操作する際に役立ててください。</p><p>The post <a href="https://aichi.blog/python-string-methods/">【Python】文字列メソッドの使い方</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
