「とりあえず動く」を卒業するためのアーキテクチャ入門編(1)

皆さんこんにちは。株式会社コスモルートでアンドロイドアプリの請け負い開発をしているT.Mです。

僕自身コスモルートは2社目で、前職はAndroidアプリの開発や機械学習の業務を行っていました。
その後コンサルタントとして入社後、2年程上流工程を経験してます。
なので仕様変更に対する上流と下流(そもそもこの呼び方が良くない笑)のせめぎ合いや、「これ要件に書いてないですよ?」や「そもそも要件というより前提だよね?」といった喧嘩会議を両側から経験してきました。
要件定義で完璧に仕様が決まり、未来永劫変更が無い—そう断言できれば安心して開発ができます。
しかし実際のところ、プロジェクトは生き物のように変化し、仕様が固まるということは殆どありません。
選択肢は2つ。
1:「仕様が確定しないのが悪いんだ!」と悪態をつく
2:プロジェクトの変化に柔軟な開発を目指す
あなたはどちらを選びますか?

1.Android/Kotlin再入門

この章ではAndroidをJavaでしか書いたことが無い人向けのKotlinの紹介と、僕のように数年開発現場を離れていた人向けのアップデート事項をまとめました

1.1 Kotlinとは?

もともとAndroidアプリの開発にはjavaが必須でしたが、2019年でのI/OではKotlinを第一言語とし、公式サポートもこれまで以上に充実させるとしました。
言語的な特徴はいくつかありますが、中でもnullを上手く扱い、エラーを発生させにくい仕組みが受け入れられました。
まずはそれをお定まりのHelloWorldで見てみましょう。

val str:String? = null
print(str?:”Hello world”)

`?`が沢山みえますが、これがnullを管理する印です。
具体的には`String?`で定義された変数は**String型**と**null**を許容します。
これはjavaの**String**に相当します。
2段目の`str?:”Hello world”`は**str**がnullの場合、**Hello world**を出力するよという意味です。
一般的にはnullをなるべく除外したいので、?はあまり使いたくありません。
Javaから受け取った値をKotlinに渡す場合や、サーバーからのレスポンスでnullが入り込む場合などは`Type?`で変数を定義します。

変数がnullでない時のみ、関数を適用したい場合があります。
そういう場合は`let`関数がおすすめです。

入力されたパスワードが正しいか判定する場合は、

val input:String? = "password"
val isHelloWorld :Boolean? = str?.let{checkValid(it)} // let内のitはnullでない

と書けば、`if(input!=null) … else …` のような条件式は不要で、簡潔に記述できます。

1.2 変数の定義方法

変数の定義の仕方について触れておきます。
javaでは変数は2種類で、代入可能なもの(可変変数)と不可能なものです。
kotlinも同じく可変変数と不変変数があり、それぞれ以下の様に定義します。

val str = “Hello, world” //不変
str = “Hello, kotlin” // 代入不可能なのでコンパイルエラー
var str2 = str
str2 = “Hello, kotlin” //エラーにならない

kotlinに限った話ではないですが、なるべく不変変数を使うようにしましょう。
たとえばユーザーの生年月日を定義する場合、`var birthday:LocalDate`とはしないはずです。
見た目は若くできても実年齢は変わらないので、不変であるべきです!

一方で、例えば「パスワードを変更する」や、「パスワードを再登録する」などのユーザーアクションが考えられるので、パスワードは可変です。

var password:String

ですね。

同様にニックネームや好きな食べ物などの情報は可変、生まれ故郷や性別は(特別な事情がない限り)不変として良いでしょう。

これで変数の定義方法については終わります。

ただし`var password`には少々問題があります。
このままだと、`0000`や`cosmoroot`のような安易なパスワードが入力される恐れがあります。
そこで、登場するのがjavaでお馴染みのsetter/getterです。

1.3 変数のカプセル化(setter/getter)

例えば開発要件として、**パスワードは4文字以上8文字以下とする**と書いてある場合はどうしますか?
更に**パスワードは暗号化してサーバーに送る**という要件も追加されました。
そこで役立つのがsetter/getterというカプセル化の概念です。
実際にkotlinのコードを見てみます。

var password:String? = null
set(newPass:String?){
// 4~8字以内の場合は
val withIn:Boolean? = newPass?.let{IntRange(4,8).contains(it)}
//newPassを代入
if(withIn) field = newPass
}
get = hashed(field) //Hash化関数
password = “password”
print(password) // Hash化されたパスワードが出力される
password = “tooLongPassword”
print(password) //条件に満たさない場合は、nullが出力される

コード内コメントにある通り、**4文字以上8文字以下**の条件を満たさない場合は代入できませんし(setterによる入力の制限)、出力はHash化されます(getterによる出力の制限)。
ただし、まだ`null`が残っていますね。
改善の余地がありますが、次のクラス定義にまわします。

1.4 クラス定義

上で見てきたユーザー情報はクラスにすることができます。

例として

– ユーザーID
– パスワード

をもつ`User`クラスを定義しましょう。
kotlinでは

//Userクラス定義 uidは不変
class User(val uid:String,var password:String)
val user:User = User(“cosmoroot”,”password”)
print(“$user.id”) // cosmoroot と出力

と書けます。
javaと雰囲気が似てますが、クラス変数`uid`および`password`をコンストラクタで定義できるのが便利ですね。
更にpasswordは勝手に変更されては困るので、改良します。

class User(val uid:String,_password:String){
var password:String = _password
set(newPass:String){
val withIn:Boolean = IntRange(4,8).contains(newPasss)
if(withIn) field = newPass
}
get = hashed(field) //Hash化関数
}
val user = User(“cosmoroot”,”password”)
user.password = “tooLongPassword” //無効なパスワードを代入しても
print(user.password) //passwordと出力

クラス化によってpasswordはUser生成時に指定すればよいので、`null`は削除できました。
ただし、Userクラスにしたので、パスワードはsetter/getterで定義するよりも、**changePassword**関数を定義した方が分かりやすいですね。

/***
@param _passowrd:パスワード
private変数もコンストラクタで定義できる。クラス外からは参照されない。
*/
class User(val uid:String,private var _password:String){
fun changePassword(newPassword:String):Boolean{
return if(IntRange(4,8).contains(newPasss)){
_password = newPassword
true
}else false
}
}
val user = User(“cosmoroot”,”passoword”)
user.changePassword(“tooLongPassword”) // return false

とてもシンプルになりました!
また`changePassword`メソッドを定義したおかげで分かりやすくなりました。

 

まだまだ続けたいところですが、長くなってきたので、今回はこのあたりで終わりにします。

最近はKotlin関係のオンラインのコースもかなり充実していますし、サンプルアプリの実装例がgithubに載っているので、それを元に勉強するのもおすすめですよ。

次回は「プロジェクトの変化に対応する」を予定しています。
お楽しみに。