【モジュール分割】図解で分かるジャクソン法

こんばんは。

 

今回は、応用情報H28秋午後

問8「情報システム」で出てきたジャクソン法について勉強したことをまとめていきます。

 

問題は「モジュール分割」についてのお話でした。

 

いろいろ調べていて、モジュール分割を意識するのはとても大事だと感じました。

 

さて、標題のジャクソン法の説明の前に以下について触れておきたいと思います。

  • なぜモジュール分割を行うのか
  • モジュール分割手法って何があるの
  • モジュール分割でよく聞く関数型プログラミングってなに

上記に触れた後、ジャクソン法についてまとめたいと思います。

なぜモジュール分割を行うのか

ソフトウェア設計を考える際、機能をいくつかの単位に分割します。

 

一般的には以下のように階層的に分割されます。

「システム」=>「サブシステム」=>「プログラム」=>「モジュール」

 

ここで重要なことは、大きな粒度 → 小さな粒度 へと問題を落とし込んでいくことです。

これは以下のようなメリットがあるためです。

  • 作業の分担化
  • 問題の複雑さを減らす
  • コードの再利用化
  • 拡張性や修正が行いやすい

モジュール分割手法って何があるの

以下の2種類の考え方がります。

  • データの流れで分割
  • データの構造で分割

データの流れで分割

データの入力(Source)、変換(Transformation)、出力(Sink:吸収)の3つに分割する手法。

これらの分割したモジュールを制御モジュールによって操作します。

  • TR分割

データの種類とその処理内容に応じて分割する方式。

  • 共通機能分割

共通して行われる処理をモジュールとして切り出す方式。

データの構造で分割

  • ジャクソン分割

入出力データの構造からプログラムの構造を決定していく方式。

  • ワーニエ分割

入力データの構造を分析してプログラムの構造を決定していく方式。

モジュール分割でよく聞く関数型プログラミングってなに

まず、プログラムの書き方には(たぶん)2種類存在します。

以下にそれぞれの特徴をあげますが、

にわかなので間違っているかもしれないです。m(_ _)m

以下の例では、どちらもRubyで記述しており、

商品合計を表す関数を実装しています。

命令(手続き)型プログラミング

# 商品リスト
item_list = [
    {
        name: "hogehoge",
        price: 100
    },
    {
        name: "hugahuga",
        price: 200
    }
]

# 商品合計計算関数
def price_counter(item_list)
    
    sum_price = 0   # 商品合計
    
    item_list.each do |item|
        sum_price += item[:price]
    end
    
    return sum_price
end

p price_counter item_list

 

上記コードでは、商品合計をsum_priceといった状態に格納しています。

 

ループ処理によって sum_priceが上書きされていきます。

 

命令型プログラミングとは,要するに

プログラムの状態を表す変数を、代入命令で変更していく

といったニュアンスです。

関数型プログラミング

# 商品リスト
item_list = [
    {
        name: "hogehoge",
        price: 100
    },
    {
        name: "hugahuga",
        price: 200
    }
]

# 商品合計計算関数
def price_counter(item_list=[])
    if item_list.length == 0
        return 0
    else
        return item_list.pop[:price] + price_counter(item_list)
    end
end

p price_counter item_list

 

一方こちらは、商品の合計金額がいくらかといった状態は現れません。

 

このように変数の初期化や、代入といった命令が存在しません。

 

関数型プログラミングとは、要するに

プログラムの状態を変化させない関数」を作成し、

これらを組み合わせて問題解決を行う手法です。

 

ちなみに、命令型プログラミングのように、プログラムの状態が変化することを副作用と言います。

また、副作用を持たない関数を純粋関数と呼びます。

 

前置きが長くなりました。

要するにここで言いたいこととしては、モジュール分割には

状態を変化させない関数型プログラミングとの相性が良いということです。

 

具体的には、関数型プログラミング高階関数やら遅延評価なんかがモジュール化に良い感じなようです。

 

この辺は本題ではないので参考リンクだけ以下に掲載しておきます。

 

 

さて、ここからが本題です。(いつも本題に入る手前が長い🙇‍♂️)

ジャクソン法

まず、ジャクソン法の目的は「モジュール分割」です。

 

しかし、本来の目的とは

 

プログラミングが簡単になる構造を見つけ出すこと

 

です。

 

そうです。結果的にモジュール分割に応用できるみたなことです。(たぶん笑)

 

具体的には以下のような特徴があります。

  • 入出力データ構造でプログラム分割
  • 基本,連接,選択,反復の4つの木構造でデータを表現

2つめの4つの木構造とは、具体的に以下です。

f:id:k12si:20190414015619p:plain

引用元:システム開発の方法

 

これだけ読んでもわからないと思うので、

ここからは実際にプログラミングの問題をジャクソン法に則って考えていきます。

 

作成するプログラムは以下の表示です。(以下ピラミッドと呼びます。)

f:id:k12si:20190414011231p:plain

 

ジャクソン法でプログラムの詳細設計を行うには以下の手順で考えます。

  1. 入力データ構造を、木構造で表現
  2. 出力データの構造を、木構造で表現
  3. 入力出力データ構造を元に、プログラム構造を設計

入力データ構造を、木構造で表現

まずピラミッドを作成する際、入力データのイメージを以下に示します。

f:id:k12si:20190414013840p:plain

空白や*の連続を部分という構造として捉えているのが重要です。

これを、データ構造として捉えて、木構造で表現したのが以下です。

 

f:id:k12si:20190414015420p:plain

 

出力データの構造を、木構造で表現

続いて出力データについても、入力データ同様に

データ構造に着目しながら、基本、連接、選択、繰り返しの4つの木構造で表現していきます。

 

といっても、今回の問題では、入力がそのまま出力になります。

f:id:k12si:20190414021137p:plain

入出力データ構造を元に、プログラム構造を設計

最後に、

前述で作成した「入力データ構造」と「出力データ構造」の対応関係を知り、

両者を付き合わせたデータ構造を作成します。(入力データ構造が主)

 

作成したデータ構造は以下になります。

 

f:id:k12si:20190414022510p:plain

 

この段階の構造図で、各階層の処理部分には「前処理」と「後処理」を記述するのが常のようです。

 

今回の問題では必要ないため省略しています。

 

さてさて、これでコードにすっきりと落とし込むことができます。

 

以下が上記のデータ構造をコード化したものになります。

 

/* 1段分のピラミッドを作成 */
for ( i=0; i < (行数); i++ ) {
    空白を(行数 - i)表示    /* 空白部分1 */
    *文字を(2i - 1)表示    /* *部分 */
    空白を(行数 - i)表示    /* 空白部分2 */
改行を表示 }

 

最後に参考にしたリンクを掲載しておきます。

まとめ

本日はジャクソン法の考え方についてまとめました。

 

入力データと出力データのデータ構造に着目し、

4種(基本,連接,選択,繰り返し)の木構造を用いて、

プログラミングを簡単にしていく手法。

 

参考リンクやいろいろ調べるともっと奥が深そうでした。

 

とりあえず、今回の学びから、いかにモジュール分割が大事かということが理解できた。

もっと複雑な問題にも、こういった考え方やアプローチから論理的に解決に導けそうだと感じました🙌