Go言語の魅力:人間とAIに優しいコード
Goでランダムなuuidを生成する
Goでランダムなuuidを生成する
Goでアプリ開発をしているときにuuidを使用しかったので調べてみた
使用するライブラリ:https://github.com/google/uuid
やること
uuidをインストールする
go get github.com/google/uuid
実装してみる 参考:https://pkg.go.dev/github.com/google/uuid#NewString
package main import ( "fmt" "github.com/google/uuid" ) func main() { id := uuid.New() fmt.Println(id) // 出力例: f47ac10b-58cc-4372-a567-0e02b2c3d479 fmt.Printf("%T\n", id) // 出力: uuid.UUID idStr := id.String() fmt.Println(idStr) // 出力例: f47ac10b-58cc-4372-a567-0e02b2c3d479 fmt.Printf("%T\n", idStr) // 出力: string }
uuid.New()を使うと ランダムなUUID(バージョン4) が生成されます。uuid.UUID型で返されるため、必要に応じてString()メソッドで文字列に変換できます。- 文字列に変換すると、データベースへの保存やAPIレスポンスへの利用などがしやすくなります。
3年目の目標
はじめに
4月から社会人として3年目になった
保守業務がメインだったので実装する能力が低く課題に感じている エンジニアとして生きていきたい気持ちが大きくなったので異動届けを出して、開発能力の求められるチームに異動することになった
そのためエンジニアとして能力を身につけるための目標を設定しようと思う
エンジニア3年目として意識すること
- 多めに見積もって目標を立てるので達成できない部分はご愛嬌でお願いしたい
技術の幅と深さをどう広げるか
- チーム内でトップレベルでGoについて詳しく高いレベルを求められるエンジニアになりたい
- 自分の領域をバックエンド(Go)とインフラ(AWS,Iac)として極めていく
- 仕事を勝手に生み出して、社内においての自分の領域や担当範囲を広げていく
目標達成のためにやること
やること
- Goの標準パッケージを理解してコードリーディングを進める
- KubernetesやTerraformなどのGoで書かれているOSSのコードリーディングする
- 技術書を読んでインプットする
- Go(5,6冊)
- React(2冊)
- AWS(3冊)
- Terraform(2,3冊)
- Kubernetes(2冊)
- Docker(1冊)
- ネットワーク(2冊)
- OAuth(3冊)
- DB(3冊)
- Linux(2冊)
達成するための習慣
- 月に2~4本テックブログを書くようにする
- 月に2~4冊技術書を読む
- 1日3ページ以上、技術書を読む
- 早めに出勤して30分以上学習する
- 技術の勉強は全部作ってみる
最後に
大抵はできないのではなくて、やるべきことを知っているけどやらないという方が近いものです
また開発に異動するという自分にとっても大きな一年となるため、チャレンジしていくことを増やしていきたい
React Nativeを書く際に実機で動作確認する(Docker)
はじめに
なんかモバイルアプリを触ってみようと思って、React Nativeで色々書こうとしていました。 Windowsで開発していたので、実機確認がXcodeなどでできず、Expoを使うことと、Dockerで立ち上げた時に実機で確認ができるようにしてみました
Expoとは?
説明しようとしたけど、綺麗にまとまっている記事があったので添付いたします。
https://zenn.dev/kamo_tomoki/books/0158a7770edeea/viewer/f58b65
Dockerファイルで立ち上がるようにする
Dockerで立ち上げができるとExpoがホットリロードで確認することができます。 そのため、動作確認などが簡単という利点があります
事前準備
- Expoにログインしてアカウントを作成する
- 端末側にもアプリをインストールしておく
Dockerをかく
- Dockerfile
# ベースイメージとしてnode:18を使用 FROM node:18 # 作業ディレクトリを/appに設定 WORKDIR /app # 現在のディレクトリの内容をコンテナにコピー COPY package*.json ./ # npmをインストール RUN npm install # 現在のディレクトリの内容をコンテナにコピー COPY . . # Expoアプリをトンネルモードで起動 CMD ["npm", "start"]
- docker-compose.yml
services: myapp: container_name: myapp-react-native tty: true build: context: . dockerfile: Dockerfile volumes: - .:/app env_file: .env ports: - 8081:8081
- .env
- PCと同じIPを端末で指定しないといけません
- そのため、
ifconfigなどでipを確認して、envに記載します - .envの内容はdocker-compose.ymlで指定します
REACT_NATIVE_PACKAGER_HOSTNAME=192.168.x.x
これでdockerのイメージをビルドして、立ち上げると以下のようにQRが出てくるので、端末で読み込みます

つまづいた部分
QRコードは出てきましたが、端末で読み込むと起動できませんでした。
PCと端末のWi-Fiが同じでないといけないので、確認したがうまくいかず
色々調べるとちゃんと設定しているけど、IPが接続できていないよと判定されてしまうことがあるみたいです。
対策として--tunnelを実行する必要があります
https://docs.expo.dev/more/expo-cli/#tunneling
package.jsonに書かれている内容に--tunnelを追加します
- "start": "expo start", + "start": "expo start --tunnel",
これでWeb側も端末側も同じ内容になりました! おわり!
PCの画面

iOSの画面

Goの雛形をBlueprintで作成してみる
はじめに
Railsをやっているとrails new app_nameで簡単に雛形を作成できてしまいます。
Goでも同じようなことができないかなと思っていたので、探してみるとgo-blueprintがあったので作成してみることにしました
実行してみる
- まずgo-blueprintをインストールする
$ go install github.com/melkeydev/go-blueprint@latest
- createコマンド
$ go-blueprint create
createコマンド後に以下のような画像が表示されます。
プロジェクト名を求められるのでsample_appを指定します
今回はGinを選択しました
Ginを選択した後に、databaseの選択を要求されます
Postgresを選択する

ここまでで基本的なファイルなどが作成されます
Makefileも自動的に作成されるので、make runで起動することができます
$ make run [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET / --> sample_app/internal/server.(*Server).HelloWorldHandler-fm (4 handlers) [GIN-debug] GET /health --> sample_app/internal/server.(*Server).healthHandler-fm (4 handlers)
おまけ:具体的な実装
- sample_app/cmd/api/main.go
package main import ( "context" "fmt" "log" "net/http" "os/signal" "syscall" "time" "sample_app/internal/server" ) func gracefulShutdown(apiServer *http.Server, done chan bool) { // Create context that listens for the interrupt signal from the OS. ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() // Listen for the interrupt signal. <-ctx.Done() log.Println("shutting down gracefully, press Ctrl+C again to force") // The context is used to inform the server it has 5 seconds to finish // the request it is currently handling ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := apiServer.Shutdown(ctx); err != nil { log.Printf("Server forced to shutdown with error: %v", err) } log.Println("Server exiting") // Notify the main goroutine that the shutdown is complete done <- true } func main() { server := server.NewServer() // Create a done channel to signal when the shutdown is complete done := make(chan bool, 1) // Run graceful shutdown in a separate goroutine go gracefulShutdown(server, done) err := server.ListenAndServe() if err != nil && err != http.ErrServerClosed { panic(fmt.Sprintf("http server error: %s", err)) } // Wait for the graceful shutdown to complete <-done log.Println("Graceful shutdown complete.") }
- sample_app/internal/database/database.go
package database import ( "context" "database/sql" "fmt" "log" "os" "strconv" "time" _ "github.com/jackc/pgx/v5/stdlib" _ "github.com/joho/godotenv/autoload" ) // Service represents a service that interacts with a database. type Service interface { // Health returns a map of health status information. // The keys and values in the map are service-specific. Health() map[string]string // Close terminates the database connection. // It returns an error if the connection cannot be closed. Close() error } type service struct { db *sql.DB } var ( database = os.Getenv("BLUEPRINT_DB_DATABASE") password = os.Getenv("BLUEPRINT_DB_PASSWORD") username = os.Getenv("BLUEPRINT_DB_USERNAME") port = os.Getenv("BLUEPRINT_DB_PORT") host = os.Getenv("BLUEPRINT_DB_HOST") schema = os.Getenv("BLUEPRINT_DB_SCHEMA") dbInstance *service ) func New() Service { // Reuse Connection if dbInstance != nil { return dbInstance } connStr := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable&search_path=%s", username, password, host, port, database, schema) db, err := sql.Open("pgx", connStr) if err != nil { log.Fatal(err) } dbInstance = &service{ db: db, } return dbInstance } // Health checks the health of the database connection by pinging the database. // It returns a map with keys indicating various health statistics. func (s *service) Health() map[string]string { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() stats := make(map[string]string) // Ping the database err := s.db.PingContext(ctx) if err != nil { stats["status"] = "down" stats["error"] = fmt.Sprintf("db down: %v", err) log.Fatalf("db down: %v", err) // Log the error and terminate the program return stats } // Database is up, add more statistics stats["status"] = "up" stats["message"] = "It's healthy" // Get database stats (like open connections, in use, idle, etc.) dbStats := s.db.Stats() stats["open_connections"] = strconv.Itoa(dbStats.OpenConnections) stats["in_use"] = strconv.Itoa(dbStats.InUse) stats["idle"] = strconv.Itoa(dbStats.Idle) stats["wait_count"] = strconv.FormatInt(dbStats.WaitCount, 10) stats["wait_duration"] = dbStats.WaitDuration.String() stats["max_idle_closed"] = strconv.FormatInt(dbStats.MaxIdleClosed, 10) stats["max_lifetime_closed"] = strconv.FormatInt(dbStats.MaxLifetimeClosed, 10) // Evaluate stats to provide a health message if dbStats.OpenConnections > 40 { // Assuming 50 is the max for this example stats["message"] = "The database is experiencing heavy load." } if dbStats.WaitCount > 1000 { stats["message"] = "The database has a high number of wait events, indicating potential bottlenecks." } if dbStats.MaxIdleClosed > int64(dbStats.OpenConnections)/2 { stats["message"] = "Many idle connections are being closed, consider revising the connection pool settings." } if dbStats.MaxLifetimeClosed > int64(dbStats.OpenConnections)/2 { stats["message"] = "Many connections are being closed due to max lifetime, consider increasing max lifetime or revising the connection usage pattern." } return stats } // Close closes the database connection. // It logs a message indicating the disconnection from the specific database. // If the connection is successfully closed, it returns nil. // If an error occurs while closing the connection, it returns the error. func (s *service) Close() error { log.Printf("Disconnected from database: %s", database) return s.db.Close() }
- sample_app/internal/server/routes.go
package server import ( "net/http" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func (s *Server) RegisterRoutes() http.Handler { r := gin.Default() r.Use(cors.New(cors.Config{ AllowOrigins: []string{"http://localhost:5173"}, // Add your frontend URL AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"}, AllowHeaders: []string{"Accept", "Authorization", "Content-Type"}, AllowCredentials: true, // Enable cookies/auth })) r.GET("/", s.HelloWorldHandler) r.GET("/health", s.healthHandler) return r } func (s *Server) HelloWorldHandler(c *gin.Context) { resp := make(map[string]string) resp["message"] = "Hello World" c.JSON(http.StatusOK, resp) } func (s *Server) healthHandler(c *gin.Context) { c.JSON(http.StatusOK, s.db.Health()) }
- docker-compose.yml
services:
psql_bp:
image: postgres:latest
restart: unless-stopped
environment:
POSTGRES_DB: ${BLUEPRINT_DB_DATABASE}
POSTGRES_USER: ${BLUEPRINT_DB_USERNAME}
POSTGRES_PASSWORD: ${BLUEPRINT_DB_PASSWORD}
ports:
- "${BLUEPRINT_DB_PORT}:5432"
volumes:
- psql_volume_bp:/var/lib/postgresql/data
volumes:
psql_volume_bp:
これでdockerを立ち上げれば完成です
Goでファイル操作のCLIツールを作成する
はじめに
別にツールじゃなくていいけど、なんかファイル操作とか触ってみないと覚えないし、遠回りしてGoでCLIツール作成してみます
想定読者
何となくGo触ったことある人 Goのファイル操作何となくわかるけど、そんなに触ってない人 GoでCLIツールの実装をしようと思っている人
概要
- 課題
- yyyymmdd.mdでタスクを管理しているが、その日に終わらなかったタスクを毎回次の日のファイルにコピペしなければならないのが面倒
最終成果物
- [ ]になっている行だけ取得して次の日のmdに追加- サブコマンドでタスクを追加
大まかな流れ 1: ファイルの中身を取得してみる 2:
- [ ]だけ抽出して翌日のmdに追加する 3: サブコマンドでタスクの追加とタグ付けをする
ファイルの中身を取得する
package main import ( "fmt" "log" "os" ) func main() { // ファイルの中身を一括で読み込む content, err := os.ReadFile("20250121.md") if err != nil { log.Fatalf("ファイルを読み取れませんでした: %v", err) } // ファイル内容を表示 fmt.Println("ファイル内容:") fmt.Println(string(content)) }
$ go run main.go ファイル内容: - [ ] hogehoge - [ ] hogehoge
ファイルの中身を別のファイルに書き出す
- 今日のmdファイルから翌日にmdファイルを出力する
- ファイル操作によってファイルの読み込み、書き込みなどをする
- 完了済みのタスクが表示されないようにする
package main import ( "bufio" "fmt" "log" "os" "strings" "time" ) func main() { // 現在自国を取得 now := time.Now() //今日の日付を取得するフォーマット nowStr := now.Format("20060102") // ファイルの中身を読み込む content, err := os.ReadFile(nowStr + ".md") if err != nil { log.Fatalf("ファイルを読み取れませんでした: %v", err) } // nowStrから1日追加 tomorrow := now.AddDate(0, 0, 1) tomorrowStr := tomorrow.Format("20060102") tomorrow_md := fmt.Sprintf("%s.md", tomorrowStr) //ファイルが存在する場合は、作成不要にしておく if _, err := os.Stat(tomorrow_md); err == nil { fmt.Println("すでに作成されているので作成しません") } else { // ファイルが存在しない場合、新規作成する file, err := os.Create(tomorrow_md) if err != nil { log.Fatalf("エラーが発生しました: %v", err) } defer file.Close() fmt.Println("新しいファイルを作成しました") } // 未完了タスクを抽出 var tasks []string scanner := bufio.NewScanner(strings.NewReader(string(content))) for scanner.Scan() { line := scanner.Text() // 未完了タスク行(`- [ ]` を含む)を抽出 if strings.Contains(line, "- [ ]") { // 未完了のタスクを slice に追加(インデントを保持) tasks = append(tasks, line) } } output := strings.Join(tasks, "\n") // 出力の例(ファイルに書き込む場合) err = os.WriteFile(tomorrow_md, []byte(output), 0644) if err != nil { log.Fatalf("エラーが発生しました: %v", err) } fmt.Println(tasks) if err := scanner.Err(); err != nil { log.Fatalf("スキャン中にエラー:%v", err) } // 未完了タスクがない場合の処理 if len(tasks) == 0 { fmt.Println("未完了タスクはありません。") return } // 未完了のタスクだけを書き込む file, err := os.OpenFile(tomorrow_md, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { log.Fatalf("ファイルを開く中にエラー: %v", err) } defer file.Close() fmt.Println("未完了のタスクを書き込みました") fmt.Println("今日の内容を更新しました!") }
- 入力ファイルの例
- 完了済みのタスクを用意します
- インデントも下げて親タスク、子タスクの関係にします
- [ ] xxx
- [x] xxxx
- [x] xxx
- [ ] yyyy
- 出力ファイルの例
- ちゃんと完了済みのタスクのみ出力されています
- [ ] xxx - [ ] yyyy
サブコマンドの実装
- サブコマンドを実装するには、フォルダ構造は大きく変更されます。
- 実装したいことは、大きく変更ないのでサブコマンド使用するならよしなにやってください
初期設定
$ go install github.com/spf13/cobra-cli@latest $ cobra-cli init --license MIT --viper=false $ go run main.go A longer description that spans multiple lines and likely contains examples and usage of using your application. For example: Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.
- main.goの中身
package main import "todo_support/cmd" func main() { cmd.Execute() }
- generate.goの中身
package cmd import ( "bufio" "fmt" "log" "os" "path/filepath" "strings" "time" "github.com/spf13/cobra" ) const tasksDir = "tasks" // タスクファイルを保存するディレクトリ // generateCmd represents the generate command var generateCmd = &cobra.Command{ Use: "generate", Short: "翌日のTODOファイルを生成します", Long: "今日のTODOファイルから未完了のタスクを抽出し、翌日のTODOファイルを新規作成します。", Run: func(cmd *cobra.Command, args []string) { // 現在自国を取得 now := time.Now() // 今日の日付を取得するフォーマット nowStr := now.Format("20060102") // ファイルの中身を読み込む content, err := os.ReadFile(filepath.Join(tasksDir, nowStr+".md")) if err != nil { log.Fatalf("ファイルを読み取れませんでした: %v", err) } // nowStrから1日追加 tomorrow := now.AddDate(0, 0, 1) tomorrowStr := tomorrow.Format("20060102") tomorrow_md := filepath.Join(tasksDir, fmt.Sprintf("%s.md", tomorrowStr)) // ファイルが存在する場合は、作成不要にしておく if _, err := os.Stat(tomorrow_md); err == nil { fmt.Println("すでに作成されているので作成しません") } else { // ファイルが存在しない場合、新規作成する file, err := os.Create(tomorrow_md) if err != nil { log.Fatalf("エラーが発生しました: %v", err) } defer file.Close() fmt.Println("新しいファイルを作成しました") } // 未完了タスクを抽出 var tasks []string scanner := bufio.NewScanner(strings.NewReader(string(content))) for scanner.Scan() { line := scanner.Text() // 未完了タスク行(`- [ ]` を含む)を抽出 if strings.Contains(line, "- [ ]") { // 未完了のタスクを slice に追加(インデントを保持) tasks = append(tasks, line) } } output := strings.Join(tasks, "\n") // 出力の例(ファイルに書き込む場合) err = os.WriteFile(tomorrow_md, []byte(output), 0644) if err != nil { log.Fatalf("エラーが発生しました: %v", err) } fmt.Println(tasks) if err := scanner.Err(); err != nil { log.Fatalf("スキャン中にエラー:%v", err) } // 未完了タスクがない場合の処理 if len(tasks) == 0 { fmt.Println("未完了タスクはありません。") return } // 未完了のタスクだけを書き込む file, err := os.OpenFile(tomorrow_md, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { log.Fatalf("ファイルを開く中にエラー: %v", err) } defer file.Close() fmt.Println("未完了のタスクを書き込みました") fmt.Println("今日の内容を更新しました!") }, } func init() { rootCmd.AddCommand(generateCmd) }
- add.goの中身
- add.goでは、タスクの内容を追加するためのコマンドを実行します
--tommorowのサブコマンドを実装して明日のタスクに追加することができます--tagのサブコマンドでタスクの最初にタグを追加することができます
package cmd import ( "fmt" "os" "time" "github.com/spf13/cobra" ) // addCmd represents the add command var addCmd = &cobra.Command{ Use: "add [タスク内容]", Short: "タスクを追加します", Long: `このコマンドは今日または翌日のタスクに新しいタスクを追加します。 使用例: add '新しいタスク内容' --tag [タグの名前]`, Run: func(cmd *cobra.Command, args []string) { if len(args) < 1 { fmt.Println("addの引数にタスク内容を指定してください") return } // タスク内容を取得 task := args[0] // フラグの値を取得 tomorrow, _ := cmd.Flags().GetBool("tomorrow") tag, _ := cmd.Flags().GetString("tag") // 今日または翌日のタスクファイルを決定 date := time.Now() if tomorrow { date = date.AddDate(0, 0, 1) // 翌日 } filePath := fmt.Sprintf("tasks/%s.md", date.Format("20060102")) // タスク行を作成 taskLine := "" if tag != "" { taskLine = fmt.Sprintf("\n- [ ] [%s] %s", tag, task) } else { taskLine = fmt.Sprintf("\n- [ ] %s", task) } // タスクファイルに書き込み f, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Println("ファイルを開けませんでした:", err) return } defer f.Close() if _, err := f.WriteString(taskLine); err != nil { fmt.Println("タスクを追加できませんでした:", err) } else { fmt.Printf("タスク '%s' を %s に追加しました\n", taskLine, filePath) } }, } func init() { // フラグの登録を最初に実行 addCmd.Flags().BoolP("tomorrow", "t", false, "Add task to tomorrow's TODO file") addCmd.Flags().StringP("tag", "g", "", "タスクにタグを付与します") // ルートコマンドに追加 rootCmd.AddCommand(addCmd) }
$ go build -o todo_cli # addだけだと今日の日付のファイルにタスクが追加される $ ./todo_cli add "今日のタスク" # -t(--tomorrow)をサブコマンドとして実行すると明日の日付のファイルにタスクが追加される $ ./todo_cli add -t "明日のタスク" # -a(--tag)をサブコマンドとして実行すると追加するタスクにタグが追加される $ ./todo_cli add "タグの追加" --tag urgent # - [ ] [urget] タグの追加
まとめ
ファイル操作覚える必要ないけど、使い機会なかったのでよかった tagの追加などは優先度などを判断するのに使用できそうなので、優先度高いタスクをパッと追加できるのはいいことだなという気がした
Goの構造体で継承っぽいことをする
はじめに
Goで継承は使えないのでどうやるかというと構造体をいい感じで利用して実装します。
使えないってよりは、継承の概念がないと思います。
この記事で書くことが⭐️の内容です。
⭐️1. 構造体⭐️ 1-1. 構造体の書き方 1-2.メソッドと構造体 ⭐️2. 構造体の埋め込み⭐️ 2-1. 構造体を増やし、親子関係を作る 3. インターフェース 4. 構造体とインターフェース
やってみよう!
1,構造体とは
構造体はtypeとstructを使用して定義します。
構造体をどうやってかくかとメソッドについても簡単に触れます
type 構造体名 structで構造体を作成できるmain関数の中でインスタンスを作成する- フィールドに値を代入して使用する
package main import "fmt" // 名前のフィールドを持つAnimalという構造体を作成 type Animal struct { Name string //名前フィールド } func main(){ // インスタンスを作成する var dog Animal dog.Name = "ポチ" fmt.Println(dog.Name) }
結果
ポチ
上記の実装に対してSpeakeメソッドを作成します
func (レシーバー名 レシーバー型) メソッド名(引数リスト) 戻り値リストでメソッドを作成するmain関数の中でSpeakメソッドを呼び出す
package main import "fmt" // 名前のフィールドを持つAnimalという構造体を作成 type Animal struct { Name string //名前フィールド } func (a Animal) Speak() { fmt.Printf("%s のSpeakメソッドの内容。\n", a.Name) } func main(){ // インスタンスを作成する var dog Animal dog.Name = "ポチ" fmt.Println(dog.Name) dog.Speak() }
結果
ポチ ポチ のSpeakメソッドの内容
2. 構造体の埋め込み
次にこれらの構造体を複数使用することで親子関係を作ります。
以下の実装では先ほどの実装におけるAnimalを親とするDogとCatの構造体を作成します。 Dogでは、名前・鳴き声、犬種を表示します。 Catでは、名前・鳴き声・好きな魚を表示します。
この時に、名前と鳴き声は共通しているのでAnimalがフィールドとして持ちます。 Dogは、犬種。Catは好きな魚を表示したいので、それぞれの構造体にフィールドを持ちます。
Animalを親としたDogとCatの構造体を作成します。Animalの持つSpeakメソッドを作成します。この時、DogとVatはこのSpeakメソッドを使用できます。main関数でインスタンスを作成します。この時、Animalのインスタンスが明示的に立ち上げていませんが、埋め込みをすることで利用できます- そんで1~3で実施した内容を
fmtでぺって実行させます
package main import "fmt" // Animalの構造体を作成 type Animal struct { Name string Voice string } // Animalを親にもつDogの構造体を作成 type Dog struct { Animal // これでAnimalの構造体を埋め込む Breed string } // Animalを親にもつCatの構造体を作成 type Cat struct { Animal // これでAnimalの構造体を埋め込む FavoriteFish string } // Speakメソッドを作成 func (a Animal) Speak() { fmt.Printf("%s の鳴き声:%s\n", a.Name, a.Voice) //fmt.Printf("%s says: %s", a.Name, a.Voice) } func main() { // dogのインスタンスを作成 dog := Dog{ Animal: Animal{Name: "いっぬ", Voice: "いっぬ!いっぬ!!"}, Breed: "柴犬", } // Catのインスタンスを作成 cat := Cat{ Animal: Animal{Name: "ねっこ", Voice: "ねっこ!ねっこ!"}, FavoriteFish: "大トロ", } // Dogのフィールドとメソッドを利用 fmt.Println("Dogの名前:", dog.Name) fmt.Println("Dogの鳴き声:", dog.Voice) fmt.Println("Dogの犬種:", dog.Breed) dog.Speak() // Catのフィールドとメソッドを利用 fmt.Println("Catの名前:", cat.Name) fmt.Println("Catの鳴き声:", cat.Voice) fmt.Println("Catの好きな食べ物:", cat.FavoriteFish) cat.Speak() }
結果
Dogの名前: いっぬ Dogの鳴き声: いっぬ!いっぬ!! Dogの犬種: 柴犬 いっぬ の鳴き声 Catの名前: ねっこ Catの鳴き声: ねっこ!ねっこ! Catの好きな食べ物: 大トロ ねっこ の鳴き声
まとめ
あと私がGoやった時は、この辺りの整理が一日でできなかったのでこれがちょうどいいボリュームだと思っています
私がインターフェースと構造体の話を書くのは、もう少し後のお話...