質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

1回答

1045閲覧

ページ分割されたリスト取得APIの全結果取得をGo言語でクラス化したい

ponyo877

総合スコア17

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2022/12/12 15:09

編集2022/12/14 15:00

前提

nextTokenとrequest/responceにpageSizeをrequestのオブジェクトに持つ別の「部分取得」メソッドがありさえすれば「全件取得」ができるようにする方法を探しています。
また、この投稿はマルチポストになります。他の投稿は以下になります。

実現したいこと

以下のようなrequest/responceオブジェクトを持つページ分割されたリスト取得メソッド(=ListSomething)があったとき、
その全結果を取得するためのクラスを作成したいです。

go

1// request リクエストオブジェクト 2type request struct { 3 nextToken *string 4 pageSize int 5} 6 7// responce レスポンスオブジェクト 8type responce struct { 9 nextToken *string 10 rows []interface{} 11} 12 13// MAX ページ分割されたリストの最大値 14var MAX = 20 15 16// ListSomething ページ分割されたリスト取得のサンプルメソッド 17// 指定したpageSize分の数列(0~)を最大20まで出力する 18func ListSomething(request request) responce { 19 var rows []interface{} 20 var nextToken *string 21 22 offset, _ := strconv.Atoi(*request.nextToken) 23 limit := offset + request.pageSize 24 if limit > MAX { 25 limit = MAX 26 nextToken = nil 27 } else { 28 limitStr := strconv.Itoa(limit) 29 nextToken = &limitStr 30 } 31 for i := offset; i < limit; i++ { 32 rows = append(rows, i) 33 } 34 return responce{ 35 rows: rows, 36 nextToken: nextToken, 37 } 38}

発生している問題・エラーメッセージ

クラスのイメージとしては以下のイメージを持っていますが、メソッドやrequest/responceオブジェクトの上手い抽象化の方法が思い浮かびません。

go

1// Looper ページ分割されたリスト取得に関するクラス 2type Looper struct { 3 // 抽象化されたメソッドやrequest/responceオブジェクト 4} 5 6// NewLooper Looperコンストラクタ 7func NewLooper(/*抽象化されたメソッドやrequest/responceオブジェクト*/) *Looper{ 8 return &Looper{ 9 // 抽象化されたメソッドやrequest/responceオブジェクト 10 } 11} 12 13// Loop responce.nextToken == nilまでループして全結果を返却する 14func (l *Looper) Loop() []interface{} { 15 firstToken := "0" 16 nextToken := &firstToken 17 var ans []interface{} 18 19 for { 20 responce := 抽象化されたメソッド( 21 /*抽象化されたリクエスト*/ 22 ) 23 ans = append(ans, responce.rows...) 24 if responce.nextToken == nil { 25 break 26 } 27 nextToken = responce.nextToken 28 } 29 return ans 30}

以下のようなListSomethingA, ListSomethingBが部分取得取得のメソッドでが抽象化の対象になり、共通点はrequestA/requestBにnextTokenとpageSize、responseA/responseBがnextTokenとrowsのメンバ変数を持つことになります。

  • ListSomethingA(request requestA) responseA
  • ListSomethingB(request requestB) responseB

json.Unmarshalのような引数にポインタを入れてそこに結果を格納する形でも良いのですがそれでも良い方法がわかりません。
先の例の場合は以下のように実行すると全結果取得ができるイメージです。

go

1fmt.Println(NewLooper(/*ListSomethingAやresponseAやrequestA*/).Loop()) 2// 出力結果: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 3fmt.Println(NewLooper(/*ListSomethingBやresponceBやrequestB*/).Loop()) 4// 出力結果: [0 1 2 3 4 5 6 7 8 9 10]

補足情報(FW/ツールのバージョンなど)

Go 1.19

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2022/12/13 02:52 編集

「上手い抽象化」を考えるには具体例を複数用意すべきです。 Aというパターン、Bというパターンがあり、それらを共通の操作でどうしたいのかを具体的に書き出す必要があると思います。 または抽象化対象の可変部分がどこなのかを明記してほしい。 例えば『「任意の型」データで「部分取得」実装がありさえすれば 「全件取得」ができるようにする方法を探している。』とか?
ponyo877

2022/12/14 12:36 編集

> 「上手い抽象化」を考えるには具体例を複数用意すべきです。 Aというパターン、Bというパターンがあり、それらを共通の操作でどうしたいのかを具体的に書き出す必要があると思います。 こちら`発生している問題・エラーメッセージ`にListSomething1とListSomething2の例を加えやりたいことの説明としました! > または抽象化対象の可変部分がどこなのかを明記してほしい。 例えば『「任意の型」データで「部分取得」実装がありさえすれば 「全件取得」ができるようにする方法を探している。』とか? nobonoboさんのほぼおっしゃる通りだったので`前提`に追加しました。 ただし任意の型データの部分を「nextTokenとrequest/responceにpageSizeをrequestのオブジェクトに持つ」に変更しています!
guest

回答1

0

以下のようなメソッドを持つ型を共通に操作をするには。

  • SomethingA.List(request) response
  • SomethingB.List(request) response

Goのインターフェースを使うのが一番向いてそうに思います。

go

1type Lister interface { 2 List(request) response 3} 4 5func ListAll(l Lister) []interface{} { 6 firstToken := "0" 7 nextToken := &firstToken 8 var ans []interface{} 9 10 for { 11 req := reqest{nextToken:nextToken, pageSize: 10} 12 responce := l.List(req) 13 ans = append(ans, responce.rows...) 14 if responce.nextToken == nil { 15 break 16 } 17 nextToken = responce.nextToken 18 } 19 return ans 20}

requestを渡したあとLoopを呼ぶ二段構えにする意義が現時点でよくわからないので、いったんListAll関数として記述してみました。

投稿2022/12/14 14:27

nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ponyo877

2022/12/14 14:57

回答ありがとうございます! ただ、抽象化したいメソッドは特定の共通クラスのメンバーメソッドでなく純粋なメソッドになりますl なので以下のようなListSomethingA, ListSomethingBが部分取得取得のメソッドでが抽象化の対象になり、共通点はrequestA/requestBにnextTokenとpageSize、responseA/responseBがnextTokenとrowsのメンバ変数を持つことになります。 - ListSomethingA(request requestA) responseA - ListSomethingB(request requestB) responseB わかりにくくすみません...
nobonobo

2022/12/14 23:24

requestが異なる型なのは必須ですか?
nobonobo

2022/12/14 23:26

おそらく、対象もreqもresも異なる型ということであれば部分取得メソッドが追加パラメータ「all bool」がtrueの場合に全件返す様にするのが無難に見えます。
ponyo877

2022/12/16 14:57

> おそらく、対象もreqもresも異なる型ということであれば部分取得メソッドが追加パラメータ「all bool」がtrueの場合に全件返す様にするのが無難に見えます。 確かにall_boolのようなパラメータがあるのが良い気がします。 ただ、今回は部分取得のみしかできない外部のサービスがあるという前提で考えていただけると幸いです!
nobonobo

2022/12/16 21:31

共通項のないものは誰かが共通に扱える実装を愚直に書くしかありません。
ponyo877

2022/12/18 03:33

nobonoboさんのおっしゃる共通項というのは抽象化対象の何か共通点ということでしょうか? であれば共通点は以下になります! > 以下のようなListSomethingA, ListSomethingBが部分取得取得のメソッドでが抽象化の対象になり、共通点はrequestA/requestBにnextTokenとpageSize、responseA/responseBがnextTokenとrowsのメンバ変数を持つことになります。
nobonobo

2022/12/19 00:11

「ListSomethingA(requestA) responseA」「ListSomethingB(requestB) responseB」という全く型一致点のないシグネチャを持つメソッドをそれぞれを同じ「ListAll(何らかの引数) []interface{}」にて全件取得したいという解釈でよろしいでしょうか?これは発想を変えないとどうやってもうまい抽象化にはならない気がします。愚直にそれぞれの全件取得を書くほうがコード量も少なくなりそうです。
nobonobo

2022/12/19 00:13

https://go.dev/play/p/gAgMNBfLNRl ここに参考コードを示します。 もっとプリミティブに要素を取り出せる抽象化を行っておき、それを部分取得に使ったり、全件取得に使ったりしつつ、req/res実装は要件別に個別に書くのがおすすめです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問