ソフトウェア工学1 第5回設計(2tsnaka/lecture/se1/第5回 設計2.pdf ·...
TRANSCRIPT
ソフトウェア工学1第5回 設計(2)
2017年5月23日
中島
1
授業内容
• 前編– 設計とは
– 設計プロセス
– 設計で重要なこと
• アーキテクチャ記述
• 抽象データ型
• 後編– 手法
– パターン
2
設計プロセス: 2つのプロセス(おさらい)
抽象から詳細へ、論理から物理へ
ソフトウェア要求仕様書
ソフトウェア外部設計 ソフトウェアの方式的記述
・アーキテクチャ・コンポーネント・外部インタフェース・コンポーネント間インタフェース・データ
プログラムの仕様記述・モジュール構成・モジュール仕様・データ構造とアルゴリズム
ソフトウェア内部設計
ソフトウェア外部設計書
ソフトウェア内部設計書
3
良い設計を行うためには
設計知識を利用する
– 設計手法に則ること
• 記述法: 作るものの記述法
• 手順: 作るためのステップ
• 品質基準: 各ステップでの確認事項
– パターンを使うこと
• 課題: 解決すべき問題
• 前提: 問題の背景、前提事項、制約
• 解決策: 問題解決の核心的アイデア
4
設計プロセスと設計知識
2つの設計段階と設計知識
ソフトウェア要求仕様
ソフトウェアの方式的記述
ソフトウェア外部設計
プログラム仕様
ソフトウェア内部設計
アーキテクチャコンポーネント
モジュール
設計手法
パターン
モジュール化手順
モジュール化基準
アーキテクチャパターン
デザインパターン イディオム
抽象データ型アーキテクチャ記述
抽象データ型カタログ
問題非依存
問題依存
言語知識依存
記述法
品質基準
手順
5
設計手法に則ること: 設計手法例で説明
複合/構造化設計: G. J. Myers データフロー図を使い,段階的に機能の詳細化を行い,プログラムモジュールまで展開する設計手法
記述法: データフロー図モジュール構成図
手順: STS分解
TR分解
品質基準: モジュール強度
モジュール間結合度
6
複合/構造化設計: 記述法
モジュール構成図データフロー図
処理A処理B
処理C
データストア
データa
データbデータc
モジュールA
モジュールB
モジュールC モジュールD
モジュールE モジュールF
7
• データフロー図: 機能の分解に用いる図
• モジュール構成図: モジュールの呼び出し関係図
複合/構造化設計:手順1 (1)
STS分解
A B入力A 出力B
入力部 処理部 出力部
処理 A処理 B
処理 C
データストア
データ a
入力Aデータ c
出力B
入力Aの最大抽象点
モジュールA.1
モジュールA.2
モジュールA.3 モジュール
A.1モジュール
A.2モジュール
A.3
モジュールA
出力Bの最大抽象点
入力A
A’ B’出力B
A’ B’
機能をデータの変換過程と捉え、機能を入力(Source)、処理(Transform)、出力(Sink) 3つの機能に分解する方法
8
複合/構造化設計:手順1 (2)
TR (Transaction) 分解
入力データに応じて処理が変わる場
合に用いるモジュール分割方法
C<0
C=0
C>0
処理 A処理 B
処理C
データストア
データ a
入力Aデータ c
出力B
データC
処理1
処理2
処理3
モジュールC.1
モジュールC.2
モジュールC.3
データD
モジュールC.1
モジュールC.2
モジュールC.3
モジュールC
データDデータD
データD
9
複合/構造化設計: 品質基準その1
モジュール間結合度: 2つのモジュール間の結合の強さ
弱
強
①データ結合(引数直接渡し)
②スタンプ結合(構造体渡し)
③制御結合(制御変数を渡す)
④外部結合(外部データを参照)
⑤共有結合(共有データ領域を参照)
⑥内容結合
必要なデータが明示的に渡される
10
データ結合(引数直接渡し)の例
#include <stdio.h>
struct student {char name[40];char id[8];char birthday[9];char school;
};
void make_initial(char* name, char* initial) // 直接処理に使うデータのみ渡す{
char first[20], last[20];sscanf(name, "%s %s", first, last);sprintf(initial, "%c%c", first[0], last[0]);
}
int main(void) {struct student s = {"Taro Shibaura", "AL15126", "19960331", 'L'};char initial[3];
make_initial(s.name, initial);printf("%s's initial is %s¥n", s.name, initial);printf("%s's initial is %s¥n", s.name, to_lower(initial));
return 0;}
11
関数A 関数B
直接必要なデータを渡す
必要
スタンプ結合(構造体渡し)の例
#include <stdio.h>
struct student {char name[40];char id[8];char birthday[9];char school;
};
void make_initial(struct student *s, char* initial) // 構造体を丸ごと渡す{
char first[20], last[20];sscanf(s->name, "%s %s", first, last);sprintf(initial, "%c%c", first[0], last[0]);
}
int main(void) {struct student s = {"Taro Shibaura", "AL15126", "19960331", 'L'};char initial[3];
make_initial(&s, initial);printf("%s's initial is %s¥n", s.name, initial);
return 0;}
12
関数A 関数B
構造体丸ごとデータを渡す必要
不要
外部結合(外部データを参照)の例
#include <stdio.h>
struct student {char name[40];char id[8];char birthday[9];char school;
} s = {“Taro Shibaura”, “AL15126”, “19960331”, ‘L’}; // 外部変数の定義(関数外)
void make_initial(char* initial){
extern struct student s; // 外部変数の参照宣言char first[20], last[20];sscanf(s.name, "%s %s", first, last);sprintf(initial, "%c%c", first[0], last[0]);
}
int main(void) {extern struct student s; // 外部変数の参照宣言char initial[3];
make_initial(initial);printf("%s's initial is %s¥n", s.name, initial);
return 0;}
13
関数A 関数B
外部変数 外部データで渡す
共有結合(共有データ領域を参照)の例
#include <stdio.h>
char s[100]; // 共有データ領域の定義
void make_initial(){
sscanf(&s[0], “%s %s”, &s[61], &s[81]); // 決められた番地にアクセス!sprintf(&s[58], "%c%c", s[61], s[81]);
}
int main(void) {sprintf(&s[0], "Taro Shibaura");sprintf(&s[40],"AL15126");sprintf(&s[48],"1996331");s[57] = 'L';
make_initial();printf("%s's initial is %s¥n", &s[0], &s[58]);
return 0;}
14
関数A 関数B
共有データ領域
制御結合例
#include <stdio.h>#include <ctype.h>
struct student { char name[40]; char id[8]; char birthday[9]; char school };
void make_initial(char* name, char* initial, int lflag) // 制御指令をフラグで渡す!
{char first[20], last[20];sscanf(name, "%s %s", first, last);if (lflag == 1)
sprintf(initial, "%c%c", tolower(first[0]), tolower(last[0]));else
sprintf(initial, "%c%c", first[0], last[0]);}
int main(void) {struct student s = {"Taro Shibaura", "AL15126", "19960331", 'L'};char initial[3];
make_initial(s.name, initial, 0);printf("%s's initial is %s¥n", s.name, initial);make_initial(s.name, initial, 1);printf("%s's initial is %s¥n", s.name, initial);
return 0;} 15
プログラムにおける主な依存関係
• 関数間: 呼び出し関係
• 関数ー変数間: 外部変数参照
• クラス間(C++,Java): 継承関係
• ファイル間: インクルード関係
16
参照を追加するのは簡単だが・・・
→ 複雑になると、修正の影響が思わぬところ発生する場合がある
⇒ しっかり設計しよう!
複合/構造化設計: 品質基準その2
モジュール強度: モジュール内の機能間の関連性
強
弱
①情報的強度
②機能的強度
③連絡的強度(手順的強度+データ依存のあるもの)
④手順的強度(手順として起動する機能群)
⑤時間的強度(ある特定のタイミングに起動する機能群)
⑥論理的強度(制御フラグで機能選択される機能群)
⑦暗号的強度(独立した機能の寄せ集め)
単機能であること
カプセル化していること優劣はない
17
情報強度例: double型のキュー
#ifndef _MYQUEUE#define _MYQUEUE
// Double型のキューの仕様を記述
extern double queueGet(void); // キューからデータを取り出す
extern int queuePut(double); // キューにデータを入れる
//(-1:オーバーフロー, 0:正常)
extern int isQueueEmpty(void); // キューが空かどうかを調べる
// (1:True, 0:false)
#endif
18
抽象データ型「キュー」の仕様 myqueue.h
情報強度例: double型のキュー
double queueGet(void){
double value = 0;// 頭が縮みデータを取り出すif (isQueueEmpty() == 0) {
value = queue[head];head = (head + 1) % MY_QUEUE_SIZE;
}return value;
}
int isQueueEmpty(void){
return (head == tail) ? 1 : 0;}
19
抽象データ型「キュー」の実装 myqueue.c#include "myqueue.h"#define MY_QUEUE_SIZE 100
// 配列を循環的に使うことによるキューの実現static double queue[MY_QUEUE_SIZE];static int tail = 0; // キューの最後尾static int head = 0; // キューの最前列
int queuePut(double d){
int stat = 0;// 尾が伸びてデータが入るif ((tail + 1) % MY_QUEUE_SIZE == head) {
stat = -1;}else {
queue[tail] = d;tail = (tail + 1) % MY_QUEUE_SIZE;
}return stat;
}
パターンを用いること
パターン:
– 型、思考や行動の様式、見本
– ボキャブラリを提供すること (例: ゴシック様式)
– 繰返し使われること
– 問題ドメイン固有の知識の蓄積
• 要求
• 制約
• 解決策
20
パターンの記述内容
名称: パターンを識別するための名称
前提: 課題が生じる文脈(利用可能な状況)や制約
課題: 前提下で繰り返し現れる要求
解決策: 課題に対する解決策。以下の側面から記述:
– 構造: 構成要素と構成要素間の関係
– ふるまい: 実行時のプログラムの構造と実行シナリオ
– 設計根拠:要求と制約が、解決策でどのように達成するか
– 具体的な適用例
21
パターンの利用例
アーキテクチャパターンの例:
– 名称: Model-View-Controller (MVC)– 前提: 人間とコンピュータ間のインタフェースに柔軟性を
持たせた対話型アプリケーション
– 課題:
[課題1] 入力手段と表示手段を頻繁に変更したい
[課題2] 同一情報を異なる表示手段で表示したい
[課題3] データの変化を、即時に表示手段に反映したい
0
2 0
4 0
6 0
8 0
1 0 0
1 月 2 月 3 月 4 月
東京
名古屋
大阪
1 月 2 月 3 月 4 月東京 20.4 27.4 90 20.4名古屋 30.6 38.6 34.6 31.6大阪 45.9 46.9 45 43.9
入力手段表示手段
データ
データ処理
22
Model-View-Controller (MVC)の例
解決策: コンポーネント間の静的な構造
Model
コアデータ
データ処理
データ
0
2 0
4 0
6 0
8 0
1 0 0
1 月 2 月 3 月 4 月
東京
名古屋
大阪
1 月 2 月 3 月 4 月東京 20.4 27.4 90 20.4名古屋 30.6 38.6 34.6 31.6大阪 45.9 46.9 45 43.9
処理(Model) - 出力(View) - 入力(Controller)の3つの部分に分割する
入力UI部品
出力UI部品
Controller
View表示データ
イベント-処理変換
*
*
23
Model-View-Controller (MVC)の例
[課題1] 入力手段と表示手段を頻繁に変更したい
解決策: ユーザインタフェースを動的に切り替える– Modelから、ViewとControllerへの接続を、ユーザ操作で切り
替えできるようにする。
利点: ユーザインタフェース(入力と表示の形態)は頻繁に変更できる
0
2 0
4 0
6 0
8 0
1 0 0
1 月 2 月 3 月 4 月
東京
名古屋
大阪
1 月 2 月 3 月 4 月東京 20.4 27.4 90 20.4名古屋 30.6 38.6 34.6 31.6大阪 45.9 46.9 45 43.9
Model入力の変更
表示の変更
Controller1
View1
View2
Controller2購読者
操作者
データ
24
Model-View-Controller (MVC)の例
[課題2] 同一情報を異なる表示手段で表示したい
解決策:表示用部品をデータの表示用として登録する
– Modelの購読者として、異なる種類のViewを複数個登録できる。
利点: 同一情報を異なる形式で表示できる
0
2 0
4 0
6 0
8 0
1 0 0
1 月 2 月 3 月 4 月
東京
名古屋
大阪
1 月 2 月 3 月 4 月東京 20.4 27.4 90 20.4名古屋 30.6 38.6 34.6 31.6大阪 45.9 46.9 45 43.9
同一データを2種類のViewで表示
Controller1
View1
View2
Model
購読者
登録
登録
グラフ
表データ
25
Model-View-Controller (MVC)の例
[課題3] データの変化を、即時に表示手段に反映したい
解決策: 出力UI部品に変更を伝え、部品が表示更新する– Modelからデータを参照している部品(購読者)に向けてデータの
更新を連絡し、購読者のViewは自ら表示を更新する
利点: データが変更すると、それを直ちに表示に反映できる
Model
データ0
2 0
4 0
6 0
8 0
1 0 0
1 月 2 月 3 月 4 月
東京
名古屋
大阪
1 月 2 月 3 月 4 月東京 20.4 27.4 90 20.4名古屋 30.6 38.6 34.6 31.6大阪 45.9 46.9 45 43.9
Controller1
View1
View2
②データ更新通知
①データ更新
③表示更新
購読者
26
今日のまとめ
• 再利用可能な設計知識の2つの形
– 手法: 記述法、手順、品質基準
– パターン: 名称、課題、前提、解決策
27