20191028のGoに関する記事は2件です。

Go言語の構造体の基本

Go言語にはJavaなどの言語でいうクラスやというものがなく、構造体というデータをひとまとめにして扱うための仕組みを使ってクラスのような機能を提供します。

この構造体を使うことで、オブジェクト指向の継承のような実装を実現することが出来、Go言語の一つの大きな特徴になっています。

この記事では構造体の宣言の仕方から、実際にどのように扱われるのかまで見ていきます。

構造体の基本

構造体の宣言

Go言語の構造体は以下のように変数として宣言して使用できます。

var japanese struct {
    name string
    age int
}
japanese.name = "hanako"
japanese.age = 23
fmt.Println(japanese)

構造体は変数だけではなく、型として宣言することもでき、こちらの方がより広く使われています。

type person struct {
    name string
    age int
}

studentA := person{
    name: "Taro",
    age: 20,
}

fmt.Println(studentA)

構造体の型がクラスで、定義した構造体の型で変数を宣言するのがインスタンス変数を宣言するのに似ています。

また、構造体に構造体を埋め込むことが可能で、これにより継承を実現しています。継承したい構造体(親クラスに当たるもの)を構造体の中に定義します。

オブジェクト指向言語と同じように、親クラスに当たる構造体のフィールドには子クラスに当たる構造体からアクセスすることが可能です。

func main() {
    studentA := person{
        name: "Taro",
        age:  20,
    }
    taro := japanese{
        language: "Japanese",
        country:  "Japan",
        person:   studentA,
    }

    fmt.Println(taro)
        fmt.Println(taro.person.name) //埋め込まれている構造体の要素にアクセスできる
}

type person struct {
    name string
    age  int
}

type japanese struct {
    language string
    country  string
    person   person
}

構造体はクラスに相当する役割を果たすため、当然メソッドも定義できます。構造体にどうメソッドを定義すればいいのか見ていきましょう。

構造体にメソッドを定義する

構造体にメソッドを定義するには、定義先の構造体をレシーバに持つ関数を定義する必要があります。

実際に見てみましょう。これが構造体にメソッドを定義する例です。

type person struct {
    name string
    age  int
}

func (p person) selfIntro() {
    fmt.Printf("My name is %v. I'm %v years old.", p.name, p.age)
}

ご覧のように、personをレシーバに持つselfIntroというメソッドを定義しています。selfIntro()メソッド内部では、レシーバとして受け取った構造体が持つ変数にアクセスし、その変数の値を出力しています。

func main() {
    studentA := person{
        name: "Taro",
        age:  20,
    }
    fmt.Println(studentA)
    studentA.selfIntro() //出力結果: My name is Taro. I'm 20 years old.
}

これが実際に使用する場合の使用例です。

構造体の使用例

ここまでで構造体の基本について見てきました。ここからは構造体を使用することでコードをより読みやすくできる例をご紹介します。

以下の構造体は、天気予報の情報を定義している構造体の型です。現時点では最高気温と最低気温、緯度経度による位置情報が入っています。

type wheatherReport struct {
    high, low float64
    latitude, longitude float64
}

現時点では、この二つだけなのでまだ分かりやすいでしょう。しかし、この構造体に最高湿度と最低湿度、降水確率など新しい情報を入れようとすると、ややこしくなってしまいます。

type wheatherReport struct {
    highTemperature, lowTemperature float64
    highHumidity, lowHumidity float64
    latitude, longitude float64
}

新たに構造体を定義し、構造体を埋め込むことでこの問題を解決することが出来ます。

func main() {
        nara := wheatherReport{
        temperature: temperature{high: 21.2, low: 11.0},
        humidity: humidity{high: 55, low: 33},
    }
    fmt.Println(nara.humidity.high)
    fmt.Println(nara.temperature.high)

}

type temperature struct {
    high, low float64
}

type humidity struct {
    high, low float64
}

type location struct {
    latitude, longitude float64
}

type wheatherReport struct {
    temperature temperature
    humidity humidity
    location location
}

どうでしょう、一気に読みやすくなったかと思います。Go言語のコードを読んでいると、至る所で構造体が使われているので、しっかりと理解しておきましょう。

おまけ:構造体を初期化するコンストラクタ関数

オブジェクト指向の言語には、インスタンスをNewで作成した時に呼び出される機能があります。Pythonではinit、Rubyではinitialize、PHPでは__construct()です。

しかし、そのような仕組みがGoにはありません。その代わりとなるものがコンストラクタ関数です。

コンストラクタ関数は名前の先頭にnewという名前をつけて宣言するという慣習があります。

func newTemperature(high, low float64) temperature {
    return temperature{high, low}
}

このようにして、コンストラクタ関数で構造体を初期化するというやり方があるので覚えておくとよいでしょう。

それでは、最後までお読みいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者でもDockerでAmazonLinux+Go(Echo)+Mysqlの環境構築しつつ簡単なAPIを作って動かす

DockerでAmazonLinux+Go+Mysqlの環境構築する

PHPerだったけど今度はGoやっていくので環境をDockerで作ってそこで簡単なAPIをGoのフレームワーク「Echo」で作っていく

環境

ちなみに作業環境はWindows10 Proのi7メモリ32GBノートPCです

ディレクトリ構成

|-Dockerfile
|-docker-compose.yml
|-./src
    |-main.go

Dockerfile

まずAmazonLinuxにGoをインストールしたイメージを作成する

FROM amazonlinux:latest

ENV GOPATH /go
ENV PATH /usr/local/go/bin:/go/bin:$PATH

WORKDIR /usr/src

# go install
RUN yum update -y \
    && yum install -y tar \
    && yum install -y gzip \
    && curl -OL https://dl.google.com/go/go1.13.3.linux-amd64.tar.gz \
    && tar -C /usr/local -xzf ./go1.13.3.linux-amd64.tar.gz \

# git install for go get command
RUN yum install -y git

# go library
RUN  go get github.com/go-sql-driver/mysql \
        && go get github.com/labstack/echo

CMD ["go", "run", "main.go"]

イメージ生成(docker build -t )

イメージ名は何でもいいですけどここではtest-aws_go-imgとします(.は同ディレクトリを指す)

docker build -t test-aws_go-img .

docker-compose.yml

上記でビルドして作成したイメージ(test-aws_go-img)をdocker-compose.ymlで指定

docker-compose.yml
version: '3.7'
services:
  test-aws_go-img:
    build: .
    container_name: test_app # わかりやすいコンテナ名にしておく(任意)
    ports:
      - 8080:8080
  db:
    container_name: test_db # わかりやすいコンテナ名にしておく(任意)
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password # 従来の認証方式にしておく
    volumes:
      - db-store:/var/lib/mysql
      - ./logs:/var/log/mysql
    environment:
      - MYSQL_DATABASE=test_todo
      - MYSQL_ROOT_PASSWORD=password
      - TZ=Asia/Tokyo
    ports:
      - 3307:3306
volumes: # データの永続化
  db-store:

これでDocker周りは完了

main.go

アクセスするとテーブルに登録されているデータをJSONで返すようなかんたんなAPIを作る

main.go
package main

import (
    "database/sql"
    "fmt"
    "net/http"

    _ "github.com/go-sql-driver/mysql"
    "github.com/labstack/echo"
)

// JSON用の構造体を定義
type Task struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    e := echo.New()
    e.GET("/tasks", getTasks)

    e.Start(":8080")
}

func dbConnect() *sql.DB {
    // DB接続処理
    db, err := sql.Open("mysql", "root:password@tcp(test_db)/test_todo")
    if err != nil {
        panic(err.Error())
    }
    return db
}

func getRows(db *sql.DB) *sql.Rows {
    rows, err := db.Query("SELECT id,name FROM tasks")
    if err != nil {
        panic(err.Error())
    }
    return rows
}

func getTasks(c echo.Context) error {
    // DB接続
    db := dbConnect()
    defer db.Close()
    rows := getRows(db)
    defer rows.Close()

    task := Task{}
    var results []Task
    for rows.Next() {
        err := rows.Scan(&task.ID, &task.Name)
        if err != nil {
            panic(err.Error())
        } else {
            results = append(results, task)
        }
    }

    return c.JSON(http.StatusOK, results)
}

Docker起動

動くか確認するためにバックグラウンドのオプションはつけないで普通に起動

docker-compose up

無事起動したら次に確認用のサンプルデータを入れていく

DBへ接続してデータを入れる

別の画面を開いて(Windowsならコマンドプロンプトとか)MySQLのコンテナの中に入る

docker exec -it test_db bash

MySQLへログイン(パスワードは上記で設定したpassword

# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.18 MySQL Community Server - GPL

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use test_todo;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

以下のSQLを流す(コピペで可)

SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';

SET NAMES utf8mb4;

CREATE DATABASE `test_todo` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `test_todo`;

DROP TABLE IF EXISTS `tasks`;
CREATE TABLE `tasks` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

INSERT INTO `tasks` (`id`, `name`) VALUES
(1,"go"),
(2, "php"),
(3, "java"),
(4, "html"),
(5, "css");

念の為入ったか確認しておく

mysql> select * from tasks;

URLを叩いてJSONが帰ってくるか確認

準備ができたのでターミナルやらでcurlコマンドを叩く

curl -s http://localhost:8080/tasks

すると以下のようにJSONでデータが取得出来る

[{"id":1,"name":"go"},{"id":2,"name":"php"},{"id":3,"name":"java"},{"id":4,"name":"html"},{"id":5,"name":"css"}]

以下GitBashから実行した様子
WS000012.JPG

以下はChromeからアクセスした際の様子

WS000016.JPG

おわり

  • 80%くらいの確率で動くと思うんですが動かなかったらおしえてほしいです
  • それ以外でもこうしたほうがいい等あればコメント下さい

参考URL

などなど

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む