代码仓库

gitee

実行方法

Hello Worldを例に取って実行方法を説明します。

1
2
3
4
5
6
7
8
// main.go
package main // packageがmainのファイルは直接実行できる

import "fmt"

func main() {
fmt.Println("Hello, World!")
}
1
2
3
4
5
# 直接実行
go run main.go
# またはコンパイルして実行
go build main.go
./main.exe

変数定義とデータ型

変数はメモリ上の特定のアドレスを指す識別子で、通常データを保存するために使用されます。
データ型は変数の型を表し、型はメモリ上でその変数が占めるスペースの大きさを決定します。
代入はデータ(値)を変数が指すアドレスに保存し、後でその変数を使ってそのデータにアクセスします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func main() {
// 整数変数を宣言して初期化
var age int = 25
fmt.Println("Age:", age)

// 浮動小数点数変数を宣言して初期化
var height float64 = 1.75
fmt.Println("Height:", height)

// 文字列変数を宣言して初期化
var name string
name = "Alice"
fmt.Println("Name:", name)

// ブール変数を宣言して初期化
isStudent := true // 自動型推論、最も一般的な変数宣言方法
fmt.Println("Is student:", isStudent)
}

フロー制御

フロー制御とはプログラムの実行順序を定義することです。

順序

順序実行は上から下へと順番に実行されます。これはプログラムの通常の実行フローです。

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
fmt.Println("Step 1")
fmt.Println("Step 2")
fmt.Println("Step 3")
}

選択

選択とは異なる条件に基づいて異なるコードを実行することです。
選択のフローは単一分岐と多分岐に分けられます。

単一分岐

単一分岐とは1つの分岐ノードしかなく、1回の条件判断しかありません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
var score int = 85

if score >= 60 {
fmt.Println("Pass")
} else {
fmt.Println("Fail")
}
}

多分岐

多分岐とは複数の分岐ノードがあり、複数の条件判断があります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
var score int = 85

if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else if score >= 70 {
fmt.Println("C")
} else {
fmt.Println("D")
}
}

ループ

ループは繰り返し実行されるプロセスを表します。

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
// 一時変数iを定義し、初期値を1に設定し、ループ条件をiが5以下であるとする
for i := 1; i <= 5; i++ {
fmt.Println(i)
}
}

関数

関数はクロージャであり、独自のスコープを持ちます。
関数は1つ以上の入力を受け取り、関数体のコードを実行し、終了時に1つ以上の出力を返すことができます。
関数を使用することで、共通のロジックを抽出し、コードを簡素化することができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

// 2つの整数パラメータを受け取り、それらの和を返す関数を定義
func add(a int, b int) int {
return a + b
}

func main() {
result := add(5, 3)
fmt.Println("Result:", result) // 出力: Result: 8
}

クラス/構造体

クラス/構造体

クラスはデータとメソッド(関数)を含む抽象構造であり、それを定義することができます。
クラスを使用するには、クラスをインスタンス化する必要があります。これは、指定されたサイズのメモリ空間を確保して保存することです。
クラスのサイズは内部で定義されたデータ(例えばint)によって決まり、コンパイラはデータのサイズに基づいて必要なメモリを自動的に割り当てます。
1つのクラスには複数のインスタンス化オブジェクトがあり、つまり複数の異なる変数がありますが、これらの変数はすべて同じクラスの構造を持ちます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

// NameとAgeフィールドを含むPerson構造体を定義
type Person struct {
Name string
Age int
}

// PersonにSayHelloメソッドを追加
func (p Person) SayHello() {
fmt.Printf("Hello, my name is %s and I am %d years old.\n", p.Name, p.Age)
}

func main() {
// Personインスタンスを作成
person := Person{Name: "Alice", Age: 25}

// メソッドを呼び出す
person.SayHello()
// 出力: Hello, my name is Alice and I am 25 years old.
}

インターフェース

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main

import "fmt"

// Speakメソッドを含むAnimalインターフェースを定義
type Animal interface {
Speak() string // Speak()メソッドを実装したすべての型はAnimal型と見なされる
}

// Dog構造体を定義
type Dog struct {
Name string
}

// DogにSpeakメソッドを実装
func (d Dog) Speak() string {
return "Woof! My name is " + d.Name
}

// Cat構造体を定義
type Cat struct {
Name string
}

// CatにSpeakメソッドを実装
func (c Cat) Speak() string {
return "Meow! My name is " + c.Name
}

func main() {
// Animalインターフェース変数を宣言
var a Animal

// Dogインスタンスを作成
dog := Dog{Name: "Buddy"}
a = dog // DogがAnimalインターフェースを実装しているため、aに代入できる
fmt.Println(a.Speak()) // 出力: Woof! My name is Buddy

// Catインスタンスを作成
cat := Cat{Name: "Whiskers"}
a = cat // Catも同様にAnimalインターフェースを実装している
fmt.Println(a.Speak()) // 出力: Meow! My name is Whiskers

// 異なる型のオブジェクトを統一的に処理できる
animals := []Animal{dog, cat}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}

フレームワーク

開発を簡素化するために、プログラマは共通のロジックを関数やクラス・メソッドにカプセル化して再利用します。このような抽象化が進むと「フレームワーク」が生まれます。
私の理解では、「ライブラリ(library)」とは共通機能を集めたツール群であり、ユーザーが必要に応じて任意のものを追加して使用できるものです。一方で「フレームワーク(framework)」は、プロジェクトにおける構造やテンプレートを定義し、そのルールに従ってユーザーが開発を行うというスタイルに近いです。ただし、実際にはこの二つの境界は曖昧な場合が多く、混同されて使われることも頻繁にあります。

Web

これまで作成してきたプログラムはすべてローカルで動作していますが、世界中のユーザーにも使えるようにするにはネットワーク上に公開する必要があります。
Webフレームワークは言語標準のネットワーク機能をラップし、HTTPサーバー構築やルーティングなどの機能を提供します。
Go言語でよく使われるWebフレームワークは Gin です。

1
go get "github.com/gin-gonic/gin"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"github.com/gin-gonic/gin"
)

func main() {
// デフォルトのルーターエンジンを作成
r := gin.Default()

// GETメソッドに対応する/helloエンドポイントを定義
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello from Gin!",
})
})

// HTTPサーバーを起動(デフォルトポート: 0.0.0.0:8080)
r.Run(":8080")
}

ブラウザで http://localhost:8080/hello にアクセスして確認してください。

DB(データベース)

現在までは変数を使ってデータをメモリ内に保存してきましたが、プログラム終了と同時にメモリ上のデータは破棄されてしまいます。そのため、データを永続的に保持したい場合には「データベース」が必要になります。
Go言語でよく使われるORM型データベースフレームワークは GORM です。

1
go get "gorm.io/driver/sqlite" "gorm.io/gorm" "github.com/glebarez/sqlite"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"github.com/glebarez/sqlite"
// 必須: go env -w CGO_ENABLED=1
// gccが必要ですが、これは公式のSQLiteドライバで推奨されています。
"gorm.io/gorm"
)

// データベーステーブルに対応する構造体を定義
type User struct {
gorm.Model
Name string
Email string `gorm:"unique"`
}

func main() {
// SQLiteデータベース(ファイル名: dev.db)に接続
db, err := gorm.Open(sqlite.Open("./dev.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}

// モデルに合わせてテーブルを自動マイグレーション
db.AutoMigrate(&User{})

// レコードを作成
db.Create(&User{Name: "Alice", Email: "alice@example.com"})

// レコードを検索
var user User
db.Where("name = ?", "Alice").First(&user)
println("Found user:", user.Name)

// レコードを更新
db.Model(&user).Update("Name", "Bob")

// レコードを削除
db.Delete(&user)
}

拡張機能の例

○ 並列処理(ゴルーチン)、マイクロサービスアーキテクチャ、リフレクション(動的プロキシ生成)、ファイル操作、ネットワーク通信など
○ フレームワークの内部原理と自作
○ WASM(WebAssembly)、gRPCによるRPC通信


本站总访问量