- 投稿日:2019-02-28T16:06:10+09:00
デザインパターン ~Builder~
1. はじめに
GoFのデザインパターンにおける、Builderパターンについてまとめます。
2. Builderパターンとは
- Buildという英単語は、構造を持っている大きなものを構築したりすることの意味になります。
- 複雑な構造をもったものを作り上げるとき、一気に完成させるのは困難です。まず全体を構築している各部分を作り、段階を踏んで組み上げていくことになります。
- Builderパターンは、構造を持ったインスタンスを組み上げていく方式です。
- GoFのデザインパターンでは、生成に関するデザインパターンに分類されます。
3. サンプルクラス図
4. サンプルコード
4-1. Builderクラス
文書を構成するためのメソッドを定義した抽象クラスです。
Builder.javapublic abstract class Builder { public abstract void makeTitle(String title); public abstract void makeString(String str); public abstract void makeItems(String[] items); public abstract void close(); }4-2. Guideクラス
1つの文書を作成するクラスです。
Guide.javapublic class Guide { private Builder builder; public Guide(Builder builder) { this.builder = builder; } public void construct() { builder.makeTitle("バーベキューについて"); builder.makeString("日時"); builder.makeItems(new String[]{ "2019/2/28 (木)", "11:00~", }); builder.makeString("場所"); builder.makeItems(new String[]{ "xxx市 xxxバーベキュー場", }); builder.makeString("持ち物"); builder.makeItems(new String[]{ "タオル", "肉", "飲み物", "(略)", }); builder.close(); } }4-3. TextBuilderクラス
プレーンテキストで文書を作成するクラスです。
TextBuilder.javapublic class TextBuilder extends Builder { private StringBuffer buffer = new StringBuffer(); public void makeTitle(String title) { buffer.append("==============================\n"); buffer.append("『" + title + "』\n"); buffer.append("\n"); } public void makeString(String str) { buffer.append('■' + str + "\n"); buffer.append("\n"); } public void makeItems(String[] items) { for (int i = 0; i < items.length; i++) { buffer.append(" ・" + items[i] + "\n"); } buffer.append("\n"); } public void close() { buffer.append("==============================\n"); } public String getResult() { return buffer.toString(); } }4-3. HTMLBuilderクラス
HTMLファイルで文書を作成するクラスです。
HTMLBuilder.javaimport java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class HTMLBuilder extends Builder { private String filename; private PrintWriter writer; public void makeTitle(String title) { filename = title + ".html"; try { writer = new PrintWriter(new FileWriter(filename)); } catch (IOException e) { e.printStackTrace(); } writer.println("<html><head><title>" + title + "</title></head><body>"); writer.println("<h1>" + title + "</h1>"); } public void makeString(String str) { writer.println("<p>" + str + "</p>"); } public void makeItems(String[] items) { writer.println("<ul>"); for (int i = 0; i < items.length; i++) { writer.println("<li>" + items[i] + "</li>"); } writer.println("</ul>"); } public void close() { writer.println("</body></html>"); writer.close(); } public String getResult() { return filename; } }4-5. Mainクラス
メイン処理を行うクラスです。
Main.javapublic class Main { public static void main(String[] args) { if (args.length != 1) { System.exit(0); } if (args[0].equals("plain")) { TextBuilder textbuilder = new TextBuilder(); Guide guide = new Guide(textbuilder); guide.construct(); String result = textbuilder.getResult(); System.out.println(result); } else if (args[0].equals("html")) { HTMLBuilder htmlbuilder = new HTMLBuilder(); Guide guide = new Guide(htmlbuilder); guide.construct(); String filename = htmlbuilder.getResult(); System.out.println(filename + "が作成されました。"); } else { System.exit(0); } } }4-6. 実行結果
4-6-1. テキスト
============================== 『バーベキューについて』 ■日時 ・2019/2/28 (木) ・11:00~ ■場所 ・xxx市 xxxバーベキュー場 ■持ち物 ・タオル ・肉 ・飲み物 ・(略) ==============================4-6-2. HTML
5. メリット
サンプルプログラムを見てみると、Mainクラスは文書の構築方法(Builderクラスのメソッド)を知りません。MainクラスはGuideクラスのconstructメソッドを呼び出すだけで、文章を構築できます。
また、GuideクラスはBuilderクラスを使って文書を構築しますが、Guideクラスは自分が実際に利用しているクラスが何なのか(TextBuilderなのかHTMLBuilderなのか)を知りません。
TextBuilderのインスタンスをGuideに与えても、HTMLBuilderのインスタンスをGuideに与えても正しく機能するのは、GuideクラスがBuilderクラスの具体的なクラスを知らないからになります。
知らないからこそ、入れ替えができる、入れ替えられるからこそ、部品としての価値が高まります。6. 参考
- 投稿日:2019-02-28T14:31:52+09:00
元プロゲーマーが知識0からプログラマーになって2年でアプリ8本リリースするまでの話
0.はじめに
プロゲーマー を辞めて、知識0から ゲームプログラマー になり2年が経ったので、
アプリを8本リリースしたりしながら得た知見を備忘録代わりに記事にします。また、理系でもなくプログラムに一切触れずに生きてきて、音大志望だった私がこの業界に飛び込み、
プログラマーとしての「あるべき思考回路」や「一般的な視点」を習得するまでに大変苦労したので、2年前の自分が知っておきたかった心構えも後半にまとめます。あと個人的な事ですが…能動的に露出する事に抵抗を覚える人間なので、その苦手意識を克服したいという目的もあります。
1.どんなプロゲーマーだった?
プロゲーマーの定義が多種多様なので、まず私自身がどのようなプロゲーマーだったのか軽く触れておきます。
私がプレーしていたのはLeague of Legends(通称LoL)という世界で最も競技人口が多く月間約1億人がプレイしているゲームで当時LoLでは日本1位のチーム「DetonatioN」に1年半所属していました。LoLのチームはゲーミングハウスといわれるシェアハウスで、「選手」「アナリスト(ゲームを分析、チームを改善する人)」「栄養管理士(シェフ)」「コーチ」「スタッフ」…等おおくの人が共同生活を送り、日々世界一を目指して活動しています。
私は「選手」として契約していて、衣食住の確保、スポンサーからの支援(Logicool、NVIDIA、KDDI etc)、大会の賞金などがあり、プロゲーミングチームとして日本初の月額給与制を取り入れたチームでもあり、とても恵まれた環境でゲームに競技として真剣に向き合う事ができました。
2.プログラマーになるまで
現役を引退した時に、ゲームをプレイする側から作る側にシフトしたいと考え、就職活動を開始しました。
人生は環境ゲーな所があるので、技術習得がしやすいか、将来像がマッチしてるか、人間関係が良好そうかを
判断基準にとりあえず自分の希望に合った会社を探して行きました。転職支援サービスをふんだんに使い、
大雑把に目星を付ける(300社)→優先順位を付け書類を送る(100社)→優先順位が高い所から書類を通し面接していくという流れでした。知識0未経験の人間がただ書類を出すだけでは通ると思えなかったので、
各会社用に企画書を作ったり元プロゲーマーとしてのキャリアをどう活用できるかをアピールしたりした結果、
幸いにも興味を持って下さった会社が複数あり、いくつかの内定を頂けました。
その中に、自分にマッチしていると感じる会社が見つかったので、およそ3カ月で就活終了。面接の中で印象的だったのは、
未経験なことを受け入れてポテンシャルを見ようとしてもらえる会社から、圧迫面接でテンプシーロールを食らわせてくる会社まで様々だったことでした。プロゲーマーという肩書きは興味を持ってもらうところまでは効果ありますが、その後は「ゲームとどうやって向き合ってきたか」が問われる気がします。3.プログラマーとして
ソシャゲの開発&運用、VR、AI…等をやっている会社で働きました。
初期は本当に何もわからなかったのでUnityとC# を中心に勉強をはじめ、ゲームのレベルデザイン、バグ対応、ソシャゲ運用(中級コンテンツの作成)などに携わり徐々にプログラマーとしての知識を蓄えていきます。
技術力が身につくと共にソシャゲ開発チームに配属されたり、個人でアプリを開発するような業務を任されるようになりました。ちなみにプログラマーとして歩み始めた頃は、独習C#をざーっと読んだり、Unityの玉転がしで阿鼻叫喚したり、Unity猫本で猫と戯れたり、会社から無理難題を投げつけられ火を吹いたり色々しました。
また、運用中のソシャゲのバランス調整やレベルデザインを適切なものにするにはゲームを誰よりも理解している必要がある→まずランキング1位になる必要があるのでは、ということで実際に対人要素で1位までやりこんだこともあります。
このあたりはプロゲーマーのスキルが直接役に立った部分です。幸運なことに、会社の先輩は人柄が良く尊敬できる方ばかりでした。こんな未経験の雑魚が業務に携われるようになったのはひとえに親身に教えて下さった方々のおかげなので、とても恵まれた環境の中でプログラマーとしてのキャリアをスタートできたと思います。
プログラム チョット デキル4.アプリリリース8本する
入社して1年ちょっとでアプリ開発をはじめ、これまでにカジュアルゲームといわれるアプリを8本リリースしました。
その中で身にしみた、プログラマーとしての忘れたくない(独断と偏見による)心構えを3つ書き記します。
5.プログラマーとしての3つの心構え
①わからないことを明確にする
「ゲーム作りたい。でもなんか難しそう。」「ゲーム上手くなりたい。でも何すれば上手くなるのかわからない」「水飲みたい。でも水って何?どれ?」「なんかよくわかんない」
.わからないことがわからない状態
そんな状態は自分も他人も誰も解決できません。
これはプログラマーにとってとても危険な状態ですが、私も入社時はこの状態になることが多くありました。
はじめはわからない事をわかる(理解する、解決する)ようにならなくてもよくて、
わからない事を明確にする事(何がわからないのか?)が重要だと考えを切り替えるのが最初の一歩。... 何がわからないかが明確になれば、あとはgoogle先生が答えを見つけてくれるんだ。
.
.勿論、はじめから理解している量が多ければ多いほど良いけれど、プログラムの世界において全部記憶しておくのはほとんどの人間にとって無理だし、この「わからないことを明確にして」、「googleに答えを見つけてもらう手法」さえ構築すれば生きていけるとわかりました。
私自身は、その認識に辿り着くまでに無駄な努力を多く挟んでしまいました。これは覚えておかないととか、これは絶対に理解しておかないとという強迫観念に追われて時間を使うよりも、その都度わからないことを調べて解決するフローに徹した方が、技術力が上がるスピードも上がりました。
googleさんに効率的に助けてもらううえで役に立つのは、英語で検索する力をつけること、Debug Log信者になること、今の状態を言語化して考えること…等でしょうか。
.
.②物事を細分化して考える
何か目的に向かっていく時、細分化して考えたほうが解決が早くなります。
例えば
「RPGゲームが作りたい」 → 「Unityで」 終わりのように、抽象的に物事を完結させて行動するよりも、
「RPGゲームが作りたい」 →「戦闘の実装をターン制にする」→「攻撃/魔法/防御コマンドをまず作る」→「攻撃時の処理」 →「会話シーンを作りたい」→「会話シーン用の汎用的な実装を作るべき」→「会話シーン発生時に呼び出す処理をかく」→ 「会話シーン用に適切なメッセージを呼び出せる処理と会話データベースを作る」といった風に、細かく具体的であればあるほど解決が早くなります。
ゲームでいえば
思い返せばプロゲーマーの時も同じことをしていたような気がします。当時の思考を辿るとこんな感じでしょうか。「対人ゲームが上手くなりたい」 「何が問題点か?」 ・「操作精度が悪い」→「試合前に30分ミスしやすい操作を練習する」、「入力の仕方を見直す」 ・「使用キャラクターの理解度が低い」→「仕様を理解する」、「上級者の動きを動画で学ぶ」 ・「何で負けてるのかわからない」→「試合ごとにターニングポイント(勝敗に最も貢献した時)を明確にする」→ 「その原因を試合ごとに言語化して、脳内でよく反芻する」ということをよくしていました。あくまで私の場合はですが、プログラミングを学習するうえで、ゲームの上達方法を応用できる部分は想像以上に多かったと感じています。
また、この能力を培う為には日々、考える視点を持つことが効果的です。
例え:
上級者のゲームプレイ動画を見た時... 自分なら同じことをするか? できるか? 彼らが何をしているか理解できてるか?
新しいゲームアプリを見た時... このゲームのこの部分はどういう実装になってるのか?
音楽を聞いた時... 曲を聞いた時に楽器構成、曲の構成を認識して脳内で遊ぶ。
外で歩いている時(美術)... あの屋根は自分ならどういう風に描くか? どういった画材を使いどういう風に色を混ぜるか?こういった視点の癖をプログラマーとして持つ事によって、 頭の中でコードを創造する技術が上がる事に気がつきました。
③形にする
とにかく「完成」を目指す。この時目指すものは「完璧」ではなくていい。
Twitterで見た開発者ツイートで印象的だったのが(ソース紛失)
リリースできる人とできない人の違い
できる人 「β版0.9リリースしました」 「バグ修正のβ版0.95リリースしました」 「機能追加のβ版0.98リリースしました」 「諸々修正のβ版0.99リリースしました」 できない人 「UIもっとよくしたい」 「戦闘部分改良中」 「デザインもっとかっこよいのにしたい」 「バグ沢山あるので修正してる」というのがとても的を射ているなぁと。
プロゲーマーなんかは特に完璧主義 な人が多いと思うのですが、そういうタイプの人って「リリースしない人」になりがちな気がします。
なんか許せないんですよね。自分で納得できない状態で人前に出すのが。しかし、ここは「開発」です。
「犬」を作りたいならば、犬の「目」を完璧に作ってから「足」を完璧に作るなんて事してたら途方に暮れてしまいます。「犬」を作るなら「最低限犬と認識できるもの」をまず作るのが優先で、「骨格」と「色」くらい作っとけば
とりあえず β版完成 です。リリースしないにしても、開発の全体像に目を向けずに欲望で細かいところにこだわっていても完成しません。
.
.今よりも良い実装なんて探せば何かしら存在するだろうし、納得する形を求めだしたら欲望の底なし沼に嵌ります、拘り出したらキリがないのが開発だと思いました。
今ある力で実現可能な中で最高の完成を目指せば良いのであって、その為には不必要な完璧は断捨離しないと、いつまで経っても完成品ができない。.
.
これは極論ですが、UnityでUpdateにGetComponentするという死刑級の事をしても、処理上問題なく動く形になってるならまずはそれでいいんです。初学者なら特に。次の修正版でより良い実装を調べて改善すればいいし、まずは完成させるための事だけに傾注すべきです。.
.
最終型を頭に描いてまず作ってみる、やってみるという姿勢は、リリースできるかどうかだけでなく自分の成長にも大きく影響してくると感じています。
これもプロゲーマー時代の知見ですが、プロゲーマーの先駆者ウメハラさんの「結果としての勝敗そのものは、それほど重要視するものではない。...継続的なところにこそ、本当の勝利はある」という考えに触れて、私がプロゲーマーだった時も「その時の勝敗ではなく、将来の強さにつながる行動」の重要性を意識するようになりました。
棋士の羽生善治さんも、「常に一番強い手でいってはいけない。その局面において一番強い手が最善の手ではない。...先を見越した一手には、見通す目と理性が必要」と言っています。
ゲームにおいて、目の前の試合で勝てる行動と将来的に長く勝てるようになる行動が違うように、開発においても完成に目を向けずに局所的な最適化をする「今の完璧」と、自分の開発力が結果的にあがる「将来の完璧」は違うように思います。※1※2将来の力を上げる上で、とにかく「完成」を目指す。
今、こんなものが作りたいと思う気持ちがあるなら半年後に作れる姿になる為の最適な行動とは? と考えると
人に見られて恥ずかしいとか、プライドがどうとか、自分が許せないとか、そういう邪魔な要素を排除して、とにかく半年後から逆算した思考でやるのが大切な気がしました。この記事を書いてるのも、アウトプットの選択肢として文章も持っていた方が強いので、その練習の一環という意味もあったりします。
6.プロゲーマーとしてのキャリアがどのようにプログラマーとして活かされたか?
野球の選手にしろ、棋士にしろ、プロゲーマーにしろ、何かの世界でトップを目指す人の中には、どんな物事に取り組んでも上に行くための筋道を立てる思考を鍛えている人が多いと思います。私の場合も、ゲームを極めようとした方法論が、プログラムの習得に想像以上に応用できた感覚があります。
特に私は現役時代から、1つの特殊な山を登る能力よりも、どんな山でも登れる普遍的な能力を意識して生きてきたので、プログラマーとしても実際に0知識から山を登り始めている感覚を持てて喜んでいます。とりあえず、ゲームをプレイするだけだった人でもゲームを作る人(まだそれがゴミコードでも)になれるらしい。
7.あとがき
ここ最近、また新しい分野に手を出していて自分の考え方とか知見を振り返る事が多く、将来の自分のために一度整理しておきたかったのに加えて、何も知識がない人間が少しずつ色んなことができるようになる過程を見て勇気を持つ人がいればと思いまとめました。少しでもプログラムの楽しさに触れる人が多くなれば嬉しいです。いまのところ、プログラミングはゲームに負けないぐらい楽しいです。
実践というより精神論的な話ですし、文章を書くことに不慣れでわれながら稚拙ですが、内向的な性格との決別も込めて投下します。
未熟者ゆえどうかお手柔らかに。参照
プロゲーマー時代の自分のTwitter https://twitter.com/yumeoti_lol
プロゲーミングチーム「Detonation」http://team-detonation.net/aboutus
※1 直感力(羽生善治著)
※2 勝負論~ウメハラの流儀~(梅原大吾著)
- 投稿日:2019-02-28T14:14:23+09:00
Git 毎回パスワードを聞かれる問題について
はじめに
職場で使用していたPCが重くなってきて、新しいPCに買い替えました。
無事に移行は完了しましたが、Sourcetreeでプッシュやプルしている時に毎回パスワードを聞かれるようになっていました。前提
- SourcetreeでGitHubのログインはSSHで済んでいる。
SSHでSourceTreeにクローン済み
git@github.com:eyemovic/hoge.git
githubのsshのkey設定などは済んでいる
~/.ssh配下のpermissionなども正しい
問題
プッシュしようとすると、
gouda:hoge-branch gouda$ git push origin hoge-branch
公開鍵のパスワードを聞かれる(毎回)
Enter passphrase for key '/Users/gouda/.ssh/id_rsa
解決方法
SSHキーを追加してあげる
gouda:hoge-branch gouda$ ssh-add ~/.ssh/id_rsa
SSHキーのパスワードを聞かれるので入力してあげる
Enter passphrase for key '/Users/gouda/.ssh/id_rsa
プッシュしてみる
gouda:hoge-branch gouda$ git push origin hoge-branch
パスワードを聞かれずに、プッシュできました!
Everything up-to-date
おわりに
設定する時間がなく、とりあえず毎回パスワードを入力していたので、ようやく改善されてよかったです。
間違えているところなどあればコメントをお願いいたします。
- 投稿日:2019-02-28T09:33:56+09:00
デザインパターン ~Prototype~
1. はじめに
GoFのデザインパターンにおける、Prototypeパターンについてまとめます。
2. Prototypeパターンとは
- Prototypeという英単語は、原型や模範という意味になります。
- Prototypeパターンは、new xxx()でクラスからインスタンスを生成するのではなく、インスタンスから別のインスタンスを作り出す方式です。
- 複製を作る操作をcloneと呼びます。
- GoFのデザインパターンでは、生成に関するデザインパターンに分類されます。
3. サンプルクラス図
4. サンプルコード
4-1. Productインターフェース
複製メソッドを定義するインターフェースです。javaのCloneableインターフェースを継承しています。
Product.javapackage framework; import java.lang.Cloneable; public interface Product extends Cloneable { public abstract void use(String s); public abstract Product createClone(); }4-2. Managerクラス
Productの作成指示や管理を行うクラスです。
Manager.javapackage framework; import java.util.HashMap; public class Manager { private HashMap showcase = new HashMap(); public void register(String name, Product proto) { showcase.put(name, proto); } public Product create(String protoname) { Product p = (Product)showcase.get(protoname); return p.createClone(); } }4-3. UnderlinePenクラス
Productインターフェースを実装するクラスです。当クラスは「文字に下線を引く」クラスになります。
UnderlinePen.javaimport framework.Product; public class UnderlinePen implements Product { private char ulchar; public UnderlinePen(char ulchar) { this.ulchar = ulchar; } public void use(String s) { int length = s.getBytes().length; System.out.println(s); for (int i = 0; i < length; i++) { System.out.print(ulchar); } System.out.println(""); } public Product createClone() { Product p = null; try { p = (Product)clone(); } catch (CloneNotSupportedException e) { // オブジェクトのクラスがCloneableインタフェースを実装していない場合にスローされる例外 e.printStackTrace(); } return p; } }4-4. MessageBoxクラス
Productインターフェースを実装するクラスです。当クラスは「文字を囲む」クラスになります。
MessageBox.javaimport framework.Product; public class MessageBox implements Product { private char decochar; public MessageBox(char decochar) { this.decochar = decochar; } public void use(String s) { int length = s.getBytes().length; for (int i = 0; i < length + 2; i++) { System.out.print(decochar); } System.out.println(""); System.out.println(decochar + s + decochar); for (int i = 0; i < length + 2; i++) { System.out.print(decochar); } System.out.println(""); } public Product createClone() { Product p = null; try { p = (Product)clone(); } catch (CloneNotSupportedException e) { // オブジェクトのクラスがCloneableインタフェースを実装していない場合にスローされる例外 e.printStackTrace(); } return p; } }4-5. Mainクラス
メイン処理を行うクラスです。
Main.javaimport framework.Manager; import framework.Product; public class Main { public static void main(String[] args) { Manager manager = new Manager(); UnderlinePen upen = new UnderlinePen('~'); MessageBox mbox = new MessageBox('*'); MessageBox pbox = new MessageBox('+'); manager.register("strong message", upen); manager.register("warning box", mbox); manager.register("slash box", pbox); Product p1 = manager.create("strong message"); p1.use("Hello world"); Product p2 = manager.create("warning box"); p2.use("Hello world"); Product p3 = manager.create("slash box"); p3.use("Hello world"); } }4-6. 実行結果
Hello World =========== ************* *Hello World* ************* +++++++++++++ +Hello World+ +++++++++++++5. メリット
サンプルプログラムでは比較的シンプルなケースでしたが、インスタンスを生成するにあたり、複雑な処理が必要だったり、時間がかかる処理が必要だったりする場合があります。インスタンスを生成する度にnew xxx()としていては、とても非効率になります。
Prototype(雛形)を作っておいてコピーするだけで、簡単にインスタンスを生成することができます。6. 参考