Slide 1

Slide 1 text

【連続講座】ソフトウェア設計原則 【 SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) パーソルクロステクノロジー株式会社 第1 技術開発本部 第4 設計部 設計2 課 阿部耕二 2023 @juraruming

Slide 2

Slide 2 text

目次 自己紹介 SOLID について 単一責務の原則(single-responsibility principle) について テーマ説明 原則違反のコード例 原則違反のコード改善例 設計についてのディスカッション・質問 今回の設計所感 参考資料 2

Slide 3

Slide 3 text

自己紹介 名前: 阿部 耕二(あべ こうじ) 所属: パーソルクロステクノロジー株式会社   第1 技術開発本部 第4 設計部 設計2 課 医療機器の組込みソフトウェア開発。C 言語。 趣味: 宇宙開発(リーマンサットプロジェクト広報メンバー) LAPRAS ポートフォリオ: https://lapras.com/public/k-abe Twitter: @juraruming 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 2023 @juraruming 3

Slide 4

Slide 4 text

SOLID について 設計の5 原則の頭文字をとったもの。 S 単一責務の原則(Single Respomsibility Principle ) O オープン・クローズドの原則(Open Closed Principle ) L リスコフの置換原則(Liskov Substitution Principle ) I インターフェイス分離の原則(Interface Segregation Principle ) D 依存関係逆転の原則(Dependency Inversion Principle ) 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 4

Slide 5

Slide 5 text

SOLID 原則の重要性 凝集度が高くなる 他のモジュールと疎結合になる 各モジュールの目的が明確に分けられると、コード変更の際の影響 は局所化される。結果、テストしやすい設計になる。 上記の特徴を持つと再利用しやすいコードになる。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 参考資料2 より引用 “ “ 5

Slide 6

Slide 6 text

単一責務の原則 (single-responsibility principle) について モジュールは単一の責務を持つようにする。 モジュールはひとつのことだけをすべきであり、変更の理由もひと つであるべきということ。 単一責務の原則を適用するとモジュールの凝集度が高くなる。 まとまりのあるひとつの目的を持つ関数とデータから構成されるモ ジュールになる。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 参考資料2 より引用 “ “ 6

Slide 7

Slide 7 text

テーマ説明 趣味で開発した連射測定ゲームをテーマにする。 連射測定ゲームとは??? 私の少年時代の1980 年後半に流行ったシュウォッチをマイコンボード で再現したもの。 動画はこちら 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 7

Slide 8

Slide 8 text

ゲーム実行画面 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 8

Slide 9

Slide 9 text

ゲーム環境紹介 Spresense メインボード Spresense 拡張ボード 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ 9

Slide 10

Slide 10 text

ゲーム環境紹介2 APS 学習ボード: このボードにLED, スイッチが実装されている 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 10

Slide 11

Slide 11 text

ゲーム環境紹介 3 Spresense メインボード + 拡張ボード + APS 学習ボード 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 11

Slide 12

Slide 12 text

マイコン内部ブロッ ク図 Spresense のプロセッサ CXD5602 の内部ブロック図 こちらから引用 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ 12

Slide 13

Slide 13 text

マイコン ソフトウ ェアフレームワーク Spresense SDK の構造 こちらから引用 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ 13

Slide 14

Slide 14 text

原則違反のコード例 対象コード: https://github.com/grace2riku/spresense_game/tree/main/shooting_ watch shooting_watch_main.c shooting_watch_gpio.c 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 14

Slide 15

Slide 15 text

概要のクラス図 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 15

Slide 16

Slide 16 text

原則違反コードの改善ポイント 1 1. ファイル名から責務が想像できない ファイル名から処理内容が想像できたほうが良い。 shooting_watch_gpio.c ではGPIO を超えた上位の知識を持ってい る。 LED の名称(USER_LED1, USER_LED2 )、スイッチの名称(SW1, SW2 )を知っているのは上位のモジュールでよい。LED 、スイッチ などのデバイスがマイコンのどのピンに接続されているか知ってい るのは下位のソフトウェアモジュールというのが自然。 上位は下位より抽象的な概念を扱う(下位は具体的)。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 16

Slide 17

Slide 17 text

原則違反コードの改善ポイント 1 ソースコード // APS 学習ボードピンアサイン #define SWITCH_1 (39) #define SWITCH_2 (29) void shooting_watch_gpio_create(void) { /* 割り込み設定 */ board_gpio_intconfig(SWITCH_1, INT_FALLING_EDGE, true, shooting_watch_gpio_switch_1_handler); board_gpio_intconfig(SWITCH_2, INT_FALLING_EDGE, true, shooting_watch_gpio_switch_2_handler); if (board_gpio_int(SWITCH_1, true) < 0) { message("gpio_create board_gpio_int(switch_1) failure.\n"); } if (board_gpio_int(SWITCH_2, true) < 0) { message("gpio_create board_gpio_int(switch_2) failure.\n"); } return; } 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 17

Slide 18

Slide 18 text

原則違反コードの改善ポイント 2 2. shooting_watch_gpio.c はGPIO デバイスドライバーとしての責務を 超えている デバイスドライバーは上位からの指示でデバイス制御することで再 利用しやすくなる。 デバイスドライバーの中に割り込みハンドラが書かれている。デバ イスドライバ単体で再利用しにくい。割り込みハンドラにはデバイ スドライバより抽象的なアプリケーションロジックを書きたいこと もある。 デバイスドライバより上位の層に割り込みハンドラを書けるように デバイスドライバは関数仕様を考えた方が使い勝手がよさそう。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 18

Slide 19

Slide 19 text

原則違反コードの改善ポイント 2 ソースコード static int shooting_watch_gpio_switch_1_handler(int irq, FAR void *context, FAR void *arg) { ++shooting_count; return 0; } static int shooting_watch_gpio_switch_2_handler(int irq, FAR void *context, FAR void *arg) { int sw2_status = board_gpio_read(SWITCH_2); int sw1_status = board_gpio_read(SWITCH_1); if (sw1_status && !sw2_status) next_state = true; if (!sw1_status && !sw2_status) exit_shooting_watch = true; return 0; } 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 19

Slide 20

Slide 20 text

原則違反コードの改善ポイント 3 3. main 関数でハードウェアアクセス関数を呼び出している 責務を意識せず作り込みしソフトウェアの階層を無視している。 アプリケーションは実現方法( ハードウェアの具体的制御) を意識し ないつくりが良い。 アプリケーション部でハードウェアアクセスすると非常に再利用し にくいコードになる。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 20

Slide 21

Slide 21 text

原則違反コードの改善ポイント 3 ソースコード int main(int argc, FAR char *argv[]) { int one_time_counter = 0; int game_coundown; shooting_watch_gpio_create(); printf("Hello, shooting_watch!!!\n"); while(exit_shooting_watch != true) { switch (game_state) { case STOP: if (one_time_counter == 0) { printf("----- Press SW2 to start the game. When the game starts, shoot SW1 continuously.-----\n"); printf("----- Press SW1 and SW2 to end the game.-----\n"); one_time_counter = 1; board_gpio_write(USER_LED_1, USER_LED_TURN_OFF); board_gpio_write(USER_LED_2, USER_LED_TURN_OFF); } 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 21

Slide 22

Slide 22 text

原則違反のコード改善例 対象コード: https://github.com/grace2riku/spresense_game/tree/refactoring_srp/ shooting_watch aps_lerning_board_bsp.c, aps_lerning_board_bsp.h controller.c, controller.h game.c, game.h initialize.c, initialize.h main.c shooting_count.c, shooting_count.h 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 22

Slide 23

Slide 23 text

改善クラス図 23

Slide 24

Slide 24 text

【再掲】原則違反コードの改善ポイント 1 1. ファイル名から責務が想像できない ファイル名から処理内容が想像できたほうが良い。 shooting_watch_gpio.c ではGPIO を超えた上位の知識を持ってい る。 LED の名称(USER_LED1, USER_LED2 )、スイッチの名称(SW1, SW2 )を知っているのは上位のモジュールでよい。LED 、スイッチ などのデバイスがマイコンのどのピンに接続されているか知ってい るのは下位のソフトウェアモジュールというのが自然。 上位は下位より抽象的な概念を扱う(下位は具体的)。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 24

Slide 25

Slide 25 text

改善ポイント 1 の改善内容 ファイル名から責務が想像できるようにファイル名変更、分割をお こなった -> 改善クラス図を参照 部品の名称を知っているモジュール、部品のピン番号をしっている モジュールを明確にした 部品の名称はaps_lerning_board_bsp.h に定義し公開する。 ピン番号はaps_lerning_board_bsp.c に実装しヘッダーファイルで公 開しないようにした。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 25

Slide 26

Slide 26 text

【再掲】原則違反コードの改善ポイント 2 2. shooting_watch_gpio.c はGPIO デバイスドライバーとしての責務を 超えている デバイスドライバーは上位からの指示でデバイス制御することで再 利用しやすくなる。 デバイスドライバーの中に割り込みハンドラが書かれている。デバ イスドライバ単体で再利用しにくい。割り込みハンドラにはデバイ スドライバより抽象的なアプリケーションロジックを書きたいこと もある。 デバイスドライバより上位の層に割り込みハンドラを書けるように デバイスドライバは関数仕様を考えた方が使い勝手がよさそう。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 26

Slide 27

Slide 27 text

改善ポイント 2 の改善内容 LED 、スイッチの制御はaps_lerning_board_bsp のled_onoff, read_user_switch のみから行う方針にした。 割り込みハンドラはデバイスドライバではなく、上位の層に定義し た controller -> nextstate_and_exit_interrupt ゲーム開始・終了のスイッチ操作の割り込み shooting_count -> shooting_count_interrupt 連射測定の割り込み 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 27

Slide 28

Slide 28 text

【再掲】原則違反コードの改善ポイント 3 3. main 関数でハードウェアアクセス関数を呼び出している 責務を意識せず作り込みしソフトウェアの階層を無視している。 アプリケーションは実現方法( ハードウェアの具体的制御) を意識し ないつくりが良い。 アプリケーション部でハードウェアアクセスすると非常に再利用し にくいコードになる。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 28

Slide 29

Slide 29 text

改善ポイント 3 の改善内容 階層構造と責務の分担を意識してクラス図を書いた 上の層はゲームの目的を達成するロジックを担当する部分 下の層はゲームの目的を達成するための具体的な手段を担当するよ う意識した。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 29

Slide 30

Slide 30 text

設計についてのディスカッション・質 問 自分以外の設計の視点が学びになると個人的に考えています。 ぜひぜひお気軽にフィードバックをよろしくお願いします こちらに学習の振り返りに使う目的でZenn のスクラップを用意しま した。 活用ください。 【SOLID 原則】" 単一責任の原則 (single-responsibility principle)" の勉 強会後の振り返り 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 30

Slide 31

Slide 31 text

今回の設計所感 階層化を意識してファイル分割、責務分割をしたつもり。階層化の デメリットとして高速な動作を求められる場合は改善が必要だと感 じた。 設計する際は性能などの非機能要件を考慮することも大事だと改め て認識した。 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 31

Slide 32

Slide 32 text

参考資料 1. オブジェクト指向習得のための5ステップ【SOLID 原則】 2. テスト駆動開発による組み込みプログラミング―C 言語とオブジェク ト指向で学ぶアジャイルな設計 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 32

Slide 33

Slide 33 text

ご清聴ありがとうございました 【連続講座】ソフトウェア設計原則【SOLID 】を学ぶ #1 単一責務の原則(single-responsibility principle) 2023 @juraruming 33