気軽に楽しくプログラムと遊ぶ

自分が興味があってためになるかもって思う情報を提供しています。

MacOS Python3系インストール

Pythonバージョン管理ツールのpyenvを入れて、3系のPythonをインストール&適用させていきます。

pyenvをインストール

brew install pyenv

pyenvの設定(シェルがzshの場合)

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
source ~/.zshrc

Pythonインストール

pyenv install --list

pyenv install 3.9.6

インストール済pythonの確認

pyenv versions 
* system (set by /Users/UserName/.pyenv/version)
  3.9.6

ローカルへのpythonの適用

pyenv global 3.9.6

python3 --version
Python 3.9.2

python3 hello.py #Pythonファイルの実行

JSON用のgrepみたいなjqコマンド

JSONを便利に編集できるjqコマンドできる基礎的なことを書いておきます。

マニュアル、参考URL

公式マニュアルをみておくと幸せになれる。
jq Manual (development version)

ここに書いていることでメモしておきたいことを書いていきます。
jq コマンドを使う日常のご紹介 - Qiita

コマンド使い方

.がルート{}を表します。

整形

% echo '{"items":[{"item_id":1,"name":"たろう"},{"item_id":2,"name":"はなこ"}]}' \
| jq .

{
  "items": [
    {
      "item_id": 1,
      "name": "たろう"
    },
    {
      "item_id": 2,
      "name": "はなこ"
    }
  ]
}

列取得

% echo '{"items":[{"item_id":1,"name":"たろう"},{"item_id":2,"name":"はなこ"}]}' 
| jq '.items[]..name'   
"たろう"
"はなこ"

# ダブルコートを消すには -r をつける
% echo '{"items":[{"item_id":1,"name":"たろう"},{"item_id":2,"name":"はなこ"}]}' \   [14:48:38]
| jq -r '.items[].name'
たろう
はなこ

フィルタ(パイプみたいなもの)

'.items[]'までの結果を.nameへ渡す。

% echo '{"items":[{"item_id":1,"name":"たろう"},{"item_id":2,"name":"はなこ"}]}' \   [14:48:44]
| jq -r '.items[] | .name'
たろう
はなこ

パイプの後で編集

項目の順序やプロパティー名を変更、配列にしたりなど

% echo '{"items":[{"item_id":1,"name":"たろう"},{"item_id":2,"name":"はなこ"}]}' \   [14:50:27]
| jq -r '.items[] | {name2: .name, item_id2: .item_id}'
{
  "name2": "たろう",
  "item_id2": 1
}
{
  "name2": "はなこ",
  "item_id2": 2
}

他にもいろいろありそうだけど、一旦ここまで。

Kotlin 正規表現

下記の例を貼らせて頂きました。
Kotlinの正規表現の使い方 | 寝室コンピューティング

// a,b,cのうちいずれかにマッチ
val regex = Regex(pattern = "[abc]")

// Raw Stringはエスケープ不要
"""\d"""

// 普通の文字列だとエスケープが必要
"\\d"

// 文字列に数字が含まれているかチェックする
val target = "1 apple. 2 bananas."
val regex = Regex("""\d""")

val isMatched = regex.containsMatchIn(target)
print(isMatched) 

val match = regex.find(target)
print(match?.value)
// 結果:1

val matches = regex.findAll(target)
matches?.forEach { match -> print("${match.value} ") }
// 結果:1 2 

val target = "090-1111-2222"
val regex = Regex("""(\d+)-(\d+)-(\d+)""")

val match = regex.find(target)
 
match?.groups?.forEach { group -> println("${group?.value}") }
/* 以下が格納されている
group[0]:090-1111-2222
group[1]:090
group[2]:1111
group[3]:2222
*/

Mockito 基礎文法

Mockitoでパッとわからなかった書き方について書いていきます。

戻り値なしメソッド

何も返さないメソッドが指定インスタンスと引数のときにMock化されます

Mockito.doNothing().when(モックインスタンス).メソッド(任意の引数);

privateメソッド、staticメソッド

MockitoではMock化できない。実現したい場合、PowerMockを使う

モックオブジェクトの検証(verify)

  • @mock @spyどちらのオブジェクトに対しても使用可能
  • 呼ばれた回数の検証ができる

指定回数呼ばれているかtimes

sample1.method1(10);
sample1.method1(20);
sample1.method1(20);    
verify(sample1,times(2)).method1(20);         // OK 
verify(sample1,times(3)).method1(20);         // エラー

指定回数呼ばれているかatLeastOnce atLeast atMost never

verify(sample1,atLeastOnce()).method1(0); // 最後に呼ばれたメソッドと引数が一致で正常
verify(sample1, atLeast(2)).method1(20);   // 2回以上でOK
verify(sample1, atMost(2)).method1(20);    // 2回以下でOK
verify(sample1,never()).method1(20);       // 0回で正常 (times(0))と等価

ホスト名からIPを調べる方法(host, dig)

host -a

ANSWER SECTIONのところにIPが表示される

% host -a stg.sample.com
Trying "stg.sample.com"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21689
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;stg.sample.com.  IN ANY

;; ANSWER SECTION:
stg.sample.com.   300 IN A 172.26.21.67

Received 80 bytes from 100.32.220.102#53 in 22 ms

dig

こちらもANSWER SECTIONの所にIPが表示される

% dig stg.sample.com                                                                        

; <<>> DiG 9.10.6 <<>> stg.sample.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22510
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;stg.sample.com.  IN A

;; ANSWER SECTION:
stg.sample.com.   103 IN A 172.26.213.67

;; Query time: 18 msec
;; SERVER: 100.32.220.102#53(100.32.220.102)
;; WHEN: Mon Jun 21 11:33:06 JST 2021
;; MSG SIZE  rcvd: 91

現場システム開発の進め方を考えてみる

システム開発を効率的に進めるための優先順位や力を抜く部分、効率化についての整理をしてみます。

前提

  • 開発機能:なんとなくこんな機能というイメージはあるが、コードは読んだことがない
  • 言語:Javaでの開発は慣れているが、新たに使うKotlinについては未経験
  • チーム:リードエンジニア、メンバーが先行して開発を進めているところに途中から参画
  • 協業チーム:フロント、アプリは別部隊があり、そちらがテストできるレベルの資材を提供することがひとまずの目的
  • 開発内容:品質の良くないソースの作り換え、Java → Kotlin移行
  • 工期:短納期ではないが、余裕があるスケジュールではない。

仕様理解、開発方針の把握、実装のバランスを取りつつ、最高パフォーマンスを発揮するにはどう行動すべきか。

目的

  • 協業チームがテストできる最低限の品質の実装を組み上げる
  • チームのコード規約や求める品質をクリアする
  • テストコードはカバレッジ80%を目指す
  • Kotlin知見を蓄積し、今後の開発効率化に繋げる。チーム貢献できる情報集約ができたらなおよし

タスク概要

スケジュール

  • 協業チームの開発状況の把握。納期必達かの確認

設計

  • IFの再確認:既にIF設計はされているが、矛盾点ある可能性あり。
  • 実装設計:処理内の細かな修正方法は未検討のため、検討する必要あり。 一部、DB→Redisへ変更する必要あり。
    • 周辺機能理解:機能全体を踏まえた適切な実装が行う。

実装

  • 機能
    • APIの枠作成
    • コードの自動変換:ざっくり変換して変換前後理解
    • コメントで処理フローをざっくり記載
    • 処理実装ステップ:Java → Kotlin変換したソースの段階的、移植
  • Kotlin学習
    • リードエンジニアソースのリーディング:チームコード規約理解、Kotlinの書き方基本理解

効率化のポイント

  • 作業計画
    • 概要タスク整理と優先順位、概算見積もり。一日にこなす作業量のイメージをしておく。
  • 実装方法の事前理解
    • よく使う実装方法を体系的に整理しておく。辞書を作っておく
    • テストコードの抽象化、JavaSandプロジェクトへの転記
  • 共通機能の利用
    • 共通機能の理解、利用の想定。機能全体の概要を掴んでおくことが肝要。
  • レビュー指摘を減らす
    • 実装済みソース読み込みでコード規約の理解、WIPでの細切れPR

作業の進め方

ここまでを踏まえて、どう作業を進めるのか。

  • 既存コード理解(作成機能)
  • 既存コード理解(周辺機能、共通機能)
  • 利用する共通機能把握、Kotlinイデオム整理
  • 疑問点整理、重要ポイント確認
  • 設計書理解と実装設計(実装ステップ)
  • 設計疑問点整理と確認
  • API枠、処理概要コメント実装
  • ステップ実装

以上

Kotlin 基礎学習

Kotlin とは?

  • 型推論のある静的型付けのOBJ指向言語
  • Javaとの連携が言語仕様に存在。KotlinからJavaのクラスやメソッドを呼べたりもする

オンライン実行環境

以下の本家サイトの実行環境で、実際に試しながら学習を進めると捗ります。

Kotlin Playground: Edit, Run, Share Kotlin Code Online

Javaとの違い

  • 文末のセミコロンが不要
    • 参照型しかない 。プリミティブ型はない
    • val:読み出しのみ、var:読み書き可
  • 定数:const。const val MAX_NUM = 10

null関係

nullチェックなしコードはコンパイル時にエラー

// non-null type
var a: String = "moji"
a = null // コンパイルエラー

// null type
var a: String? = "moji"
a = null // エラーにならない

nullじゃないとき実行する

var str: String? = null // null typeにnull挿入
str?.split(",") // strがnullじゃないときだけsplit

nullじゃないとき値設定(エルビス演算子)

Company.setName(name?: "ななしの会社")

スコープ関数

// hogeがnullでないときのみ{}の処理を実行させる関数
hoge?.let { 
    textView.text = it.name
}

他にrun, also, applyなどが存在。おいおい理解していく。

演算子

値比較、参照比較

val value1 = "a"
val value2 = "b"
if (value1 == value2) // 値比較で false。否定は !=

var list1 = listOf(1,2,3)
var list2 = listOf(1,2,3)
if (list1 === list2) // true =が3つ。javaは== だったので間違えないように

文字列

文字列中に埋め込み文字

var shopName: String = "sevene"
println("${shopName}")
println("$shopName") // 型推論可能な場合は{}は省略可能

改行入り文字列

行頭の| は目印の記号。trimMarginで除去可能

fun main(args: Array<String>) {
    var strStory = """
           |本日は
           |晴天なり
           |明日もそうなる
           """.trimMargin() 
 
    println("$strStory")
}

キャスト

val hoge = obj as Hoge // objをHoge型に変換する

関数 function

基本の書き方

fun strConbine(a: String, b: String): String {
    return a + b
}

fun strConbine(a: String, b: String): = return a + b
    
fun strConbine(a: String, b: String): Unite {  // Unite はJavaのvoid。省略可
    println(a + b)
}

名前付き引数

引数が多いかつ、同じ型の場合、どの項目に設定しているのか間違えやすい場合がある
その場合に値設定時にあえて関数側の引数名をあえて指定して関数を呼び出す書き方です。
JavaではBuilderパターンを使って回避したりするが、いかんせんコード量が増えがち。

class Book(
    val title: String,
    val price: Int,
    val author: String,
    val isbn: String,
    val remarks: String
) {
    override fun toString(): String {
        return "Book(title='$title', price=$price, author='$author', isbn='$isbn', remarks='$remarks')"
    }
}

呼び出し側のコードは以下です。

val book = Book(
    title = "Kotlinイン・アクション",
    price = 4115,
    author = "Dmitry Jemerov",
    isbn = "978-4839961749",
    remarks = "サイズ: 23.6x18.2x3.6 cm")

println(book)

関数オブジェクト

fun main() {
    val function = ::func
    println( function() )

    val function2: () -> String = ::func // 型を明示する書き方
    println( function2() )
}

fun func(): String {
    return "test"
}

ラムダ式

fun main() {
    // ラムダ式、記述省略なし。=の前が型(ラムダ)、後が関数。関数の ->の前が引数、後が処理
    val square: (Int) -> Int = { i: Int -> i * i }  
    val square2: (Int) -> Int = { i -> i * i }      // 関数内の引数を型推論
    val square3 = { i:Int -> i * i }                // 関数の型を型推論
    val square4: (Int) -> Int = { it * it }         // 引数が1つの場合はitの名で変数が使える。引数部分も省略
}

制御文

if

// ifは式として書ける。ブロックの最後の式が値になる。
val max = if (a > b) {
    print("Choose a")
    a
} else { //この場合、elseがないとエラーになる。返却する値の型はif側と合わせる必要がある
    print("Choose b")
    b
}

for

// インデックス
for (i in array.indices)
    print(array[i])

// インデックスと値
for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

// null考慮
for (i in list.orEmpty())  {
}

while

while (x > 0) {

}

配列、コレクション

配列

val list = arrayOf(1, 2, 3)               // 型類推
val list = intArrayOf(1, 2, 3)            // int指定

println(list[0])      // 要素アクセス

for(item in list){   // ループ
    println(item)
}

コレクション

//. リスト(要素は重複あり)
val list = listOf(1, 2, 3) // 変更不可
val list = mutableListOf(1, 2, 3) // 変更可
val list = mutableListOf<TMP>() // 空で初期化

// Set(重複なし)
val list = setOf(1, 2, 2, 3)  // 変更不可、変更時はmutableSetOf

// Map
val map = mapOf(    // 変更不可、変更時はmutableMapOf
    1 to "one",
    2 to "two",
    3 to "three"
)

for(item in map)
    println("${item.key} to ${item.value}")

for((key, value) in map)
    println("${key} to ${value}")

クラス

データクラス

データのみを保持するクラスは、classの前にdataを付与して宣言

// 定義
data class User(val name: String = "", val age: Int = 0)

// 初期化
val jack = User(name = "Jack", age = 1)  // 名前付き引数
val olderJack = jack.copy(age = 2)       // OBJのコピー&一部書き換え
val jane = User("Jane", 35)  // 通常初期化

// 参照
val (name, age) = jane       // 要素複数Get、nameやageを後続で参照可能

// 定義&コンストラクタ
data class User(val name: String = "", val age: Int = 0) {
        constructor(name: String, age: Int, email: String) : this(name, age)
}

コンパニオンオブジェクト

class MyClass {
    // コンパニオンオブジェクトは1クラスに1つだけ定義可能
    companion object { 
        const val FREE_PRICE = 0
        fun hello(greeting: String) { println(greeting) }
    }
    // companion object myCompanion { } 別名付与も可能

   // objectならば、1クラスに複数定義可能
  object {}
}

fun MyClass.Companion.foo() {
    println("hoge")
}

fun main(args: Array<String>) {
    MyClass.Companion.foo()
    MyClass.foo() // Companionを省略する方が一般的
}

通常クラス

class Hoge {
    // プロパティの定義。getterとsetterが定義される。valならgetterのみ
    var name: String = ""

    // コンストラクタ
    constructor(name: String) {
        this.name = name
    }

    // メソッドの定義
    fun printMethod(strPrint: String) {
        println(strPrint)
    }
}

プライマリコンストラク

// クラスの引数に直接値を渡して初期化するタイプのコンストラクタ
class Hoge(n: String) {
    val name = n
}

// クラスの引数に直接値を渡して初期化するタイプのコンストラクタ、プロパティーも同時に定義
class Hoge(val name: String) {
}

継承

// openをつけると継承可能となる
open class Base {
}

class Hoge : Base() {
}

インターフェース

interface Hoge {
    fun hoge()
}

// 継承と同じ書き方になる
class Sample: Hoge {
    override fun hoge() { // 実装クラスはoverrideを付与
        println("hogehoge")
    }
}

// 無名クラスでインターフェースを実装
class Sample {
    private val h = object: Hoge { // 無名クラス(object)にHogeを実装(具象化)する
        override fun hoge() {
            println("hogehoge")
        }
    }
}