20191112のJavaに関する記事は6件です。

LINE Messaging API SDKのFlexMessageをキレイに作る方法

はじめに

FlexMessageを使ってみたのですが、送信時にエラーが起きる体裁がイメージ通りではない、とかでかなり時間がかかりました。
Flex Message Simulator β を利用すると体裁を確認しながら、かつエラーを起きないいい感じに実装できることに気づいたのでやり方を紹介しようと思います。

1.デザインを決める

「Showcase」で自分のイメージにあったサンプルを選びます。

1.サンプル集.png

今回はデフォルトの「Restaurant」を例に紹介します。

2.FlexMessageをJavaで実装する

2-1. Bobbleを作成する

サンプルを選ぶとこんな感じなっています。

2bubble.png

これは、Bubble1つで構成されたメッセージであることを表しています。
Javaで書くとこんな感じです。

Bubble bubble = Bubble.builder()
        .hero(hero)
        // .header(header)
        .body(body)
        .footer(footer)
        .build();
FlexMessage f = new FlexMessage("altText", bubble);
pushMessage(new PushMessage(userId, Collections.singletonList(f)));

Bubbleは、heroheaderbodyfooterという要素で構成されています。
右側に表示されている要素「direction」、「size」、「Action.type」にも記載がないので特に上記以外に設定する必要はないです。
ただし、headerは空の要素なのでコメントアウトしています。(headerを開いても何も表示されません。)

2-2.heroを作成する

hero -> image を開くとこんな感じになります。

3hero.png

また右側をスクロールするとAction(画像押した時の挙動)の設定もあります。

3hero-action.png

これをJavaで実装するとこんな感じになります。

Action = new URIAction(
        null, // label
        "http://linecorp.com/", // uri
        null //altUri.desktop
)
Image hero = Image.builder()
        .url("https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png")
        .size(Image.ImageSize.FULL_WIDTH)
        .aspectRatio(20, 13)
        .aspectMode(Image.ImageAspectMode.Cover)
        .action(heroAction)
        .build();

これも画像見比べたらイメージしやすいと思います。
右側の設定に沿って記載していくだけです。

2-3.bodyを作成する

body を開いてみると結構あるので大変ですが、コツコツ見ていきます。
まずは、body -> vertical box を開いてみます。

そうすると、bodylayoutがFlexLayout.VERTICALで、Brown Cafe(Text)baseline box(Box)vertical box(Box)の要素からなっています。
それをJavaで書くとこんな感じになります。

Box body = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                body_brownCafe,
                body_baseline,
                body_vertical
        ))
        .build();

次に、Brown Cafeを開いてみます。
↑のソースのbody_brownCafeにあたる部分です。

body2.png

これはTextなので割とシンプルで、右側の定義に沿って記載すればOKです。

Text body_brownCafe = Text.builder()
        .text("Brown Cafe")
        .size(FlexFontSize.XL)
        .weight(Text.TextWeight.BOLD)
        .build();

次に、baseline boxを見てみます。
icon4つと4.0(Text)から構成されているので、それぞれの定義を確認しつつ実装します。

Icon body_baseline_icon = Icon.builder()
        .url("https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png")
        .size(FlexFontSize.SM)
        .build();
Icon body_baseline_noIcon = Icon.builder()
        .url("https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png")
        .size(FlexFontSize.SM)
        .build();
Text body_baseline_4 = Text.builder()
        .flex(0)
        .margin(FlexMarginSize.MD)
        .text("4.0")
        .size(FlexFontSize.SM)
        .color("#999999")
        .build();
Box body_baseline = Box.builder()
        .layout(FlexLayout.BASELINE)
        .margin(FlexMarginSize.MD)
        .contents(Arrays.asList(
                body_baseline_icon,
                body_baseline_icon,
                body_baseline_icon,
                body_baseline_icon,
                body_baseline_noIcon,
                body_baseline_4)
        )
        .build();

最後のvertical boxを見てみます。
この配下にまたbaseline boxがあるので、配下を1つずつ確認すると以下のようになります。

Box body_vertical_baseline1 = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                Text.builder()
                        .flex(1)
                        .text("Place")
                        .color("#aaaaaa")
                        .build(),
                Text.builder()
                        .flex(1)
                        .text("Miraina Tower, 4-1-6 Shinjuku, Tokyo")
                        .size(FlexFontSize.SM)
                        .color("#666666")
                        .wrap(true)
                        .build())
        )
        .build();
Box body_vertical_baseline2 = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                Text.builder()
                        .flex(1)
                        .text("Time")
                        .color("#aaaaaa")
                        .build(),
                Text.builder()
                        .flex(5)
                        .text("10:00 - 23:00")
                        .size(FlexFontSize.SM)
                        .color("#666666")
                        .wrap(true)
                        .build())
        )
        .build();
Box body_vertical = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .spacing(FlexMarginSize.SM)
        .margin(FlexMarginSize.LG)
        .contents(Arrays.asList(
                body_vertical_baseline1,
                body_vertical_baseline2
        ))
        .build();

これでbodyは実装できました。

2-4.footerを作成する

同じ要領でfooterを作ります。
bodyと同じ要領で実装するとこんな感じになります。

URIAction callAction = new URIAction(
        "CALL",
        "https://linecorp.com",
        null

);
URIAction websiteAction = new URIAction(
        "WEBSITE",
        "https://linecorp.com",
        null

);
Spacer spacer = Spacer.builder()
        .size(FlexMarginSize.SM)
        .build();
Button call = Button.builder()
        .height(Button.ButtonHeight.SMALL)
        .style(Button.ButtonStyle.LINK)
        .action(callAction)
        .build();
Button website = Button.builder()
        .height(Button.ButtonHeight.SMALL)
        .style(Button.ButtonStyle.LINK)
        .action(websiteAction)
        .build();
Box footer = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                call,
                website,
                spacer)
        )
        .build();

結果

最終的なソースは(↑のソースをまとめると)こちらです。

URIAction heroAction = new URIAction(
        null,
        "http://linecorp.com/",
        null
);
Image hero = Image.builder()
        .url("https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png")
        .size(Image.ImageSize.FULL_WIDTH)
        .aspectRatio(20, 13)
        .aspectMode(Image.ImageAspectMode.Cover)
        .action(heroAction)
        .build();

Text body_brownCafe = Text.builder()
        .text("Brown Cafe")
        .size(FlexFontSize.XL)
        .weight(Text.TextWeight.BOLD)
        .build();
Icon body_baseline_icon = Icon.builder()
        .url("https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png")
        .size(FlexFontSize.SM)
        .build();
Icon body_baseline_noIcon = Icon.builder()
        .url("https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png")
        .size(FlexFontSize.SM)
        .build();
Text body_baseline_4 = Text.builder()
        .flex(0)
        .margin(FlexMarginSize.MD)
        .text("4.0")
        .size(FlexFontSize.SM)
        .color("#999999")
        .build();
Box body_baseline = Box.builder()
        .layout(FlexLayout.BASELINE)
        .margin(FlexMarginSize.MD)
        .contents(Arrays.asList(
                body_baseline_icon,
                body_baseline_icon,
                body_baseline_icon,
                body_baseline_icon,
                body_baseline_noIcon,
                body_baseline_4)
        )
        .build();
Box body_vertical_baseline1 = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                Text.builder()
                        .flex(1)
                        .text("Place")
                        .color("#aaaaaa")
                        .build(),
                Text.builder()
                        .flex(1)
                        .text("Miraina Tower, 4-1-6 Shinjuku, Tokyo")
                        .size(FlexFontSize.SM)
                        .color("#666666")
                        .wrap(true)
                        .build())
        )
        .build();
Box body_vertical_baseline2 = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                Text.builder()
                        .flex(1)
                        .text("Time")
                        .color("#aaaaaa")
                        .build(),
                Text.builder()
                        .flex(5)
                        .text("10:00 - 23:00")
                        .size(FlexFontSize.SM)
                        .color("#666666")
                        .wrap(true)
                        .build())
        )
        .build();
Box body_vertical = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .spacing(FlexMarginSize.SM)
        .margin(FlexMarginSize.LG)
        .contents(Arrays.asList(
                body_vertical_baseline1,
                body_vertical_baseline2
        ))
        .build();
Box body = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                body_brownCafe,
                body_baseline,
                body_vertical
        ))
        .build();
URIAction callAction = new URIAction(
        "CALL",
        "https://linecorp.com",
        null

);
URIAction websiteAction = new URIAction(
        "WEBSITE",
        "https://linecorp.com",
        null

);
Spacer spacer = Spacer.builder()
        .size(FlexMarginSize.SM)
        .build();
Button call = Button.builder()
        .height(Button.ButtonHeight.SMALL)
        .style(Button.ButtonStyle.LINK)
        .action(callAction)
        .build();
Button website = Button.builder()
        .height(Button.ButtonHeight.SMALL)
        .style(Button.ButtonStyle.LINK)
        .action(websiteAction)
        .build();
Box footer = Box.builder()
        .layout(FlexLayout.VERTICAL)
        .contents(Arrays.asList(
                call,
                website,
                spacer)
        )
        .build();
Bubble bubble = Bubble.builder()
        .hero(hero)
//      .header(header)
        .body(body)
        .footer(footer)
        .build();
FlexMessage f = new FlexMessage("altText", bubble);
lineMessagingClient.pushMessage(new PushMessage(userId, Collections.singletonList(f)));

これで送られるメッセージはこちらです。

result.png

おわりに

これでいい感じにデザインして、実装できるようになったのではないでしょうか。
さらに、公式のFlexMessageの要素を見るとより理解が進むと思います。

ただ、作るのが結構大変なので、次があればいい感じに実装する方法を紹介したいと思います。

(もっといい感じに実装する方法があれば教えてください!)

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

1文字が2文字になる?toCharArrayメソッドの罠

はじめに

以下は、@tak777 さんの【Java】文字列を一文字ずつ切り出しする方法という記事に書かれた、@saka1029 さんの以下のコメントです。

Javaのchar型は16ビットですが、Unicodeの文字集合の符号空間は0x0から0x10FFFFで16ビットを超えます。
Javaは内部コードとしてUTF-16という符号化方式を採用していて、16ビットを超える文字はchar2個で1文字を表現します。
例えば「?(ほっけ)」のUnicode番号は0x29E3Dであり、Javaでは0xD867と0xDE3Dの連続した2個のcharから構成されます。
記事にあるコードでは1文字の半分を切り出してしまう可能性があることに注意する必要があります。

このコメントを読んで、サロゲートペアを用いて表された文字だとtoCharArrayメソッドがどのような結果になるのかが気になったので、実際に検証してみました。

テスト

テストコード

  • 【Java】文字列を一文字ずつ切り出しする方法に私がコメントとして書いたコードをベースとして、以下のテストコードを作成しました。
  • @saka1029 さんから「?(ほっけ)」という字を例として挙げて頂いたので、「?」を使った文言をテストに使ってみました。
    • この他に、「?野家」の「?」もサロゲートペアで表された文字だそうです。
CharArray.java
public class CharArray {
    public static void main(String[] args) {
        String text = "?の煮付け";
        char[] charAry = text.toCharArray();

        for(char ch : charAry) {
            System.out.println(ch);
        }
    }
}

実行結果

標準出力
?
?
の
煮
付
け

@saka1029 さんが指摘した通り、「?」という文字が2文字に分かれてしまい、正しく取得できませんでした...

解決策

色々と検索した結果、サロゲートペアを含む文字列を1文字ずつ切り出すには、StringクラスのcodePointAtメソッドやoffsetByCodePointsメソッドを使えば実現できることが分かりました。

この他に、BreakIteratorクラスを使う方法もあるそうですが、今回は割愛させて頂きます。

作成したコード

  • text.length()はサロゲートペアを考慮せずに文字列の長さを求めるため、lenには6が代入されます。
    • ?という文字が2つのcharとして判定されるため、人間が見ると5文字でも、内部的には6文字としてカウントされます。
  • text.offsetByCodePoints(i,1)は、「text中の指定された位置(i)から、1コードポイント分だけオフセットされた位置のインデックスをを返しています。
    • text="?の煮付け"の時、1文字目がサロゲートペアとなるためiの値が0,2,3,4,5と変化します。
    • 一方でtext="鰈の煮付け"となる場合は、サロゲートペアが存在しないのでiの値が0,1,2,3,4と変化します。
  • text.codePointAt(i)では、指定された位置のコードポイントを取得しています。
CharArray2.java
public class CharArray2 {
    public static void main(String[] args) {
        String text = "?の煮付け";

        int len = text.length();

        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
            System.out.println(Character.toChars(text.codePointAt(i)));
        }
    }
}

実行結果

  • サロゲートペアで表される文字についても、きちんと出力されました。
標準出力
?
の
煮
付
け

補足

  • 以下のコードは @saka1029 さんにコメント欄で教えて頂いた方法で、上記のコードと同様にサロゲートペアを含む文字列を1文字ずつ切り出すことができます。
  • Java9以降ではString#codePoints()というメソッドが使えるため、以下のようにシンプルなコードにすることができます。
    • String#codePoints()を使うだけでなく、StreamAPIを利用してさらにスッキリしたコードになっています。
CharArray3.java
public class CharArray3 {
    public static void main(String[] args) throws Exception {
        String text = "?の煮付け";
        text.codePoints()
            .forEach(c -> System.out.println(Character.toChars(c)));
    }
}

まとめ

  • これまでの業務ではサロゲートペアを考慮したコーディングをする機会が無かったので、とても良い勉強になりました。
  • IBMのサイトには「サロゲートペアを考慮したコードを定型処理の標準として採用すべき」という趣旨の記事もあるので、今後はこういった部分にもう少し気を付けてみようと思いました。
  • サッと書いた記事なので、記事に誤りなどがあればご指摘頂けると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

後輩養成のすすめ。~あの辛い徹夜ラッシュをもうしないために~

1.はじめに

1.1 経緯

年代によって大きく技術力は変わります。僕は学生ですので会社では年代は関係ないのかもしれませんが、プログラミングを必要としない世界(例えば非IT企業、研究室、中高学校)では大きく変わってしまうのです。もちろん一定数そうゆう人は入ってきます。学校ではパソコン関連の同好会みたいな部署が学園祭のアプリや動画を作っていました。僕の学校で問題だったのは人が足りないということ、そして学年によって差が多いところでした。

1.2 なにが問題?

まずは人が足りないということです。学校でやってるアプリとかっていうのは企業の真似事ですからアプリはデザインの人が仕様書作ってみんなで共同作業を...って感じですし、動画は絵コンテから映像化...ここからここまでは○○で...って感じでやるわけですよ。でも人が足りなければできることも少ない、でもやりたい、だから一人でやる。っていう感じに一人に背負わされるわけです。学生なのに労働基準法に抵触(学生だから適法だけども)してるわけですね(笑)

そして二つ目は代によって差があるということです。学校なので留年しない限り進級して卒業します。生憎、これでも進学校ですので高校3年生は学園祭には参加しないのです。そして学園祭は4月の終わりにやるので入ってきてすぐの中学1年生はどちらかというとお客さん側です。
つまりは中2、中3、高1、高2で作り上げます。もっと言うと学園祭は4月の終わりということで準備は前年度になるわけです。今は2019年11月ですから、次は2020年4月です。準備は今年度なんですね。準備は今の中1、中2、中3、高1でやるってことですね。中1はまだ一年を経験していないので見習いです。この状態でもやばいわけです。

lRtTSQg.gif

えー。取り乱しました。つまりは実質、全校の技術者の半分で行うわけです。そしてその差が開くとどうなるかといいますと...。

A期技術者a「よっしゃ、めっちゃ技術力あるで!すごいの作ったろ!」
その年の学校「すげぇー!」
ーーーA期引退ーーー
その年の学校「今年もすごいの作るんやろ?」
B期技術者b「よし、がんばるぞ...まずは前の年のコードは...わからん」
B期技術者c「わからん」
B期技術者d「わからん」
B期技術者bcd「やーめた」
A期技術者a「俺たちの努力が...一代だけ...」
その年の学校「あーあ」

ということになりかねません。というかなりました。(入学する前の学園祭アプリがたびたび途切れてます)そして、今まさにこの状況でこのままいけば僕たちがA期、一個下がB期を演じることになります。(幸い、僕の代は技術力が高いです)

1.3 解決策

講座を開講して技術者ではない人も取り込んで全体的に技術力をアップさせます。ここで気を付けたところ、失敗したところ、成功したところをまとめます。

2.技術者養成講座の歩み

2.1 2019年4月

そのときは全校が学園祭ムードに包まれていました。僕は中3になってすぐの時でした。前年度の体育祭のときに先輩にTwitterを特定され、Androidができると知られた僕はAndroidのアプリを一人で作っていました。正直、技術力は劣らないと思っていましたし、もちろん成功すると思っていました。しかし、いつまでたってもホーム画面が完成しないそうで「Activityは順番に作っていく」僕にとっては一個目の画面が来ないのでなにもできませんでした。先に他の画面をつくればそれぞれへIntentをつなぐ処理を書き忘れそうで怖かったですし、一度書いたコードは自分が書いたコードであっても解読に時間がかかっていましたのでできるだけ順番につくっていました。結果から言うと1週間の睡眠時間は10時間に達していませんでした。このとき、モンスター(魔材)とお友達どころか親友になりました。アプリは学園祭前一週間にはリリースしていました。ですが、画像を多く使うという理由で一部のくそスぺ端末ではOut of memory連発。また、徹夜しました。

2.2 2019年5月

ゴールデンウイークは毎日15時間は寝てました。このさなか、アプリを担当する部署の長になりました。ありがとうございます。同時に「技術者養成講座」(以下、養成講座)の話が同期の動画部署の長からありました。そのひとは僕以上の社畜で、いろんな仕事を掛け持ちしてました。もう、辛い思いをするのはやめようということで他の部署もまとめてこの講座をすることになりました。

2.3 2019年7月

この月に開催しようと思いましたが全然準備できてなくて延期になりました。

2.4 2019年8月

中国にいました。https://qiita.com/Cyber_Hacnosuke/items/e67df2b02cdd7b3e98c1

2.5 2019年9月

技術者ではない人も人員を増やすことにしようと決めました。ここで問題になったのはパソコンの貸し出しです。非技術者のなかにがパソコンを持っていない人が大半でしょう。また、技術者のなかでもデスクトップのみの人もいました。学校にパソコン室はあるのですが、Win8でexe等の実行ファイルは一切実行できない、ダウンロードしたファイルはシャットダウンすると削除...となかなかに難しかったです。

2.6 2019年10月

学校に政府からの補助金でかったマウスのパソコンがあるのが判明。すぐさま交渉にいきました。用途と合致している(研究者を育成するという政府の目的と合致?)ので使用可になりました。22台ほどありました。この確認が済んだ後、安心して中間テストに臨めたのはよかったです。中間テスト後には選択制の外部演習がありました。それが終わった後そのレポートとその演習の発表プレゼン、養成講座、学生研究で押しつぶされそうになりましたが、魔材の力で乗り切りました。幸い、養成講座の準備は8月からしていたので間に合いました。講座の募集が終わりました。全部で39人の応募があり、貸し出し台数はぴったり22台でした。なにかの集計間違えだと思ったのをいまでも覚えています。こんなにひとが集まるとは思いませんでした。このうち、アプリの講座を取ったのは11人で動画部署の12人に続いて2番目の多さでした。

2.7 2019年11月2日

土曜授業の終了後高2フロアにいき準備をしました。そして開講。結果はまた後で。

3. 講座の成功と失敗

3.1 内容

講座内容は以下です。

  • 挨拶
  • 自己紹介
  • OSってなに
  • byteと文字
  • 拡張子ってなに
  • プログラミングに必要なもの エディタ 開発環境
  • プログラミングサイクル
  • カウントアプリを作ろう
  • 変数
  • メソッド
  • イメージピッカーを作ろう

終わらないのを前提でもし理解が早かったらまずいので長めに設定しました。

3.2 失敗

あえて失敗から言いますと、

  • 時間が足りねぇ

のみです。カウントアプリを作り終えるところまでは行きたかったのですが途中までで、プログラミングは数行書いただけ...

3.3 成功

それでも楽しく授業はできました。かなり、わかりやすかったと反響はいただいています。プログラミングもっとしたかった?と聞いてみるとこれでもいいと言ってくれました…ありがとう!文字の解説では「1byte文字と2byte文字を今説明したけど、なにか気づいた?そう!全角と半角だよ!」といったときにみんなが驚いて納得したようでよかったです。途中、Fateのセイバー派か凛派かで生徒と戦争(?)になりましたが、アニメやラノベ、小説のネタを入れて面白く解説できました。プログラミングの続きをしたい人はぜひ同好会へ!と宣伝することでいまでは37人もの人が所属しています。ちなみに僕はアルトリア・ペンドラゴンちゃんが大好きです。とくにメイドオルタ。

4.なにをすればいいか

まず、激務を自分のせいだと思って抱え込まないのが大切です。予算が足りない、人が足りない、時間が足りない...そういったことを上に訴えることによって僕たちは(学校で)講座を開くことができました。では授業内容にはどのような配慮が必要でしょうか?

4.1 授業内容の配慮

初心者も交じっていればそのレベルに合わせるのが必要でしょう。最低レベルに合わせるということです。でも、上級者や経験者はつまらないと思ってしまいます。個人的な感情ですが、自分が知ってることを先生が自慢げに話すと、「先生ってそんな感じなんだ...」という感じになってしまいます。なので、みんなをアッと驚かせるコラムをいれることで「え!そうゆうことだったの?」と興味を持ってもらうことができます。また、このような内容は身近なものかつあまり知られていないことにすべきでしょう。僕の場合これが「1byte文字が半角、2byte文字が全角」というものでした。そしてこれは特に初心者向けにやったほうがいいことですが、プログラミングの先入観や固定概念を捨ててもらう必要があります。したような結果も出ています。(自社調べ)

○○中高の陽キャラに聞いた!プログラミングのイメージ(自社調べ)
3位「パソコンメガネ」
「陰キャラ?(笑)」「ウザい」「アニオタ」
2位「0と1の羅列」
「2進法?」「指が全部で2本しかない人の末路」
1位「ハッキング」
「危ない」「社会の敵」「暗い部屋」「緑色の文字」「Matrix」「一瞬でのっとり」「○ね」

こういった考えをなくし、プログラミングのプロセスを説明するというのが必要になってきます。そしてよく上級者が初心者がやってしまうのは「文法教えて課題」「ソースコード書かせて解説の繰り返し」です。これは時には中級者にも反感をかいます。理由としては言い方を考慮せずに言えば「初心者は思っているよりバカ」ということです。そもそも教える側にたてばそれだけで一般レベルから大きく外れています。僕たちなら別の言語に結びつけたりしてすぐ覚えますが初心者は頼るもの、すなわちプールサイドがない状態で泳いでるのです。説明しましょう、1からいや0から!そしてソースコードをガンガン書くのも同様の理由でよくないです。僕たちは単語帳のようにソースコードのフレーズを覚えられますが、それができない人は非常におおいです。書ききったとしてもそれはあなたの模範解答を映してる可能性が高いです。

  1. まとめ ということで、徹夜ラッシュ回避のため講座を開講したという話でした。https://twitter.com/Cyber_Hacnosuke
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オブジェクト指向という幻惑

オブジェクトしこうは マヌーサを となえた!

はじめに

この記事は、オブジェクト指向と言う幻惑に包まれた人に届けたい記事となります。
オブジェクト指向やってるぜ!と言う勇者に読んでほしい記事です。

オブジェクト指向の幻惑1

Javaやってるからオブジェクト使ってます!

・・・

そんな訳はありません。

Javaもマルチパラグイムを採用しており、以下のスタイルでコーディング可能です。
- オブジェクト指向
- 手続き型
- 関数型

Java=オブジェクト指向となってしまってる原因は
Javaを広げるためのマーケティング用語としてオブジェクト指向というワードを使ってしまったから。
オブジェクト指向を正しく理解しない人を増やした犯人はJava自体だったわけです。

オブジェクト指向の幻惑2

データクラス作って、ロジッククラスを作って、役割毎にちゃんとクラスわけしてます
俺、オブジェクト指向やってるぜ!

・・・

それは手続き型です。

これは従来の開発スタイルが
機能毎に担当を別けて、機能毎に開発を行う、機能分割式のスタイルが多いために
手続き型と親和性が高く利用されるようになったと考えられます。

コロスケは マヌーハを となえた!

オブジェクト指向とは簡単に言うと、次の通りです

データクラスとロジッククラスを同じクラスに実装する

これが本来、オブジェクト指向が目指す「変更容易性」です。

具体的に「変更容易性」とは何なのか

オブジェクト指向
Aクラス(ロジック+データ)
Bクラス(ロジック+データ)

手続き型
データクラスα
データクラスαを使うAクラス(ロジック)
データクラスαを使うBクラス(ロジック)

この場合、データクラスに修正が入った場合、
同じデータクラスを使い回す手続き型では
Aクラス、Bクラスともに改修・テストの必要が出てきます。

オブジェクト指向の場合、Aクラスの方に修正が入っても
Bクラスは揺らぎません。

これがオブジェクト指向の真髄です。

ここまで読んでオブジェクト指向を利用してると言える人がいるでしょうか?

ここまで読んでオブジェクト指向に興味を持ったか方は以下
オブジェクト指向をきちんと使いたいあなたへ (Software Design別冊)

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

Azure App Service の Tomcat で Javaのヒープサイズを変更する

前提

Azure WebApp のサーバータイプはWindows

操作

  1. Azure Webコンソール上で App Service を開く
  2. Development Toolsのうち「Advanced Tools」を開く(Kudu)
  3. Debug Consoleから「CMD」を開く(単なるコマンドラインではなくGUI的にも使える)
  4. デフォルトで「D:\home」が開いている状態なので「D:\home\site\wwwroot」を開く(マウス操作でも可能)
  5. [wwwroot]の右側にある「+」をクリックして「New File」を選択する
  6. 「web.config」というファイルを作成して以下のようにする。「%AZURE_TOMCAT90%」はTOMCATのバージョンによって異なるので確認する。(SETコマンドで環境変数一覧が表示されるのでそこで確認できる)
  7. アプリを再起動する
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <handlers>
        <add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
    </handlers>
    <httpPlatform processPath="%AZURE_TOMCAT90_HOME%\bin\startup.bat" arguments="">
      <environmentVariables>
        <environmentVariable name="JAVA_OPTS" value="-Xmx1500m -Xms1024m -Djava.net.preferIPv4Stack=true" />
      </environmentVariables>
    </httpPlatform>
  </system.webServer>
</configuration>

以上。

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

Scalaで日時の範囲を表すクラス群をつくる

Scalaで日時の範囲を表すクラス群をつくる

きっかけ

Javaで開発をしていて、日時の範囲を表すクラスがなかったので少し不便に感じた。java.time.Periodjava.time.Durationは範囲というより2点間の距離に関心があるクラスなので違うんですよね。
よく使いそうなので私的メモとして残しておきます。

基底クラス

本筋とはそれますが、java.time.LocalXxxのラッパーも用意しました。始端、終端をラッパークラスにするイメージです。

日時を表すTrait

継承先クラスは型引数で受け取り、内部で保持する値の型はtypeで受け取るというダブルスタンダードになりました。継承先クラスの情報を型引数で受け取っている理由は、Orderedを継承する際に型引数を指定する必要があったからです。この型に大小関係がなければ、この型を始端と終端にした範囲という概念があり得ないと考えたのでTraitでOrderedを継承しました。

Chronology.scala
trait Chronology[ClassType <: Chronology[_]] extends Ordered[ClassType]{
  type ValueType //LocalTimeとかが指定される想定

  val value: ValueType

  def isAfter(other: ClassType): Boolean

  def isEquals(other: ClassType): Boolean

  final def isAfterOrEqual(other: ClassType): Boolean = isAfter(other) || isEquals(other)

  def isBefore(other: ClassType): Boolean

  final def isBeforeOrEqual(other: ClassType): Boolean = isBefore(other) || isEquals(other)
}

範囲を表すTrait

日時の型を受け取り、受け取った型を始端と終端にもつTraitです。
始端と終端の大小関係を事前条件として指定しています。

ChronologyRange.scala
trait ChronologyRange {
  type ChronologyType <: Chronology[_]

  val start: ChronologyType

  val end: ChronologyType

  require(start <= end)

  def overlaps(other: ClassType): Boolean
}

使い方

例:Date

Date.scala
case class Date(value: LocalDate) extends Chronology[Date] {
  override type ValueType = LocalDate
  def toInstant: Instant = value.atStartOfDay(ZoneId.systemDefault()).toInstant

  def isAfter(other: Date): Boolean = value.isAfter(other.value)

  def isBefore(other: Date): Boolean = value.isBefore(other.value)

  override def isEquals(other: Date): Boolean = value.isEqual(other.value)

  override def compare(that: Date): Int = value.compareTo(that.value)
}
DateRange.scala
case class DateRange(start: Date, end: Date) extends ChronologyRange {
  override type ChronologyType = Date

  override type ClassType = DateRange

  override def overlaps(other: DateRange): Boolean = ???
}

学び

  • Traitにもフィールドを宣言できること
  • Traitにもイニシャライザを書けること
  • Orderedを継承したクラスのインスタンス同士を不等号で比較できること

あとで調べること

  • typeとtype parameterってどう違うの
  • TraitとAbstract Classってどう使い分けるの
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む