emahiro/b.log

Drastically Repeat Yourself !!!!

【go】構造体にメソッドを追加する

goはオブジェクト指向言語とは違って、書いていてC言語を書いている印象に近く、OOPで言うところのクラスが構造体で、クラスにメソッドを定義することは、構造体にメソッドを定義することになる。

しかし、クラスでカプセル化したりするわけではないので、構造体にメソッドを定義する、その定義のしかたがちょっと理解しづらかったのでそのメモ。

理解しづらかったポイント

goを使って簡易的なHTTPサーバーを立てたときに、http.Handle にServeHTTPのメソッドを持つ構造体を作るって箇所で詰まって色々調べた備忘録。

package main

import (
    "fmt"
    "net/http"
)

// AppHandler アプリ
type AppHandler struct {
    appName string
}

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(h.appName)
}

func main() {
    http.Handle("/", &AppHandler{appName: "myApp"})
    if err := http.ListenAndServe(":8080", nil); err != nil {
        fmt.Println("エラー発生")
    }

    fmt.Println("サーバースタート")
}

こういうコードを想定。特に最初???だったのはここ

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(h.appName)
}

これは

type AppHandler struct {
  appName string
}

の構造体に ServeHTTP メソッドを定義している。

https://golang.org/pkg/net/http/#Handler を確認すると、

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Handlerはinterface型なので、定義先はどんな型でもいいのだけれど、ServeHTTPというメソッドを持っていなくてはならない。
つまりAppHandlerの構造体であってもServeHTTPを定義することができる

func (構造体) (登録するfunc) {}

という形で定義が可能。

これでAppHandlerの構造体はServeHTTPというメソッドを持つことができ、http.Handleに登録して、指定したPortでHTTPリクエストを受け付けることができるようになる。

構造体以外にメソッドが定義できる。

HTTPサーバーを立てる際に以下のような定義の仕方もある。

func main() {
  http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        res, err := w.Write([]byte(`Test`))
        if err != nil {
            log.Println("Error")
        }
        log.Printf("%d", res)
  }))
}

HTTPサーバーを立てる上で行っている動作は直観的でわかりやすいのであるが、HanderFuncメソッドは一体どこからきていた何なのかがわからず。これもドキュメントを確認すると下記のようになっている。

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)

HandlerFuncfunc(ResponseWriter, *Request) の関数型として定義されていて、HanderFunc 型に ServeHTTP が定義されている。これは構造体に ServeHTTP を定義していたのと似ていると思う。特定のエイリアスを関数型として定義して、その関数型に関数を持たせるという個人的には摩訶不思議な言語仕様のなせる技なんだなーと。

goは言語仕様がシンプルで柔軟というはこういうところから来ているような気がしました。

fishに乗り換えた話

zshの管理がめんどくさくなってきたのでもう少しイケてるshell環境をつくれないものかと調べてたら、fish なるものがあるらしく、使い勝手がよさげなので、期間限定で乗り換えてたときの備忘録

パッケージ管理

  1. oh-my-fish
  2. fisherman

1と2もどちらもパッケージ管理ツールに変わりはないので、どちらでも良さげ。
軽く調べたらfishermanの方がイケてるらしい。
最初に1を入れてその後2を入れたものの、 agnoster というテーマを使いたかったのに fisherman では表示がおかしかったので、oh-my-fish を使用。

shellの設定

fishでの標準的なシェルの設定は、 ~/.config/fish/config.fish を使用する。
しかし、oh_my_fish を使うと ~/.config/fish/conf.d/omf.fish になる。
ただし、ここに書きすぎると後々やっぱり fisherman 使いたいなーとか思った時に足かせになるので、自前で ~/.config/fish/config.fish を作って、そちらにちまちま設定を書いていった方が無難(のような気がする)

簡単に設定

pecoとghqを入れておく。

$ brew install peco
$ brew insatll ghq

冗長にbrewで入れようと思ったけど、oh-my-fishを入れると自動的にpecoもghqも入るっぽい。

というわけでoh-my-fishを入れる。

$ curl -L https://get.oh-my.fish | fish

これで omf コマンドが使えるようになる。
fishではoh-my-fishにしろ、fishermanにしろ、パッケージ管理ツールを使ってテーマ等々shellのカスタマイズを行うツールをインストールする。

例えば、agnoster というテーマを入れようと思った場合、

$ omf install agnoster

と入力すると、テーマがインストールされて自動的に反映される。インストールしているテーマは ~/.config/omf/theme のファイルに記録されている。 設定ファイル(config.fish)上で set theme (theme名) と書いておくことで使いたいテーマを決める。※ 1つしか入っていないと自動的に決まるみたい。

※ 注 : agnosterというテーマはPowerlineというフォントを指定しないと正確にテーマが反映されないので注意

とりあえず今回はここまで。
fishはパッケージ管理の容量で簡単にshellがカスタマイズできるので使い勝手が良さげ。専用のコマンドがあるのもありがたい。
設定ファイルはもう少し使い込んで、もろもろ設定してからまたエントリーにする予定。

go で簡易HTTPサーバー立てる

http.HandlerFuncを使う場合

package main

import (
  "fmt"
  "net/http"
)

func main () {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
    w.Writer([]bytes(`表示内容`))
  }
}

if err := http.ListenAndServe(":8080", nil); err ! = nil{
  fmt.Printf("エラー表示")
}

http.Handleを使う

package main

import (
  "fmt"
  "net/http"
)

type AppHandler struct{
  appName string
}

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  fmt.Printf(h.appName)
}

func main () {
  http.Handler("/", &AppHandler{appName: "myApp"})
  if err := http.ListenAndSerce(":8080", nil); err ! = nil {
    fmt.Printf("エラー発生")
  }
}

Linterのエラーが発生する

exported type AppHandler should have comment or be unexported (golint)  というエラーが発生。これはキャメルケースの構造体(外部からアクセス可能)なのにコメントが無いよっていうエラー

// AppHandler
type AppHandler struct{
  appName string
}

とコメントをつける

comment on exported type AppHandler should be of the form "AppHandler ..." (with optional leading article) (golint) というLinterのエラーがまだ発生。これはコメントの形式の警告。 構造体名 (半角スペース) 説明 みたいな書き方が必要になる。

// AppHandler アプリケーションに関わる〜
type AppHandler struct{
  appName string
}

というコメントに説明箇所を半角スペースで追加する。

go言語を学び始めてみた

go言語を学び始めました。
最近専らクライアントサイドの開発がメインだったので、サーバーサイドやりたくなったので、徐々に浸透してきたgoについてキャッチアップはじめました。

環境構築

環境

エディター

goのインストール

とりあえず自前のPCで動かす環境を簡単に構築します。

$ brew install go
$ go version
go version go1.8.1 darwin/amd64

atomの設定

goを書くにあたってエディターもろもろ調べましたが、どうやらatomでのgoの開発環境構築がイケている感じを受けたので、atomでgoの開発環境を構築します。

Package

go-plusをどうさせるためにgoに依存パッケージをインストールする必要があります。
※ go-plusのドキュメントに記載してあります。

goではrubyのgemのように簡単にgoのパッケージを管理するインターフェースが用意されています。

$ go get ~

コマンドでgoのパッケージをローカルの環境にインストールできます。
普段は、ドキュメントに書いている順序でコマンド打ち込んでいくのですが、めんどくさいのでshellスクリプトを書いて一括でインストールさせます。

#!/bin/sh

#go-delveをbrewで入れる
$ brew install go-delve/delve/delve

$ go get -u golang.org/x/tools/cmd/goimports
$ go get -u golang.org/x/tools/cmd/gorename
$ go get -u github.com/golang/lint/golint
$ go get -u github.com/sqs/goreturns
$ go get -u github.com/nsf/gocode
$ go get -u github.com/alecthomas/gometalinter
$ go get -u github.com/zmb3/gogetdoc
$ go get -u github.com/rogpeppe/godef
$ go get -v github.com/golang/lint/golint

完了したら、atomで上記go-defgo-plus の2つのパーケージを入れます。

GOPATHを設定する

packageをインストール後、atomを再起動すると、 liter-golint のパッケージのところでエラーが発生します。
これはGOPATHを設定していなかったことが問題で、GOPATHを設定し、packageの設定画面のGOPATHに設定したGOPATHのパスを入力します。 GOPATHを設定しないと、goでインストールしたライブラリが正常に動作しません。

GOPATHは適当に決めていいらしいので、今回は ~/.go にGOPATHを設定しました。
設定方法はPATHを通すだけなので、bashrc or zshrc にGOPATHのパスを追加します。

$ emacs .zshrc

# GOPATH を追加
export GOPATH=$HOME/.go

# シェルを再起動
$ source .zshrc

atomの設定画面から「パッケージ」を選択し、go-plusの設定画面のGOPATH項目、 ~/.go を入力します。 これでlinter-golintのエラーは解消され、atomでgoを書く上で最低限の設定は完了しました。

GOPATH設定のところで少しハマりましがが、Goの言語仕様を説明した入門本もさくっと読んだので、コツコツ開発始めていこうと思います。

読書 Note -『なぜ、あなたの仕事は終わらないのか スピードが最強の武器である』

『なぜ、あなたの仕事は終わらないのか スピードが最強の武器である』を読んでみての感想。
新卒時代に、当時の上司から口酸っぱく言われていたことが全て書いてあり、当時の上司はこの著者の言っていることを指して指導してしくれていたのだとここに来て気づきました。

書籍そのものは、厚みの割に文字が大きくさっと読み流せるボリューム感です。
著者が伝えたいことは一冊を通して同じで「スピードが最強の武器である」ってことだけです。
それを色んなエピソードや仕事の仕方を交えて解説しています。

多分読む人によっては拍子抜けするかもしれません。
とは言え、同じエンジニアとして、仕事をする上で改めて大事だと思うことも書いてあったのでまとめてみました。

書かれていた内容

  1. 納期を守ること
  2. 時間を守ること

の2つです。これは著者も色んな言葉で書いていましたが、僕が感じたのは、一冊を通して、ほぼこの内容しか言っていないということです。
多分この2つさえ、ちゃんとできるようになれば、意味不明な飲み会のルールなんか覚えなくてもいいと思います。

この本に書いてあるのは、大事なことは上の2つで、それを常に実践するためにはどうするかのスタンスが書かれているのみです。
中でもしきりに書かれているのは、 仕事全体を2:8に分け、最初の2割の日程で仕事全体の8割を終わらせる ってこと。

このセンテンスは頻出で、それだけ大事なことで、著者である中島さんのキャリアは全て上記のスタンスに集約されているのでしょう。

もちろん、今の立場にあるからこそできる内容(ex 昼寝の仕方 etc...)もあって全てが全て実施できるわけではありませんが、もし、仕事の仕方として自分で納期を切れる、もしくは納期を明確に伝えられている場合は、参考になる内容がかなりあると思います。

とりあえずやってみて、肌感覚掴んでから一旦報告して、納期までに終わりそうか確認する。
とはいえ、これって案外難しくて、やはり任された仕事である以上、1人でやり遂げたいし、周りに相談したり、ましては納期を変更するなんて、自分の無能をさらけ出しているみたいでかっこ悪くてしづらいと思います。
自身にも似たような経験がありますし、それで迷惑かけたこともあります。
でも、結局仕事は自分1人でしているわけではないので、そこはちゃんと伝えるべきなんですよね...(自戒も込めて)

報告が遅くて困ることはありますが、報告が速くて困ることはまずありません。

書籍の内容は平易で、エピソードもなかなかわかりづらいところもありますが、これは社会人歴浅い自分みたいな年代の人にはぜひ知ってほしい内容です。
仕事の仕方は組織や社会のルールに染まる前の方が矯正し易いです。

個人的に印象に残ったこと

約束の納期を常に守り続けていれば、自分より高い能力の人間よりも上に行ける。 と触れられている内容の箇所です。
約束を守ること = 信頼 を勝ち取ることであって、スキル上優秀だから信頼できるわけでなく、セルフマネジメントまでできて、常に予定通りに成果を出してくる、または早いタイミングで予定の変更を依頼してくる方の方が、信頼感がある、なんて当たり前のことのようですが、それが実は仕事の全てではないかと。
仕事は結局、人と人の関わりの中で行われることである以上、スキルセットは優秀でも、人として信頼できるかどうかが最終的な意思決定にもっとも効いてきます。
そして、信頼されて任された仕事 = キャリアなので、信頼される人の方がキャリアアップにつながっていく。

もちろん、有無を言わせない天才的なスキルセットを持っている人間はいるので、そういう人間には勝負しても勝てないかもしれませんが、とは言え、そういう一握りの人間と勝負するよりも、凡人同士の中で勝負する中で、いかに相手より信頼されるか、どう差別化して、少しでも上に行くのか、ってことの方が自分にとっては身近な内容で、再現性が高いです。

どう活かしていくか

とは言え、最初の2割の日程の中で普段の10~20倍の生産性で業務にとりかかるなんて凡人には無理ですよね。
著者の中島さんも凡人みたいな事欠かれますが、僕から見たら十分天才の部類です。

なので、実践で活かすとするならば、プロトタイプを作るってところから始めようかなと。
そして、日程の算出の仕方とスケジュールの組み方。
現在、あまり納期が明示されることはありませんが、その場合、仮納期を設定して、どれくらい終わるのかってところ、仮納期を設定して一気に作業することで、自分が躓いているポイント、時間がかかっているポイントを洗い出して、予め潰す、もしくは、部分的に変わってもらう、アドバイスをもらいやすくするなど、色々方法は考えられると思います。

あくまでゴールは時間、納期を守ることで、少しずつ回していくのはその訓練にすぎません。少しずつやっていけばそのうち慣れて行くと思います。

まとめ

書籍はHowToを謳っていますが、書いてあるのは、即実践で使える具体的なHowToではなく、どちらかというと、スタンスや意識、考え方の側面が強いです。

納期と時間は守るもの、そういう風に最初に決めてしまえばいいのだと思います。
そうすれば、「どうすれば納期を守れるのか▷とりあえずやってみて報告しよう」といった具合に仕事のやり方に行き着きます。

こんな時期なので、フレッシャーズの皆さんには一読してほしい本かと思います。

MacOSをアップデートしたらbrew でインストールしたツールが`command not found`になったときの対処

トラブル概要

MacOSX Sierraを2017/04/06時点の最新版にアップデートしたら

$ mysql
mysql not found

になってしまった。

対応した手順

brew list でインストールされているか確認

$ brew list | grep mysql
mysql56
mysql@5.6

入っている…
mysqlの最新版は5.7ですが、現状の業務では5.6を使用していたので、5.6を動作させなければなりません。

※ ちなみにbrew でインストールできるソフトウェアのバージョンは以下で調査できる

$ brew info mysql # インストールできる最新版を表示してくれる。
mysql: stable 5.7.17 (bottled)
Open source relational database management system
https://dev.mysql.com/doc/refman/5.7/en/
Conflicts with: mariadb, mariadb-connector-c, mysql-cluster, mysql-connector-c, percona-server
Not installed

brew doctor で Homebrew の状態を確認

$ brew doctor
Your System is ready to brew

問題ない…

brew uninstall ▷ install で入れ直そう

$ brew uninstall mysql56
Uninstalling /usr/local/Cellar/mysql@5.6/5.6.35... (345 files, 154.7MB)
mysql@5.6 5.6.32, 5.6.34 are still installed.
Remove all versions with `brew uninstall --force mysql@5.6`.
$ brew install mysql56
brew install mysql56
Updating Homebrew...

#中略

Warning: The post-install step did not complete successfully
You can try again using `brew postinstall mysql@5.6`
==> Caveats
A "/etc/my.cnf" from another install may interfere with a Homebrew-built
server starting up correctly.

To connect:
    mysql -uroot

This formula is keg-only, which means it was not symlinked into /usr/local.

This is an alternate version of another formula.

If you need to have this software first in your PATH run:
  echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc

For compilers to find this software you may need to set:
    LDFLAGS:  -L/usr/local/opt/mysql@5.6/lib
    CPPFLAGS: -I/usr/local/opt/mysql@5.6/include


To have launchd start mysql@5.6 now and restart at login:
  brew services start mysql@5.6
Or, if you don't want/need a background service you can just run:
  /usr/local/opt/mysql@5.6/bin/mysql.server start
==> Summary
🍺  /usr/local/Cellar/mysql@5.6/5.6.35: 345 files, 154.7MB
➜  ~ which mysql
mysql not found

ここで、

To have launchd start mysql@5.6 now and restart at login:
  brew services start mysql@5.6
Or, if you don't want/need a background service you can just run:
  /usr/local/opt/mysql@5.6/bin/mysql.server start

パスが /usr/local/opt/mysql@5.6/bin/ 以下であることに気づく。 Homebrewで入れた場合は自動的に /usr/local/bin 配下に設置されるものだと理解していたけど、ちょっと違う???

とりあえずパスの在り処がわかったので /usr/local/bin 配下にコマンドをコピーする

$ sudo cp /usr/local/opt/mysql@5.6/bin/mysql /usr/local/bin
$ which mysql
/usr/local/bin/mysql

と、ここまで来てコマンドの場所がわかったのなら、PATH通せば終わりじゃね?ってことに気づき、 .zshrcexport PATH=/usr/local/opt/mysql@5.6/bin:$PATH を通し

$ which mysql
/usr/local/opt/mysql@5.6/bin/mysql

と表示されたので完了。

WebViewを使ったHTMLの描画

アプリのTableViewから詳細画面に遷移するインターフェースを考えます。
詳細画面に遷移する時に、HTMLをViewに描画するとき、

  1. URLをLoadして直にwebViewを読み込む
  2. HTMLの文字列をHTMLの段組みに合わせてWebViewで表示する。

参考 How to load a HTML string into a WKWebView or UIWebView: loadHTMLString()

URLをLoadして直にwebViewを読み込む

let webView = UIWebView()
let urlRequest: URLRequest = URLRequest(URL: URL(string: "https://sample.com/XXX")!) // 表示したいページのURL(URLRequest型)
webView.loadRequest(urlRequest)

URLを指定して、UIWebViewで表示させるだけなので、簡単ではあるものの、ページの描画に時間がかかってしまう。 そこで、APIのレスポンスでHTMLの文字列が返ってきたような場合、レスポンスで返ってきたHTMLの文字列をHTMLの段組みでWebViewに表示してみます。

HTMLの文字列をHTMLの段組みに合わせてWebViewで表示する。

QiitaのAPIを例にとって試してみます。
qiitaのAPIのレスポンスは

$ curl https://qiita.com/api/v2/items/97819a1910859a6f2ef9 | jq
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 14941    0 14941    0     0  29195      0 --:--:-- --:--:-- --:--:-- 29181
{
  "rendered_body": "HTMLの文字列",
  "coediting": false
  # 略
  
}

という形で rendered_body のプロパティにHTMLの文字列が返ってきます。
この rendered_body プロパティのHTMLの文字列を取り出してUIWebViewの loadHTMLString メソッドに渡します。

var webView: UIWebView!
let html: String = "rendered_bodyの中身"
webView.loadHTMLString(html, baseURL: nil)

これでHTMLの文字列をWebViewに描画することができます。
以下のような感じになります。

f:id:ema_hiro:20170403161835g:plain

【2017年度版】今年度はどんなこと学んでいこうかなってこと

今日から新年度ですね。
渋谷ではおそらく新卒入社と呼ばれるみなさんが飲み会をしてました。
賑やかでこの時期ならではの華やかな空気感が色んなところに感じられるようになりました。

自身も新卒で社会に出てから4年目になってしまい、世間的ににはまだまだ若造の部類ですが、業界的にはもうおっさんの域に差し掛かってきたので、目下焦りしかありません。
こんな時期なので今年度はどういったことに意識して技術を学習していこうかなとということを備忘録として記録しておきます。

目次

  1. 現状の自分のスキルセットと傾向
  2. 今年度学ぼうと思っていること

現状の自分のスキルセットと傾向

Web系の言語を中心に1からサービスを立ち上げた経験はあり。
サービスをグロースさせた経験 はなし

新卒で入社した会社でかなり幅広く経験させてもらいましたが、一つの言語に特化して深く理解したことはなし。
言語仕様を完璧に理解する前に、サービスを作りながら徐々に覚えていくタイプ。

今年度学ぼうと思っていること

年明けからiOSの開発を続けているので、今年はクライアントサイドの開発の知見を深めようと今のところは考えています。

クライアントサイドは、端末の規格が変わったり、プラットフォームの影響をもろに受けるので、実は避けていた分野でした。
ただ、年明けからクライアントサイドの開発に携わる中で、ユーザーに直接触れる領域の良し悪しって露骨にサービスの成長に直結しているなという当たり前のことに気づくようになってきました。
世の中に浸透しているサービスには何か使っている心地よさや、使わないと行けない、使うことが当たり前だよね!といえる感覚があり、また、そういったサービスはアプリだけで完結せず、アプリを中心としたコミュニティというか世界観まで作り上げているものが多く、実装に囚われない、 ソフトウェアだけに収まらないものづくり をしているなという感覚があります。

ユーザーに価値を届けるってソフトウェアだけに留まらない体験を届けることなのではないかと漠然と考えたいことが、ストンと腹落ちしました。

少し話はそれますが、個人的に考えていたことが2つあります。

  1. バズっているIoTや人工知能といった分野も結局それらは考え方やツールでしかなく、ユーザーにはアプリというインターフェースを経ないと価値を届けられないこと。
  2. サーバーサイドは技術の選択肢は増加してくる一方、インフラ周りの仕組みは自動化されたり、ブラックボックス化されていて、知見を持っていることの技術的な優位性は徐々に薄れてきているようにも感じること。

この2点から、流行りに乗っかったり、今のスキルセットのままいくには漠然とリスクを感じていました。

もともとあるスキルに加えて、ユーザー体験を考えることは、ユーザーの理解、技術の理解、領域の理解、そしてその先にある世界観の理解まで含めて作っていく必要があると思います。

おそらくエンジニアという職業において今後数十年のうちに実際にプログラムを書くことは、人間がやる範囲のことではなくなっていくと思います。
実際、Googleの作っている人口知能はプログラムを書けるようになっているらしいですし、実際にコードを書くということはもはや人間がやらなくてもいいことになりつつあるのかなと。(コード書くのは楽しいですが。)

そんなときに、エンジニアとして何ができるのか、機械に代替できないことはなんなのか、ってことを考えた時、それは体験を作り出し、提供することではないかと一つの仮説を持ちました。
UXを提供する領域、ユーザーに直に触れる領域の知見は今だからこそ身につけるに最適なタイミングなのではないかと。
自分のスキルセットと照らし合わせても、キャリアの幅を増やせるいい機会になると思っています。

今年こそ、何か作りきった!と言えるものを残せるように。

UIRefreshControlの実装でハマったところ

目次

  1. unrecognized selector sent to instance のエラーが出た時
  2. Cannot override with a stored property refreshControl

unrecognized selector sent to instance のエラーが出た時

RefreshControlを使ってSwipe & Refreshを実装しようとした際に、 unrecognized selector sent to instance というエラーが出ました。

selectorに指定したメソッドがないよ

っていうエラーでコードを確認したら、selectorに指定したメソッドがprivate指定してしまっていました。

Selector で指定できるのは public なメソッドのみ。

またこのエラーはコンパイルチェックではエラーが出なかったので、実機で動作させるまで気づきませんでした。

Cannot override with a stored property refresh control

UIRefreshControl は 親クラスである UITableViewController クラスのメンバ変数ですでに宣言されているため、サブクラスでの宣言もオーバーライドもできない。

親クラスに宣言されているので self.refreshControl で当該ViewContollerでのrefreshControlの実装を編集できる

実装としては以下

override func viewDidLoad() {
    super.viewDidLoad()
    
    // UIRefreshControl
    self.refreshControl = UIRefreshControl()
    refreshControl?.addTarget(self, action: #selector("publicなメソッド"), for: .valueChanged)
    self.tableView.addSubview(self.refreshControl!) // viewにUIRefreshControlを追加
}

WebフレームワークにおいてDDDを適用しようとした時の違和感

概要

エリックエバンズの「ドメイン駆動設計」を読む中で、アプリケーション開発に適用する時に感じてきた違和感をまとめてみました。
まだ前半しか読み終えてないので、後半を読んだ際にはまた別のエントリーでまとめようと思います。

レイヤー化アーキテクチャとWebフレームワーク

レイヤー化アーキテクチャ

  • プレゼンテーションレイヤー
  • アプリケーションレイヤー
  • ドメインレイヤー
  • インフラレイヤー

大まかにまとめるとアプリケーションは上記の4つのレイヤーに分けられる。
これを一般的なMVC構造を持ったWebフレームワークで考えると

  • プレゼンテーションレイヤー (View)
  • アプリケーションレイヤー (Controller)
  • ドメインレイヤー (Model)
  • インフストラクチャ (O/Rマッパー/Queue/ログ etc..)

のように大まかに分けることができると思う。

DDDに則れば、

となり、レイヤーに分けることで、責務を分けて凝集度の高い、柔軟なアプリケーションを開発できる。

Webアプリ開発に置いてレイヤー化が曖昧になってしまうのはなぜか

DDDの古典的名著である本書を読むにあたって、事例等が古く、理解が浅い箇所があったため、精読する中で、一般的に使われているWebFWを念頭に置きながら読み進めていった時にFatControllerやドメインモデル貧血症が発症するのかを考えてました。
理想を言えば全てのエンジニアが本著を読んだ上でアプリケーション開発を進めることができれば、そういった問題も少なくなっていくのではないかとさえ考えていたのですが、開発をしていく中で技術的な負債として溜まっていく課題が生まれる原因を少し考察してみました。

  • 開発の質とビジネススピードとのトレードオフなって、開発を優先させて、少なからずSmartUI的な何かを許容しながら開発を進めていた。
  • webフレームワークを一番最初に覚えてしまってドメインを考えずに開発を進めていってしまう。

上記2点が自分が考えついたことで、特に後者については、僕自身開発手法や設計手法を知らないまま、エンジニアとしてのキャリアを始めており、WebFW(Laravel/Rails)からアプリケーション開発に入ってしまっているので、ORマッパーが便利過ぎて、Controllerにビジネスロジックをべた書きしたりとかアンチパターンをひたすら繰り返していました。

なぜ厳格にレイヤ分けされていない状態になってしまったのか考えると、Webフレームワークがすでにレイヤー分けされていると考えていて、DDDに基づいたドメインの隔離やレイヤー化を考えなくなってしまっているのではないかと思います。

余談ですが、PHPを使って開発していた頃は、Controllerにビジネスロジックを書くことはアンチパターンだと念頭に置きながら、拙いながらも、責務を分けようとしていたのですが、Railsを使い始めた時にControllerにバリバリActiveRecordを使ったビジネスロジックが書いてあるのを見たときには軽く衝撃を受けたのを覚えています。ActiveRecordは便利で簡潔にデータベースとのやり取りを書けるので、コードとして局所的には見通しが効くものの、DDDの最初の方で提唱しているレイヤー化、ドメインの隔離には反します。
ORマッパーの機能性が高い現在のWebフレームワークでは、こういったControllerにビジネスロジックを書いても、コードの見た目上ある程度見通しが効いてしまうこともレイヤー分けが曖昧になってしまう原因のように感じます。

DDDを読んでいて感じたWebフレームワークにDDDを適用しようとした違和感

書籍に戻ると、この書籍が書かれた時期にはまだWebフレームワークそこまでが一般的ではなかったのではないかと思いました。
WebアプリケーションというよりはATMみたいなGUI操作をともなうGUIアプリケーションの開発が念頭におかれているのだと思います。
読み進めていくうちに少なくとも以下の点については書籍を読みながら理解することができました。

  • Webフレームワークを使ったアプリケーションとGUIアプリケーションではレイヤー化アーキテクチャの厳格度合いに差があること
  • GUIの方がレイヤー化アーキテクチャには厳格
  • DDDでもFWに依存せずに、DDD適用時に必要になるレイヤーはスクラッチで実装すべきだと提唱している。

そして、自分の感じていた違和感の正体はWebフレームワークにおけるMVCをレイヤー化されたアーキテクチャとして理解してしまっていたことだと気づきました。
DDDの中でサンプルとしてあげられているGUIアプリケーションを開発する際に説明されているレイヤー化アーキテクチャが本来的なMVCで責務が分けれている状態で、WebフレームワークにおけるMVCはそれ自体では厳格にレイヤー化されていないんですね。
インフラレイヤーはまとまっているものの、ドメインモデルの作成は開発者の設計に依存するところが大きい。
Webフレームワークだけしか知らないとドメインの隔離とレイヤー化アーキテクチャという大元を見逃してしまう可能性があるということでした。

参考

チーム開発する上での前提に関する備忘録

※ 自戒をこめて

チーム開発する上で認識甘かったことがあって、最近反省することが多かったので、備忘録として残しておきます。

開発する上での前提

「開発の前提知識が整わないと生産性高く開発ってできないよね」っていうお話です。

今回反省したのは、技術レベルや実装レベル云々の話ではなく、開発する上で、同じプロトコルで話せないと生産性が上がらないということに気付かされたことです。

問題だったこと

  1. githubを使った開発フローのベストプラクティスを理解していなかったこと
  2. 言語仕様や開発環境(IDE)を息を吸うが如く使えない

issueを交えたgithubのプラクティス知らなかった

  • オレオレコミットログを書かない
  • rebaseやらsquashを使って開発ログをきれいする
  • refsやらfixesやらを使いながらissueを交えて作業ログを綺麗にする

という基礎的なところから勉強し直し。
慣れればどうってことないんですが、、、

言語仕様やら開発環境を息を吸うように使えない

こればっかりは必至に覚えました。
iOS開発のキャッチアップをしているときも、まともに経験のある言語がphprubyだったので、swiftみたいな書き方(scalaなんかに似てる)、言語仕様が慣れずにとりあえずプロダクションコード触ろうと思ったんですが、そもそもの言語仕様を理解していなかったり、開発に際して、xcodeの使い方を調べたりということを繰り返して中々生産性が上がってこなかったので、とりあえず地に足つけてひたすらどういう言語なのかを理解することに重きを置きました。

設計やら、実装やらを考えるにあたって、そもそもその前に前提条件が揃ってないと、全く生産性が上がってこない、もしくは開発速度が遅いというのが明確に見えてしまい、そういったところをサボってきた(言語仕様等々は作りながら覚えていく派だった)ツケが回ってきたような気がします。

レビューで言語仕様レベルで指摘されてたらリファクタリングとかしてても終わらないレビューとかになりそうなので、前提知識覚えるのってすごく大事だと実感。

開発の生産性とは?

これって、お作法も含めた常識を知っている(同じプロトコルで話ができる)同士のエンジニアが集まると、そもそも話している会話のレイヤーが同じだから開発速度が速いっていうことに直結すると感じました。
言語を知らない、前提が同じでないと、そもそも、何を話されているのかという翻訳が必要になってしまって、そこで脳みそのエネルギーが使われてしまい、そもそも、開発に効率よくエネルギーをかけることができない。

だから、そもそもチームとして生産性高いっていうのは、上記あげたような超初歩的なお作法、バックボーンが同じレベルの人間が集まることで、余計なコミュニケーションが発生することなく、結果として生産性高いっていう状態が生まれるのだなと。

そこに全く行き着いていなかったことを反省しつつ、サボらずに1からちゃんと言語仕様、開発のお作法を直していく日々…

Computed Property で for を使った構文を map を使った構文へ書き換える

computed property内でforを使った実装をしていた部分をmapを使ったswiftらしい構文へ書き換えました。

変更対象コード

var isSuccess: Bool {       
    for q in questions {
        if !q.isSuccess {
            return false
        }
    }
    return true
}

変換第一段階は以下

var isSuccess: Bool {   
    questions.map { q in
        if !q.isSuccess {
            return false
        }
        return true
    }
}

このままだと Ambiguous reference to member mapxcodeに怒られる

var isSuccess: Bool {
    questions.map { q -> Bool in
        if !q.isSuccess {
            return false
        }
        return true
    }
}

参考: http://stackoverflow.com/questions/34368958/ambiguous-reference-to-member-map-when-attempting-to-append-replace-array-elem

mapはクロージャーの中で返り値の方を指定する必要がある。 しかしこれでも返り値はBoolなので、返り値の型が違うのでおこらえる。 mapでの返り値はCollection型で返ってくるので、返り値はBool指定しないと行けない。

そこでやりたかった意図は questionsの回答状況が全てSuccessであればisSuccessがtrue ということなので、mapで返されたCollectionの中身が全てtrueである、つまりfalseを含まなければいいというロジックになる。

var isSuccess: Bool {
    let status = questions.map { q -> Bool in
        if !q.isSuccess {
            return false
        }
        return true
    }
    return !status.contains(false)
}

プロトコルでのオプショナルなインターフェースを作る。

Protocolの定義

宣言したインターフェースは継承先のクラス、もしくはプロトコル、構造体で、宣言必須。
とはいえ、必要ないインターフェースは継承先で記述省きたいとも思う。
そこでswiftで使えるオプショナルなインターフェースについて調べてみた。

オプショナルなインターフェース

参考 swiftの公式ドキュメント
https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID267

You can define optional requirements for protocols, These requirements do not have to be implemented by types that conform to the protocol. Optional requirements are prefixed by the optional modifier as part of the protocol’s definition. Optional requirements are available so that you can write code that interoperates with Objective-C. Both the protocol and the optional requirement must be marked with the @objc attribute. Note that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objc classes. They can’t be adopted by structures or enumerations.

オプショナルなインターフェースの定義方法

protocol SomeProtocol{

  @objc optional func someMethod

}

@objc修飾子とoptional修飾子が必須 特に@objc修飾子がないとオプショナルなインターフェースは定義できない。

使い所

オプショナルなインターフェースにすれば、継承先のオブジェクトでインターフェースの宣言は必須でなくなる。
クラス設計に寄っては、継承先のオブジェクトで不必要なインターフェースも存在するので、設計を厳格にコードに落とし込む上でも有用。

ただし、このこちらのブログにあるように、optionalにすることで厳格性が失われたり、@objc 修飾子をつけることで、swiftの良さがなくなることもあるので、導入には議論が必要になりそう。

既存のコードの書き換え時には使えるなと感じつつも

  1. オプショナルを使わずに厳格にインターフェース定義できるプロトコルを設計段階で実装できることの方が大事
  2. メソッドに対して ?! といった明示的なunwrapを使うのがコードとしては美しくないと感じる

とも感じるので、設計の初手から使うことは考えずにいたい。

swift学習ログ「タプル」

swiftのタプルについて

公式リファレンス呼んだ備忘録
php, rubyと経験してきて、swiftを学習し始めてから「tuple」という概念を知ったのでまとめます。

タプルとは?

タプル(tuple)とは、複数のものからなっている組の構成要素数を表している集合数詞である。2つ組、3つ組などのそれぞれを表す英単語が一般化された言葉から派生し、数学や計算機科学などの分野では、通常順序つけられた対象の並びを表すために用いられる (出典: 日立ソリューションズ IT用語辞典)

タプルとは集合数詞ということで、なんかよくわからないものと捉えてましたが、Collectionというより変数に近い扱いをするものだと理解して続けます。

タプルの定義と使い方

参考 : Swift Gideline

// 定義の仕方
let httpStatus404 = (404, "Not Found")

// タプルごと定数定義もでき、アクセスもできる
let (statusCode, statusMessage) = httpStatus404
print(statusCode) // => 404
print(statusMessage) // => Not Found

// ワイルドカードもつかえる
let (statusCode, _) = httpStatus404
print(statusCode) // => 404

// tapleで宣言された変数へのアクセス方法
let firstElement = httpStatus404.0 // 404
let SecondElement = httpStatus404.1 // Not Found

// 要素に名前をつけることもできる
let httpStatus200 = (statusCode: 200, statusMessage: 'OK')
let statusCode = httpStatus200.statusCode // 200
let statusMessage = httpStatus200.statusMessage // OK

使い所

イマイチ最初tupleの使い所がわからなかったのですが、座標とかまさにこの形だなと思ったり、要素でまとめられる場合、配列やディクショナリといったCollectionを使わずに、要素を扱いたい時に使えるかなと言った印象。 積極的に現場で使っていきたい。

読書 Note -「人工知能は人間を超えるか」~ディープラーニングの先にあるもの~

背景

今年は間違いなくAIだったり深層学習だったりがバズワードになり、色んな製品にそういった技術が使われてくる世の中になるだろうと思います。
そんな中、結局「AI」とか「人工知能」とか「深層学習」ってそもそも何?っていうことの背景だったり歴史だったりを知ろうと思って読みました。

書評

  • 深層学習
  • AI

といったバズワードの実態を知るには最適の書籍でした。
エンジニアとして、そもそもそういう技術が何なのか?ということを全く理解していなかったことがわかり、これから自分がどういった姿勢でこれらの技術に対峙していくべきなのかを学ぶことができたと思います。

そもそもAIって?

結局、僕が知りたかった、というか疑問に思っていた箇所はこの一点でした。
何でもかんでも「AI」とか「人工知能」って言う言葉が軽々しく使われていて、一体何が人工知能なのかを理解していなかったのですが、

  • 現時点においても、AI(artificial intelligence)といえるものは存在していないこと。
  • 深層学習とAIは厳密は異なること
  • 特徴を理解する / 差異を区別すること を機械が自律的にできるようになることが今で言うところの人工知能的な何かであること

ということを知ることができただけでも、最近の動向なり、流行りを大分整理できました。

AIにどういう姿勢で望んでいけばいいか

僕の立場としては「ふーん」程度にまずは時代の流れをしっかり観察していこうと思います。
理由ですが、そもそも深層学習や人工知能と言っても、そのベースとなる特徴を理解させるには、大量かつ自分たちの意図するデータが返ってくるような特徴的なデータが必要で、これを学習させるのには大変なコストが掛かるので、おいそれと手を出せないと考えています。
そして

  • 意図するデータを返すための特徴的なデータ

は自分たちで用意する必要があり、学習させる際もパラメータをチューニングしていく必要があって、それは現状、初期段階では人力が必要になりそう。

そのため、一口に人工知能とか言っても、じゃあそもそも何に使って、そのためにどういうデータが必要なのかということがわからないと「人工知能なんで使うんだっけ?」っていうことになりかねないと考えています。

では、一般的なデータについてはおそらくですが、Googleだったり、Facebookだったり、Amazonだったり、すでに大量のデータを持っている企業にはそもそも量的な意味で勝負できないので、となると彼らが開発したAIを使うことのできるAPIを使って何ができるのか、それはビジネスに対してどういうインパクトがあるのか、そういことを考えることができる方が大事。

そういったことを考える機会になった本でした。

仕組みを理解したり、実際に作れることも大事ですが、こういう非連続的にポッと出てくる技術って世界を変える可能性がある反面、それに対して自分はどういう態度で望むべきなのか、ということを考えさせられます。
背景も含めて正確に理解することで、今後何が来て、どうなっていくのか、その時自分はどういうレベルにいればいいのか。
文系出身でニューラルネットワークについて全く理解していない自分でも、なんとなく概要を理解できたので、機械学習やAIについて漠然とした不安を持っているのであれば一度通読してみるといいかと思います。