Androidアプリ設計パターン入門を読んだ

この本を読みました.Androidアーキテクチャ周りの知識があまりなくても読みやすく,凄くためになる本でした.

peaks.cc

構成

本の構成は以下の三部構成になっていました.

  1. アプリの設計を知る
  2. 生きた設計を見る
  3. 設計を考える

以下で少し学んだ内容をまとめていきたいと思います.(もしかしたら間違った解釈をしている可能性があります)

1. アプリの設計を知る

MVVMパターン

MVVMパターンの概略図を図1に示します.

流れをざっくり文字化すると,

  • Viewで何かしらactionがあったら,ViewModelを呼ぶ
  • ViewModelはactionに対応した処理をModelに対して行う
  • Modelは処理を実行した後,ViewModelに結果を返すか通知を返す
  • ViewModelはその結果を受け取り,自身の状態を変更する
  • Viewの状態はViewModelとbindされており,ViewModelが変更されるとViewも変更される


f:id:MitubaEX:20180209191852p:plain
図1 MVVMパターンの概略図


このアーキテクチャの特徴はViewModelからViewへの参照が無い点です.これによりView部分とLogic部分を切り離せています.

しかし参照がないことで苦しむ面もあります.本で取り上げられていた事例として,Snackbarの例がありました.前提として,ViewModelはViewの参照を持っていません.なのでViewModelからViewに対して,処理の完了通知を表示する処理を実行できません.そこで本では,Viewにcallbackを登録しておきViewModelの値が変わったら,ViewにSnackbarを出力するようにしていました.

なるほど.

MVPパターン

MVPパターンの概略図を図2に示します.


f:id:MitubaEX:20180209200202p:plain
図2 MVPパターンの概略図


図2からわかるように,ViewとPresenterに参照があります.実際にはContractというインターフェースが,間にありますが省略しています.MVPはあんまり印象に残ってないのですが,PresenterがViewを触れるので,直接データをViewに送れます.

申し訳ない.

2. 生きた設計を見る

ここでは,どういったプロジェクトでアーキテクチャを使ったかなどの興味深い話が載っていました. 勉強になったこととしては,

Fluxは少し知っているつもりでしたが,改めて勉強になりました.

是非読んでみてください.

3. 設計を考える

Androidのライブラリが紹介されていました.

詳しくは本で

感想

MVVM,MVPを少し知ることができて嬉しかったです.Rxの理解がなかなか足りなくて,今後勉強しないとなぁとか思いました.

ABC007 幅優先探索

備忘録です.

問題

入力例

7 8 <- 縦, 横
2 2 <- start_y, start_x
4 5 <- goal_y, goal_x
########
#......#
#.######
#..#...#
#..##..#
##.....#
########

コード

言語はPython3.6です.

# Pair
class Pair:
    x = 0
    y = 0
    def __init__(self, x, y):
        self.x = x
        self.y = y

import sys
import queue
sys.setrecursionlimit(10000)

# define
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
input_map = []

# input
height, width = map(int, input().split())
start_y, start_x = map(int, input().split())
goal_y, goal_x = map(int, input().split())
sy = start_y - 1
sx = start_x - 1
gy = goal_y - 1
gx = goal_x - 1

for i in range(height):
    input_map.append(list(input()))

# bfs
def bfs():
    que = queue.Queue()
    INF = sys.maxsize
    d = [[INF for i in range(width)] for l in range(height)] # width x heightの配列を作って,INFで埋める
    que.put(Pair(sx, sy))
    d[sy][sx] = 0 # スタートを0に

    while not que.empty():
        p = que.get()

        # ゴールに到達した場合
        if p.x == gx and p.y == gy:
            break

        for i in range(4):
            nx = p.x + dx[i]
            ny = p.y + dy[i]
            if 0 <= nx and nx < width and 0 <= ny and ny < height and input_map[ny][nx] != '#' and d[ny][nx] == INF:
                que.put(Pair(nx, ny))
                d[ny][nx] = d[p.y][p.x] + 1
    return d[gy][gx]

print(bfs())

感想

置いておきます.

LINE BotのとりあえずHello Worldまでしてみた

LINE BotのとりあえずHello Worldまで

LINE Developer Meetup in Kyoto#26に参加して,botを作りたくなったのでとりあえずHelloWorldしてみた.

アカウント作成

以下のサイトでLINE@アカウントを作成します.作成するアカウントの種類は,一般アカウントを選択しました.

アカウントが作成できたら,下のような画面になると思います.

f:id:MitubaEX:20180130015330p:plain

Messaging API設定

以下の流れでAPI設定を行います.

  • 左のメニュー -> アカウント設定 -> Messaging API設定 を選択する.
  • リクエスト設定のWebhook送信を利用するにチェックを入れる
  • LINE Developersで設定するを選択する.

そうすると以下のような画面になると思います.

f:id:MitubaEX:20180130015410p:plain

アクセストークンの発行

以下の流れで必要な情報を取得していきます.

  • アクセストークンという項目があるので,そこでアクセストークンを発行する.時間を聞かれると思いますが適当で.
  • Channel Secretという項目があるので,その値をメモっておく.

サーバコードの記述

以下のSDKを利用し,サンプルコードもここに書かれているものを利用しました.

line-bot-sdk-python

実行方法はREADME.mdを参照してください.

ngrokの使用

サーバを起動するだけではLINEとの通信ができないので,今回はngrokでローカルのサーバにアクセスする形を取りました.

インストール

brew cask install ngrok

起動

pythonでサーバを立てている状態で以下のコマンドを実行します.

ngrok http <port番号>

後はLINEで自分の作ったbotにメッセージをオウム返ししてくれます.

不明点

接続確認を押した場合,replyTokenが0の羅列になっており不正だと起こられる問題がよくわからなかったができた.

感想

これから機能を追加していきたいと思います.

neovimをインストールしてdein, deniteの導入まで

環境

mac os 10.13.2

python3のインストール

# install
brew install python3

# PATH_setting (bashやらzshの設定ファイルに記述してください)
export PYTHONPATH=python3:pip3

neovimのインストール

pip3 install neovim

deinのインストール

# get installer.sh
curl https://raw.githubusercontent.com/Shougo/dein.vim/master/bin/installer.sh > ./installer.sh

# run installer.sh
sh ./installer.sh ~/.cache/dein/. ; rm ./installer.sh

以下のようなことを~/.config/nvim/init.vimに書きます

"dein Scripts-----------------------------
if &compatible
  set nocompatible               " Be iMproved
endif

" Required:
set runtimepath+=~/.cache/dein/repos/github.com/Shougo/dein.vim

" Required:
if dein#load_state('~/.cache/dein')
  call dein#begin('~/.cache/dein')

  " Let dein manage dein
  " Required:
  call dein#add('~/.cache/dein/repos/github.com/Shougo/dein.vim')

  " Add or remove your plugins here:
  call dein#add('Shougo/neosnippet.vim')
  call dein#add('Shougo/neosnippet-snippets')
  call dein#load_toml('~/dein.toml')
  if !has('nvim')
    call dein#add('roxma/nvim-yarp')
        call dein#add('roxma/vim-hug-neovim-rpc')
  endif

  " You can specify revision/branch/tag.
  call dein#add('Shougo/deol.nvim', { 'rev': 'a1b5108fd' })

  " Required:
  call dein#end()
  call dein#save_state()
endif

" Required:
filetype plugin indent on
syntax enable

" If you want to install not installed plugins on startup.
"if dein#check_install()
"  call dein#install()
"endif

"End dein Scripts-------------------------

package管理用dein.tomlを~/直下に置いています dein.tomlは以下のような感じです.

[[plugins]]
repo = 'Shougo/dein.vim'

deniteのインストール

dein.tomlに以下のように追加します

[[plugins]]
repo = 'Shougo/denite.nvim'

vimを起動して,:call dein#update()やら:UpdateRemotePluginsを打ってインストールしてあげます

:Denite grepなどと打ってDeniteが起動したら完了です

感想

ぼちぼち楽

neovimにminpacを導入してみた

こんにちは,ミツバです. 今回はneovimに切り替えた時に,package managerどうしようかなぁと考えつつminpacを導入してみた時の備忘録です.

github.com

neovimのinstall

brewでinstallできました.

brew install neovim

minpacの導入

install

以下のコマンドでinstallします.

git clone https://github.com/k-takata/minpac.git ~/.config/nvim/pack/minpac/opt/minpac

config

neovimの設定ファイルは~/.config/nvim/init.vimに置くので,なければ作ります. そして以下の内容を追加しました.

set packpath^=~/.config/nvim/pack/minpac/opt/minpac
packadd minpac

call minpac#init()

" minpac must have {'type': 'opt'} so that it can be loaded with `packadd`.
call minpac#add('k-takata/minpac', {'type': 'opt'})

" Add other plugins here.
call minpac#add('vim-jp/syntax-vim-ex')
" ...

" Load the plugins right now. (optional)
"packloadall

これでcall minpac#init()コマンドなどをneovim上で打つと動くと思います.

以上です.

P.S. dein.vimに乗り換えました

reduxについての自分的なメモ

reduxについての自分的なメモ

先日reduxについていろいろ学んだので、自分なりにまとめてみました.多少間違っている箇所あるかもしれませんが,温かい目で指摘してもらえると助かります.

reduxは以下の記事が参考にしつつ頑張りました.

hello world

今回はcreate-react-appを利用して書いていきました.

npm install -g create-react-app
create-react-app my-app

以上のコマンドでmy-appというディレクトリができると思います. cdでそのディレクトリに移動して中身を確認します.

~/my-app >>  ls
README.md         node_modules      package-lock.json package.json      public            src
~/my-app >>  tree src/
src/
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── registerServiceWorker.js

以下のコマンドを実行することでlocalhost:3000にアクセスするとWelcome to Reactと表示されると思います.

npm install
npm start

reduxアーキテクチャの導入

今のままだとただのReactなので,少しずつReact + Reduxの形にしていきます. 今回は以下のサンプルを参考にコードを書いていきます.

とりあえずいらないファイルを削除します.

rm src/App.js
rm src/App.css
rm src/App.test.js
rm src/index.css
rm src/logo.svg
rm src/registerServiceWorker.js

reduxをinstallします.

npm install redux
npm install react-redux  

src/index.jsの内容を変更します.

  • src/index.js
import React from 'react';
- import ReactDOM from 'react-dom';
- import './index.css';
- import App from './App';
- import registerServiceWorker from './registerServiceWorker';
+ import { createStore } from 'redux';
+ import { Provider } from 'react-redux';
+ import App from './components/App';
+ import reducer from './reducers';
+ import { render } from 'react-dom';

- ReactDOM.render(<App />, document.getElementById('root'));
- registerServiceWorker();

+ const store = createStore(reducer)
+ render(
+   <Provider store={store}>
+     <App />
+   </Provider>,
+   document.getElementById('root')
+ )

必要なディレクトリを作成していきます.

mkdir reducers
mkdir containers
mkdir components
mkdir actions

とりあえずcomponentにApp.jsを作成します. このApp.jsがindex.jsに呼ばれて,こいつがいろいろ呼んでいきます.

  • components/App.js
import React, { Component } from 'react';
import AddUser from '../containers/AddUser'

class App extends Component {
    render() {
        return (
            <div className="App">
                <AddUser />
            </div>
        );
    }
}

export default App;

container

とりあえずAddUserというcontainerを呼びます. このcontainerとはcomponentを呼んだりactionを呼んだりする発火点的な役割の場所です.

  • containers/AddUser.js
import React from 'react'
import { connect } from 'react-redux'
import { addUser } from '../actions'

let AddTodo = ({ dispatch }) => {
  let input

  return (
    <div>
      <form onSubmit={e => {
        e.preventDefault()
        if (!input.value.trim()) {
          return
        }
        dispatch(addUser(input.value))
        input.value = ''
      }}>
        <input ref={node => {
          input = node
        }} />
        <button type="submit">
          Add User
        </button>
      </form>
    </div>
  )
}
AddTodo = connect()(AddTodo)

export default AddTodo

これはpreventDefault()を呼ぶことによって本来のsubmitを止めて,dispatchを呼んだ後inputを空にする処理を記述しています. 試しにe.preventDefault()を消すとページが更新されてしまいます.ページが更新されてしまうとフロント側で保持していた状態が消えてしまうのでとりあえずページ更新を行わないようにする感じです.

ここでAddUser.jsでは,dispatchが呼ばれています.この関数はactionの関数を引数として呼び出します.そしてactionreducerを呼んで,その後reducerが状態を更新するという処理が実行されます.詳しくは後述します.

action

actionを作成します.このactionは,dispatch関数で呼ばれる関数を記述する場所です.

  • actions/index.js
let nextTodoId = 0
export const addUser = (text) => ({
    type: 'ADD_USER',
    id: nextTodoId++,
    text
})

ここではaddUserという関数を定義しており,これはtextをもらってきて,type, id, status, textを入れたオブジェクトを返します.

reducer

reducerはactionの返したオブジェクトを受け取り,action.typeによって処理をいろいろと変えられる場所です.

  • reducers/index.js
import { combineReducers } from 'redux'
import users from './users'

const App = combineReducers({
  users
})

export default App
  • reducers/users.js
const users = (state = [], action) => {
  switch (action.type) {
    case 'ADD_USER':
      return [
        ...state,
          {
              id: action.id,
              name: action.text
          }
      ]
    default:
      return state
  }
}

export default users

index.jscombineReducersを呼んでいることがわかると思います.これは複数のreducerをまとめることができる凄いやつです. 肝心のreducerは別ファイルのusers.jsに書いておき,index.jsで呼び出します. これで複数ファイルにreducerを分けられます.(便利)

reducerでやっていることは,action.typeでswitch caseを書いて,変更した状態をreturnする感じです.今回のstateはArrayですが,Objectなども返せます.

これで一通りの登録処理が終わったので,ここから追加したuserを表示するcomponentを作成していきます.

UserListの作成

追加されたUserを表示するためのcontainerとcomponentを作成していきます. まずcontainerから.

  • UserList.js
import { connect } from 'react-redux'
import UserTable from '../components/UserTable'

const mapStateToProps = (state) => ({
  users: state.users
})

const UserList = connect(
  mapStateToProps
)(UserTable)

export default UserList

connectというものが呼ばれているのがわかります.これはstateやらactionやらを渡してcomponentを作成できる便利なやつです. 渡されたstateやcomponentはcomponentがそのまま使用できます.

次にcomponentです.

  • UserTables.js
import React from 'react';

const UserTables = (users) => (
    <table>
        {users.users.map(user =>
            <tr><td>{user.id}</td><td>{user.name}</td></tr>
        )}
    </table>
);

export default UserTables;

これでnpm startすると以下のようなページになっていると成功です.

f:id:MitubaEX:20171215000115p:plain

ユーザを追加するとこんな感じです.

f:id:MitubaEX:20171215000147p:plain

所感

reduxはexample見ても頭がぶっ飛んでいたので,一から自分で作りました. 他にも使っていない機能(middlewareなど)があるので,それもまた触っていきたい.

macにmongodbをinstallして起動するまで

brewでmongodbを入れて起動するまでを行ったので,そのメモです.

install

brew update
brew install mongodb

run

mongod

ここで以下のようなエラーが出ました.

2017-11-23T00:15:39.775+0900 I STORAGE  [initandlisten] exception in initAndListen: 29 Data directory /data/db not found., terminating

どうやら/data/dbが見つからないらしい

fix

sudo mkdir -p /data/db

mongodコマンドで起動することができました.

run mongo client

mongo

これでmongodbにアクセスできました.

以上.