<?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/tag/%E6%96%87%E6%B3%95/feed/" rel="self" type="application/rss+xml" />
	<link>https://aichi.blog</link>
	<description>学びて富み　富みて学ぶ</description>
	<lastBuildDate>Wed, 28 May 2025 09:12:18 +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/tag/%E6%96%87%E6%B3%95/feed/"/>
	<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>
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">os.Getenv関数</a></li><li><a href="#toc2" tabindex="0">os.LookupEnv関数</a></li><li><a href="#toc3" tabindex="0">実用的な使い分け例</a></li><li><a href="#toc4" tabindex="0">サードパーティ：github.com/caarlos0/envの使用</a></li><li><a href="#toc5" tabindex="0">基本的な使用方法</a></li><li><a href="#toc6" tabindex="0">os.Getenv/LookupEnvとの比較</a><ol><li><a href="#toc7" tabindex="0">従来の方法（os.Getenv使用）</a></li><li><a href="#toc8" tabindex="0">envライブラリを使用した方法</a></li></ol></li><li><a href="#toc9" tabindex="0">高度な機能の例</a></li><li><a href="#toc10" tabindex="0">実際の使用例（設定ファイル vs 環境変数）</a></li><li><a href="#toc11" tabindex="0">メリットとデメリット</a><ol><li><a href="#toc12" tabindex="0">envライブラリのメリット</a></li><li><a href="#toc13" tabindex="0">envライブラリのデメリット</a></li><li><a href="#toc14" tabindex="0">使い分けの指針</a></li></ol></li><li><a href="#toc15" tabindex="0">環境変数のテスト</a></li><li><a href="#toc16" tabindex="0">t.Setenvメソッドの基本</a></li><li><a href="#toc17" tabindex="0">従来の方法 vs t.Setenv</a><ol><li><a href="#toc18" tabindex="0">従来の方法（非推奨になった理由）</a></li><li><a href="#toc19" tabindex="0">t.Setenvを使った方法（推奨）</a></li></ol></li><li><a href="#toc20" tabindex="0">実践的な使用例</a><ol><li><a href="#toc21" tabindex="0">envライブラリとの組み合わせ</a></li><li><a href="#toc22" tabindex="0">複雑な設定のテスト</a></li></ol></li><li><a href="#toc23" tabindex="0">t.Setenvのメリット</a><ol><li><a href="#toc24" tabindex="0">1. 自動クリーンアップ</a></li><li><a href="#toc25" tabindex="0">2. パラレルテスト対応</a></li><li><a href="#toc26" tabindex="0">3. シンプルなコード</a></li></ol></li><li><a href="#toc27" tabindex="0">注意点とベストプラクティス</a><ol><li><a href="#toc28" tabindex="0">1. Go 1.17以降限定</a></li><li><a href="#toc29" tabindex="0">2. サブテストでの使用</a></li></ol></li><li><a href="#toc30" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<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>
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4" checked><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">状態をもつ関数</a></li><li><a href="#toc2" tabindex="0">ミドルウェアの生成：異なるシグネチャ（型）を揃える</a><ol><li><a href="#toc3" tabindex="0">シナリオ：既存のビジネスロジック関数を HTTP ハンドラーとして使いたい場合</a></li><li><a href="#toc4" tabindex="0">既存の関数（シグネチャが合わない）</a></li><li><a href="#toc5" tabindex="0">&#x274c; 無名関数を使用しない場合（コンパイルエラー）</a></li><li><a href="#toc6" tabindex="0">&#x2705; 無名関数を使用した場合（正常動作）</a></li></ol></li><li><a href="#toc7" tabindex="0">より複雑な例：認証付きハンドラー</a><ol><li><a href="#toc8" tabindex="0">既存の関数</a></li><li><a href="#toc9" tabindex="0">&#x274c; 無名関数なしの場合</a></li><li><a href="#toc10" tabindex="0">&#x2705; 無名関数ありの場合</a></li></ol></li><li><a href="#toc11" tabindex="0">ルーチンで無名関数から外部変数を参照することの問題</a><ol><li><a href="#toc12" tabindex="0">1. 競合状態（Race Condition）</a></li><li><a href="#toc13" tabindex="0">2. 変数の予期しない共有</a></li><li><a href="#toc14" tabindex="0">3. スライスの共有による問題</a></li><li><a href="#toc15" tabindex="0">4. Web アプリケーションでの実例</a></li><li><a href="#toc16" tabindex="0">5. チャネルを使った解決方法</a></li></ol></li><li><a href="#toc17" tabindex="0">対策方法一覧</a><ol><li><a href="#toc18" tabindex="0">1. 値渡し</a></li><li><a href="#toc19" tabindex="0">2. 同期プリミティブ</a></li><li><a href="#toc20" tabindex="0">3. アトミック操作</a></li><li><a href="#toc21" tabindex="0">4. チャネル</a></li></ol></li><li><a href="#toc22" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<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>
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6" checked><label class="toc-title" for="toc-checkbox-6">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">メモリ処理とストリーム処理の違い（概要）</a></li><li><a href="#toc2" tabindex="0">エンコードとデコードとは？</a></li><li><a href="#toc3" tabindex="0">メモリで JSON を処理する</a><ol><li><a href="#toc4" tabindex="0">json.Marshal / json.Unmarshal の実装例</a></li><li><a href="#toc5" tabindex="0">メモリ処理の特徴</a></li></ol></li><li><a href="#toc6" tabindex="0">ストリームで JSON を処理する</a><ol><li><a href="#toc7" tabindex="0">json.NewDecoder / json.NewEncoder の実装例</a></li><li><a href="#toc8" tabindex="0">ストリーム処理の特徴</a></li></ol></li><li><a href="#toc9" tabindex="0">メモリ処理とストリーム処理の比較</a></li><li><a href="#toc10" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<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>ReactのuseStateとuseEffectの基本</title>
		<link>https://aichi.blog/react%e3%81%aeusestate%e3%81%a8useeffect%e3%81%ae%e5%9f%ba%e6%9c%ac/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=react%25e3%2581%25aeusestate%25e3%2581%25a8useeffect%25e3%2581%25ae%25e5%259f%25ba%25e6%259c%25ac</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Tue, 07 Nov 2023 02:10:20 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[next.js]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[文法]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=3350</guid>

					<description><![CDATA[<p>Reactは、ユーザーインターフェースを構築するためのJavaScriptライブラリです。その中核をなす概念の一つが「状態（state）」です。 useStateとuseEffectは、Reactのフックと呼ばれる機能で [&#8230;]</p>
<p>The post <a href="https://aichi.blog/react%e3%81%aeusestate%e3%81%a8useeffect%e3%81%ae%e5%9f%ba%e6%9c%ac/">ReactのuseStateとuseEffectの基本</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Reactは、ユーザーインターフェースを構築するためのJavaScriptライブラリです。その中核をなす概念の一つが「状態（state）」です。</p>



<p>useStateとuseEffectは、Reactのフックと呼ばれる機能で、状態管理と副作用の管理を行います。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-8" checked><label class="toc-title" for="toc-checkbox-8">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">useStateがない場合</a></li><li><a href="#toc2" tabindex="0">useStateを使う場合</a></li><li><a href="#toc3" tabindex="0">useEffectの使い方</a><ol><li><a href="#toc4" tabindex="0">マウント時に一度だけ実行</a></li><li><a href="#toc5" tabindex="0">依存配列の変数が変わった時に発火</a></li><li><a href="#toc6" tabindex="0">無限ループの危険性</a></li></ol></li><li><a href="#toc7" tabindex="0">おわり</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">useStateがない場合</span></h2>



<pre class="wp-block-code javascript"><code>function App() {
  let count = 0;

  const handleClick = () =&gt; {
    count++;
    console.log(count);
  };

  return (
    &lt;&gt;
      &lt;h1&gt;普通のカウント&lt;/h1&gt;
      &lt;button onClick={handleClick}&gt;Click Me!&lt;/button&gt;
      &lt;p&gt;{count}&lt;/p&gt;
    &lt;/&gt;
  );
}</code></pre>



<p>上記のコードでは、<code>count</code>はただの変数です。</p>



<p>ボタンをクリックすると<code>count</code>の値は増加しますが、これはReactのレンダリングシステムとは独立しています。そのため、画面上のカウントは更新されず、常に0が表示されます。</p>



<h2 class="wp-block-heading"><span id="toc2">useStateを使う場合</span></h2>



<pre class="wp-block-code"><code>import { useState } from "react";

function App() {
  const &#91;count, setCount] = useState(0);

  const handleClick = () =&gt; {
    setCount(count + 1);
  };

  return (
    &lt;&gt;
      &lt;h1&gt;UseState&lt;/h1&gt;
      &lt;button onClick={handleClick}&gt;Click Me!&lt;/button&gt;
      &lt;p&gt;{count}&lt;/p&gt;
    &lt;/&gt;
  );
}</code></pre>



<p>useStateを使うと、<code>count</code>は状態として管理され、<code>setCount</code>関数によって更新されます。これにより、状態が変更されるとReactはコンポーネントを再レンダリングし、画面上のカウントが実際の値に更新されます。</p>



<h2 class="wp-block-heading"><span id="toc3">useEffectの使い方</span></h2>



<p>useEffectは、コンポーネントのライフサイクルに応じた副作用（データの取得、購読の設定など）を扱います。</p>



<h3 class="wp-block-heading"><span id="toc4">マウント時に一度だけ実行</span></h3>



<pre class="wp-block-code"><code>import { useEffect } from "react";

function App() {
  useEffect(() =&gt; {
    console.log("マウント時に一度だけ実行");
  }, &#91;]);

  return (
    &lt;&gt;
      &lt;h1&gt;UseEffect：デフォルト（マウント時に一度だけ実行）&lt;/h1&gt;
    &lt;/&gt;
  );
}</code></pre>



<p>上記のコードでは、空の依存配列（<code>[]</code>）を渡すことで、コンポーネントがマウントされた時に一度だけ副作用が実行されます。</p>



<h3 class="wp-block-heading"><span id="toc5">依存配列の変数が変わった時に発火</span></h3>



<pre class="wp-block-code"><code>import { useEffect, useState } from "react";

function App() {
  const &#91;count, setCount] = useState(0);

  const handleClick = () =&gt; {
    setCount(count + 1);
  };

  useEffect(() =&gt; {
    console.log("countが変更されました");
  }, &#91;count]);

  return (
    &lt;&gt;
      &lt;h1&gt;UseEffect：変数が変更された時に実行&lt;/h1&gt;
      &lt;button onClick={handleClick}&gt;+1&lt;/button&gt;
      &lt;p&gt;count: {count}&lt;/p&gt;
    &lt;/&gt;
  );
}</code></pre>



<p>ここでは、<code>count</code>が更新されるたびに副作用が実行されます。これは、<code>count</code>を依存配列に含めることで実現されます。</p>



<h3 class="wp-block-heading"><span id="toc6">無限ループの危険性</span></h3>



<pre class="wp-block-code"><code>import { useEffect, useState } from "react";

function App() {
  const &#91;count, setCount] = useState(0);

  useEffect(() =&gt; {
    console.log("NG：countが永遠に変更されるので、無限ループになる");
    setCount(count + 1);
  }, &#91;count]);

  return (
    &lt;&gt;
      &lt;h1&gt;UseEffect：無限ループ&lt;/h1&gt;
      &lt;button onClick={handleClick}&gt;+1&lt;/button&gt;
      &lt;p&gt;count: {count}&lt;/p&gt;
    &lt;/&gt;
  );
}</code></pre>



<p>上記のコードは、<code>count</code>が更新されるたびに<code>setCount</code>を呼び出しているため、無限ループに陥ります。これは避けるべきです。</p>



<h2 class="wp-block-heading"><span id="toc7">おわり</span></h2>



<p>以上がuseStateとuseEffectの基本的な使い方と、それらを使ってReactでの状態管理と副作用を扱う方法です。</p>



<p>これらのフックを適切に使うことで、効率的で再利用可能なコンポーネントを作成することができます。</p><p>The post <a href="https://aichi.blog/react%e3%81%aeusestate%e3%81%a8useeffect%e3%81%ae%e5%9f%ba%e6%9c%ac/">ReactのuseStateとuseEffectの基本</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Dart言語におけるexportキーワードの利用とその重要性</title>
		<link>https://aichi.blog/dart-export-keyword-usage-and-importance/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=dart-export-keyword-usage-and-importance</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Wed, 09 Aug 2023 05:21:30 +0000</pubDate>
				<category><![CDATA[Flutter]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[文法]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=3117</guid>

					<description><![CDATA[<p>Dart言語におけるexportキーワードは、コードの整理と管理に重要な役割を果たします。この記事では、exportキーワードの利点と、それを使用しない場合に生じる可能性のある問題について解説します。 目次 export [&#8230;]</p>
<p>The post <a href="https://aichi.blog/dart-export-keyword-usage-and-importance/">Dart言語におけるexportキーワードの利用とその重要性</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Dart言語における<code>export</code>キーワードは、コードの整理と管理に重要な役割を果たします。この記事では、<code>export</code>キーワードの利点と、それを使用しない場合に生じる可能性のある問題について解説します。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-10" checked><label class="toc-title" for="toc-checkbox-10">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">exportキーワードとは？</a></li><li><a href="#toc2" tabindex="0">exportの利点</a><ol><li><a href="#toc3" tabindex="0">1. コードの整理</a></li><li><a href="#toc4" tabindex="0">2. 名前空間の管理</a></li></ol></li><li><a href="#toc5" tabindex="0">exportを使用しない場合の問題点</a><ol><li><a href="#toc6" tabindex="0">1. 冗長なインポート</a></li></ol></li><li><a href="#toc7" tabindex="0">2. 名前空間の衝突</a></li><li><a href="#toc8" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">exportキーワードとは？</span></h2>



<p><code>export</code>キーワードは、Dart言語においてライブラリの一部を公開するために使用されます。<code>export</code>を使用すると、特定のライブラリから別のライブラリへクラス、関数、変数などを公開することができます。</p>



<h2 class="wp-block-heading"><span id="toc2">exportの利点</span></h2>



<h3 class="wp-block-heading"><span id="toc3">1. コードの整理</span></h3>



<p><code>export</code>を使用すると、関連するクラスや関数を一つのファイルから公開することができます。これにより、他のファイルからは一つのファイルをインポートするだけで、必要なすべてのクラスや関数にアクセスできます。</p>



<h3 class="wp-block-heading"><span id="toc4">2. 名前空間の管理</span></h3>



<p><code>export</code>を使用すると、特定のライブラリの一部だけを公開することができます。これにより、名前の衝突を避けることができます。</p>



<h2 class="wp-block-heading"><span id="toc5">exportを使用しない場合の問題点</span></h2>



<h3 class="wp-block-heading"><span id="toc6">1. 冗長なインポート</span></h3>



<p><code>export</code>を使用しない場合、各クラスや関数を使用するためには、それぞれの定義が含まれるファイルを個別にインポートする必要があります。これはコードが冗長になり、管理が難しくなる可能性があります。</p>



<p>例えば、以下のようなファイル構成があるとします：</p>



<pre class="wp-block-code"><code class="">lib/
  ├── commands/
  │   ├── command1.dart
  │   ├── command2.dart
  │   └── command3.dart
  └── main.dart

</code></pre>



<p>各<code>command</code>ファイルには異なるクラスや関数が定義されているとします。<code>main.dart</code>でこれらのクラスや関数を使用する場合、<code>export</code>を使用しないと以下のように個別にインポートする必要があります：</p>



<pre class="wp-block-code"><code class="">import 'commands/command1.dart';
import 'commands/command2.dart';
import 'commands/command3.dart';

void main() {
  // Use classes or functions from command1.dart, command2.dart, and command3.dart
}

</code></pre>



<p>これは冗長であり、新たな<code>command</code>ファイルが追加されるたびに<code>main.dart</code>も更新する必要があります。</p>



<p>一方、<code>commands.dart</code>というファイルを作成し、その中で各<code>command</code>ファイルを<code>export</code>すると、<code>main.dart</code>からは一つのファイルをインポートするだけで済みます：</p>



<pre class="wp-block-code"><code class="">// commands.dart
export 'command1.dart';
export 'command2.dart';
export 'command3.dart';

</code></pre>



<pre class="wp-block-code"><code class="">// main.dart
import 'commands.dart';

void main() {
  // Use classes or functions from command1.dart, command2.dart, and command3.dart
}

</code></pre>



<p>このように、<code>export</code>を使用するとコードの整理が容易になり、冗長なインポートを避けることができます。</p>



<h2 class="wp-block-heading"><span id="toc7">2. 名前空間の衝突</span></h2>



<p><code>export</code>を使用しないと、異なるライブラリで同じ名前のクラスや関数が定義されている場合、名前の衝突が発生する可能性があります。これは、特に大規模なプロジェクトや複数のライブラリを使用する場合に問題となります。</p>



<p>例えば、あるライブラリ<code>LibraryA</code>と<code>LibraryB</code>があり、それぞれに<code>Foo</code>という名前のクラスが定義されているとします。以下のように、これらのライブラリを直接インポートすると、どちらの<code>Foo</code>クラスを参照しているのかが明確でなくなります：</p>



<pre class="wp-block-code"><code class="">import 'package:LibraryA/LibraryA.dart';
import 'package:LibraryB/LibraryB.dart';

void main() {
  var foo = Foo(); // Is this Foo from LibraryA or LibraryB?
}

</code></pre>



<p>このような名前の衝突は、コードの可読性を低下させ、バグの原因となる可能性があります。</p>



<p>一方、<code>export</code>を使用すると、特定のライブラリの一部だけを公開することができます。これにより、名前の衝突を避けることができます。例えば、<code>LibraryA</code>から<code>Foo</code>クラスだけを公開し、<code>LibraryB</code>からは<code>Bar</code>クラスだけを公開するような設定が可能です：</p>



<pre class="wp-block-code"><code class="">// LibraryA.dart
export 'Foo.dart';

// LibraryB.dart
export 'Bar.dart';

</code></pre>



<p>そして、これらのライブラリを使用する側では、必要なクラスだけを明示的にインポートすることができます：</p>



<pre class="wp-block-code"><code class="">import 'package:LibraryA/LibraryA.dart'; // Only imports Foo
import 'package:LibraryB/LibraryB.dart'; // Only imports Bar

void main() {
  var foo = Foo(); // Clearly from LibraryA
  var bar = Bar(); // Clearly from LibraryB
}

</code></pre>



<p>このように、<code>export</code>を使用することで、名前空間の衝突を避け、コードの可読性とメンテナンス性を向上させることができます。</p>



<h2 class="wp-block-heading"><span id="toc8">まとめ</span></h2>



<p>以上のように、<code>export</code>キーワードはコードの整理と名前空間の管理に役立つ重要な機能です。適切に使用することで、コードの可読性とメンテナンス性を向上させることができます。</p><p>The post <a href="https://aichi.blog/dart-export-keyword-usage-and-importance/">Dart言語におけるexportキーワードの利用とその重要性</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Dart言語のcallメソッド: インスタンスを関数のように扱う深掘りガイド</title>
		<link>https://aichi.blog/dart-call-method-deep-dive/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=dart-call-method-deep-dive</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Wed, 09 Aug 2023 06:57:50 +0000</pubDate>
				<category><![CDATA[Flutter]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[文法]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=3119</guid>

					<description><![CDATA[<p>Dart言語には、クラスのインスタンスを関数のように扱うための特別なメソッド、callメソッドがあります。この記事では、callメソッドの使い方、使用しない場合との違い、そしてその使用ケースについて深掘りします。 目次  [&#8230;]</p>
<p>The post <a href="https://aichi.blog/dart-call-method-deep-dive/">Dart言語のcallメソッド: インスタンスを関数のように扱う深掘りガイド</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Dart言語には、クラスのインスタンスを関数のように扱うための特別なメソッド、<code>call</code>メソッドがあります。この記事では、<code>call</code>メソッドの使い方、使用しない場合との違い、そしてその使用ケースについて深掘りします。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-12" checked><label class="toc-title" for="toc-checkbox-12">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">callメソッドとは？</a><ol><li><a href="#toc2" tabindex="0">callメソッドの利点</a></li></ol></li><li><a href="#toc3" tabindex="0">callメソッドを使用しない場合</a><ol><li><a href="#toc4" tabindex="0">通常のメソッドの利点</a></li></ol></li><li><a href="#toc5" tabindex="0">使用ケースと注意点</a></li><li><a href="#toc6" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">callメソッドとは？</span></h2>



<p><code>call</code>メソッドは、クラスのインスタンスを関数のように呼び出すことができる特別なメソッドです。以下の例では、<code>Greeter</code>クラスに<code>call</code>メソッドが定義されており、そのインスタンスを関数のように呼び出しています。</p>



<pre class="wp-block-code"><code class="">class Greeter {
  String call(String name) {
    return 'Hello, $name!';
  }
}

void main() {
  var greeter = Greeter();
  print(greeter('John'));  // Outputs: Hello, John!
}

</code></pre>



<h3 class="wp-block-heading"><span id="toc2">callメソッドの利点</span></h3>



<ul class="wp-block-list">
<li><strong>シンプルな構文</strong>: インスタンスを関数のように呼び出すことで、コードがシンプルになります。</li>



<li><strong>柔軟性</strong>: インスタンスに対してパラメータを渡すことで、動的な挙動を実現できます。</li>
</ul>



<h2 class="wp-block-heading"><span id="toc3">callメソッドを使用しない場合</span></h2>



<p><code>call</code>メソッドを使用しない場合、通常のメソッドを定義し、そのメソッドを呼び出す必要があります。</p>



<pre class="wp-block-code"><code class="">class Greeter {
  String greet(String name) {
    return 'Hello, $name!';
  }
}

void main() {
  var greeter = Greeter();
  print(greeter.greet('John'));  // Outputs: Hello, John!
}

</code></pre>



<h3 class="wp-block-heading"><span id="toc4">通常のメソッドの利点</span></h3>



<ul class="wp-block-list">
<li><strong>明確な意図</strong>: メソッド名を使用することで、そのメソッドが何をするのかが明確になります。</li>



<li><strong>一般的な構文</strong>: 他のプログラミング言語との類似性があり、読み手にとって理解しやすいかもしれません。</li>
</ul>



<h2 class="wp-block-heading"><span id="toc5">使用ケースと注意点</span></h2>



<p><code>call</code>メソッドは、以下のようなケースで特に有用です。</p>



<ul class="wp-block-list">
<li><strong>関数オブジェクトの実装</strong>: インスタンスが関数のように振る舞うべき場合。</li>



<li><strong>DSLの構築</strong>: ドメイン固有言語（DSL）を構築する際に、自然な構文を提供するため。</li>
</ul>



<p>しかし、以下の注意点もあります。</p>



<ul class="wp-block-list">
<li><strong>可読性の低下</strong>: <code>call</code>メソッドの使用が過度になると、コードの可読性が低下する可能性があります。</li>



<li><strong>名前の衝突</strong>: 同じクラス内で<code>call</code>メソッドと通常のメソッドを混在させると、名前の衝突が発生する可能性があります。</li>
</ul>



<h2 class="wp-block-heading"><span id="toc6">まとめ</span></h2>



<p><code>call</code>メソッドは、クラスのインスタンスを関数のように扱うための強力な機能です。シンプルな構文と柔軟性が魅力ですが、適切な場合にのみ使用することが推奨されます。コードのシンプルさと可読性を向上させるために、<code>call</code>メソッドの使用を検討する価値がありますが、その使用には注意が必要です。</p><p>The post <a href="https://aichi.blog/dart-call-method-deep-dive/">Dart言語のcallメソッド: インスタンスを関数のように扱う深掘りガイド</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Neumorphism（ニューモーフィズム）とFlutterでの実装方法</title>
		<link>https://aichi.blog/neumorphism-implementation-in-flutter/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=neumorphism-implementation-in-flutter</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Fri, 11 Aug 2023 06:06:03 +0000</pubDate>
				<category><![CDATA[Flutter]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[文法]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=3132</guid>

					<description><![CDATA[<p>Neumorphism（ニューモーフィズム）は、近年のUIデザインのトレンドとして注目されているスタイルです。この記事では、Neumorphismの概要と、Flutterでの具体的な実装方法について解説します。 目次 N [&#8230;]</p>
<p>The post <a href="https://aichi.blog/neumorphism-implementation-in-flutter/">Neumorphism（ニューモーフィズム）とFlutterでの実装方法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Neumorphism（ニューモーフィズム）は、近年のUIデザインのトレンドとして注目されているスタイルです。この記事では、Neumorphismの概要と、Flutterでの具体的な実装方法について解説します。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-14" checked><label class="toc-title" for="toc-checkbox-14">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">Neumorphismとは？</a><ol><li><a href="#toc2" tabindex="0">陰影と光の効果</a></li><li><a href="#toc3" tabindex="0">シンプルで洗練されたデザイン</a></li><li><a href="#toc4" tabindex="0">ユーザー体験への影響</a></li></ol></li><li><a href="#toc5" tabindex="0">FlutterでのNeumorphismの実装</a><ol><li><a href="#toc6" tabindex="0">1. シンプルなNeumorphicボタン</a></li><li><a href="#toc7" tabindex="0">2. Neumorphicカード</a></li><li><a href="#toc8" tabindex="0">3. Neumorphicスイッチ</a></li></ol></li><li><a href="#toc9" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">Neumorphismとは？</span></h2>



<p>Neumorphism（ニューモーフィズム）は、リアルな物理的な要素をシンプルに表現するデザイン手法で、近年のUIデザインのトレンドとして注目されています。このデザインスタイルは、陰影と光の効果を巧みに利用して、ボタンやカードなどの要素が画面から浮き出るように見せることが特徴です。</p>



<h3 class="wp-block-heading"><span id="toc2">陰影と光の効果</span></h3>



<p>Neumorphismの最大の特徴は、陰影と光の効果を使って3Dのような視覚効果を生み出すことです。通常のフラットデザインとは異なり、Neumorphismでは、要素の外側に柔らかい陰影を配置し、内側には光の反射を表現します。この組み合わせによって、要素が画面から浮き出ているかのような錯覚を生み出します。</p>



<h3 class="wp-block-heading"><span id="toc3">シンプルで洗練されたデザイン</span></h3>



<p>Neumorphismは、シンプルで洗練されたデザインを提供します。色彩は控えめで、主に中立的な色調が使用されることが多いです。このため、ユーザーの目を引きつけることなく、全体のデザインと調和します。</p>



<h3 class="wp-block-heading"><span id="toc4">ユーザー体験への影響</span></h3>



<p>Neumorphismのデザインは、ユーザーに対して直感的な操作感を提供します。要素が浮き出ているように見えることで、タッチやクリックが期待される場所を自然に示すことができます。これによって、ユーザーは迷うことなく操作を行うことができるでしょう。</p>



<h2 class="wp-block-heading"><span id="toc5">FlutterでのNeumorphismの実装</span></h2>



<p>Flutterでは、以下のように3つの異なるNeumorphicデザインを簡単に実装することができます。</p>



<h3 class="wp-block-heading"><span id="toc6">1. シンプルなNeumorphicボタン</span></h3>



<p>ボタンに陰影と光の効果を追加して、浮き出るように見せます。</p>



<pre class="wp-block-code"><code class="">// Neumorphicボタンの実装例
Container(
  decoration: BoxDecoration(
    color: Colors.grey[300],
    borderRadius: BorderRadius.circular(12),
    boxShadow: [
      BoxShadow(
        color: Colors.white,
        offset: Offset(-5, -5),
        blurRadius: 15,
      ),
      BoxShadow(
        color: Colors.black.withOpacity(0.3),
        offset: Offset(5, 5),
        blurRadius: 15,
      ),
    ],
  ),
  child: FlatButton(
    onPressed: () {},
    child: Text("Button"),
  ),
)
</code></pre>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box"><input id="toggle-checkbox-20230811145959" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-20230811145959">dartpad用</label><div class="toggle-content">
<p>下記のサイトでコピペして確認してください。</p>





<a rel="noopener" href="https://dartpad.dev/?" title="DartPad" 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%2Fdartpad.dev%2F%3F?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">DartPad</div><div class="blogcard-snippet external-blogcard-snippet">An online Dart editor with support for console and Flutter apps.</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://dartpad.dev/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">dartpad.dev</div></div></div></div></a>




<pre class="wp-block-code"><code class="">import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Container(
            decoration: BoxDecoration(
              color: Colors.grey[300],
              borderRadius: BorderRadius.circular(12),
              boxShadow: [
                BoxShadow(
                  color: Colors.white,
                  offset: Offset(-5, -5),
                  blurRadius: 15,
                ),
                BoxShadow(
                  color: Colors.black.withOpacity(0.3),
                  offset: Offset(5, 5),
                  blurRadius: 15,
                ),
              ],
            ),
            child: TextButton(
              onPressed: () {},
              child: Text("Button"),
            ),
          ),
        ),
      ),
    );
  }
}</code></pre>
</div></div>



<h3 class="wp-block-heading"><span id="toc7">2. Neumorphicカード</span></h3>



<p>カードにも同様の効果を適用し、リアルな見た目を実現します。</p>



<pre class="wp-block-code"><code class="">// Neumorphicカードの実装例
Container(
  decoration: BoxDecoration(
    color: Colors.grey[300],
    borderRadius: BorderRadius.circular(12),
    boxShadow: [
      BoxShadow(
        color: Colors.white,
        offset: Offset(-5, -5),
        blurRadius: 15,
      ),
      BoxShadow(
        color: Colors.black.withOpacity(0.3),
        offset: Offset(5, 5),
        blurRadius: 15,
      ),
    ],
  ),
  child: Text("Card"),
)
</code></pre>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box"><input id="toggle-checkbox-20230811150412" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-20230811150412">dartpad用</label><div class="toggle-content">
<pre class="wp-block-code"><code class="">import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Container(
            decoration: BoxDecoration(
              color: Colors.grey[300],
              borderRadius: BorderRadius.circular(12),
              boxShadow: [
                BoxShadow(
                  color: Colors.white,
                  offset: Offset(-5, -5),
                  blurRadius: 15,
                ),
                BoxShadow(
                  color: Colors.black.withOpacity(0.3),
                  offset: Offset(5, 5),
                  blurRadius: 15,
                ),
              ],
            ),
            child: Text("Card"),
          ),
        ),
      ),
    );
  }
}</code></pre>
</div></div>



<h3 class="wp-block-heading"><span id="toc8">3. Neumorphicスイッチ</span></h3>



<p>スイッチにもNeumorphismを適用することで、一貫したデザインを提供します。</p>



<pre class="wp-block-code"><code class="">// Neumorphicスイッチの実装例
Switch(
  value: _isSwitched,
  onChanged: (value) {
    setState(() {
      _isSwitched = value;
    });
  },
  activeThumbImage: AssetImage('path/to/your/image'),
  inactiveThumbImage: AssetImage('path/to/your/image'),
  activeTrackColor: Colors.grey[300],
  inactiveTrackColor: Colors.grey[300],
)
</code></pre>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box"><input id="toggle-checkbox-20230811150454" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-20230811150454">dartpad用</label><div class="toggle-content">
<pre class="wp-block-code"><code class="">import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&gt; _MyAppState();
}

class _MyAppState extends State&lt;MyApp&gt; {
  bool _isSwitched = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Switch(
            value: _isSwitched,
            onChanged: (value) {
              setState(() {
                _isSwitched = value;
              });
            },
            activeTrackColor: Colors.grey[300],
            inactiveTrackColor: Colors.grey[300],
          ),
        ),
      ),
    );
  }
}</code></pre>
</div></div>



<h2 class="wp-block-heading"><span id="toc9">まとめ</span></h2>



<p>Neumorphismは、現代のUIデザインにおいて魅力的な選択肢となっています。Flutterを使用すれば、このデザインを簡単に実装することができます。</p>



<p>上記のコードを参考にして、自分のプロジェクトにNeumorphismを取り入れてみてください。</p><p>The post <a href="https://aichi.blog/neumorphism-implementation-in-flutter/">Neumorphism（ニューモーフィズム）とFlutterでの実装方法</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Dart言語の基本概念: コンストラクタ、インスタンス生成、継承、抽象クラス、Mixin</title>
		<link>https://aichi.blog/dart-basics-constructors-inheritance-abstract-mixin/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=dart-basics-constructors-inheritance-abstract-mixin</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Fri, 11 Aug 2023 09:50:48 +0000</pubDate>
				<category><![CDATA[Flutter]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[文法]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=3139</guid>

					<description><![CDATA[<p>この記事では、Dartの基本的なオブジェクト指向プログラミングの概念について解説します。 目次 クラスにおける５つの基本概念1. コンストラクタ2. インスタンスの生成3. 継承4. 抽象クラス5. Mixinまとめ ク [&#8230;]</p>
<p>The post <a href="https://aichi.blog/dart-basics-constructors-inheritance-abstract-mixin/">Dart言語の基本概念: コンストラクタ、インスタンス生成、継承、抽象クラス、Mixin</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>この記事では、Dartの基本的なオブジェクト指向プログラミングの概念について解説します。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-16" checked><label class="toc-title" for="toc-checkbox-16">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">クラスにおける５つの基本概念</a><ol><li><a href="#toc2" tabindex="0">1. コンストラクタ</a></li><li><a href="#toc3" tabindex="0">2. インスタンスの生成</a></li><li><a href="#toc4" tabindex="0">3. 継承</a></li><li><a href="#toc5" tabindex="0">4. 抽象クラス</a></li><li><a href="#toc6" tabindex="0">5. Mixin</a></li></ol></li><li><a href="#toc7" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">クラスにおける５つの基本概念</span></h2>



<h3 class="wp-block-heading"><span id="toc2">1. コンストラクタ</span></h3>



<p>コンストラクタは、クラスのインスタンスが生成される際に自動的に呼び出されるメソッドです。クラス名と同名のメソッドとして定義します。</p>



<pre class="wp-block-code"><code>class Person {
  String name;

  // コンストラクタ
  Person(this.name) {
    print('インスタンスが生成されました。');
  }
}

void main() {
  var person = Person('John'); // インスタンス生成時にコンストラクタが呼ばれる
}

</code></pre>



<h3 class="wp-block-heading"><span id="toc3">2. インスタンスの生成</span></h3>



<p><code>new</code>キーワードを使用してクラスのインスタンスを生成します。</p>



<p>ただ、Dart 2以降では<code>new</code>キーワードはオプショナルです。</p>



<pre class="wp-block-code"><code>class Dog {
  String name;

  Dog(this.name);
}

void main() {
  var dog = new Dog('Rex'); // 'new'を使用してインスタンスを生成
}

</code></pre>



<h3 class="wp-block-heading"><span id="toc4">3. 継承</span></h3>



<p><code>extends</code>キーワードを使用して、親クラスのプロパティとメソッドを子クラスに継承します。</p>



<pre class="wp-block-code"><code>class Animal {
  void eat() {
    print('食べる');
  }
}

class Cat extends Animal { // Animalクラスを継承
  void meow() {
    print('ニャー');
  }
}

void main() {
  var cat = Cat();
  cat.eat(); // 親クラスのメソッドを呼び出す
  cat.meow();
}

</code></pre>



<h3 class="wp-block-heading"><span id="toc5">4. 抽象クラス</span></h3>



<p>抽象クラスは、インスタンス化できないクラスで、継承を前提としています。</p>



<p>またその際、**<code>abstract</code>**キーワードを使用します。</p>



<pre class="wp-block-code"><code>abstract class Shape {
  void draw(); // 抽象メソッド
}

class Circle extends Shape {
  void draw() {
    print('円を描く');
  }
}

void main() {
  var circle = Circle();
  circle.draw();
}

</code></pre>



<h3 class="wp-block-heading"><span id="toc6">5. Mixin</span></h3>



<p>Mixinを使用すると、他のクラスのフィールドやメソッドを、継承せずにクラスに追加できます。</p>



<p>またその際、**<code>with</code>**キーワードを使用します。</p>



<pre class="wp-block-code"><code>mixin Flyer {
  void fly() {
    print('飛ぶ');
  }
}

class Bird with Flyer {} // Flyerの機能をBirdクラスに追加

void main() {
  var bird = Bird();
  bird.fly(); // Mixinからのメソッドを呼び出す
}

</code></pre>



<h2 class="wp-block-heading"><span id="toc7">まとめ</span></h2>



<p>この記事では、Dartの基本的なオブジェクト指向プログラミングの概念について解説しました。これらの概念は、Flutterアプリケーションの開発において非常に重要です。コードの再利用、保守性の向上、柔軟な設計が可能になります。</p><p>The post <a href="https://aichi.blog/dart-basics-constructors-inheritance-abstract-mixin/">Dart言語の基本概念: コンストラクタ、インスタンス生成、継承、抽象クラス、Mixin</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Dart言語の基本: staticフィールド/メソッドとsetter/getterの解説</title>
		<link>https://aichi.blog/dart-basics-static-fields-methods-setters-getters/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=dart-basics-static-fields-methods-setters-getters</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Fri, 11 Aug 2023 09:53:49 +0000</pubDate>
				<category><![CDATA[Flutter]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[文法]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=3142</guid>

					<description><![CDATA[<p>Dart言語はFlutter開発で使用されるプログラミング言語です。今回は、Dartの基本的な概念であるstaticフィールド/メソッドとsetter/getterについて解説します。 目次 staticフィールド/メソ [&#8230;]</p>
<p>The post <a href="https://aichi.blog/dart-basics-static-fields-methods-setters-getters/">Dart言語の基本: staticフィールド/メソッドとsetter/getterの解説</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p></p>



<p>Dart言語はFlutter開発で使用されるプログラミング言語です。今回は、Dartの基本的な概念である<code>static</code>フィールド/メソッドと<code>setter/getter</code>について解説します。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-18" checked><label class="toc-title" for="toc-checkbox-18">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">staticフィールド/メソッド</a><ol><li><a href="#toc2" tabindex="0">何故staticが必要か？</a></li><li><a href="#toc3" tabindex="0">例1: staticフィールドの使用</a></li><li><a href="#toc4" tabindex="0">例2: staticメソッドの使用</a></li><li><a href="#toc5" tabindex="0">例3: staticフィールドとメソッドの組み合わせ</a></li></ol></li><li><a href="#toc6" tabindex="0">staticフィールド/メソッドがない場合</a><ol><li><a href="#toc7" tabindex="0">不具合1: インスタンス間でのデータ共有が困難</a></li><li><a href="#toc8" tabindex="0">不具合2: インスタンスの作成なしにメソッドを呼び出せない</a></li></ol></li><li><a href="#toc9" tabindex="0">setter/getter</a><ol><li><a href="#toc10" tabindex="0">何故setter/getterが必要か？</a></li><li><a href="#toc11" tabindex="0">例1: 基本的なsetter/getter</a></li><li><a href="#toc12" tabindex="0">例2: setterでのバリデーション</a></li><li><a href="#toc13" tabindex="0">例3: 計算プロパティのgetter</a></li></ol></li><li><a href="#toc14" tabindex="0">setter/getterがない場合</a><ol><li><a href="#toc15" tabindex="0">不具合1: フィールドへの直接アクセスによるデータの不整合</a></li><li><a href="#toc16" tabindex="0">不具合2: 計算プロパティの不足</a></li></ol></li><li><a href="#toc17" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">staticフィールド/メソッド</span></h2>



<h3 class="wp-block-heading"><span id="toc2">何故staticが必要か？</span></h3>



<p><code>static</code>キーワードは、フィールドやメソッドがクラス自体に属することを示します。</p>



<p>これにより、インスタンスを作成せずにアクセスできるようになります。</p>



<h3 class="wp-block-heading"><span id="toc3">例1: staticフィールドの使用</span></h3>



<pre class="wp-block-code java"><code lang="dart" class="language-dart line-numbers">class Counter {
  // staticフィールドはクラスに属するため、インスタンス間で共有されます。
  static int count = 0;

  void increment() {
    count++;
  }
}

void main() {
  var counter1 = Counter();
  counter1.increment();
  print(Counter.count); // 1

  var counter2 = Counter();
  counter2.increment();
  print(Counter.count); // 2
}
</code></pre>



<p>この例では、<code>count</code>フィールドがクラスに属しているため、異なるインスタンス間で共有されます。</p>



<h3 class="wp-block-heading"><span id="toc4">例2: staticメソッドの使用</span></h3>



<pre class="wp-block-code"><code lang="dart" class="language-dart">class MathHelper {
  // staticメソッドはクラスに属するため、インスタンスを作成せずに呼び出せます。
  static int add(int a, int b) {
    return a + b;
  }
}

void main() {
  print(MathHelper.add(3, 4)); // 7
}
</code></pre>



<p>ここでは、<code>add</code>メソッドがクラスに属しているため、インスタンスを作成せずに直接呼び出せます。</p>



<h3 class="wp-block-heading"><span id="toc5">例3: staticフィールドとメソッドの組み合わせ</span></h3>



<pre class="wp-block-code"><code class="">class Logger {
  static int logCount = 0;

  // staticメソッド内でstaticフィールドにアクセス
  static void log(String message) {
    print(message);
    logCount++;
  }
}

void main() {
  Logger.log("Hello, World!");
  print(Logger.logCount); // 1
}
</code></pre>



<p>この例では、staticメソッド内でstaticフィールドにアクセスしています。</p>



<h2 class="wp-block-heading"><span id="toc6">staticフィールド/メソッドがない場合</span></h2>



<h3 class="wp-block-heading"><span id="toc7">不具合1: インスタンス間でのデータ共有が困難</span></h3>



<p><code>static</code>フィールドがないと、クラスのインスタンス間でデータを共有するのが困難になります。</p>



<pre class="wp-block-code"><code class="">class Counter {
  int count = 0; // staticでないため、インスタンスごとに異なる値を持つ

  void increment() {
    count++;
  }
}

void main() {
  var counter1 = Counter();
  counter1.increment();
  print(counter1.count); // 1

  var counter2 = Counter();
  counter2.increment();
  print(counter2.count); // 1, しかし期待値は2
}

</code></pre>



<p>この例では、<code>count</code>がstaticでないため、<code>counter1</code>と<code>counter2</code>のインスタンス間で値が共有されず、期待した結果が得られません。</p>



<h3 class="wp-block-heading"><span id="toc8">不具合2: インスタンスの作成なしにメソッドを呼び出せない</span></h3>



<p><code>static</code>メソッドがないと、クラスのインスタンスを作成しなければメソッドを呼び出せません。これは、ユーティリティ関数などの場合に非効率です。</p>



<h2 class="wp-block-heading"><span id="toc9">setter/getter</span></h2>



<h3 class="wp-block-heading"><span id="toc10">何故setter/getterが必要か？</span></h3>



<p><code>setter/getter</code>は、フィールドへのアクセスを制御するために使用されます。これにより、フィールドへの直接アクセスを制限し、特定のロジックを適用できます。</p>



<h3 class="wp-block-heading"><span id="toc11">例1: 基本的なsetter/getter</span></h3>



<pre class="wp-block-code"><code class="">class Person {
  String _name;

  // getter
  String get name =&gt; _name;

  // setter
  set name(String value) {
    _name = value;
  }
}

void main() {
  var person = Person();
  person.name = "John"; // setter
  print(person.name);   // getter, "John"
}
</code></pre>



<p>この例では、<code>name</code>フィールドへのアクセスを制御しています。</p>



<h3 class="wp-block-heading"><span id="toc12">例2: setterでのバリデーション</span></h3>



<pre class="wp-block-code"><code class="">class Age {
  int _value;

  // getter
  int get value =&gt; _value;

  // setterで年齢のバリデーション
  set value(int age) {
    if (age &gt;= 0 &amp;&amp; age &lt;= 120) {
      _value = age;
    } else {
      print("Invalid age");
    }
  }
}

void main() {
  var age = Age();
  age.value = 25; // Valid
  age.value = -5; // Invalid age
}
</code></pre>



<p>ここでは、setterを使用して年齢のバリデーションを行っています。</p>



<h3 class="wp-block-heading"><span id="toc13">例3: 計算プロパティのgetter</span></h3>



<pre class="wp-block-code"><code class="">class Circle {
  double radius;

  // 計算プロパティで円の面積を取得
  double get area =&gt; 3.14 * radius * radius;

  Circle(this.radius);
}

void main() {
  var circle = Circle(5.0);
  print(circle.area); // 78.5
}
</code></pre>



<p>この例では、計算プロパティを使用して円の面積を取得しています。</p>



<h2 class="wp-block-heading"><span id="toc14">setter/getterがない場合</span></h2>



<h3 class="wp-block-heading"><span id="toc15">不具合1: フィールドへの直接アクセスによるデータの不整合</span></h3>



<p>setter/getterがないと、フィールドへの直接アクセスが可能になり、データの不整合が生じる可能性があります。</p>



<pre class="wp-block-code"><code class="">class Age {
  int value; // 直接アクセス可能
}

void main() {
  var age = Age();
  age.value = -5; // 不正な値が設定される
}

</code></pre>



<p>この例では、年齢が負の値に設定されるなど、不正な状態になる可能性があります。</p>



<h3 class="wp-block-heading"><span id="toc16">不具合2: 計算プロパティの不足</span></h3>



<p>getterを使用しないと、計算プロパティが作成できず、コードの再利用性と可読性が低下する可能性があります。</p>



<pre class="wp-block-code"><code class="">class Circle {
  double radius;
  // areaの計算プロパティがない
}

void main() {
  var circle = Circle();
  double area = 3.14 * circle.radius * circle.radius; // 円の面積の計算が複数箇所で必要になる場合
}

</code></pre>



<p>この例では、円の面積の計算が複数箇所で必要になる場合、コードの重複が生じる可能性があります。</p>



<h2 class="wp-block-heading"><span id="toc17">まとめ</span></h2>



<p><code>static</code>フィールド/メソッドと<code>setter/getter</code>は、Dartプログラミングの基本的な概念です。適切に使用することで、コードの再利用性と保守性を向上させることができます。</p><p>The post <a href="https://aichi.blog/dart-basics-static-fields-methods-setters-getters/">Dart言語の基本: staticフィールド/メソッドとsetter/getterの解説</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Dart言語の魅力的な演算子: ドット、カスケード、スプレッド</title>
		<link>https://aichi.blog/dart-operators-dot-cascade-spread/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=dart-operators-dot-cascade-spread</link>
		
		<dc:creator><![CDATA[愛知郎]]></dc:creator>
		<pubDate>Mon, 14 Aug 2023 14:06:52 +0000</pubDate>
				<category><![CDATA[Flutter]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[文法]]></category>
		<guid isPermaLink="false">https://aichi.blog/?p=3173</guid>

					<description><![CDATA[<p>DartはGoogleが開発した多様性に富むプログラミング言語で、開発者に堅牢で効率的なアプリケーションの構築を可能にします。その中でも、ドット演算子、カスケード演算子、スプレッド演算子は、コードを洗練させ、一般的なプロ [&#8230;]</p>
<p>The post <a href="https://aichi.blog/dart-operators-dot-cascade-spread/">Dart言語の魅力的な演算子: ドット、カスケード、スプレッド</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>DartはGoogleが開発した多様性に富むプログラミング言語で、開発者に堅牢で効率的なアプリケーションの構築を可能にします。その中でも、ドット演算子、カスケード演算子、スプレッド演算子は、コードを洗練させ、一般的なプログラミングの課題を解決するための重要なツールです。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-20" checked><label class="toc-title" for="toc-checkbox-20">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">ドット演算子 (.)</a><ol><li><a href="#toc2" tabindex="0">例1: インスタンスのフィールドへのアクセス</a></li><li><a href="#toc3" tabindex="0">例2: 静的フィールドへのアクセス</a></li></ol></li><li><a href="#toc4" tabindex="0">カスケード演算子 (..)</a><ol><li><a href="#toc5" tabindex="0">例3: カスケード演算子なし</a></li><li><a href="#toc6" tabindex="0">例4: カスケード演算子あり</a></li></ol></li><li><a href="#toc7" tabindex="0">スプレッド演算子 (&#8230;)</a><ol><li><a href="#toc8" tabindex="0">例5: スプレッド演算子なし</a></li><li><a href="#toc9" tabindex="0">例6: スプレッド演算子あり</a></li></ol></li><li><a href="#toc10" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">ドット演算子 (.)</span></h2>



<p>ドット演算子は、クラスのメンバー（変数、メソッド、ネストされたクラスなど）にアクセスするための基本的な構文です。</p>



<h3 class="wp-block-heading"><span id="toc2">例1: インスタンスのフィールドへのアクセス</span></h3>



<pre class="wp-block-code"><code class="">class Person {
  String name;

  Person(this.name);
}

void main() {
  var person = Person('John');
  print(person.name); // ドット演算子でnameフィールドにアクセス
}</code></pre>



<h3 class="wp-block-heading"><span id="toc3">例2: 静的フィールドへのアクセス</span></h3>



<pre class="wp-block-code"><code class="">class Math {
  static const double pi = 3.14;
}

void main() {
  print(Math.pi); // ドット演算子で静的フィールドpiにアクセス
}</code></pre>



<h2 class="wp-block-heading"><span id="toc4">カスケード演算子 (..)</span></h2>



<p>カスケード演算子は、同じオブジェクトに対する一連の操作を連鎖させるための強力なツールです。</p>



<h3 class="wp-block-heading"><span id="toc5">例3: カスケード演算子なし</span></h3>



<pre class="wp-block-code"><code class="">class Car {
  void start() {}
  void drive() {}
}

void main() {
  var car = Car();
  car.start();
  car.drive(); // オブジェクトの参照を繰り返す
}</code></pre>



<h3 class="wp-block-heading"><span id="toc6">例4: カスケード演算子あり</span></h3>



<pre class="wp-block-code"><code class="">void main() {
  var car = Car()
    ..start()
    ..drive(); // カスケード演算子で連鎖させる
}</code></pre>



<h2 class="wp-block-heading"><span id="toc7">スプレッド演算子 (&#8230;)</span></h2>



<p>スプレッド演算子は、コレクションの要素を個々のコンポーネントに展開するためのエレガントな方法を提供します。</p>



<h3 class="wp-block-heading"><span id="toc8">例5: スプレッド演算子なし</span></h3>



<pre class="wp-block-code"><code class="">var list1 = [1, 2];
var list2 = [3, 4];
var combined = list1 + list2; // [1, 2, 3, 4]</code></pre>



<h3 class="wp-block-heading"><span id="toc9">例6: スプレッド演算子あり</span></h3>



<pre class="wp-block-code"><code class="">var combined = [...list1, ...list2]; // [1, 2, 3, 4]</code></pre>



<h2 class="wp-block-heading"><span id="toc10">まとめ</span></h2>



<p>Dartの演算子 <strong><code>.</code></strong>, <strong><code>..</code></strong>, <strong><code>...</code></strong> は、コードの効率化と可読性の向上に貢献します。これらの演算子の理解と適切な使用は、Dartプログラミングのスキル向上につながります。この記事が、Dartの演算子の使用方法とその利点を理解するための参考になれば幸いです。</p><p>The post <a href="https://aichi.blog/dart-operators-dot-cascade-spread/">Dart言語の魅力的な演算子: ドット、カスケード、スプレッド</a> first appeared on <a href="https://aichi.blog">AichiLog</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
