実践!オブジェクト指向 文系出身ひよっこエンジニアの勉強記

こんにちは、弥生の野川です。

この記事はMisoca+弥生+ALTOA Advent Calendar 2018の23日目のエントリーになります。 いよいよクリスマスが目前ですね!

簡単に自己紹介をさせていただきます。

・2018年4月に弥生に新卒入社
・入社以前にプログラミングの経験は全くなし(学生時代は文系の学科に所属)
・入社後のJavaの研修でプログラミングに興味を持ち、開発の部署への配属を希望
・現在は自動テストを開発・運用するチームでエンジニアとして奮闘中(歴6ヶ月)

今回はこんな私が、オブジェクト指向について勉強して思ったことを書き連ねます。

はじめに:一般的なオブジェクト指向の説明

一般的な参考書では、オブジェクト指向は以下のように説明されることが多いかと思います。

  • 対象(オブジェクト)そのものに重点をおき、対象のふるまいや操作が対象の属性として備わるという考え方。
  • 現実世界の部品(モノ)をモデリングし、プログラムに置き換えるという考え方。
  • オブジェクト間のメッセージ・パッシングでプログラムを動作させるという考え方。

しかしながら、プログラミング未経験者にこの説明は本当にキツイ! なぜかと言いますと・・・

初心者はここがわからない

  • 「オブジェクト指向」という名前に馴染みが1ミリもない。
  • 「対象に重点をおく」のは良いけど、おいてどうするのかがわからない。
  • 「ふるまいや操作が属性として備わる」のイメージがつかめない。
  • 「現実世界のモノをプログラムに置き換える」なんてできるんですか?って思う。
  • 「オブジェクト間のメッセージ・パッシング」がまるで呪文のように聞こえる(つまり何もわからない)。

などなど、初心者の前には高すぎる壁が立ちはだかります! (よくこれでへこたれずに勉強したな自分・・・)と思うぐらいです。

オブジェクト指向はこうして理解する

そんな壁を乗り越え、私がオブジェクト指向を理解することができたのは、 以下のことを実践したおかげでした。

  1. とにかく複数クラスのプログラムを実装してみる
  2. 先輩エンジニアと話す
  3. 複数人での開発を、身をもって経験する

これだけです。しかしこれが私にとってはすごく重要でした。

1. とにかく複数クラスのプログラムを実装してみる

 概念を文字だけで理解しようとするのではなく、 実際にメインクラスで他クラスをインスタンス化してメソッドを呼び出すプログラムをいくつか書くと、 「オブジェクト間のメッセージ・パッシング」は呪文ではなくなりました。

特に引数・戻り値を使うメソッドだと良いです。メッセージ・パッシングめっちゃ楽しい~!ってなります。

 また、オブジェクト指向に特有な「継承」「ポリモーフィズム」などの考え方も、 実装してみないことには、なぜ必要か、どのような時に必要になるのかがわからないんですよね。

 私もそうなんですけど、文系出身の人間には結構ありがちな「概念を理解しないと先に進めない!」という思考は、 オブジェクト指向の勉強ではちょっと横に置いておくのが良さそうです。

2. 先輩エンジニアと話す

 自主勉強の一環で、トランプのゲームを実装している時のことです。 1枚のカードを表すカードクラスはサッと実装できたのですが、 束になったトランプクラスを作ろうと思ったところで手が止まりました。

「た、束・・・!?」

私が悩んでいると、プログラミング経験のある同期と大ベテランの先輩が声をかけてくださって、 トランプクラスについて一緒に議論してくれました。 ポイントは、「そこにあるかのように、トランプの束の状態を想像すること」でした。

・トランプの束に含まれているカードは、4種類のマーク×13の数字の組み合わせで、計52枚。
・カードの引き方は、束の好きなところから引くパターンや、
 山の上から1枚引くパターンがある(ゲームによる)。
・カードが引かれると、そのカードは束の中からなくなる。

などなど、トランプの特徴を1つ1つあげて、それらに対応するように実装していくと まさに現実世界のトランプ(=モノ)をプログラムで実現することができました! 下図は、束の好きなところから引くパターンで実装しました。 f:id:kenta_nakayama:20190116100338p:plain

このレベルの話を先輩エンジニアとする必要が果たしてあるのか? と思われてしまいそうですが、私は一人で中身を考えていると、合っているのか間違っているのかもわからずモヤモヤしてしまいます・・・。 先輩エンジニアが「うんうん、そうそう」と言ってくださるだけで脳内の設計が明快になるので、とてもありがたいです。

3. 複数人での開発を、身をもって経験する

 ソフトウェア開発では、機能ごとに分担して実装したり、既存のコードを別の人が後になって改修したり ということが当たり前のように行われています。

しかし、私はエンジニアになるまで、ソフトウェア開発の工程なんて全く気にしたことがなかったので、 オブジェクト指向の目的である「効率化」「保守性」の必要性もわからないというレベルでした。

最近ようやく、私も開発プロセスの一端を担うようになったので、 「開発ってみんなで分担してやるから、機能ごとに分かれてると開発・改修しやすいんだな~」 ということを肌で感じることができています。

このイメージを持てた瞬間、オブジェクト指向への親しみやすさはぐんと跳ね上がりました。 特に、なぜこんなにもアクセス修飾子を気にしないといけないのか、その理由がよくわかるようになりました。 仕事では、既存の自動テストが安定して動作するようにコードを改修することが多いのですが、 いじるものがprivateメソッドだと、あまりびくびくせずに変更を加えることができます(笑)

まとめ:オブジェクト指向を理解するために必要なこと

色々書きましたが、未経験者にとってはとにかく【イメージや実感をつかむこと】が重要です。

個人的に、上で述べた①~③は、イメージや実感をつかむための最速の手段だと思っています。

もしこの先、プログラミング経験の浅いメンバーが入ってきて、 オブジェクト指向で大変な思いをしている時には 設計について一緒にあれこれ話したり、私がやってみて良かった課題を共有したりしたいです!

さいごに

ここまで読んでいただき、ありがとうございました。

Misoca+弥生+ALTOA Advent Calendar 2018、次は@ryo_mt09spさんの「たぶんVue.js + Django REST framework」です。お楽しみに!

AWS AppSyncについて勉強してみた

こんにちは、弥生の八木です。

この記事は、Misoca+弥生+ALTOA Advent Calendar 2018の 22日目の記事です。

記事を書くにあたって、せっかくなので業務とは関係なく、気になる技術を勉強しようと思ったので、 今回は、AWS AppSyncとそのバックグラウンドの技術であるGraphQLを勉強したことや簡単に作ってみたものについて書きます。

AWS AppSyncとは

GraphQLベースのAPIを作成でき、APIに合わせてDynamoDBを自動で作成できる「フルマネージドGraphQLサービス」です。 AWSのコンソール画面からDBを設計、設定をするだけで簡単にAPIとDBを作成できます。

GraphQLとは

GraphQLは、Facebookにより開発されたオープンソースのクエリ言語のことです。 クエリ言語とは「問い合わせ言語」といい、コンピュータのデータに対して問い合わせるための言語で、代表的なクエリ言語だと、SQLが当てはまります。

以下のようなリクエスト、レスポンスになります。

リクエストサンプル

query list {
  listHoge {
    items {
      id  hoge
    }
  }
}

レスポンスサンプル

{
  "data": {
    listHoge": {
      "items": [
        {
          "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
          "hoge": "hogehoge"
        }
      ]
    }
  }
}

実際に動かしてみた

やること

AWS AppSyncでAPIを作成し、クライアントからAPIにリクエストして、なんでもいいから結果を受け取って「AppSync使ってるぜ」感を味わう!

やったこと

  1. WebクライアントからAWS AppSyncにアクセスできるようにする
  2. AWS AppSyncでAPIとDBを作る
  3. 作成したAPIのエンドポイントなど設定する
  4. WebクライアントからAPIにアクセスし、レスポンス結果を画面に表示する

APIとDBの作り方

  1. AWSコンソールにログインし、「Create API」ボタンをクリックする f:id:ym_AdventC:20181218183733p:plain

  2. 「Create with wizard」にチェックを入れ、「Start」ボタンをクリックする f:id:ym_AdventC:20181218183842p:plain

  3. 「Model Name」に任意にDBの名称を付けて、「Configure model fields」でDBのカラムの設定をする、完了したら「Create」ボタンをクリックする f:id:ym_AdventC:20181218184133p:plain

  4. 「API Name」に任意にAPIの名称を付けて、「Create」ボタンをクリックして完了です f:id:ym_AdventC:20181218184716p:plain

作成したAPIは、AWSコンソール上でも動作させることができます

登録のクエリ f:id:ym_AdventC:20181218192142p:plain

取得のクエリ f:id:ym_AdventC:20181218192344p:plain

APIにアクセスして結果を取得

簡単にReactのサンプルプロジェクトをいじって取得した内容を表示させます。

Reactプロジェクト内のApp.jsの設定にGraphQLのクエリを記述し、レスポンス結果を取得する関数を作成

~~略~~

const ListTodos = `
query list{
  listTodos{
    items{
      id name description
    }
  }
}
`;

class App extends Component {
  state = {todos: []}
  async componentDidMount() {

    const todos = await API.graphql(graphqlOperation(ListTodos))
    console.log('todos:', todos)
    this.setState({todos: todos.data.listTodos.items})
  }

~~略~~

実際にWebブラウザからAPIにアクセス f:id:ym_AdventC:20181219180840p:plain

GraphQL APIでDynamoDBにアクセスして取得したデータを表示しています。 (画面下部のWebブラウザの開発ツールのコンソールに表示しているのが、レスポンス結果です。)

まとめ

AWS AppSyncについて触ってみてわかったこと

  • とても簡単にAPI(GraphQLベース)とDB(DynamoDB)を作成できる
  • APIアプリケーションを作成する必要なし
  • APIサーバーを用意する必要なし
  • DBサーバーを用意する必要なし
  • 簡単なWebアプリやスマホアプリ作成に便利

おわりに

AWS AppSyncで作成したAPIレスポンスを検証するために、React+AWS Amplifyを使用しましたが、環境構築やReactのJSXでWebブラウザ画面に表示するほうが大変でした。。 当たり前ですが、継続的に勉強しなきゃいけませんね!

Misoca+弥生+ALTOA Advent Calendar 2018 、次は@s_nogawaさんの「実践!オブジェクト指向 文系出身ひよっこエンジニアの勉強記」です!お楽しみに!

UWSCでデスクトップアプリの自動テスト

こんにちは。弥生の内山です。
この記事はMisoca+弥生+ALTOA Advent Calendar 2018の21日目の記事です。

はじめに

弥生の主力製品は弥生会計をはじめとしたWindowsアプリです。
弥生ではテストの自動化を進めていますが、結合テスト、統合テストといった工程では、やはり手動でのテストが多く、テストの実施のためにかなりの工数が費やされています。
この工数を少しでも削減しようと、UWSCというツールを使って手動テストを自動化する取り組みを進めてきましたので、その内容をご紹介します。

UWSCとは

UWSCはWindowsの自動操縦ツールです。
20年近く歴史のあるツールで、キーボードやマウスの操作を記録して再生する機能だけでなく、VBA風の独自のスクリプト言語を搭載しており、スクリプトを作成することによってかなり複雑な自動操作を行わせることができます。

やったこと

弥生のWindowsアプリの1つである弥生給与を対象に、手動テストを自動化するための取り組みを進めてきました。
ここでは、具体的にどのようなことを行ってきたかをご紹介します。

まずは1つ自動化してみる

あるテストシナリオに対して、まずはベタにUWSCのスクリプトを書き進めて自動テストができるようにしました。
従来通りのテスト手順書の作成と比較するとかなり時間がかかりましたが、このときは「このテストをどうしても自動化したい」というオーダーを受けていたので、十分に工数を確保することができました。

ライブラリ化する

スクリプトを書き進めていくと、やはり処理を関数などに切り出して整理したくなってきます。
自動化を始めたときから、チームメンバーや他のチームにも展開することを念頭に置いていたので、共通で使えると思った処理は積極的に関数化し、ライブラリとしてまとめていきました。

以下にライブラリを使ったスクリプトの例を示します。

call yayoi-uwsc-lib\kyuyo

事業所データを新規作成する("弥生株式会社", "H3001")

call文は他のUWSCスクリプトファイルを取り込みます。
ここでは、yayoi-uwsc-lib\kyuyo.uwsというスクリプトファイルを読み込んでいます(拡張子は省略可能です)。
このスクリプトファイルがライブラリ本体になります。

「事業所データを新規作成する」は、前述のライブラリに含まれる関数です。
この関数を実行すると、起動している弥生給与の新規作成ウィザードを開き、「弥生株式会社」という事業所名で、導入月度を平成30年1月に設定したデータファイルを作成します。
参考: 給与データの新規作成
弥生給与の新規作成ウィザードは画面数が多く、手動で進めるのは面倒なのですが、この関数はそれを数秒で完了させてくれます。

ここで「え?日本語名の関数…?」と思われた方も多いかもしれません。
ライブラリ中では、関数などの名前に積極的に日本語名を使うようにしました。
理由としては以下です。

  • テスト対象が日本語の製品である: 弥生の製品は基本的に日本国内向けに作られており、画面上に配置される文字列も基本的に日本語です。それらをいちいち英語に翻訳するには時間がかかりますし、訳が適切でないなどの理由で意図が伝わらない可能性も考えられます。そのため、日本語は日本語のまま使った方がよい、という判断をしました。
  • スクリプトを手順書として読めるようにしたい: スクリプトを見た人が、それを手順書として内容を把握できるようにしておきたい、という狙いの他にも、手動テスト用のテスト手順書をベースにスクリプトを作成したとき、スクリプトの内容が手順書と乖離していないことを確認しやすくしたい、と考えました。

テストフレームワークを作成する

ここまでで手動テストをある程度自動化できてきましたが、それを手動テストの代替として使うには、スクリプトがきちんとテストとして振る舞える仕組みが必要だと考えました。
「きちんとテストとして振る舞える」とは、具体的には以下のことだと考えました。

  1. 期待値と実際の値を比較できる
  2. テストの実施結果からレポートを作成できる
  3. 期待通りにテスト対象が動作しなかった場合にテストを終了できる

1は他のプログラミング言語におけるassertのようなものがあればよさそうです。

2は1の結果などを保存しておき、テスト終了時に整形してprintするようにします。
他のプログラミング言語のユニットテストには、結果を詳細に表示しないものもありますが、本件では開発プロセス中のテストフェーズで実施することを想定しているため、実施に関する情報(日時、OS等)も含めてなるべく詳細なレポートを作成するようにします。

3がやや問題で、UWSCではexitexit文を実行するとスクリプトの実行を強制終了させることができるのですが、そうすると2のレポート作成が実行されずに終了してしまうので、テスト実施者は何が起きたのかわからなくなってしまいます。
テストが途中で終了されたらそのことがわかり、レポートも必ず生成されるような仕組みが必要です。

これらの条件を満たすような仕組みを実装していったところ、結果的にテストフレームワークのようなものができあがりました。
このテストフレームワークを使ったスクリプトの例を以下に示します。

// テストフレームワークの読み込み
call yayoi-uwsc-lib\testing

// テスト開始処理
try; try; TEST.START()

// ↓ここからテストスクリプト本体を記述する
TEST.TESTSECTION("テスト1")

dim a = 1
TEST.TESTCASE("テスト1-1", a, 1)
TEST.TESTCASE("テスト1-2", a, 2)
// ↑ここまでテストスクリプト本体を記述する

// テスト終了処理
except; TEST.FAIL(TRY_ERRMSG + " @ " + TRY_ERRLINE); endtry
finally; TEST.END(); endtry

「テスト開始処理」と「テスト終了処理」がだいぶ呪文になっていますが…。
これはUWSCのtry-except構文と、try-finally構文を入れ子にしてあるものを、複文でまとめて書いてあります。
きちんと整形すると、以下のようになります。

try
  try
    TEST.START()
    // テスト本体...
  except
    TEST.FAIL(TRY_ERRMSG + " @ " + TRY_ERRLINE)
  endtry
finally
  TEST.END()
endtry

まずTESTというのが、UWSCのモジュール定義を使って書かれたモジュールです。
UWSCのモジュールは、シングルトン限定のクラスのようなもの、と考えてください。
このTESTモジュールがテストフレームワークの実装になります。

TESTモジュールは以下のような関数を保有しています。

  • START() : テストの開始処理を行う。開始時刻の記録など。
  • END() : テストの終了処理を行う。レポートの出力など。
  • FAIL(エラーメッセージ) : 実行した時点でテストを失敗として強制終了させる
  • TESTCASE(テストケース名, 期待値, 実際の値) : 値の比較を行い、その結果をレポートに追加する。
  • TESTSECTION(セクション名) : 上記TESTCASEをグループ分けする。今のところはレポートの表のヘッダを出力しているだけ…。

テスト本体は、try-excepttry節で実行されます。
これにより、テストの処理中にエラーが発生した場合、except節に飛ばされ、TEST.FAILが実行されます。
TEST.FAILは、テストが失敗であるとレポートに出力するよう設定して、テストを強制終了させます。

外側のtry-finallyは、テストの最後に必ずTEST.ENDを実行させるためのものです。
TEST.ENDは、レポートの出力処理などを行います。
なお、finally節が必ず実行されるように、テストフレームワーク内部ではOPTFINALLYオプションを設定しています。

このような書き方にすることによって、スクリプト終了時に必ずテスト結果のレポート出力が行われるようにしました。
実際に上記のスクリプトを実行すると、UWSCのログウィンドウに以下のようにレポートが出力されます。

UWSCのログウィンドウにレポートのテキストが出力される

レポートのテキストはTracのWiki文法にて整形するようにしてみました。
(弥生ではTracをよく使っています。参考: 6日目の記事
レポートをそのままTracのWikiやチケットにコピペするだけで、テストの実施結果を共有できるようにすることを狙いました。
実際にTracに貼り付けると、以下のようになります。

レポートをTracに貼り付けた様子

テストスクリプトのテンプレートを作成する

これらのライブラリやテストフレームワークを利用してテストを書こう、と思った場合、例えば前述のテストフレームワークの呼び出しなどをゼロから書くのは難しいため、スクリプトを書き始めるためのテンプレートを用意しました。
以下に、テンプレートの抜粋を示します。

//******************************************************************************
// (このスクリプトが何を行うかを説明する)
//******************************************************************************

//==============================================================================
// ライブラリの読み込み
//==============================================================================

call yayoi-uwsc-lib\kyuyo
call yayoi-uwsc-lib\testing

// ↓他にファイルを読み込む場合はここに書く↓


//==============================================================================
// メインスクリプト
//==============================================================================

// テスト開始処理
try; try; TEST.START()

//------------------------------------------------------------------------------
// ↓ここからテストスクリプト本体を記述する↓
//------------------------------------------------------------------------------



//------------------------------------------------------------------------------
// ↑ここまでテストスクリプト本体を記述する↑
//------------------------------------------------------------------------------

// テスト終了処理
except; TEST.FAIL(TRY_ERRMSG + " @ " + TRY_ERRLINE); endtry
finally; TEST.END(); endtry

//==============================================================================
// 関数・モジュール定義
//==============================================================================

// ↓このスクリプトで使う関数やモジュールの定義はここに書く↓

ガイドコメントに従って書いていくことで、ある程度スクリプトを書きやすくすることを狙いました。
ここまでで、他の人がテストを書くために使う道具を準備できました。

チームに展開する

テストをチームメンバーに書いてもらうために、チームに展開する活動を行いました。
具体的には以下のようなことです(といっても最低限やるべきようなことばかりで、特別なことはしていないです)。

  • 作り方を文書化する:テストの書き方や、テストを書いていく上でのノウハウなどを文書にして共有しておく。
  • 説明する: 説明会を開き、上記の文書をもとに、活動の狙いなども含めてチームに説明。
  • サポートする: スクリプトの作成でわからないことや困ったことがあれば、いつでも相談に乗れるようにしておく。

この記事の執筆時点では、チームメンバーがいくつかのテストシナリオについて自動化を進めてくれている…といった状況です。
この取り組みがどれくらいの効果を出せるかは、まだこれからの話になります。

おまけ:UWSCの選定について

UWSCは、現在開発が停止している状態であり、今後のアップデートやサポートは期待できない状況にあります(参考)。
そんな状況で、このままUWSCを使い続けてよいのか? ということについてはかなり悩みました。
結局、以下のような理由で、UWSCを当分は使い続けることにしました。

  • 配布自体は続いているので、現状のまま使い続けることは可能
  • 最悪、乗り換える先がある: 同様の自動操縦ツールにメジャーなものとしてAutoItがあり、これはUWSCでできることはほぼできるので、代替は可能

おわりに

UWSCを使ってテストを自動化する取り組みをご紹介してきました。
もしみなさまの何かの参考になれば幸いです…。

明日は@yusuke_yayoiさんの「AWS AppSyncについて勉強してみた」です。楽しみですね!