OOP への道:if と別れる50の方法《08》The Java™ Tutorials の事例から
Python.use(better) # OOP への道 《Python3.1, Jython2.5.0, IronPython2.6.x》
The Java™ Tutorials の事例から
#3: 動的スキーマの適用
■ 概要
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
》作業中です《