- 投稿日:2020-01-01T20:51:58+09:00
google books api をjavaで実装したときのメモ
webで持っている本をバーコードリーダーで読み込んだものを登録するプログラムアプリを作ったときのメモです
概要
java
mavenで
gsonはJavaのオブジェクトとJSON形式のデータを相互で変換するためのライブラリ
gson バージョン 2.8.6使用
説明のあるサイト
https://qiita.com/naoi/items/6b184700b2a41fb46356OkHttpはデフォルトで効率的なHTTPクライアント
okhttp バージョン 3.7.0使用
説明サイト
https://square.github.io/okhttp/<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.7.0</version> </dependency>google books api
公式ページ
https://developers.google.com/books/今回はバーコードリーダでisbnで読み込んだものをリクエストとして送信するので下のコードになります
https://www.googleapis.com/books/v1/volumes?q=isbn:一番後ろにisbnコード //下のようになる https://www.googleapis.com/books/v1/volumes?q=isbn:9784844336778google books api でリクエストを送信し帰ってきたjson
正直いってよくわからないので
階層がわかる方法があればだれか載せてほしい// 20200101165936 // https://www.googleapis.com/books/v1/volumes?q=isbn:9784844336778 { "kind": "books#volumes", "totalItems": 1, "items": [ { "kind": "books#volume", "id": "FQKOBAAAQBAJ", "etag": "YZLyBEypyf4", "selfLink": "https://www.googleapis.com/books/v1/volumes/FQKOBAAAQBAJ", "volumeInfo": { "title": "スッキリわかるJava入門 実践編 第2版", "authors": [ "中山清喬" ], "publisher": "(株)インプレス", "publishedDate": "2014-09-22", "description": "ラムダ式や日付APIなどJava8の注目機能の解説を増補し、アジャイル関連やデザインパターンについての解説も徹底強化した改訂版登場!姉妹書の『Java入門』と同じく「なぜ?」にしっかりと答えながら解説が進んでいきますので、正規表現やコレクション、データベース連携、リファクタリング、並列処理など、Javaを仕事で使いこなすために必須の知識が、スッキリ、楽しく、グングン身に付きます。基本文法やオブジェクト指向の概念はわかった、さらにステップアップしたい、という方にお勧めです。 発行", "industryIdentifiers": [ { "type": "ISBN_13", "identifier": "9784844336778" }, { "type": "ISBN_10", "identifier": "4844336770" } ], "readingModes": { "text": false, "image": true }, "pageCount": 628, "printType": "BOOK", "categories": [ "Computers" ], "maturityRating": "NOT_MATURE", "allowAnonLogging": true, "contentVersion": "2.3.0.0.preview.1", "panelizationSummary": { "containsEpubBubbles": false, "containsImageBubbles": false }, "imageLinks": { "smallThumbnail": "http://books.google.com/books/content?id=FQKOBAAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api", "thumbnail": "http://books.google.com/books/content?id=FQKOBAAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api" }, "language": "ja", "previewLink": "http://books.google.co.jp/books?id=FQKOBAAAQBAJ&printsec=frontcover&dq=isbn:9784844336778&hl=&cd=1&source=gbs_api", "infoLink": "https://play.google.com/store/books/details?id=FQKOBAAAQBAJ&source=gbs_api", "canonicalVolumeLink": "https://play.google.com/store/books/details?id=FQKOBAAAQBAJ" }, "saleInfo": { "country": "JP", "saleability": "FOR_SALE", "isEbook": true, "listPrice": { "amount": 2596.0, "currencyCode": "JPY" }, "retailPrice": { "amount": 2336.0, "currencyCode": "JPY" }, "buyLink": "https://play.google.com/store/books/details?id=FQKOBAAAQBAJ&rdid=book-FQKOBAAAQBAJ&rdot=1&source=gbs_api", "offers": [ { "finskyOfferType": 1, "listPrice": { "amountInMicros": 2596000000, "currencyCode": "JPY" }, "retailPrice": { "amountInMicros": 2336000000, "currencyCode": "JPY" } } ] }, "accessInfo": { "country": "JP", "viewability": "PARTIAL", "embeddable": true, "publicDomain": false, "textToSpeechPermission": "ALLOWED", "epub": { "isAvailable": false }, "pdf": { "isAvailable": true, "acsTokenLink": "http://books.google.co.jp/books/download/%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8BJava%E5%85%A5%E9%96%80_%E5%AE%9F%E8%B7%B5-sample-pdf.acsm?id=FQKOBAAAQBAJ&format=pdf&output=acs4_fulfillment_token&dl_type=sample&source=gbs_api" }, "webReaderLink": "http://play.google.com/books/reader?id=FQKOBAAAQBAJ&hl=&printsec=frontcover&source=gbs_api", "accessViewStatus": "SAMPLE", "quoteSharingAllowed": false }, "searchInfo": { "textSnippet": "ラムダ式や日付APIなどJava8の注目機能の解説を増補し、アジャイル関連やデザインパターンについての解説も徹底強化した改訂版登場!姉妹書の『Java入門』と同じく「なぜ?」に ..." } } ] }JSONのモデルクラス(pojo)を生成
https://qiita.com/riversun/items/cdd990e96c2bbf9cb043
このページでjsonschema2pojoを使ってモデルクラス(pojo)を自動生成するを参考にしました生成したクラスファイル
Modelクラスの中身です
@SerializeName("名前")
@Expose
public 型名 名前itemsは "items": [ ]となっているので List< Item > items= null; になります
注意
モデルクラスを自動生成しますが不足していることもありますので生成されていないものは自分で作ってください
javaメインコード
package main; import java.io.IOException; import java.util.List; import com.example.qson.Item; import com.example.qson.ListPrice; import com.example.qson.Model; import com.example.qson.SaleInfo; import com.example.qson.VolumeInfo; import com.google.gson.Gson; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class main { public static final String url = "https://www.googleapis.com/books/v1/volumes?q=isbn:"; public static void main(String[] args) { //isbnコード Long isbn = 9784844336778L; //jsonを保持 String json = ""; OkHttpClient okHttpClient; okHttpClient = new OkHttpClient(); Request.Builder builder = new Request.Builder(); //urlでリクエストを送信するアドレスを挿入 builder.url(url + isbn); Request request = builder.build(); Response response = null; try { response = okHttpClient.newCall(request).execute(); //ここのjson上で上記したでweb api から帰ってきたjson情報を持っている json = response.body().string(); } catch (IOException e) { e.printStackTrace(); } Gson gson = new Gson(); Model models = gson.fromJson(json, Model.class); List<Item> items2 = models.items; VolumeInfo volume = items2.get(0).volumeInfo; SaleInfo saleInfo = items2.get(0).saleInfo; ListPrice listPrice = saleInfo.listPrice; //titleをmodelクラスから書いた場合 //itemsクラスはList<Item>型なので.get()がつく System.out.println(models.items.get(0).volumeInfo.title);//title //モデルクラスをvolumeinfoクラスまで変更して表示した場合 System.out.println("title: " + volume.title);//title System.out.println("author: " + volume.authors);//author System.out.println("出版日: " +volume.publishedDate);//publishedDate System.out.println("出版社: " + volume.publisher);//publisher System.out.println("定価:" + listPrice.amount); System.out.println("説明:" + volume.description);//description System.out.println("ページ数:"+ volume.pageCount);//pageCount System.out.println("スモールサムネイル:"+ volume.imageLinks.smallThumbnail);//smallThumbnail System.out.println("サムネイル:" + volume.imageLinks.thumbnail);//thumbnail } }という感じまで行けました
okhttpとjackson バージョンです
概要
java
mavenで
OkHttpはデフォルトで効率的なHTTPクライアント
okhttp バージョン 3.7.0使用
説明サイト
https://square.github.io/okhttp/jackson
jackson-core バージョン:2.10.0
jackson-annotations バージョン:2.10.0
jackson-databind バージョン:2.10.0を入れています
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.7.0</version> </dependency>javaメインです
import java.io.IOException; import java.sql.Date; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class main { public static final String url = "https://www.googleapis.com/books/v1/volumes?q=isbn:"; public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ Long isbn = 9784295007807L; //jsonを保持 String json =""; //本の題名 String title = ""; //作者 String author = ""; //出版日 Date publishedDate = null; String day = ""; //出版社 String publisher = ""; //定価 Integer listPrice = null; //説明文 String description = ""; //ページ数 Integer pageCount = null; //smallサムネイル String smallThumbnail =""; //サムネイル String thumbnail = ""; OkHttpClient okHttpClient; okHttpClient = new OkHttpClient(); Request.Builder builder = new Request.Builder(); //urlでリクエストを送信するアドレスを挿入 builder.url(url + isbn); Request request = builder.build(); Response response = null; try { response = okHttpClient.newCall(request).execute(); json = response.body().string(); } catch (IOException e) { e.printStackTrace(); } //jsonの内容を表示する System.out.println(json); //ここからjsonの中身を取り出して表示していく処理 ObjectMapper mapper = new ObjectMapper(); try { JsonNode node = mapper.readTree(json); //titleはString型なので末尾に .asText(); を付けます title = node.get("items").get(0).get("volumeInfo").get("title").asText(); author = node.get("items").get(0).get("volumeInfo").get("authors").get(0).asText(); publisher = node.get("items").get(0).get("volumeInfo").get("publisher").asText(); //出版日をDate型に変換するためにString dayで受け取り day = node.get("items").get(0).get("volumeInfo").get("publishedDate").asText(); //文字列をsql型のDateに変換 publishedDate = Date.valueOf(day); //listPriceは int型なので末尾に .asInt(); を付けます listPrice = node.get("items").get(0).get("saleInfo").get("listPrice").get("amount").asInt(); description = node.get("items").get(0).get("volumeInfo").get("description").asText(); pageCount = node.get("items").get(0).get("volumeInfo").get("pageCount").asInt(); smallThumbnail = node.get("items").get(0).get("volumeInfo").get("imageLinks").get("smallThumbnail").asText(); thumbnail = node.get("items").get(0).get("volumeInfo").get("imageLinks").get("thumbnail").asText(); } catch (IOException e) { e.printStackTrace(); } System.out.println("title: " +title); System.out.println("author: " +author); System.out.println("出版社: "+publisher); System.out.println("出版日: " + publishedDate); System.out.println("定価: " + listPrice + "円"); System.out.println("説明文: " + description); System.out.println("ページ数: " + pageCount); System.out.println("小さいサムネイル: " + smallThumbnail); System.out.println("サムネイル: " +thumbnail); } }という風にできました
- 投稿日:2020-01-01T20:51:58+09:00
google books api をjavaで実装したときのメモ okhttp と gson バージョンと okhttp と jacksonバージョンです
webで持っている本をバーコードリーダーで読み込んだものを登録するプログラムアプリを作ったときのメモです
okhttp と gson バージョン
概要
java
mavenで
gsonはJavaのオブジェクトとJSON形式のデータを相互で変換するためのライブラリ
gson バージョン 2.8.6使用
説明のあるサイト
https://qiita.com/naoi/items/6b184700b2a41fb46356OkHttpはデフォルトで効率的なHTTPクライアント
okhttp バージョン 3.7.0使用
説明サイト
https://square.github.io/okhttp/<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.7.0</version> </dependency>google books api
公式ページ
https://developers.google.com/books/今回はバーコードリーダでisbnで読み込んだものをリクエストとして送信するので下のコードになります
https://www.googleapis.com/books/v1/volumes?q=isbn:一番後ろにisbnコード //下のようになる https://www.googleapis.com/books/v1/volumes?q=isbn:9784844336778google books api でリクエストを送信し帰ってきたjson
正直いってよくわからないので
階層がわかる方法があればだれか載せてほしい// 20200101165936 // https://www.googleapis.com/books/v1/volumes?q=isbn:9784844336778 { "kind": "books#volumes", "totalItems": 1, "items": [ { "kind": "books#volume", "id": "FQKOBAAAQBAJ", "etag": "YZLyBEypyf4", "selfLink": "https://www.googleapis.com/books/v1/volumes/FQKOBAAAQBAJ", "volumeInfo": { "title": "スッキリわかるJava入門 実践編 第2版", "authors": [ "中山清喬" ], "publisher": "(株)インプレス", "publishedDate": "2014-09-22", "description": "ラムダ式や日付APIなどJava8の注目機能の解説を増補し、アジャイル関連やデザインパターンについての解説も徹底強化した改訂版登場!姉妹書の『Java入門』と同じく「なぜ?」にしっかりと答えながら解説が進んでいきますので、正規表現やコレクション、データベース連携、リファクタリング、並列処理など、Javaを仕事で使いこなすために必須の知識が、スッキリ、楽しく、グングン身に付きます。基本文法やオブジェクト指向の概念はわかった、さらにステップアップしたい、という方にお勧めです。 発行", "industryIdentifiers": [ { "type": "ISBN_13", "identifier": "9784844336778" }, { "type": "ISBN_10", "identifier": "4844336770" } ], "readingModes": { "text": false, "image": true }, "pageCount": 628, "printType": "BOOK", "categories": [ "Computers" ], "maturityRating": "NOT_MATURE", "allowAnonLogging": true, "contentVersion": "2.3.0.0.preview.1", "panelizationSummary": { "containsEpubBubbles": false, "containsImageBubbles": false }, "imageLinks": { "smallThumbnail": "http://books.google.com/books/content?id=FQKOBAAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api", "thumbnail": "http://books.google.com/books/content?id=FQKOBAAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api" }, "language": "ja", "previewLink": "http://books.google.co.jp/books?id=FQKOBAAAQBAJ&printsec=frontcover&dq=isbn:9784844336778&hl=&cd=1&source=gbs_api", "infoLink": "https://play.google.com/store/books/details?id=FQKOBAAAQBAJ&source=gbs_api", "canonicalVolumeLink": "https://play.google.com/store/books/details?id=FQKOBAAAQBAJ" }, "saleInfo": { "country": "JP", "saleability": "FOR_SALE", "isEbook": true, "listPrice": { "amount": 2596.0, "currencyCode": "JPY" }, "retailPrice": { "amount": 2336.0, "currencyCode": "JPY" }, "buyLink": "https://play.google.com/store/books/details?id=FQKOBAAAQBAJ&rdid=book-FQKOBAAAQBAJ&rdot=1&source=gbs_api", "offers": [ { "finskyOfferType": 1, "listPrice": { "amountInMicros": 2596000000, "currencyCode": "JPY" }, "retailPrice": { "amountInMicros": 2336000000, "currencyCode": "JPY" } } ] }, "accessInfo": { "country": "JP", "viewability": "PARTIAL", "embeddable": true, "publicDomain": false, "textToSpeechPermission": "ALLOWED", "epub": { "isAvailable": false }, "pdf": { "isAvailable": true, "acsTokenLink": "http://books.google.co.jp/books/download/%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8BJava%E5%85%A5%E9%96%80_%E5%AE%9F%E8%B7%B5-sample-pdf.acsm?id=FQKOBAAAQBAJ&format=pdf&output=acs4_fulfillment_token&dl_type=sample&source=gbs_api" }, "webReaderLink": "http://play.google.com/books/reader?id=FQKOBAAAQBAJ&hl=&printsec=frontcover&source=gbs_api", "accessViewStatus": "SAMPLE", "quoteSharingAllowed": false }, "searchInfo": { "textSnippet": "ラムダ式や日付APIなどJava8の注目機能の解説を増補し、アジャイル関連やデザインパターンについての解説も徹底強化した改訂版登場!姉妹書の『Java入門』と同じく「なぜ?」に ..." } } ] }JSONのモデルクラス(pojo)を生成
https://qiita.com/riversun/items/cdd990e96c2bbf9cb043
このページでjsonschema2pojoを使ってモデルクラス(pojo)を自動生成するを参考にしました生成したクラスファイル
Modelクラスの中身です
@SerializeName("名前")
@Expose
public 型名 名前itemsは "items": [ ]となっているので List< Item > items= null; になります
注意
モデルクラスを自動生成しますが不足していることもありますので生成されていないものは自分で作ってください
javaメインコード
package main; import java.io.IOException; import java.util.List; import com.example.qson.Item; import com.example.qson.ListPrice; import com.example.qson.Model; import com.example.qson.SaleInfo; import com.example.qson.VolumeInfo; import com.google.gson.Gson; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class main { public static final String url = "https://www.googleapis.com/books/v1/volumes?q=isbn:"; public static void main(String[] args) { //isbnコード Long isbn = 9784844336778L; //jsonを保持 String json = ""; OkHttpClient okHttpClient; okHttpClient = new OkHttpClient(); Request.Builder builder = new Request.Builder(); //urlでリクエストを送信するアドレスを挿入 builder.url(url + isbn); Request request = builder.build(); Response response = null; try { response = okHttpClient.newCall(request).execute(); //ここのjson上で上記したでweb api から帰ってきたjson情報を持っている json = response.body().string(); } catch (IOException e) { e.printStackTrace(); } Gson gson = new Gson(); Model models = gson.fromJson(json, Model.class); List<Item> items2 = models.items; VolumeInfo volumeInfo = items2.get(0).volumeInfo; ImageLinks imageLinks = volumeInfo.imageLinks; SaleInfo saleInfo = items2.get(0).saleInfo; ListPrice listPrice = saleInfo.listPrice; //titleをmodelクラスから書いた場合 //itemsクラスはList<Item>型なので.get()がつく System.out.println(models.items.get(0).volumeInfo.title);//title //モデルクラスをvolumeinfoクラスまで変更して表示した場合 System.out.println("title: " + volumeInfo.title);//title System.out.println("author: " + volumeInfo.authors);//author System.out.println("出版日: " + volumeInfo.publishedDate);//publishedDate System.out.println("出版社: " + volumeInfo.publisher);//publisher System.out.println("定価:" + listPrice.amount); System.out.println("説明:" + volumeInfo.description);//description System.out.println("ページ数:" + volumeInfo.pageCount);//pageCount System.out.println("スモールサムネイル:" + imageLinks.smallThumbnail);//smallThumbnail System.out.println("サムネイル:" + imageLinks.thumbnail);//thumbnail } }という感じまで行けました
okhttpとjackson バージョンです
概要
java
mavenで
OkHttpはデフォルトで効率的なHTTPクライアント
okhttp バージョン 3.7.0使用
説明サイト
https://square.github.io/okhttp/jackson
jackson-core バージョン:2.10.0
jackson-annotations バージョン:2.10.0
jackson-databind バージョン:2.10.0を入れています
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.7.0</version> </dependency>javaメインです
import java.io.IOException; import java.sql.Date; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class main { public static final String url = "https://www.googleapis.com/books/v1/volumes?q=isbn:"; public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ Long isbn = 9784295007807L; //jsonを保持 String json =""; //本の題名 String title = ""; //作者 String author = ""; //出版日 Date publishedDate = null; String day = ""; //出版社 String publisher = ""; //定価 Integer listPrice = null; //説明文 String description = ""; //ページ数 Integer pageCount = null; //smallサムネイル String smallThumbnail =""; //サムネイル String thumbnail = ""; OkHttpClient okHttpClient; okHttpClient = new OkHttpClient(); Request.Builder builder = new Request.Builder(); //urlでリクエストを送信するアドレスを挿入 builder.url(url + isbn); Request request = builder.build(); Response response = null; try { response = okHttpClient.newCall(request).execute(); json = response.body().string(); } catch (IOException e) { e.printStackTrace(); } //jsonの内容を表示する System.out.println(json); //ここからjsonの中身を取り出して表示していく処理 ObjectMapper mapper = new ObjectMapper(); try { JsonNode node = mapper.readTree(json); //get("value")のvalueはjsonの"title": "スッキリわかるJava入門 実践編 第2版" //titleの部分がvalueになります //titleはString型なので末尾に .asText(); を付けます title = node.get("items").get(0).get("volumeInfo").get("title").asText(); author = node.get("items").get(0).get("volumeInfo").get("authors").get(0).asText(); publisher = node.get("items").get(0).get("volumeInfo").get("publisher").asText(); //出版日をDate型に変換するためにString dayで受け取り day = node.get("items").get(0).get("volumeInfo").get("publishedDate").textValue(); //文字列をsql型のDateに変換 publishedDate = Date.valueOf(day); //listPriceは int型なので末尾に .asInt(); を付けます listPrice = node.get("items").get(0).get("saleInfo").get("listPrice").get("amount").asInt(); description = node.get("items").get(0).get("volumeInfo").get("description").asText(); pageCount = node.get("items").get(0).get("volumeInfo").get("pageCount").asInt(); smallThumbnail = node.get("items").get(0).get("volumeInfo").get("imageLinks").get("smallThumbnail").asText(); thumbnail = node.get("items").get(0).get("volumeInfo").get("imageLinks").get("thumbnail").asText(); } catch (IOException e) { e.printStackTrace(); } System.out.println("title: " +title); System.out.println("author: " +author); System.out.println("出版社: "+publisher); System.out.println("出版日: " + publishedDate); System.out.println("定価: " + listPrice + "円"); System.out.println("説明文: " + description); System.out.println("ページ数: " + pageCount); System.out.println("小さいサムネイル: " + smallThumbnail); System.out.println("サムネイル: " +thumbnail); } }という風にできました
- 投稿日:2020-01-01T15:17:46+09:00
springでパターンにマッチする複数のResourceを得る
たとえば、クラスパス直下のすべてのxmlファイルの
Resource
を得る場合、以下のようにする。PathMatchingResourcePatternResolver resolover = new PathMatchingResourcePatternResolver(); Resource[] resources = resolover.getResources("classpath:*.xml"); for (Resource resource : resources) { System.out.println(resource); }
- 投稿日:2020-01-01T13:16:30+09:00
やっぱり(入門書の)オブジェクト指向ってしっくりこないんです! :気分はextends
今こそオブジェクト指向の闇を語ろう
あけおめことよろ2020年。企業のネットが星を被い、電子や光が駆け巡っても動物クラスから継承された犬クラスが無くなるほど情報化されていない近未来、Staticおじさんの登場から早くも10年が経ったわけですが、皆さんはいかがお過ごしでしょうか。
私はJAVAの求人探したり、「関数型言語全然分からん」とのたうち回ったり、応用情報の勉強を始めてみたりと、それなりに充実した年末年始を送っていますが、JAVAの入門書をペラペラ捲るたびに思うのですよ…………………ここに書いてる内容って文法の解説しかしてないのに死ぬほど面倒くさくない?
という訳で忘備録を兼ねてオブジェクト指向プログラミング(以下OOP)について思うことを書いていきたいと思います。ちなみに"オブジェクト指向"の話をすると不思議な力で死ぬことになるので、今回の内容はOOPだけです。
【追記】
と思ったけど、よく考えたらOOPの定義的にオブジェクトはデータと処理の両方を持つべきなので、記事の内容がOOPを指してるのか不安になってきた件。1から全部書き直そうかと思ったけどもう次の記事のテーマ決めた後だわF◯ck。軌道修正は若干したけどちょっと未完成感は否めません。ゴメンネ、弱クッテ?
継承とはなんぞや
さてまずは何と言ってもOOP3大要素の一つ、継承です。用語や文法に関しての紹介は省略しますが、これ一つでパッケージの中に生態系を作れる程強力な機能。その活用に関してはポリモーフィズムの助力が必要ながらも、継承使わずにJAVA書けって言われたら正気を疑う。私はStatic穏健派だけど、Staticおじさんは一体どうやってコードを書くつもりだったんだろうか。
とは言え、これに関しては名著とされる『何故オブジェクト指向で作るのか』ですらコードをまとめる為の仕組みで~みたいなノリの解説なのでもうちょっと踏み込んで考えたい。というのも個人的に継承だけでクラスの関係性を表現するのはちょっと無理があると思う。
BridgeパターンとDIで継承されまくった神クラスを圧倒撃滅する
継承の使いすぎはOOPのコンセプト『システム開発のボトルネック(人間の能力)を補う』と矛盾します。可能な限りコードをまとめた方がオブジェクト指向チックだけど、人間油断するとファルシのルシがコクーンでパージみたいなコードを書きがちなので「それを予防する方が先では?」というのが自分の考えです。
一般的な対策としてはコメントやリファクタリングといった方法がありますが、どちらも工数かかるし、コメントに関しては『コードと違ってテスト出来ない』という観点からもあまり推奨できません。そこでデータ系と処理系でクラスの分類軸を分けて、それぞれをDIで繋ぐという手法を考えました。IQ高めに言うとDIによるBridgeパターンの応用ですね。詳しくは文章で語るよりコードを見てもらった方が早いでしょう。
図書館の貸し出しシステム//データ系のクラス public class Document{ private int documentNumber; private Date addDate; private int documentType; private int rentalDay; /*コンストラクタ*/ /*セッターとゲッター*/ } public class Book extends Document { /*クラスの内容*/ } public class DVD extends Document { /*クラスの内容*/ } public class PictureStory extends Document { /*クラスの内容*/ } //処理系のクラス public class Counter{ private int staffId; private Document[]; /*コンストラクタ*/ /*セッターとゲッター*/ public void lending(Document[] documents){ /*処理の内容*/ } public void return(Document[] documents){ /*処理の内容*/ } public void reserve(Document[] documents){ /*処理の内容*/ } } public class AdminCounter extends Counter{ /*クラスの内容*/ } //クソ雑穴あき実装だけどクラスの関係性を示すコードなので許して //Counterクラスのprivate Document[];はセッターインジェクションでインスタンスを入れたい //あと記事全体書き終わった後に「ひょっとしてこれDI使ったトランザクションスクリプトじゃね?」って気がしたけど時既に遅し上記の例では処理系のクラスにDocumentクラスをぶち込むことで、互いの独立性を担保しています。
こう書くことで例えば「コミックを追加するけど貸出期間を他の本と分けたい」ってなった時も簡単だし、逆に処理系クラスを書き換える場合でもデータ系のクラスに影響はありません。あとこの手法『データと処理で分類軸を分けて』では無く、データ系と処理系を分けるという点が何気に肝です。勘の良い方は気づいたかもしれませんが、上の例だと処理系のクラスにint型のstaffIdが紛れ込んでます。これをデータ系クラスに混ぜると「おい、その先は地獄だぞ」となるのでやめましょう。
高度な継承? interface? なにそれ美味しいの?
次は高度な継承について、と言ってもabstractはまだしもイン何とかさんは本当に使い所が分からない。さっき出した例で言えばDocumentはそのままnew出来ないようにabstractクラスにした方が良いよねとは思うけど、interfaceをどう使うべきか考えると21世紀が終わりかねない。
一応interfaceクラスには親のメゾッド群を強制的に実装させる効果があるけど、多くの場合abstractクラスで事足りるし、多重継承もいつ使うの?と言われればPrincessHeroクラス作る時ぐらいしか思いつかない。あと強いて言えばインターフェースが欲しい時に使うぐらいか?とりあえずinterfaceクラスを作る為にinterface使おうとする輩は所有PCのHDDが全部ぶっ壊れればいいと思う。
カプセル化とはなんぞや
カプセル化はコードの可用性を支える裏方で~てな解説をされがちだけど、自分はこの記事と同じように、継承の前に来る要素だと思ってます。というのも多分我々はOOPを使う限りカプセル化から逃れる事が出来ないからです。クラスを分割してsetterとgetter使うだけで最低限のカプセル化は機能するとは言え、メゾッドの中で依存関係が金魚のフンみたいに連なってるならそれはそれでクソです。
ただ実際のコードに関して何が正解かは状況によるとしか言いようがないと思います。処理を切り出す目的でStaticなクラスを作るとか思いっきりアンチパターンだけど、それと関数との境目はどこかと言われると難しい。まあ極端になると扱いづらいので結局は匙加減です。強いて言うなら基本はデータ結合スタンプ結合に留めつつ、例外的に制御結合するのがベターかなとは思ったり………………
あとカプセル化と合わせて語られがちなsetterとgetterは、どちらかと言うとエラー制御において有効な機能のような気がします。(まあ無いとprivateなフィールド作るときに死ぬほど面倒くさいですが……………)
ポリモーフィズムとはなんぞや
ポリモーフィズムとはポリモーフィズムの事で主にポリモーフィズムを指します。
冗談です。これはざっくり言うと、スーパークラスの型やメゾッドを使ってサブクラスのインスタンスを扱う実装全般の事ですね。
ポリモーフィズムを使うことで、例えばBookクラスのインスタンスをDocument型の配列にぶち込んだり、引数がParson型のメゾッドにStudentクラスのインスタンスをぶち込んだりと色々応用が効きます。EngineerクラスとProgrammerクラスとStaticManクラス、これらのインスタンスをより抽象的なEmployeeクラスのメゾッドで動かすのもポリモーフィズムですね。
全集中、Staticの呼吸 壱の型 関数の定義
これさえマスターできれば君もS柱だ!
という与太は置いておくにしてもstaticはデータを保持しない関数を作りたいときに便利だったりする。あんまり調子乗ると強強エンジニアに「ラムダ式使えや」って詰められるかもだけど、こっちの方が絶対可読性良いし誰でも読めるのでオススメ。
そりゃ精鋭チームが短期間でイテレーションガンガン回すなら記述量の少なさは正義だろうけど、アジャイル童貞の自分からしたら他人が書いたラムダ式のコードとかあんまり触りたくないので私はstaticで良いです。
まとめ:オブジェクト指向プログラミングとはなんぞや
まあこれは人によって答えが変わってくるかもだけど、私的にはカプセル化されたデータと処理を継承で体系的に表現し、ポリモーフィズムで扱う実装のことを指すと思う。
いくら継承使いこなした所でカプセル化が十分で無ければそれはゴミだし、ポリモーフィズムの不足はコードに混沌をもたらすので控えめに言ってクソです。継承がないコード?……………C言語で書けば良くない???
あと思うにいくらOOPが便利であろうと、コードの全てをOOPで記述する必要は無いかもしれません。PCの仕事は入力、処理、出力の3つに分かれますが、製作者がフォーカスすべきはどう考えても処理でしょう。つまり入出力自体が目的にならない限り、それをOOPの守備範囲に含めるのは悪手です。
入出力をOOPで書くのは、コードの規模が大きすぎてI/Oを別のパッケージで書くという段階まで待っても良い気がします。小規模な開発ではI/Oは関数的に実装したほうが便利です多分。怒られなければstaticもアリよりのアリ。
という訳でOOPについて一通り語らせて貰いましたが如何せんオレオレOOPな所もあるのでマサカリは歓迎です。それでは2020年も良い年であることを願ってまた次回、Thank you for watching ……Goodbye‼
- 投稿日:2020-01-01T12:50:47+09:00
TomcatにWarファイルをSCP転送
Tomcatフォルダの権限設定
前回、VPSにTomCatをインストールできたので、試しにEclipseでHelloWorldのようなWarファイルをエクスポートしてデプロイしようとしたらちょっとつまづいた。
/opt/tomcat/webapps/
直下にscpするだけでできるはずなのだが、permission deniedと言われた。
権限を確認するとroot以外読み込みのみになっていたので、
cd /opt/tomcat chmod -R 766 webappsを指定してやり直し、書き込むだけだからこれで行けるよね?
と思ったけどpermission denied.
なんで?そこで、ユーザーアカウントにtomcatグループ属性を追加してみる。
permission denied.・・んんん??あ、もしかしてプログラマーはコピーするだけだけど、自動的にunzip展開されるし、コピーする人にも実行権限がいるとか?
で、
cd /opt/tomcat chmod -R 777 webappsしてみたら無事コピーできて、ブラウザでアクセスしてみてもちゃんと動いた。
ただ、このままではこのフォルダ誰でも書き込みできてしまうので、アプリのデプロイユーザーをtomcatグループにして770にすればいいのかな。
補足
tomcatにはwebコンソールでデプロイする仕組みがあるので本来はそれを設定してそこからやる方がいいらしい。
やってみたけど、設定箇所が足りないらしく403で入れなかったのでとりあえずSCPでデプロイしました。
- 投稿日:2020-01-01T12:44:08+09:00
code smell リファクタリング[lazy class編]
第四回のリファクタリングは、lazy classです。
lazy classとは
怠け者のクラス。
クラスがほとんど振る舞いを持たずに怠けている状態のこと。(存在している意味がわからない)なぜ悪いのか
必要がない。
冗長になる。よくあるパターン
よくあるのは、以下のようなとりあえずserviceクラスを作って、そこに処理を投げている構造です。
package study.lazyclass class PaymentController { def payment(amount: Int) = { val service = new Service service.pay(amount) } } class PaymentService { def pay(amount: Int): Unit = { paymentRepository.save(amount) } }payメソッドだけが、PaymentServiceクラスに定義されています。
Serviceクラスがpayメソッドと言う振る舞いしか持っていません。ソリューション
ソリューション自体は非常に簡単です。
関数のインライン化
必要な時に作ればいい。
クラスを作るタイミングとしては、二つ以上の箇所から呼び出されたりなど、関数をまとめるクラスが必要になった時に作ればいいだけです。class PaymentController { def payment(amount: Int) = { paymentRepository.save(amount) } }PaymentControllerから直接、paymentRepositoryを呼び出すように変更しています。
これは、よく言われているYAGNIの法則にもそっています。
[https://ja.wikipedia.org/wiki/YAGNI:embed:cite]
考え方
ここからがこのcode smellの重要な観点です。
lazy classリダファクタリング自体は非常に簡単にできますが、リファクタリングを実際にするのかどうかが開発手法によって変わってきます。
レイヤードアーキテクチャでアプリケーション構成を考えていくなど、最初から適応するアーキテクチャを考えているならば、上記のようなリファクタリングをする必要はないです。
関数が一つしかない場合でも、きちんとレイヤーを分けて、定義する必要があります。
例えば、レイヤードアーキテクチャを採用しているなら、下図のように依存関係は上から下の一方通行になるように設計しないといけないので、
リファクタリングのコードのような、controller(presentation層)からrepository(infrastructure層)を呼ぶことはできない。なのである程度のcode smellを許容しながら、開発を進める必要があります。
しかし、アーキテクチャを最初から考えていくと、そのアーキテクチャのルールに乗らないといけないので、コードを書くのが窮屈になる、と言う理由で、基本的に開発の序盤からアーキテクチャを考えるべきではないという開発者もいる。
なので、どんな開発手法をとるかはチームで考えて開発ルールを決定すべき。
まとめ
lazy classのリファクタリングを行いました。
最後までありがとうございます。
参考
- 投稿日:2020-01-01T12:37:34+09:00
code smellリファクタリング[feature envy編]
第三回のリファクタリングは、feature envyです。
feature envyとは
あるモジュールの関数が、内部のモジュールよりも、外部のモジュールの関数やデータ構造とやりとりしているということ。
例えば、よくある例としては、他のモジュールのgetメソッドをチェーンして使っている場合など。
悪いコード例
例えば、以下のコードを見てください。
Houseクラス
から、Personクラス Adressクラス
を辿って、Adressクラス
のcalcurateDistanceメソッド
を呼び出しています。明らかに、Houseクラスは他のクラスを知りすぎており、参照しすぎています。
House().getPerson().getAdress().calcurateDistance()なぜ悪いのか
では、そもそもなぜこのような過度の外部モジュールへの参照が悪いのでしょうか?
そもそも良い設計をする時に、内部モジュールとよくやりとりし、外部モジュールとのやりとりは最小限にすることが良いとされています。
こうすることで、
処理をまとめることで凝集度が上がり、変更しても影響が少なくなる。
ソリューション
主に考えられるソリューションは以下の二つがあります。
関数の移動
フィールド値の移動
では実際に、コード例を見ていきます。
このコード例では、四つのモデルがあります。
House Price Address SalesPerson家の値段を計算したり、家までの距離を計算したりするプログラムだと仮定します。
リファクタリング前
class Main { def main(args: Array[String]): Unit = { val address = Address(1L, 1L, 100L) val price = Price(10000) val house = House(address, price) val salesPerson = SalesPerson(1, "kin", house) salesPerson.calculatePrice } case class House(address: Address, price: Price) case class Price(number: Int) case class Address( longitude: Long, latitude: Long, price: Long ) case class SalesPerson( id: Int, name: String, house: House ) { def calculatePrice: Int = { (house.price.number * 1.1).toInt } def calculateDistance: Long = { house.address.longitude } } }SalesPersonクラスにsmellがありそうですね。
よく見てみると、calculatePriceメソッドとcalculateDistanceメソッドの両方が、houseクラスのフィールド値を参照しています。
典型的なfeature envy smellですね。つまり、自分のフィールド値よりも外部モジュールのフィールド値をよく参照しています。
これを、リファクタリングしていきます。
この場合のリファクタリングには、フィールド値の移動・関数の移動のどちらが適しているでしょうか。
フィールド値の移動を行う、
case class House() { } case class SalesPerson(id: Int, name: String, house: House, address: Address, price: Price) { def calculatePrice: Int = { (price.number * 1.1).toInt } def calculateDistance: Long = { address.longitude } }二つの関数内のsmellは無くなったかもしれませんが、modelの凝集度が破綻していますね。
SlaesPersonクラスが、address priceなどの関係のないデータを持つようになってしまいます。
これは、素直に関数の移動をしてあげましょう。
リファクタリング後
def main(args: Array[String]): Unit = { val address = Address(1L, 1L, 100L) val price = Price(10000) val house = House(address, price) val salesPerson = SalesPerson(1, "kin", house) // salesPerson.calculatePrice house.calculatePrice house.calculateDistance } case class House(address: Address, price: Price) { def calculatePrice: Int = { (price.number * 1.1).toInt } def calculateDistance: Long = { address.longitude } } case class SalesPerson( id: Int, name: String, house: House )二つの関数をHouseクラスに移動させることで、feature envy smellは消え、さらにHouseクラスの振る舞いも増えて凝集度が高まりました。
まとめ
僕の感覚として、
隣のさらにまた隣のモジュールのデータにまでアクセスしようとしているなら、それはfeature envyだと疑った方がいいかと思います。上記のリファクタリングのように、関数を定義する場所が違うのか、それとも、オブジェクトのフィールド値を移動させた方がいいかと。
最後までありがとうございました。
次回は、lazy classに関しての記事を書きます。
参考
- 投稿日:2020-01-01T11:16:35+09:00
WeakReferenceの動作確認
JavaのWeakReferenceの動作を確認する。
import java.lang.ref.WeakReference; import java.util.WeakHashMap; public class Main { static class Entry{ private final String name; public Entry(String name) { this.name = name; } @Override public String toString() { return name; } } public static void main(String[] args) { var val = new Entry("Hello"); var ref = new WeakReference<>(val); System.out.println("ref: "+ val + ", weak-ref: "+ ref.get()); System.out.println("gc"); System.gc(); System.out.println("ref: "+ val + ", weak-ref: "+ ref.get()); System.out.println("e = null"); val = null; System.out.println("gc"); System.gc(); System.out.println("ref: "+ val + ", weak-ref: "+ ref.get()); } }実行結果は以下。
ref: Hello, weak-ref: Hello gc ref: Hello, weak-ref: Hello e = null gc ref: null, weak-ref: nullちなみに,
var e = "Hello";
でも同じ結果を期待したのだけれど,GCされずに残った。ref: Hello, weak-ref: Hello gc ref: Hello, weak-ref: Hello e = null gc ref: null, weak-ref: Hello多分,コード自体が"Hello"を保持しているため,GC対象にならなかった可能性が高い。そこで以下のように明示的にnewすると同じ結果になった。
var e = new String("Hello");StringでなくIntegerとかで試すときもvalueOfは勝手にキャッシュされるので注意は必要。
- 投稿日:2020-01-01T10:21:58+09:00
Javaのオブジェクトのサイズ感
Javaのオブジェクトのサイズ。間違いもありそう。
- reference
- 4byte (32bit JVM or heapが32GB未満の64bit JVM)
- 8byte (heapが32GB以上の64bit JVM)
- Object
- 12byte (heapによらず)
- Array
- 12byte (32bit JVM or heapが32GB未満の64bit JVM)
- 24byte(heapが32GB以上の64bit JVM)
ただし,オブジェクトのサイズは8byteの倍数になるようにパディングされるので,オブジェクトの最小サイズは16byte
参考:Javaパフォーマンス, p.202