はじめに
この記事では「構造化プログラミング」と「構造化設計」について、初学者向けに説明します。名前は似ていますが、実は コードの書き方の話 と 設計の話 で違うものです。 ただし、現場では両方をまとめて「構造化」と呼ぶことも多かったので、そのあたりも含めて紹介します。
私が新人研修からOJTを経て実務に進んだ頃はまだオブジェクト指向が普及していなかったのでこの「構造化」が大きな課題でした。そういった意味の過去形で書いていますが、今でもオブジェクト指向の前提になる大事な考え方です。またもしC言語だけで書かれているプログラムをメンテナンスする場合にも役に立つと思います。
この記事は以下の記事の流れで書いています。実際のプログラム動作確認はクラウド開発環境PaizaCloudクラウドIDEを使っています。
クラウド開発環境PaizaCloudクラウドIDEでHello World(C言語編)
ざっくりと図に書くと
簡単なテキストの図に書いてみました。
構造化プログラミングとは
構造化プログラミングは、プログラムを書くときの基本的な制御構造に関する考え方です。
プログラムを3つの制御で組み立てることを基本にします:
- 順次:上から下へ順に実行する
-
分岐:条件によって処理を分ける(
if
など) -
繰り返し:同じ処理を繰り返す(
for
やwhile
など)
これらを組み合わせれば、理論的にはどんな処理でも書けるとされています。オブジェクト指向言語であっても同じことでメソッド(C++の場合はメンバ関数と表現する場合が多いですが)内の細かい処理では「順次・分岐・繰り返し」で動いていると言えます。
小話:順次だけで書いた「1から10の足し算」
少し小話にお付き合い下さい。大学のC言語の試験で「1から10を足せ」という問題が出ました。 おそらく教授は for
文(繰り返し)を使わせたかったのでしょう。(while文でも書こうと思えば書けますね。) しかし当時の私はまだ for
文をよく理解していなかったので、素直に「順次」だけで書きました。(言い訳ですけど当時は今ほど簡単にパソコンも使えなかったのです。だからどんどん書いて実行させて覚えていくことが出来なかったのです。)
以下のような書き方です。
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 0;
a = a + 1;
a = a + 2;
a = a + 3;
a = a + 4;
a = a + 5;
a = a + 6;
a = a + 7;
a = a + 8;
a = a + 9;
a = a + 10;
printf("sum = %d\n", a);
return 0;
}
10行書ければ確かに「1から10」を足せるのです。後日、テストのフォロー授業で教授が「10行書いた学生がいた」と悔しそうに言い、次は「1から100まで足せ」を出題すると言っていました。
私は「それなら100行書いてやる」と思ったものです。
もちろん今なら以下のようにさっと for 文で書けます。for文の繰り返しで1から10と明確に書きました。
include <stdio.h>
int main(int argc, char *argv[])
{
int a = 0;
for(int i=1; i<=10; i++)
{
a = a + i;
}
printf("sum = %d\n", a);
return 0;
}
結果はどちらも55を確認しました。
構造化設計とは
一方で、構造化設計は プログラム全体をどう整理するか という設計手法です。
- データ構造(例: レコード、構造体など)を定義する
- それを操作するモジュール(関数群)を整理する
- プログラムを階層的に分割して見通しを良くする
大規模なプログラムを複数人で開発するときにも、見やすく保守しやすくするための考え方でした。
以下のようなプログラムを書いてみました。
#include <stdio.h>
#include <string.h>
// 学生データ(構造体)
typedef struct {
int id;
char name[50];
int age;
} Student;
// 操作モジュール(関数群)
void print_student(Student s) {
printf("%d:%s (%d)\n", s.id, s.name, s.age);
}
// 構造体と関数を利用してデータを操作する
int main(int argc, char *argv[])
{
Student student[2];
// 1人目
student[0].id = 1;
strcpy(student[0].name,"Taro");
student[0].age = 11;
// 2人目
student[1].id = 2;
strcpy(student[1].name,"Hanako");
student[1].age = 12;
// 表示
print_student(student[0]);
print_student(student[1]);
return 0;
}
簡単なプログラムではありますが、ここでは「構造化設計」を基にして
- 構造体(Student) で「データ」を整理
-
関数(print_student) で「操作」を整理
と分けて設計しています。
main関数の処理ではデータが2件なので、「順次」で処理しています。
実行結果は以下です。
1:Taro (11)
2:Hanako (12)
オブジェクト指向へ
同じプログラムをC++で書くと以下のように書けます。
#include <iostream>
#include <string>
using namespace std;
// 学生クラス
class Student {
private:
// メンバ変数(データ管理)
int id;
string name;
int age;
public:
// コンストラクタ(初期処理)
Student(int i, string n, int a) {
id = i;
name = n;
age = a;
}
// メンバ関数
void print_student() {
cout << id << ":" << name << " (" << age << ")" << endl;
}
};
int main(int argc, char *argv[]) {
// 学生オブジェクトを作成
Student s1(1, "Taro", 11);
Student s2(2, "Hanako", 12);
// メンバ関数で表示
s1.print_student();
s2.print_student();
return 0;
}
実行結果は前のプログラムと同じです。
これを見ていただくとデータと操作をクラスにまとめているということが分かると思います。さらにmainの処理では順次処理しているのは同じです。これでオブジェクト指向の理解というか拒否反応が少なくなればうれしいです。構造化設計での開発が大規模開発で問題が出てきたからオブジェクト指向にシフトしたということです。
オブジェクト指向の次へ
しかしながら今現在ではオブジェクト指向が絶対的なパラダイムではないです。オブジェクト指向も含めて マルチパラダイムの時代です。 ということで私もこれに追いついていく必要があると自覚しています。
まとめ
- 構造化プログラミング = コードを書くときの制御構造の基本
- 構造化設計 = プログラム全体を整理する設計の方法
- 名前は似ているけれど、扱うレベルが違う
- 現場では両方まとめて「構造化」と呼ばれることもあった
※この記事は初学者向けに書いていますので、厳密さよりも「大きな流れを理解する」ことを優先しています。細かい用語や歴史的背景については専門書をご参照ください。
ありがとうございます。