20200628のGoに関する記事は9件です。

【DDD】ドメイン駆動設計ってなんじゃらほい

ドメイン駆動設計についての読書記録です。
主に下記書籍を参考にしています。
ドメイン駆動設計入門 ボトムアップでわかる! ドメイン駆動設計の基本 | 成瀬 允宣 |本 | 通販 | Amazon
ドメイン駆動設計 モデリング/実装ガイド - little-hands - BOOTH

用語の整理

まずはDDDに出てくる用語の整理をしていきます。

ドメイン

プログラムを適応する対象となる領域のことを指します。

利用者に焦点を当てる

利用者が何に課題を感じていて、何を解決したいのかを考える必要があります。
この2つを理解してソフトウェアに反映していきます。
これが大原則です。

モデル

現実の事業もしくは概念を抽象化したものです。
モデリングは、このモデルを得るまでの過程を指しています。

ドメインオブジェクト

上であげたモデルをソフトウェア上で動作するようにしたものです。
image.png

値オブジェクト(ValueObject)

性質

  • 不変
  • 交換可能
  • 等価性による比較

エンティティ

データベースのエンティティとは異なる概念です。

性質

  • 可変
  • 同じ属性でも区別
  • 同一性により区別

値オブジェクトとの違い

どこまでを値オブジェクトにして、どこまでをエンティティにするかは非常に難しいです。
その一つの解決策として、 ライフサイクルがあります。
対象が仮に「ユーザー」だった場合、アプリケーションで会員登録したり、ログインしたり、退会したりとアプリケーションの中でライフサイクルが存在する場合は、エンティティになります。
なので、可変か不変かで判断できそうです。
ただしこの判断は何を題材するかによって大きく変わるのでサービスによってしっかり考えるようにします。

ドメインサービス

オブジェクトに持たせる振る舞いとして正しくない場合にドメインサービスを使用します。
メソッドを実装した場合、オブジェクトのメソッドを日本語にした場合に日本がおかしい場合は、ドメインサービスに持たせれば良い。
ただ気をつけなければいけないのは、なんでもかんでもドメインサービスにいれてはいけないということです。

リポジトリ

ドメインオブジェクトの永続化や再構築を行います。
集約ごとに一つのリポジトリが存在し、

リポジトリを使用する理由

ソフトウェアに柔軟性を持たせるのが一番だと思います。
さらに具体的にいうと、テストが容易に実行できるようになります。

アーキテクチャ

ビジネスロジックが正しい場所にかき続けられることを助けるのがアーキテクチャです。
アーキテクチャはあくまで方針であることを忘れてはいけません。

集約

必ず守りたい強い整合性を持ったオブジェクトのまとまりのことをさします。

境界

集約する場合、必ず付いて回ります。

依存関係のコントロール

具象が抽象に依存するようにしなければいけません。
そうすることで変更に強いソフトウェアを作りことはsが可能になります。

依存関係逆転の法則(Dependency Inversion Principle)

抽象に依存させることが推奨されています。
より具体的に言うと、ドメイン層で定義したインターフェースに依存することをさします。
依存関係逆転の原則の重要性について - Eureka Engineering - Medium
1分でわかる依存関係逆転の原則(DIP) - Qiita

責務

このクラスが何をするクラスなのかを示します。

設計

DDDから実際の設計に落とすのは下記が非常に参考になりました。
「実践ドメイン駆動設計」を読んだので、実際にDDDで設計して作ってみた! - Qiita

コンテキストモデル
要求モデル(要望の洗い出し) → プロダクトマネージャーからビジネスで実現したいことヒアリング、洗い出し
ドメインモデル
ユースケースモデリング(ユースケース図)
画面設計(画面遷移図)
画面設計(ワイヤーフレーム)
ユースケースモデリング(ユースケース記述)
ロバストネス分析
クラス図
データモデリング(ER図)
UI設計
実装に入る...
「実践ドメイン駆動設計」を読んだので、実際にDDDで設計して作ってみた! - Qiita より引用

徐々にやっていこう。。。。

ユースケース

ユーザーストーリーを作成し、それに基づいたユースケースを洗い出します。
ここの洗い出しに関しては、下記書籍を参考に掘り下げていきたいと思っています。
ユースケース駆動開発実践ガイド (OOP Foundations)
良いユースケースを書くための発想法
ユースケースをモデリングする - Qiita
若手エンジニア必読!超絶分かるユースケース図-全知識と書き方5ステップ

Goでの実装

アーキテクチャ

アーキテクチャは、レイヤードアーキテクチャを想定します。
image.png

Goで,レイヤードアーキテクチャ - Qiita
ドメイン駆動設計で実装を始めるのに一番とっつきやすいアーキテクチャは何か[DDD] - little hands' lab
【GO/DDD】レイヤードアーキテクチャの整理 - Qiita

全体図のフォルダ構成は下記の通りです。

├── app
│   ├── application
│   │   └── usecase
│   │       ├── survey.go
│   ├── domain
│   │   ├── model
│   │   │   ├── survey.go
│   │   ├── repository
│   │   │   ├── survey.go
│   │   └── service
│   │       ├── survey.go
│   ├── infrastructure
│   │   ├── database.go
│   │   └── persistence
│   │       ├── survey.go
│   └── presentation
│       ├── handler
│       │   ├── survey.go
│       ├── middleware
│       │   └── middleware.go
│       └── router.go
├── db

ファイルの分割

集約の境界については、ファイルが境界になっています。
また、値オブジェクトやエンティティは domain層で表現されることになります。

application

ドメインロジックを呼び出し、その結果をただ返すことに特化します。
基本薄い層になります。

domain

model, repository, serviceで構成されます。

model

使用するデータ構造とドメインロジックを書きます。

// データ構造の部分
package model

type Survey struct {
    SurveyID         uint           `json:"survey_id" db:"survey_id"`
    Question         string         `json:"question" db:"question"`
    SurveyAnswerType string         `json:"survey_answer_type" db:"survey_answer_type"`
}

// ドメインロジック
....

repository

ここが依存させるべき抽象の箇所です。
依存関係逆転の法則では、抽象に依存させることが推奨されていました。
理由は、上位モジュールが下位モジュールに依存しません。
つまりこれは、インターフェースを満たすモジュールであればどんなものでも使用できることを指しましす。
上位モジュールが使いたい下位モジュールを選択することができるのです。

package repository

import "github.com/app/domain/model"

type SurveyAccessor interface {
    ListSurveyByCampaignID(campaignID uint) (*[]model.Survey, error)
}

service

ドメインサービスに書くべき実装を書きます。

package service

import (
    "github.com/app/domain/model"
    "github.com/app/domain/repository"
)

// repository経由でアクセスするためのAccessor作成
type SurveyService struct {
    serviceAccesor repository.SurveyAccessor
}

func NewSurveyService(sa repository.SurveyAccessor) *SurveyService {
    return &SurveyService{
        serviceAccesor: sa,
    }
}

func (ss *SurveyService) ListSurveyByCampaignID(campaignID uint) (*[]model.Survey, error) {
    surveys, err := ss.serviceAccesor.ListSurveyByCampaignID(campaignID)
    if err != nil {
        return nil, err
    }
    return surveys, nil
}

infrastracture

データベース(データストア)関連の処理(SQL)などを書きます。

presentation

http などの入出力のプロトコルを定義します。

その他気になったところ

Go言語での実装

Goのpackage構成と開発のベタープラクティス - Tech Blog - Recruit Lifestyle Engineer
Go言語でClean Architectureを実現して、gomockでテストしてみた - Qiita
Goにおけるドメインオブジェクト設計の考察 - 日記マン
【Go+DDD】エンティティと値オブジェクトの実装方法(自己流) - yyh-gl's Tech Blog

ドメインモデルからデータベースの設計をする順番

DDDのモデリングとは何なのか、 そしてどうコードに落とすのか

プロダクトでの活用

競合他社との差別化要因にリソースを割きたい自社プロダクトでは、
グロースさせていきたいビジネスの概念を型(クラス)に落とし込むこと がドメインモデリングになる、と考えています。
Goにおけるドメインオブジェクト設計の考察 - 日記マン より引用

フロントエンドとの融合

このDDDとフロントエンドの関係については、次回調べていきたいと思います。
ドメイン駆動Vuexで複雑さに立ち向かう - スタディスト開発ブログ - Medium
Full-Stack JavaScript meets DDD. - Qiita

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

go修行12日目 channelなど

channel

package main

import "fmt"

// SlieceとChannelを扱う関数
func gorooutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    // <- Channelに送信
    c <- sum
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    // channel integerを扱う
    c := make(chan int)
    go gorooutine1(s, c)
    // <- c でChannelから受け取るまで待機
    x := <-c
    fmt.Println(x)
}

15

キューのような使い方

  • 2つのチャンネルを並列実行しても2つの処理の実行を待つ
package main

import "fmt"

// SlieceとChannelを扱う関数
func gorooutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    // <- Channelに送信
    c <- sum
}

func gorooutine2(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    // <- Channelに送信
    c <- sum
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    // channel integerを扱う
    c := make(chan int)
    go gorooutine1(s, c)
    go gorooutine2(s, c)
    // <- c でChannelから受け取るまで待機
    x := <-c
    fmt.Println(x)
    y := <-c
    fmt.Println(y)
}
15
15

Bufferd channel

package main

import "fmt"

func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))
    // rangeで呼び出す場合はcloseしないと次のChannelを呼ぼうとしてエラーになる
    close(ch)

    // forで1つづつ呼び出す
    for c := range ch {
        fmt.Println(c)
    }
}
1
2
100
200

channelのrange、Close

package main

import "fmt"

func goroutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
        c <- sum
    }
    // 処理完了後はcloseを入れる
    close(c)
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    c := make(chan int, len(s))
    go goroutine1(s, c)
    // channelを1回ずつ呼び出す
    for i := range c {
        fmt.Println(i)
    }
}
1
3
6
10
15

Producer Consumer

package main

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

func producer(ch chan int, i int) {
    // Something
    ch <- i * 2
}

func consumer(ch chan int, wg *sync.WaitGroup) {
    for i := range ch {
        func() {
            fmt.Println("process", i*1000)
            wg.Done()
        }()
    }
    fmt.Println("#################################")
}

func main() {
    var wg sync.WaitGroup
    ch := make(chan int)

    // Producer
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go producer(ch, i)
    }

    // Consumer
    go consumer(ch, &wg)
    wg.Wait()
    close(ch)
    time.Sleep(2 * time.Second)
    fmt.Println("Done")
}
process 0
process 4000
process 2000
process 6000
process 8000
process 10000
process 12000
process 14000
process 16000
process 18000
#################################
Done

fan-out fan-in

package main

import "fmt"

func producer(first chan int) {
    defer close(first)
    for i := 0; i < 10; i++ {
        first <- i
    }
}

func multi2(first chan int, second chan int) {
    defer close(second)
    for i := range first {
        // 1 * 2 = 2
        // 2 * 2 = 4
        second <- i * 2
    }
}

func multi4(second chan int, third chan int) {
    defer close(third)
    for i := range second {
        // 2 * 4 = 8
        // 4 * 4 = 4
        third <- i * 4
    }
}

func main() {

    first := make(chan int)
    second := make(chan int)
    third := make(chan int)

    go producer(first)
    go multi2(first, second)
    go multi4(second, third)
    for result := range third {
        fmt.Println(result)
    }
}
0
8
16
24
32
40
48
56
64
72

select

package main

import (
    "fmt"
    "time"
)

func goroutine1(ch chan string) {
    for {
        ch <- "packat from 1"
        time.Sleep(3 * time.Second)
    }
}

func goroutine2(ch chan string) {
    for {
        ch <- "packat from 2"
        time.Sleep(1 * time.Second)
    }
}

func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    go goroutine1(c1)
    go goroutine2(c2)

    for {
        select {
        case msg1 := <-c1:
            fmt.Println(msg1)
        case msg2 := <-c2:
            fmt.Println(msg2)
        }
    }
}
packat from 1
packat from 2
packat from 2
packat from 2
packat from 1
packat from 2
packat from 2
packat from 2

Default Section

package main

import (
    "fmt"
    "time"
)

func main() {
    tick := time.Tick(100 * time.Millisecond)
    boom := time.After(500 * time.Millisecond)

    for {
        select {
        // 100Millisocond毎
        case <-tick:
            fmt.Println("tick,")
            // 500Millisocond毎
        case <-boom:
            fmt.Println("BOOM")
            // loop終了
            return
            // それ以外はピリオド
        default:
            fmt.Println("    .")
            time.Sleep(50 * time.Millisecond)
        }
    }
}
    .
    .
tick,
    .
    .
tick,
    .
    .
tick,
    .
    .
tick,
    .
    .
tick,
BOOM
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Go言語】Goのテストでキャッシュを利用しない方法

Go言語のテストにおいては、Go1.10から結果がキャッシュされるようになってしまいます。
キャッシュを利用せずにテストを行うには以下のオプションを付与する必要があります。

-count=1

また、キャッシュを削除するには以下のコマンドが必要です。

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

競プロメモ | 約数カウント

最近、競技プログラミングを始めたのですが、才能はなく、要領も悪く、解法を覚えるのも苦手でどうしようもない弱者なのだと思い知る日々です。でも、それでも、強くなりたいので、よく出てきそうなアルゴリズムやデータ構造や細かい Tips など何でも、メモできることはして、少しずつ知識と経験を蓄えていきたいと思って書いています。

今日は正の約数の個数の数え上げです。

最後にこれを使う問題を挙げます。こちらは、共通点のある問題に出会ったら随時追加していきたいと思います。知っている方はコメントで教えていただけると嬉しいです。

また、アドバイスなどもあったらコメントで教えていただけるととても嬉しいです。

やること

$1$ からある正の整数 $n$ までの整数のそれぞれについて、正の約数の個数を数え上げる。

方法

  1. 長さ $n + 1$ の整数配列 $A$ を用意する。(つまり $n$ までのインデックスを持つ配列)
  2. $1$ から $n$ までの整数について、配列 $A$ 内でその整数の倍数をインデックスとする要素をインクリメントしていく。
  • 計算量: $O(n \log n)$

コード

divisorCounts := make([]int, n+1, n+1)
for i := 1; i <= n; i++ {
    for j := i; j <= n; j += i {
        divisorCounts[j]++
    }
}

これを使う問題

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

Goで競プロメモ | 約数カウント

最近、競技プログラミングを始めたのですが、才能はなく、要領も悪く、解法を覚えるのも苦手でどうしようもない弱者なのだと思い知る日々です。それでも、強くなりたいので、よく出てきそうなアルゴリズムやデータ構造や細かい Tips など何でも、メモできることはして、少しずつ知識と経験を蓄えていきたいと思って書いています。

今回は「約数カウント」についてメモします。

「約数カウント」

何をやるか

$1$ からある正の整数 $n$ までの整数のそれぞれについて、正の約数の個数を数え上げる。

どうやるか

  1. 長さ $n + 1$ の整数配列 $A$ を用意する。(つまり $n$ までのインデックスを持つ配列)
  2. $1$ から $n$ までの整数について、配列 $A$ 内でその整数の倍数をインデックスとする要素をインクリメントしていく。
  • 計算量: $O(n \log n)$

コード

divisorCounts := make([]int, n+1, n+1)
// 1 から n までの i について
for i := 1; i <= n; i++ {
    // i の倍数を処理
    for j := i; j <= n; j += i {
        divisorCounts[j]++
    }
}

「約数カウント」を使う問題

AtCoder ABC172 D Sum of Divisors

私の解答はこちら: https://atcoder.jp/contests/abc172/submissions/14795780

上の説明で示した通りのコードを使います。

func Solve(n int) int {
    divisorCounts := make([]int, n+1, n+1)
    // 1 から n までの整数 i について
    for i := 1; i <= n; i++ {
        // i の倍数を処理
        for j := i; j <= n; j += i {
            divisorCounts[j]++
        }
    }

    ans := 0
    for i, v := range divisorCounts {
        ans += i * v
    }
    return ans
}

AtCoder ABC134 D Preparing Boxes

私の解答はこちら: https://atcoder.jp/contests/abc134/submissions/14843714

この問題では、「約数カウント」を少し変形した形で使います。

「約数カウント」では $n$ の約数は $n$ 以下の範囲にしか存在しない ことを利用して、小さい $i$ から順番にその倍数を加算していくことで効率的に約数の個数を数え上げていきました。配列において インデックスの小さい要素から値を確定させていく イメージです。

それに対して以下では、これを逆転して $n$ の倍数は $n$ 以上の範囲にしか存在しない ことを利用します。配列において インデックスの大きな要素から値を確定させていく イメージです。

したがって、「約数カウント」とは対照的に、配列の後方 から要素を処理します。

func Solve(a []int) []int {
    inOrNotList := make([]int, len(a)+1, len(a)+1)
    var ans []int
    // n から 1 までの整数 i について
    for i := len(a); i >= 1; i-- {
        sum := a[i-1]
        // i の倍数を処理
        for j := i * 2; j <= len(a); j += i {
            sum += inOrNotList[j]
        }
        if sum%2 == 1 {
            inOrNotList[i] = 1
            ans = append(ans, i)
        }
    }
    return ans
}

AtCoder ABC170 D Not Divisible

私の解答はこちら: https://atcoder.jp/contests/abc170/submissions/14849721

この問題は、 配列において他の要素のどれでも割り切れない要素を数える というものです。

これは言い換えれば、 配列において他の要素の倍数になっていない要素を数える ということですから、これは配列の各要素について、そのすべての倍数に対して(その数の約数が配列に含まれること示す)印をつけていくことで確かめられます。

func Solve(A []int) int {
    sort.Sort(sort.IntSlice(A))
    max := A[len(A)-1]
    divisible := make([]int, max+1, max+1)
    // 配列内の要素それぞれについて
    for _, n := range A {
        if divisible[n] != 0 {
            divisible[n]++
            continue
        }
        // 要素の倍数を処理
        for multiple := n; multiple <= max; multiple += n {
            divisible[multiple]++
        }
    }
    notDivisibleCount := 0
    for _, n := range A {
        if divisible[n] == 1 {
            notDivisibleCount++
        }
    }
    return notDivisibleCount
}

最後に

私は記事を書くのも初心者ですから、分かりにくいところや説明不足なところがあったらどんどん厳しく教えていただけると幸いです。

また、自分で過去問に取り組む中で同系統の問題に遭遇したら、随時ここへ追加していきたいと思いますが、知っている方はコメントで教えていただけるととても嬉しいです。

最後まで読んで下さってありがとうございました。

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

競プロメモ | 正の約数の個数の数え上げ

最近、競技プログラミングを始めたのですが、才能はなく、要領も悪く、解法を覚えるのも苦手でどうしようもない弱者なのだと思い知る日々です。でも、それでも、強くなりたいので、よく出てきそうなアルゴリズムやデータ構造や細かい Tips など何でも、メモできることはして、少しずつ知識と経験を蓄えていきたいと思って書いています。

今日は正の約数の個数の数え上げです。

最後にこれを使う問題を挙げます。こちらは、共通点のある問題に出会ったら随時追加していきたいと思います。知っている方はコメントで教えていただけると嬉しいです。

また、アドバイスなどもあったらコメントで教えていただけるととても嬉しいです。

やること

$1$ からある正の整数 $n$ までの整数のそれぞれについて、正の約数の個数を数え上げる。

方法

  1. 長さ $n + 1$ の整数配列 $A$ を用意する。(つまり $n$ までのインデックスを持つ配列)
  2. $1$ から $n$ までの整数について、配列 $A$ 内でその整数の倍数をインデックスとする要素をインクリメントしていく。
  • 計算量: $O(n \log n)$

コード

divisorCounts := make([]int, n+1, n+1)
for i := 1; i <= n; i++ {
    for j := i; j <= n; j += i {
        divisorCounts[j]++
    }
}

これを使う問題

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

Goのインターフェースについて

参考文献

丸々引用している箇所が多いので最初に紹介いたします。
Goのinterfaceがわからない人へ

投稿までの背景

インターフェースがよう分からんかった~(Tour of Goで関数をMとかIとか1文字で定義されてもややこしい)~
Tour of Goに出てくるStringerの使い方が謎すぎた
間違っているところが確実にあるので、コメント等いただけたら幸いです。

インターフェスの使い方

  • インターフェースの定義
  • インターフェースを使った関数
  • インターフェースの実装
  • インターフェースの使い方

1.インターフェースの定義

A:参考にしたコード(Tour of Goより食べる動作を例にしているので非常にわかやすいです)
B:Tour of Go

type Eater interface{
    PutIn() 
    Chew()
    Swallow() 
}

type Human struct{
    Height int 
}

B

type Stringer interface {
    String() string
}

type Person struct {
    Name string
    Age  int
}

違いといえば

type hoge interface{
    fuga() 
}

のところでfuga()の後に型を定義しているか

2.インターフェースを使った関数

A

func EatAll(e Eater){
    e.PutIn()
    e.Chew()
    e.Swallow()
}

B

なし(fmtパッケージだから必要ない?すみませんここよくわかってないです)

3.インターフェースの実装

A

func (h Human) PutIn(){
    fmt.Println("道具を使って丁寧に口に運ぶ")
}
func (h Human) Chew(){
    fmt.Println("歯でしっかり噛む")
}
func (h Human) Swallow(){
    fmt.Println("よく噛んだら飲み込む")
}

B

func (p Person) String() string {
    return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

BはSprintfを使用しているのでreturnで値を返しています

4.インターフェースの使い方

A

func main() {
    var man Human = Human{Height: 300} 
    var eat Eater 
    eat = man 
    EatAll(eat) 
}

B

func main() {
    a := Person{"Arthur Dent", 42}  
    fmt.Println(a)
}

まとめ

インターフェースについては理解することができましたが、今度はStringerを学習せねば、、、
最後まで読んでいただきありがとうございます!

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

Goで簡単なオーダーシステムを作る

こんにちは。そうはぶです。
A Tour of GoスターティングGo言語でGoの基本を勉強したのでアウトプットとしてProgateのRuby講座で作ったオーダーシステムに似たようなものをGoで実装してみました!
ポインタのやりとりも少しですが入っているので、A Tour of Go終わったけど、なにから作ろうと困っている人の参考になれば幸いです。

コード

main.go
package main

import "fmt"

//Menu型を定義
type Menu struct {
    Name  string
    Price int
}

//商品番号(orderNumber)と注文数(count)を定義
var orderNumber, count int

//料金を計算する
func Calc(totalPricePtr *int, menuPricePtr *int, count int) {
    *totalPricePtr += *menuPricePtr * count
}

func main() {
    fmt.Printf("商品番号を選択してください。\n\n")

    //スライスに全メニューを入れる
    menus := []Menu{
        {Name: "カレーライス", Price: 500},
        {Name: "ラーメン", Price: 700},
        {Name: "牛丼", Price: 300},
    }

    //menusをrangeで回して各々出力させる
    for i, m := range menus {
        fmt.Printf("%d:%s(%d円)\n", i, m.Name, m.Price)
    }

    //合計金額を初期値0円として定義
    totalPrice := 0

    //注文処理
    for {
        //商品番号の入力を受け付ける
        fmt.Scan(&orderNumber)
        //9が入力されたら合計金額を出力して終了
        if orderNumber == 9 {
            fmt.Printf("合計%d円です。", totalPrice)
            break
        }
        //選択された商品番号の商品の個数を入力し、追加注文の受付を続ける
        fmt.Printf("%sが選択されました。\n", menus[orderNumber].Name)
        fmt.Printf("注文個数を入力してください。\n")
        fmt.Scan(&count)
        Calc(&totalPrice, &menus[orderNumber].Price, count)
        fmt.Printf("合計%d円です。", totalPrice)
        fmt.Println("他にご注文はございますか?")
        fmt.Println("なければ9を、ある場合は商品番号を入力してください。")
    }

}

参考

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

go修行11日目 タイプアサーションとか

タイプアサーション

  • 型変換をすること
  • interface型で受け取ると型の判断ができないため

intger型

package main

import "fmt"

func do(i interface{}){
    // タイプアサーション interface型だとそのまま利用できないのでintergerへ変換する必要がある
    ii := i.(int)
    ii *= 2
    fmt.Println(ii)
}

func main(){
    var i interface{} = 10
    do(i)
}

出力

20

string型

package main

import "fmt"

func do(s interface{}){
    ss := s.(string)
    fmt.Println(ss + "!")
}

func main(){
    do("Mike")
}
Mike!

Switch type

package main

import "fmt"

func do(i interface{}){
    // いろいろな型に対応できるようにできるswitch type
    switch v := i.(type){
    // intgerの場合
    case int:
        fmt.Println(v * 2)
    // stringの場合
    case string:
        fmt.Println(v + "!")
  // それ以外
    default:
        fmt.Println("I don't know %T%n", v)
    }
}

func main(){
    // string
    do("Mike")
    // integer
    do(10)
    // boolean
    do(true)
}
Mike!
20
I don't know %T%n true

Stringer

package main

import "fmt"

type Person struct{
    Name string
    Age int
}

// String()で出力方法を変えられる
func (p Person) String() string{
    // 名前だけ返す(年齢は返さない)
    return fmt.Sprintf("My name is %v", p.Name)
}

func main() {
    mike := Person{"Mike", 22}
    fmt.Println(mike)
}
My name is Mike

カスタムエラー

package main

import (
    "fmt"
)

type UserNotFound struct{
    Username string
}

func (e *UserNotFound) Error() string{
    return fmt.Sprintf("User not found: %v", e.Username)
}

func myFunc() error{
    // 何かエラーが出たら
    ok := false
    // okならnil
    if ok {
        return nil
    }
    // ユーザーが見つからなかったら
    return &UserNotFound{Username: "Mike"}
}

func main() {
    if err := myFunc(); err != nil {
        fmt.Println(err)
    }
}
User not found: Mike

goroutine

  • 並列処理
package main

import (
    "fmt"
    "sync"
)

func goroutine(s string, wg *sync.WaitGroup){
    for i := 0; i < 5; i++{
        // time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
    wg.Done()
}

func normal(s string){
    for i := 0; i < 5; i++{
        // time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main(){
    var wg sync.WaitGroup
    wg.Add(1)
    // 並列処理
    go goroutine("world", &wg)
    normal("hello")
    // time.Sleep(2000 * time.Millisecond)
    wg.Wait()
}
hello
hello
hello
hello
hello
world
world
world
world
world
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む