nirasan's tech blog

趣味や仕事の覚え書きです。Linux, Perl, PHP, Ruby, Javascript, Android, Cocos2d-x, Unity などに興味があります。

Unity の UnityEditor.BuildPipeline:BuildAssetBundles で出たエラーと対策

前提条件

  • Unity 5.6.1f
  • UnityEditor.BuildPipeline:BuildAssetBundles で Asset Bundle をビルドしたらエラーが発生した

エラー内容

  • ポップアップで “Build failure Internal error: Target platform mismatch”
  • コンソールに “Menu Edit/Graphics Emulation/OpenGL ES 3.0 can’t be checked because it doesn’t exist” とエラー表示

対策

  • Unity 5.6.1f から最新の Unity 5.6.2f にアップグレードしたらエラーにならずに正常に Asset Bundle のビルドができるようになった

参考URL

https://forum.unity3d.com/threads/got-an-error-when-building-assetbundles-target-platform-mismatch.476663/

ユースケース駆動開発実践ガイドまとめと感想

はじめに

感想

  • ウォーターフォール的なちゃんと設計ドキュメントを作ってから実装する、みたいな流れで開発をしたことがなかったので勉強になったし、ユースケースの作り方なんかはためになった
  • これをそのまま実践するかというとしないと思うが、選択肢として堅いやり方を知っておきながら柔らかいやり方と使い分けていけるようになれればいいかなと思う

簡単なまとめ

  • 全体を通して ICONIX(アイコニクス)プロセスという手法を使ったソフトウェア開発について書かれている
  • ざっくりまとめると、要求を、ドメインモデル、ユースケースロバストネス図、シーケンス図、コード、と変換していくことで、要求漏れがなく手戻りが少ない開発ができる、みたいなことだと理解
  • 以下各章ごとに順にまとめ

第1章 ICONIXプロセス

  • 全体の大まかなまとめ
  • 全部読んでからならここだけ読めばなんとなく復習できるかも

第2章 ドメインモデリング

  • 静的モデルを一番初めに定義しておき、後の工程で曖昧さのない用語を利用する

感想

  • エリック・エヴァンス本で言うユビキタス言語のことか
  • エリック・エヴァンス本でドメインモデルというと図のことではなく振る舞いなども含めた概念を指すと思うので同じ言葉だが別のものを指しているはず

第3章 ユースケースモデリング

  • 要求からユースケースを作っていく
  • ユースケースとは「AはBをする、次にCはDをする」という叙述的なものになる
    • ユーザーがログインする場合のユースケースの例は「システムはログイン画面を表示する。ユーザーはユーザー名とパスワードを入力してログインボタンを押す。システムはユーザー名とパスワードをチェックする。ユーザーはシステムにログインする。」といったもの
  • ユースケースは基本コース(正常系)と代替コース(異常系)すべてを網羅すること
  • ユビキタス言語を使用して記述されていること
  • 「ログイン画面を表示する」というようにGUIの具体的なパーツ名を使用して記述されていること
  • 必要であればドメインモデルを更新する

3つの魔法の質問

  • ユースケースを書くときには、「何が起きるか?」「そして何が起きるか?」「他にどのようなことが起きそうか?」という質問を繰り返して正常系異常系すべてのケースをあぶり出していく

第4章 要求レビュー

  • ドメインモデルとユースケースが要求を満たしているか顧客と共にレビューを行う
  • 必要なら紙芝居やモックやプロトタイプを作って説明を行う

第5章 ロバストネス分析

感想

  • ロバストネス図を作ることで予備的な設計が行われ、UIやドメインや処理の不足や用語のブレなどがあらかじめ浮き彫りになってくる
  • 最近 twitter で流れてきた、先の工程に進むと前の工程のアラが見えてくる法則を逆に利用してはやめに先の工程に進んでミスを潰していくという戦術なのかも
  • オブジェクトでもコントローラでもない、間の線に書かれた文字の意味がわからない
    • なんとなくユーザーの振る舞いは線上に書いている気がする
    • ユーザーの振る舞いでもコントローラになっているものもある
    • 粒度の問題?
    • たぶん説明がないのでロバストネス図について理解が曖昧になっている

第6章 予備設計レビュー

第7章 テクニカルアーキテクチャ

第8章 シーケンス図

  • ロバストネス図をシーケンス図に変換する
  • 正常系も異常系も同じシーケンス図上に記述する
  • ロバストネス図上では各コントローラについて誰が主体になって行っているかが記述されていないので、シーケンス図を使って処理の主体をはっきりさせてオブジェクトとメソッドを定義していく
  • 活性区間(線上の長方形、オブジェクトのライフタイムを表す)は利用しない
  • 必要であればロバストネス図を更新する

第9章 詳細設計レビュー

  • シーケンス図を開発者のみでレビューする
  • シーケンス図とユースケースの割り当てができてるかや、適切な属性と操作が割り当てられているかなどを確認する

第10章 実装

  • ここから具体的に Spring Framework を使って具体的に実装していく
  • 汎用的に使えそうな内容としては、設計をそのまま実装しなさい、コーディング中につまったら設計から見直しなさい、設計とコードを常に動悸させなさい、コメントを書きすぎてはいけません、正常系だけでなく異常系も書くのです

感想

  • ここから先は Spring Framework についての具体的な話になっており流し読みしたくらいなので省略

GAE/Go 環境で echo が実行できない問題

問題

  • GAE/Go 環境で echo を使ったアプリケーションを goapp serve で実行しようとすると以下のエラーが発生して実行できない
2017/01/16 16:08:42 go-app-builder: Failed parsing input: parser: bad import "unsafe" in github.com/valyala/fasttemplate/template.go from GOPATH

原因

  • echo が依存している fasttemplate でエラーが出ている
  • Issuesは出ている
  • 修正パッチのPull requestも出ている

対策

  • Pull request がマージされるのを待つ
  • 待てない場合は変更内容をローカルの fasttemplate のソースコードに適用する。手元では動いた。

Go 言語のアプリを heroku にデプロイするときは vender を .gitignore にいれるとだめ

  • ビルドエラーになる
  • godep 使わせている意味なくない?
  • デプロイ用のブランチ作って .gitignore から外すとかするのかな?
$ git push heroku master
Counting objects: 280, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (260/260), done.
Writing objects: 100% (280/280), 36.31 KiB | 0 bytes/s, done.
Total 280 (delta 123), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Go app detected
remote: -----> Checking Godeps/Godeps.json file.
remote: -----> Installing go1.6... done
remote: -----> Running: godep go install -tags heroku ./...
remote: my_go_file.go:5:2: cannot find package "any/dep/package" in any of:
... 略 ...
remote: godep: go exit status 1
remote:  !     Push rejected, failed to compile Go app.
remote:
remote:  !     Push failed
remote: Verifying deploy...
remote:
remote: !       Push rejected to myapp.
remote:
To https://git.heroku.com/myapp.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/myapp.git'

Rubyのリファクタリングでイケてないコードをなんとかするやつをやってみた

はじめに

リファクタリングできそうな点

  • initialize で start_date, end_date を渡しているのが不自然。
    • OrdersReport という汎用的なクラス名なので期間以外の条件で絞り込みたくなった時に応用が効かない
    • クラス名が OrdersDailyReport だったり、仕様として集計時には必ず期間が指定されると決まっていたりしたら気にしなくていいかも
  • total_sales_within_date_range で orders の絞り込みと集計を同時にやっている
    • ここも他の条件で絞り込みたくなった時に応用が効かないので絞り込み処理と集計処理を分けたい

リファクタリング結果

変更後

  • 呼び出し方は変わって OrdersReport.new(orders).select_within_date_range(start_date, end_date).total_amount となった
  • 行数が削減された
  • 複数の責務を持つ一つの大きなメソッドから単一の責務を持つ複数の小さなメソッドに分割された
  • 絞り込み処理と集計処理をメソッドチェーンで繋げるようにしたので、新しい絞り込み条件が入ってきても拡張しやすくなった
require 'ostruct'

class OrdersReport
  def initialize(orders)
    @orders = orders
  end

  def select_within_date_range(start_date, end_date)
    select { |o| start_date <= o.placed_at && o.placed_at <= end_date }
  end

  def select(&block)
    self.class.new(@orders.select(&block))
  end

  def total_amount
    @orders.map(&:amount).inject(:+)
  end
end

class Order < OpenStruct
end

変更前

require 'ostruct'

class OrdersReport
  def initialize(orders, start_date, end_date)
    @orders = orders
    @start_date = start_date
    @end_date = end_date
  end

  def total_sales_within_date_range
    orders_within_range = []
    @orders.each do |order|
      if order.placed_at >= @start_date && order.placed_at <= @end_date
        orders_within_range << order
      end
    end

    sum = 0
    orders_within_range.each do |order|
      sum += order.amount
    end
    sum
  end
end

class Order < OpenStruct
end

Go 言語でソリティアを作った

termbox-go を知ったので、息抜きに Go 言語でソリティアを作りました。
https://github.com/nirasan/go-solitaire

f:id:nirasan:20160610081329p:plain

仕事中にやりやすいように ls っぽいモードも搭載してあります。

f:id:nirasan:20160610081347p:plain

mac 用だけサンプルがあります。操作は README で。
https://github.com/nirasan/go-solitaire/blob/master/sample/solitaire?raw=true

Go 言語の列挙型的な定数と定数でのフラグ管理

列挙型的定数

  • Go では const と iota を使って列挙型的な定数を定義する
    • iota は const 宣言開始時に 0 になり、定数を定義する毎にインクリメントする
    • 定数定義は値を指定しないと直前の定数と同じ値になるので iota と組み合わせることで連続した値が定義される
// 定義
type Flag int
const (             // iota = 0 (仮想コード)
  Flag1 Flag = iota // Flag1 = iota; iota++
  Flag2             // Flag2 = iota; iota++
  Flag3             // Flag3 = iota; iota++
)
// 利用例
f := Flag1
if f == Flag1 {
  // Flag1 の場合の処理
}

列挙型的定数を使ったフラグ管理

  • 列挙型的な定数の定義時に一工夫すると定数をフラグ的に管理できるようになる
// 定義
type Flag int
const (
  Flag1 Flag = 1 << iota // 1 << 0 (10進数: 1, 2進数: 00000001)
  Flag2                  // 1 << 1 (10進数: 2, 2進数: 00000010)
  Flag3                  // 1 << 2 (10進数: 4, 2進数: 00000100)
  Flag4                  // 1 << 3 (10進数: 8, 2進数: 00001000)
)
// 利用例
f := Flag1
if f & Flag1 != 0 {
  // Flag1 の場合の処理
  // &(and) 演算子で双方にビットが立っていた場合に 0 以外が返るので Flag1 が有効かどうかの判定ができる
}
if f & (Flag1 | Flag2) != 0 {
  // Flag1 か Flag2 の場合の処理
  // |(or) 演算子で Flag1 かつ Flag2 の値を作ることができるので複数のフラグの判定処理がちょっと楽になる
}

ひとつの定数で複数種類のフラグを管理する

  • 例えば文字の色と書体を同一の方で管理するような場合
type Flag int
const Flag_N_Start = 0
const (
    Flag1 Flag = 1 << (iota + Flag_N_Start) // 1 (00000001)
    Flag2                                   // 2 (00000010)
    Flag3                                   // 4 (00000100)
    Flag_N_Bits = iota                      // 3 (Flag_N で 3bit 使っている)
    Flag_N_Mask = 1 << Flag_N_Bits - 1      // 7 (00000111) (Flag_N 用のビットマスク)
)

const Flag_A_Start = Flag_N_Bits                       // Flag_A の開始ビット
const (
    FlagA Flag = 1 << (iota + Flag_A_Start)              // 1 << (0 + 3) =  8 (00001000)
    FlagB                                                // 1 << (1 + 3) = 16 (00010000)
    FlagC                                                // 1 << (2 + 3) = 32 (00100000)
    Flag_A_Bits = iota                                   // 3 (Flag_A で 3bit 使っている)
    Flag_A_Mask = (1 << Flag_A_Bits - 1) << Flag_A_Start // 56 (00111000) (Flag_A 用のビットマスク) (使用ビット分だけマスクを作って開始ビット分だけ左シフトする)
)

f := (Flag1 | FlagB)
if f & (Flag1 | FlagA) != 0 {
    // Flag1 か Flag2 だった時の処理
}