3.2K Views
July 20, 23
スライド概要
2023/7/20(木)社内勉強会、2023/7/21(金)Twitterスペースの資料です。
学習の振り返り・深掘りにZennのスクラップを用意しました。ご利用ください。
https://zenn.dev/k_abe/scraps/9533a6f533dd8a
組込みソフトウェアエンジニア。 技術バックボーンはC言語・ベアメタル。 CQ EVカートのオーナーで、ハード・ソフトウェアの改造を通じて自身のスキルアップを日々考え中・・・。 LAPRASポートフォリオ: https://lapras.com/public/k-abe GitHub: http://github.com/grace2riku Qiita: https://qiita.com/juraruming Zenn: https://zenn.dev/k_abe よろしくね。
【連続講座】ソフトウェア設計原則 【SOLID】を学ぶ 単一責務の原則(single-responsibility principle) パーソルクロステクノロジー株式会社 第1技術開発本部 第4設計部 設計2課 阿部耕二 #1 2023 @juraruming
目次 自己紹介 SOLID について 単一責務の原則(single-responsibility principle)について テーマ説明 原則違反のコード例 原則違反のコード改善例 設計についてのディスカッション・質問 今回の設計所感 参考資料 2
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 自己紹介 名前: 阿部 耕二(あべ こうじ) 所属: パーソルクロステクノロジー株式会社 第1技術開発本部 第4設計部 設計2課 医療機器の組込みソフトウェア開発。C言語。 趣味: 宇宙開発(リーマンサットプロジェクト広報メンバー) LAPRAS ポートフォリオ : https://lapras.com/public/k-abe Twitter: @juraruming 2023 @juraruming 3
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) SOLID について 設計の5原則の頭文字をとったもの。 S 単一責務の原則( Single Respomsibility Principle ) O オープン・クローズドの原則( Open Closed Principle ) L リスコフの置換原則( Liskov Substitution Principle ) I インターフェイス分離の原則( Interface Segregation Principle ) D 依存関係逆転の原則( Dependency Inversion Principle ) 4
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) SOLID 原則の重要性 参考資料2より引用 凝集度が高くなる 他のモジュールと疎結合になる 各モジュールの目的が明確に分けられると、コード変更の際の影響 は局所化される。結果、テストしやすい設計になる。 上記の特徴を持つと再利用しやすいコードになる。 “ “ 5
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 単一責務の原則(single-responsibility principle)について 参考資料2より引用 モジュールは単一の責務を持つようにする。 モジュールはひとつのことだけをすべきであり、変更の理由もひと つであるべきということ。 単一責務の原則を適用するとモジュールの凝集度が高くなる。 まとまりのあるひとつの目的を持つ関数とデータから構成されるモ ジュールになる。 “ “ 6
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) テーマ説明 趣味で開発した連射測定ゲームをテーマにする。 連射測定ゲームとは??? 私の少年時代の1980年後半に流行ったシュウォッチをマイコンボード で再現したもの。 動画はこちら 7
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) ゲーム実行画面 8
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ ゲーム環境紹介 メインボード Spresense 拡張ボード Spresense 9
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) ゲーム環境紹介2 APS 学習ボード: このボードにLED, スイッチが実装されている 10
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) ゲーム環境紹介3 Spresense メインボード + 拡張ボード + APS学習ボード 11
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ マイコン内部ブロッ ク図 のプロセッサ の内部ブロック図 こちらから引用 Spresense CXD5602 12
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ マイコン ソフトウ ェアフレームワーク の構造 こちらから引用 Spresense SDK 13
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 原則違反のコード例 対象コード: https://github.com/grace2riku/spresense_game/tree/main/shooting_ watch shooting_watch_main.c shooting_watch_gpio.c 14
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 概要のクラス図 15
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 原則違反コードの改善ポイント1 1. ファイル名から責務が想像できない ファイル名から処理内容が想像できたほうが良い。 shooting_watch_gpio.c では GPIO を超えた上位の知識を持ってい る。 LED の名称( USER_LED1, USER_LED2 )、スイッチの名称( SW1, SW2 )を知っているのは上位のモジュールでよい。 LED 、スイッチ などのデバイスがマイコンのどのピンに接続されているか知ってい るのは下位のソフトウェアモジュールというのが自然。 上位は下位より抽象的な概念を扱う(下位は具体的)。 16
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle)
原則違反コードの改善ポイント1 ソースコード
学習ボードピンアサイン
// APS
#define SWITCH_1
#define SWITCH_2
(39)
(29)
void shooting_watch_gpio_create(void)
{
/*
*/
board_gpio_intconfig(SWITCH_1, INT_FALLING_EDGE,
board_gpio_intconfig(SWITCH_2, INT_FALLING_EDGE,
割り込み設定
true, shooting_watch_gpio_switch_1_handler);
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;
}
17
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 原則違反コードの改善ポイント2 2. shooting_watch_gpio.c はGPIOデバイスドライバーとしての責務を 超えている デバイスドライバーは上位からの指示でデバイス制御することで再 利用しやすくなる。 デバイスドライバーの中に割り込みハンドラが書かれている。デバ イスドライバ単体で再利用しにくい。割り込みハンドラにはデバイ スドライバより抽象的なアプリケーションロジックを書きたいこと もある。 デバイスドライバより上位の層に割り込みハンドラを書けるように デバイスドライバは関数仕様を考えた方が使い勝手がよさそう。 18
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 原則違反コードの改善ポイント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; } 19
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 原則違反コードの改善ポイント3 関数でハードウェアアクセス関数を呼び出している 責務を意識せず作り込みしソフトウェアの階層を無視している。 アプリケーションは実現方法(ハードウェアの具体的制御)を意識し ないつくりが良い。 アプリケーション部でハードウェアアクセスすると非常に再利用し にくいコードになる。 3. main 20
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle)
原則違反コードの改善ポイント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);
}
21
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 原則違反のコード改善例 対象コード: 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 22
改善クラス図 23
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 【再掲】原則違反コードの改善ポイント1 1. ファイル名から責務が想像できない ファイル名から処理内容が想像できたほうが良い。 shooting_watch_gpio.c では GPIO を超えた上位の知識を持ってい る。 LED の名称( USER_LED1, USER_LED2 )、スイッチの名称( SW1, SW2 )を知っているのは上位のモジュールでよい。 LED 、スイッチ などのデバイスがマイコンのどのピンに接続されているか知ってい るのは下位のソフトウェアモジュールというのが自然。 上位は下位より抽象的な概念を扱う(下位は具体的)。 24
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 改善ポイント1の改善内容 ファイル名から責務が想像できるようにファイル名変更、分割をお こなった -> 改善クラス図を参照 部品の名称を知っているモジュール、部品のピン番号をしっている モジュールを明確にした 部品の名称はaps_lerning_board_bsp.hに定義し公開する。 ピン番号はaps_lerning_board_bsp.cに実装しヘッダーファイルで公 開しないようにした。 25
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 【再掲】原則違反コードの改善ポイント2 2. shooting_watch_gpio.c はGPIOデバイスドライバーとしての責務を 超えている デバイスドライバーは上位からの指示でデバイス制御することで再 利用しやすくなる。 デバイスドライバーの中に割り込みハンドラが書かれている。デバ イスドライバ単体で再利用しにくい。割り込みハンドラにはデバイ スドライバより抽象的なアプリケーションロジックを書きたいこと もある。 デバイスドライバより上位の層に割り込みハンドラを書けるように デバイスドライバは関数仕様を考えた方が使い勝手がよさそう。 26
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 改善ポイント2の改善内容 、スイッチの制御はaps_lerning_board_bspのled_onoff, のみから行う方針にした。 割り込みハンドラはデバイスドライバではなく、上位の層に定義し た LED read_user_switch controller -> nextstate_and_exit_interrupt ゲーム開始・終了のスイッチ操作の割り込み shooting_count -> shooting_count_interrupt 連射測定の割り込み 27
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 【再掲】原則違反コードの改善ポイント3 関数でハードウェアアクセス関数を呼び出している 責務を意識せず作り込みしソフトウェアの階層を無視している。 アプリケーションは実現方法(ハードウェアの具体的制御)を意識し ないつくりが良い。 アプリケーション部でハードウェアアクセスすると非常に再利用し にくいコードになる。 3. main 28
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 改善ポイント3の改善内容 階層構造と責務の分担を意識してクラス図を書いた 上の層はゲームの目的を達成するロジックを担当する部分 下の層はゲームの目的を達成するための具体的な手段を担当するよ う意識した。 29
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 設計についてのディスカッション・質 問 自分以外の設計の視点が学びになると個人的に考えています。 ぜひぜひお気軽にフィードバックをよろしくお願いします こちらに学習の振り返りに使う目的でZennのスクラップを用意しま した。 活用ください。 【SOLID原則】"単一責任の原則 (single-responsibility principle)"の勉 強会後の振り返り 30
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 今回の設計所感 階層化を意識してファイル分割、責務分割をしたつもり。階層化の デメリットとして高速な動作を求められる場合は改善が必要だと感 じた。 設計する際は性能などの非機能要件を考慮することも大事だと改め て認識した。 31
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) 参考資料 オブジェクト指向習得のための5ステップ【SOLID原則】 2. テスト駆動開発による組み込みプログラミング ―C 言語とオブジェク ト指向で学ぶアジャイルな設計 1. 32
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #1 単一責務の原則(single-responsibility principle) ご清聴ありがとうございました 2023 @juraruming 33