OOP への道:if と別れる50の方法《08》The Java™ Tutorials の事例から

記事一覧if 篇for 篇配列 篇

Python.use(better) # OOP への道 《Python3.1, Jython2.5.0, IronPython2.6.x》
The Java™ Tutorials の事例から
#3: 動的スキーマの適用

《著》真樹育未・後藤いるか・小粒ちゃん《監修》小泉ひよ子とタマゴ倶楽部
第0版♪1988/10/12 ● 第1版♪1993/05/23 ● 第2版♪2003/05/25

■ 概要

if/switch への未練を断ち切るのは容易ではありません。

伝統的なC言語風の for 文や悪名高い switch 文、配列の呪縛から解かれ、オブジェクト指向プログラミング〔OOP〕の醍醐味を堪能するための準備を行います。

The switch Statement

》作業中です《
The Java™ Tutorials では、次の《事例》を紹介しています。

class SwitchDemo2 {
    public static void main(String[] args) {

        int month = 2;
        int year = 2000;
        int numDays = 0;

        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                numDays = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                numDays = 30;
                break;
            case 2:
                if ((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0) )
                    numDays = 29;
                else
                    numDays = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }
        System.out.println("Number of Days = " + numDays);
    }
}

閏年を判定して、その月の日数が得られます。


最初の事例「16進表記」が

  • 指折り数えて何番目になるか

に相当して、次の事例「5段階評価」が

  • 指で寸法を測るといくつになるか

に相当するなら、今回の事例「閏年」は

  • 手探りでどの箱に入っているかを探る

に相当します。

def example():
    ## ----------------------------------------
    print("#", "-"*20, "SwitchDemo2")

    def isLeap(year):
        return (year % 4 == 0) and not(year % 100 == 0) or (year % 400 == 0)

    months = [
        set([1,3,5,7,8,10,12]),
        set([4,6,9,11]),
        set([2]),
        ]
    days = [
        31,
        30,
        28,         # (@.@) Keep your eyes!
        ]
    demo = TemplateDemo(
        months,
        days,
        -1,
        lambda s, e: s in e,
        )
    for e in range(1900, 2001, 10):
        demo.values[2] = 28 + isLeap(e)         # (@.@) Keep your eyes!
        s = []
        for month in range(1, 13):
            s.append(demo.index(month))
        print(e, ":", s, sum(s))

ここでは、実行時に閏年を判定して、インスタンス属性 .values を再設定しています。

《Note》コンパイル時に解決できる問題と、実行時にしか解決できない問題とは、異なる戦略が求められます。本格的な動的スキーマの適用に関する話題は、中級篇で紹介します。初級篇では、動的な問題解決の扉を開く話題に止め、解説を続けます。

これを実行すると、次のような結果が得られます。

>>> example()
# -------------------- SwitchDemo2
1900 : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 365
1910 : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 365
1920 : [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 366
1930 : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 365
1940 : [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 366
1950 : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 365
1960 : [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 366
1970 : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 365
1980 : [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 366
1990 : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 365
2000 : [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 366

何が問題か

switch 文の問題点はいくつも指摘されていますが、ここでは OOP を実践する立場から論議します。そのひとつが、動的な問題解決です。switch 文が有効なのは、コンパイル時にすべての条件が確定する状況です。

SwitchDemo2 の事例には当てはまりませんが、一般には仕様変更に伴って、新たな case を追加する必要が生じます。対象となる switch 文が散在していると、変更の度にそれらをすべて改訂することになります。これは、コードを汚染する典型的な要因のひとつで、Y2K問題に象徴されるメンテナンスの悪夢という病巣を抱えます。また、switch 文を含むモジュールに手を加えて、再コンパイルする必要があります。すると、いつまでたってもそのモジュールを閉じることができず、開放閉鎖原則にも反することから望ましい状況とは言えません。

これらの問題を解決するひとつの有効な戦略に、動的スキーマを適用するという戦略が考えられます。つまり(先付けで)すべての問題解決をコンパイル時に行うのではなく(後付けで)実行時に行うのです。これは、静的な問題解決を基調とする switch 文では適えられない夢のひとつです。

受講者への課題:英字をもとに判定する場合について考えます。

Tips

》作業中です《

Last updated♪2009/07/27